[
  {
    "path": ".cargo/config.toml",
    "content": "[alias]\nxtask = \"run --quiet --package xtask --\"\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\nCargo.lock linguist-generated=false\n*.png filter=lfs diff=lfs merge=lfs -text\n*.gif filter=lfs diff=lfs merge=lfs -text\n\n# Exclude some small files from LFS:\ncrates/eframe/data/* !filter !diff !merge text=auto eol=lf\ncrates/egui_demo_lib/data/* !filter !diff !merge text=auto eol=lf\ncrates/egui/assets/* !filter !diff !merge text=auto eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n<!--\nFirst look if there is already a similar bug report. If there is, upvote the issue with 👍\n\nPlease also check if the bug is still present in latest main! Do so by adding the following lines to your Cargo.toml:\n\n\n[patch.crates-io]\negui = { git = \"https://github.com/emilk/egui\", branch = \"main\" }\n# if you're using eframe:\neframe = { git = \"https://github.com/emilk/egui\", branch = \"main\" }\n-->\n\n**Describe the bug**\n<!-- A clear and concise description of what the bug is. An image is good, a gif or movie is better! -->\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. <!-- Go to '…' -->\n2. <!-- Click on '…' -->\n3. <!-- Scroll down to '…' -->\n4. <!-- See error -->\n\n**Expected behavior**\n<!-- A clear and concise description of what you expected to happen. -->\n\n**Screenshots**\n<!-- If applicable, add screenshots to help explain your problem. -->\n\n**Desktop (please complete the following information):**\n - OS: <!-- e.g. iOS -->\n - Browser <!-- e.g. chrome, safari -->\n - Version <!-- e.g. 22 -->\n\n**Smartphone (please complete the following information):**\n - Device: <!-- e.g. iPhone6 -->\n - OS: <!-- e.g. iOS8.1 -->\n - Browser <!-- e.g. stock browser, safari -->\n - Version <!-- e.g. 22 -->\n\n**Additional context**\n<!-- Add any other context about the problem here. -->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: feature-request\nassignees: ''\n\n---\n\n<!--\nFirst look if there is already a similar feature request. If there is, upvote the issue with 👍\n-->\n\n\n**Is your feature request related to a problem? Please describe.**\n<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when […] -->\n\n**Describe the solution you'd like**\n<!-- A clear and concise description of what you want to happen. -->\n\n**Describe alternatives you've considered**\n<!-- A clear and concise description of any alternative solutions or features you've considered. -->\n\n**Additional context**\n<!-- Add any other context or screenshots about the feature request here. -->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/other.md",
    "content": "---\nname: Other\nabout: For issues that are neither bugs or feature requests\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\nIf you are asking a question, use [the egui discussions forum](https://github.com/emilk/egui/discussions/categories/q-a) instead!\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "<!--\nPlease read the \"Making a PR\" section of [`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/main/CONTRIBUTING.md) before opening a Pull Request!\n\n* Keep your PR:s small and focused.\n* The PR title is what ends up in the changelog, so make it descriptive!\n* If applicable, add a screenshot or gif.\n* If it is a non-trivial addition, consider adding a demo for it to `egui_demo_lib`, or a new example.\n* Do NOT open PR:s from your `master` branch, as that makes it hard for maintainers to test and add commits to your PR.\n* Remember to run `cargo fmt` and `cargo clippy`.\n* Open the PR as a draft until you have self-reviewed it and run `./scripts/check.sh`.\n* When you have addressed a PR comment, mark it as resolved.\n\nPlease be patient! I will review your PR, but my time is limited!\n-->\n\n* Closes <https://github.com/emilk/egui/issues/THE_RELEVANT_ISSUE>\n* [ ] I have followed the instructions in the PR template\n"
  },
  {
    "path": ".github/workflows/cargo_machete.yml",
    "content": "name: Cargo Machete\n\non: [push, pull_request]\n\njobs:\n  cargo-machete:\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n    steps:\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          toolchain: 1.92\n      - name: Machete install\n        ## The official cargo-machete action\n        uses: bnjbvr/cargo-machete@v0.9.1\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Machete Check\n        run: cargo machete\n"
  },
  {
    "path": ".github/workflows/deploy_web_demo.yml",
    "content": "name: Deploy web demo\n\non:\n  # We only run this on merges to main\n  push:\n    branches: [\"main\"]\n  # Allows you to run this workflow manually from the Actions tab\n  workflow_dispatch:\n# to only run when you do a new github release, comment out above part and uncomment the below trigger.\n# on:\n#   release:\n#     types: [\"published\"]\n\npermissions:\n  contents: write # for committing to gh-pages branch\n\n# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.\n# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.\nconcurrency:\n  group: \"pages\"\n  cancel-in-progress: false\n\nenv:\n  RUSTFLAGS: -D warnings\n  RUSTDOCFLAGS: -D warnings\n\njobs:\n  # Single deploy job since we're just deploying\n  deploy:\n    name: Deploy web demo\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          target: wasm32-unknown-unknown\n          toolchain: 1.92.0\n          override: true\n\n      - uses: Swatinem/rust-cache@v2\n        with:\n          prefix-key: \"web-demo-\"\n\n      - name: Install wasm-opt\n        uses: sigoden/install-binary@v1\n        with:\n          repo: WebAssembly/binaryen\n          tag: version_123\n          name: wasm-opt\n\n      - run: |\n          scripts/build_demo_web.sh --release\n\n      - name: Deploy\n        uses: JamesIves/github-pages-deploy-action@v4\n        with:\n          folder: web_demo\n          # this option will not maintain any history of your previous pages deployment\n          # set to false if you want all page build to be committed to your gh-pages branch history\n          single-commit: true\n"
  },
  {
    "path": ".github/workflows/enforce_branch_name.yml",
    "content": "name: PR Branch Name Check\n\non:\n  pull_request_target:\n    types: [opened, reopened, synchronize]\n\npermissions:\n  issues: write\n\njobs:\n  check-source-branch:\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    steps:\n      - name: Check PR source branch\n        env:\n          IS_FORK: ${{ github.event.pull_request.head.repo.fork }}\n          HEAD_REF: ${{ github.event.pull_request.head.ref }}\n        run: |\n          # Check if PR is from a fork\n          if [[ \"$IS_FORK\" == \"true\" ]]; then\n            # Check if PR is from the master/main branch of a fork\n            if [[ \"$HEAD_REF\" == \"master\" || \"$HEAD_REF\" == \"main\" ]]; then\n              echo \"ERROR: Pull requests from the master/main branch of forks are not allowed, because it prevents maintainers from contributing to your PR\"\n              echo \"Please create a feature branch in your fork and submit the PR from that branch instead.\"\n              exit 1\n            fi\n          fi\n\n      - name: Leave comment if PR is from master/main branch of fork d\n        if: ${{ failure() }}\n        uses: actions/github-script@v6\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          script: |\n            github.rest.issues.createComment({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              body: '⚠️ **ERROR:** Pull requests from the `master`/`main` branch of forks are not allowed, because it prevents maintainers from contributing to your PR. Please create a feature branch in your fork and submit the PR from that branch instead.'\n            })\n"
  },
  {
    "path": ".github/workflows/labels.yml",
    "content": "# https://github.com/marketplace/actions/require-labels\n# Check for existence of labels\n# See all our labels at https://github.com/rerun-io/rerun/issues/labels\n\nname: Pull Request Labels\n\non:\n  pull_request:\n    types:\n      - opened\n      - synchronize\n      - reopened\n      - labeled\n      - unlabeled\n\njobs:\n  label:\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    steps:\n      - name: Check for a \"do-not-merge\" label\n        uses: mheap/github-action-required-labels@v3\n        with:\n          mode: exactly\n          count: 0\n          labels: \"do-not-merge\"\n\n      - name: Require at least one label\n        uses: mheap/github-action-required-labels@v3\n        with:\n          mode: minimum\n          count: 1\n          labels: \"CI, dependencies, docs and examples, ecolor, eframe, egui_extras, egui_glow, egui_kittest, egui-wgpu, egui-winit, egui, emath, epaint, epaint_default_fonts, exclude from changelog, typo\"\n"
  },
  {
    "path": ".github/workflows/link_checker.yml",
    "content": "name: Link checker\n# on: [pull_request]  # Disabled because it is so broken\non: workflow_dispatch\n\njobs:\n  lychee:\n    name: lychee\n    runs-on: ubuntu-latest\n    timeout-minutes: 30\n    steps:\n      - uses: actions/checkout@v4\n      - name: Don't check CHANGELOG.md files\n        # This is really stupid but lychee doesn't have a way of excluding files via GLOB:\n        # https://github.com/lycheeverse/lychee/issues/1608\n\n        # We need to exclude CHANGELOG.md since we don't want to have a CI failure everytime some contributor decides\n        # to change their username.\n        run: rm -r  */**/CHANGELOG.md CHANGELOG.md\n      - name: Link Checker\n        uses: lycheeverse/lychee-action@v2\n        with:\n          args: \"'**/*.md' '**/*.toml' --exclude localhost --exclude reddit.com\" # I guess reddit doesn't like github action IPs\n"
  },
  {
    "path": ".github/workflows/png_only_on_lfs.yml",
    "content": "name: All; .png on git LFS\n\non: [push, pull_request]\n\njobs:\n  check-binary-files:\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v2\n\n    - name: Check that png files are on git LFS\n      run: |\n        binary_extensions=\"png\"\n        exclude_paths=(\n          \"crates/eframe/data\"\n          \"crates/egui_demo_lib/data/\"\n          \"crates/egui/assets/\"\n        )\n\n        # Find binary files that are not tracked by Git LFS\n        for ext in $binary_extensions; do\n          # Create grep pattern to exclude multiple paths\n          exclude_pattern=$(printf \"|^%s\" \"${exclude_paths[@]}\" | sed 's/^|//')\n\n          if comm -23 <(git ls-files | grep -Ev \"$exclude_pattern\" | sort) <(git lfs ls-files -n | sort) | grep \"\\.${ext}$\"; then\n            echo \"Error: Found binary file with extension .$ext not tracked by git LFS. See https://github.com/emilk/egui/blob/main/CONTRIBUTING.md#working-with-git-lfs\"\n            exit 1\n          fi\n        done\n"
  },
  {
    "path": ".github/workflows/preview_build.yml",
    "content": "# This action builds and deploys egui_demo_app on each pull request created\n# Security notes:\n# The preview deployment is split in two workflows, preview_build and preview_deploy.\n# `preview_build` runs on pull_request, so it won't have any access to the repositories secrets, so it is safe to\n# build / execute untrusted code.\n# `preview_deploy` has access to the repositories secrets (so it can push to the pr preview repo) but won't run\n# any untrusted code (it will just extract the build artifact and push it to the pages branch where it will\n# automatically be deployed).\n\nname: Preview Build\n\non:\n  - pull_request\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - uses: actions/checkout@v4\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          toolchain: 1.92.0\n          targets: wasm32-unknown-unknown\n      - uses: Swatinem/rust-cache@v2\n        with:\n          prefix-key: \"pr-preview-\"\n\n      - name: Install wasm-opt\n        uses: sigoden/install-binary@v1\n        with:\n          repo: WebAssembly/binaryen\n          tag: version_123\n          name: wasm-opt\n\n      - run: |\n          scripts/build_demo_web.sh --release\n\n      - name: Remove gitignore file\n        # We need to remove the .gitignore, otherwise the deploy via git will not include the js and wasm files\n        run: |\n          rm -rf web_demo/.gitignore\n\n      - uses: actions/upload-artifact@v4\n        with:\n          name: web_demo\n          path: web_demo\n\n      - name: Generate meta.json\n        env:\n          PR_NUMBER: ${{ github.event.number }}\n          URL_SLUG: ${{ github.event.number }}-${{ github.head_ref }}\n        run: |\n          # Sanitize the URL_SLUG to only contain alphanumeric characters and dashes\n          URL_SLUG=$(echo $URL_SLUG | tr -cd '[:alnum:]-')\n          echo \"{\\\"pr_number\\\": \\\"$PR_NUMBER\\\", \\\"url_slug\\\": \\\"$URL_SLUG\\\"}\" > meta.json\n\n      - uses: actions/upload-artifact@v4\n        with:\n          name: meta.json\n          path: meta.json\n"
  },
  {
    "path": ".github/workflows/preview_cleanup.yml",
    "content": "name: Preview Cleanup\n\npermissions:\n  contents: write\n\non:\n  pull_request_target:\n    types:\n      - closed\n\njobs:\n  cleanup:\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n      - run: mkdir -p empty_dir\n      - name: Generate URL_SLUG\n        env:\n          PR_NUMBER: ${{ github.event.pull_request.number }}\n          URL_SLUG: ${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.ref }}\n        run: |\n          # Sanitize the URL_SLUG to only contain alphanumeric characters and dashes\n          URL_SLUG=$(echo $URL_SLUG | tr -cd '[:alnum:]-')\n          echo \"URL_SLUG=$URL_SLUG\" >> $GITHUB_ENV\n      - name: Deploy\n        uses: JamesIves/github-pages-deploy-action@v4\n        with:\n          folder: empty_dir\n          repository-name: egui-pr-preview/pr\n          branch: 'main'\n          clean: true\n          target-folder: ${{ env.URL_SLUG }}\n          ssh-key: ${{ secrets.DEPLOY_KEY }}\n          commit-message: \"Remove preview for PR ${{ env.URL_SLUG }}\"\n          single-commit: true\n"
  },
  {
    "path": ".github/workflows/preview_comment.yml",
    "content": "name: preview_comment.yml\non:\n  pull_request_target:\n    types: [opened]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Comment PR\n        uses: thollander/actions-comment-pull-request@v2\n        env:\n          URL_SLUG: ${{ github.event.number }}-${{ github.head_ref }}\n        with:\n          message: |\n            Preview is being built...\n            \n            Preview will be available at https://egui-pr-preview.github.io/pr/${{ env.URL_SLUG }}\n            \n            View snapshot changes at [kitdiff](https://rerun-io.github.io/kitdiff/?url=${{ github.event.pull_request.html_url }})\n          comment_tag: 'egui-preview'\n\n"
  },
  {
    "path": ".github/workflows/preview_deploy.yml",
    "content": "name: Preview Deploy\n\npermissions:\n  contents: write\n  pull-requests: write\n\non:\n  workflow_run:\n    workflows:\n      - \"Preview Build\"\n    types:\n      - completed\n\n# Since we use single_commit and force on the deploy action, only one deploy action can run at a time.\n# Should this create a bottleneck we might have to set single_commit and force to false which should allow\n# for the deployments to run in parallel.\nconcurrency:\n  group: preview_deploy\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n      - name: 'Download build artifact'\n        uses: actions/download-artifact@v4\n        with:\n          name: web_demo\n          path: web_demo_artifact\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          run-id: ${{ github.event.workflow_run.id }}\n      - name: 'Download build meta'\n        uses: actions/download-artifact@v4\n        with:\n          name: meta.json\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          run-id: ${{ github.event.workflow_run.id }}\n\n      - name: Parse meta.json\n        run: |\n          echo \"PR_NUMBER=$(jq -r .pr_number meta.json)\" >> $GITHUB_ENV\n          echo \"URL_SLUG=$(jq -r .url_slug meta.json)\" >> $GITHUB_ENV\n\n      - name: Deploy\n        uses: JamesIves/github-pages-deploy-action@v4\n        with:\n          folder: web_demo_artifact\n          repository-name: egui-pr-preview/pr\n          branch: 'main'\n          clean: true\n          target-folder: ${{ env.URL_SLUG }}\n          ssh-key: ${{ secrets.DEPLOY_KEY }}\n          commit-message: \"Update preview for PR ${{ env.URL_SLUG }}\"\n          single-commit: true\n\n      - name: Comment PR\n        uses: thollander/actions-comment-pull-request@v2\n        with:\n          message: |\n            Preview available at https://egui-pr-preview.github.io/pr/${{ env.URL_SLUG }}\n            Note that it might take a couple seconds for the update to show up after the preview_build workflow has completed.\n            \n            View snapshot changes at [kitdiff](https://rerun-io.github.io/kitdiff/?url=https://github.com/emilk/egui/pull/${{ env.PR_NUMBER }})\n          pr_number: ${{ env.PR_NUMBER }}\n          comment_tag: 'egui-preview'\n"
  },
  {
    "path": ".github/workflows/rust.yml",
    "content": "on: [push, pull_request]\n\nname: Rust\n\nenv:\n  RUSTFLAGS: -D warnings\n  RUSTDOCFLAGS: -D warnings\n  NIGHTLY_VERSION: nightly-2025-09-16\n\njobs:\n  fmt-crank-check-test:\n    name: Format + check\n    runs-on: ubuntu-22.04\n    timeout-minutes: 60\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          lfs: true\n\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          toolchain: 1.92.0\n\n      - name: Install packages (Linux)\n        if: runner.os == 'Linux'\n        uses: awalsh128/cache-apt-pkgs-action@v1.4.3\n        with:\n          packages: libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev libgtk-3-dev # libgtk-3-dev is used by rfd\n          version: 1.0\n          execute_install_scripts: true\n\n      - name: Set up cargo cache\n        uses: Swatinem/rust-cache@v2\n\n      - name: Rustfmt\n        run: cargo fmt --all -- --check\n\n      - name: Lint vertical spacing\n        run: ./scripts/lint.py\n\n      - run: cargo clippy --locked --all-features --all-targets\n\n      - run: cargo clippy --locked --all-features -p egui_extras\n\n      - run: cargo clippy --locked --all-targets\n\n      - run: cargo clippy --locked --no-default-features --lib --all-targets\n\n      - run: cargo clippy --locked --no-default-features --lib -p eframe --features x11\n\n      - run: cargo clippy --locked --no-default-features --lib -p eframe --features x11,wgpu_no_default_features\n\n      - run: cargo clippy --locked --no-default-features --lib -p egui_extras\n\n      - run: cargo clippy --locked --no-default-features --lib -p epaint\n\n      # Regression test for https://github.com/emilk/egui/issues/4771\n      - run: cargo clippy -p test_egui_extras_compilation\n\n      - run: cargo doc --lib --no-deps --all-features\n\n      - run: cargo doc --document-private-items --no-deps --all-features\n\n      - name: clippy release\n        run: cargo clippy --all-targets --all-features --release -- -D warnings\n\n  # ---------------------------------------------------------------------------\n\n  check_wasm:\n    name: Check wasm32 + wasm-bindgen\n    runs-on: ubuntu-22.04\n    timeout-minutes: 60\n    steps:\n      - uses: actions/checkout@v4\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          toolchain: 1.92.0\n          targets: wasm32-unknown-unknown\n\n      - run: sudo apt-get update && sudo apt-get install libgtk-3-dev libatk1.0-dev\n\n      - name: Set up cargo cache\n        uses: Swatinem/rust-cache@v2\n\n      - name: clippy wasm32 egui_demo_app\n        run: cargo clippy -p egui_demo_app --lib --target wasm32-unknown-unknown\n\n      - name: clippy wasm32 egui_demo_app --all-features\n        run: cargo clippy -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features\n\n      - name: clippy wasm32 eframe\n        run: cargo clippy -p eframe --lib --no-default-features --features wgpu,persistence --target wasm32-unknown-unknown\n\n      - name: wasm-bindgen\n        uses: jetli/wasm-bindgen-action@v0.1.0\n        with:\n          version: \"0.2.100\" # Keep wasm-bindgen version in sync in: setup_web.sh, Cargo.toml, Cargo.lock, rust.yml\n\n      - run: ./scripts/wasm_bindgen_check.sh --skip-setup\n\n      - name: clippy wasm32\n        run: ./scripts/clippy_wasm.sh\n\n  # requires a different toolchain from the other checks (nightly)\n  check_wasm_atomics:\n    name: Check wasm32+atomics\n    runs-on: ubuntu-22.04\n    timeout-minutes: 60\n    steps:\n      - uses: actions/checkout@v4\n      - run: sudo apt-get update && sudo apt-get install libgtk-3-dev libatk1.0-dev\n\n      - name: Set up cargo cache\n        uses: Swatinem/rust-cache@v2\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          toolchain: ${{env.NIGHTLY_VERSION}}\n          targets: wasm32-unknown-unknown\n          components: rust-src\n\n      - name: Check wasm32+atomics eframe with wgpu\n        run: RUSTFLAGS='-C target-feature=+atomics' cargo +${{env.NIGHTLY_VERSION}} check -p eframe --lib --no-default-features --features wgpu,wgpu/webgpu --target wasm32-unknown-unknown -Z build-std=std,panic_abort\n\n  # ---------------------------------------------------------------------------\n\n  cargo-deny:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - target: aarch64-apple-darwin\n          - target: aarch64-linux-android\n          - target: i686-pc-windows-gnu\n          - target: i686-pc-windows-msvc\n          - target: i686-unknown-linux-gnu\n          - target: wasm32-unknown-unknown\n          - target: x86_64-apple-darwin\n          - target: x86_64-pc-windows-gnu\n          - target: x86_64-pc-windows-msvc\n          - target: x86_64-unknown-linux-gnu\n          - target: x86_64-unknown-linux-musl\n          - target: x86_64-unknown-redox\n\n    name: cargo-deny ${{ matrix.target }}\n    runs-on: ubuntu-22.04\n    timeout-minutes: 10\n    steps:\n      - uses: actions/checkout@v4\n      - uses: EmbarkStudios/cargo-deny-action@v2\n        with:\n          rust-version: \"1.92.0\"\n          log-level: error\n          command: check\n          arguments: --target  ${{ matrix.target }}\n\n  # ---------------------------------------------------------------------------\n\n  android:\n    name: android\n    runs-on: ubuntu-22.04\n    timeout-minutes: 60\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          toolchain: 1.92.0\n          targets: aarch64-linux-android\n\n      - name: Set up cargo cache\n        uses: Swatinem/rust-cache@v2\n\n        # Default features disabled to turn off accesskit, which does not work\n        # with NativeActivity.\n      - run: cargo check --features wgpu,android-native-activity --target aarch64-linux-android --no-default-features\n        working-directory: crates/eframe\n\n  # ---------------------------------------------------------------------------\n\n  ios:\n    name: ios\n    runs-on: ubuntu-22.04\n    timeout-minutes: 60\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          toolchain: 1.92.0\n          targets: aarch64-apple-ios\n\n      - name: Set up cargo cache\n        uses: Swatinem/rust-cache@v2\n\n        # Default features are disabled because glutin doesn't compile for ios.\n      - run: cargo check --features wgpu --target aarch64-apple-ios --no-default-features\n        working-directory: crates/eframe\n\n  # ---------------------------------------------------------------------------\n\n  windows:\n    name: Check Windows\n    runs-on: windows-latest\n    timeout-minutes: 60\n    steps:\n      - uses: actions/checkout@v4\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          toolchain: 1.92.0\n\n      - name: Set up cargo cache\n        uses: Swatinem/rust-cache@v2\n\n      - run: cargo clippy --all-targets --all-features\n\n      - run: cargo clippy -p hello_world\n\n  # ---------------------------------------------------------------------------\n\n  tests:\n    name: Run tests\n    # We run the tests on macOS because it will run with an actual GPU\n    runs-on: macos-latest\n    timeout-minutes: 60\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          lfs: true\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          toolchain: 1.92.0\n\n      - name: Set up cargo cache\n        uses: Swatinem/rust-cache@v2\n\n      - name: Run tests\n        run: cargo test --all-features\n\n      - name: Run doc-tests\n        run: cargo test --all-features --doc\n\n      - name: Upload artifacts\n        uses: actions/upload-artifact@v4\n        if: always()\n        with:\n          name: test-results\n          path: \"**/tests/snapshots\"\n"
  },
  {
    "path": ".github/workflows/typos.yml",
    "content": "name: Typos\non: [pull_request]\n\njobs:\n  typos:\n    # https://github.com/crate-ci/typos\n    # Add exceptions to .typos.toml\n    # install and run locally: cargo install typos-cli && typos\n    name: typos\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    steps:\n      - name: Checkout Actions Repository\n        uses: actions/checkout@v4\n\n      - name: Check spelling of entire workspace\n        uses: crate-ci/typos@v1.38.0\n"
  },
  {
    "path": ".github/workflows/update_kittest_snapshots.yml",
    "content": "on:\n  workflow_dispatch:\n    inputs:\n      run_id:\n        description: 'The run ID that produced the artifact'\n        required: true\n        type: string\n\npermissions:\n  actions: read\n\nname: Update kittest snapshots\njobs:\n  update-snapshots:\n    name: Update snapshots from artifact\n    runs-on: ubuntu-latest\n    if: github.ref_name != 'main' # We never want to update snapshots directly on main\n    permissions:\n      contents: write\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          lfs: true\n          # We can't use the workflow token since that would prevent our commit to cause further workflows.\n          # See https://github.com/stefanzweifel/git-auto-commit-action#commits-made-by-this-action-do-not-trigger-new-workflow-runs\n          # This token should be a personal access token with at least Read and write permission to `Contents`.\n          # The commit action below will use the token this the code was checked out with.\n          token: '${{ secrets.SNAPSHOT_COMMIT_GITHUB_TOKEN }}'\n\n      - name: Accept snapshots\n        env:\n          GH_TOKEN: ${{ github.token }}\n          RUN_ID: ${{ github.event.inputs.run_id }}\n        run: ./scripts/update_snapshots_from_ci.sh\n\n      - name: Git status\n        run: git status\n\n      - uses: stefanzweifel/git-auto-commit-action@v6\n        with:\n          commit_message: 'Update snapshot images'\n\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n**/target\n**/target_ra\n**/target_wasm\n**/tests/snapshots/**/*.diff.png\n**/tests/snapshots/**/*.new.png\n**/tests/snapshots/**/*.old.png\n/.*.json\n/.vscode\n/media/*\n.idea/\n"
  },
  {
    "path": ".typos.toml",
    "content": "# https://github.com/crate-ci/typos\n# install:  cargo install typos-cli\n# run:      typos\n\n[default.extend-words]\nime = \"ime\" # Input Method Editor\nnknown = \"nknown\" # part of @55nknown username\nisse = \"isse\" # part of @IsseW username\ntye = \"tye\" # part of @tye-exe username\nro = \"ro\" # read-only, also part of the username @Phen-Ro\ntyp = \"typ\" # Often used because `type` is a keyword in Rust\nwdth = \"wdth\" # The `wdth` tag is used in variable fonts\n\n# I mistype these so often\ntesalator = \"tessellator\"\nteselator = \"tessellator\"\ntessalator = \"tessellator\"\ntesselator = \"tessellator\"\ntesalation = \"tessellation\"\nteselation = \"tessellation\"\ntessalation = \"tessellation\"\ntesselation = \"tessellation\"\n\n# For consistency\npostfix = \"suffix\"\n\n# Use the more common spelling\nadaptor = \"adapter\"\nadaptors = \"adapters\"\n\n# For consistency we prefer American English:\naeroplane = \"airplane\"\nanalogue = \"analog\"\nanalyse = \"analyze\"\nappetiser = \"appetizer\"\narbour = \"arbor\"\nardour = \"arbor\"\narmour = \"armor\"\nartefact = \"artifact\"\nauthorise = \"authorize\"\nbehaviour = \"behavior\"\nbehavioural = \"behavioral\"\nBritish = \"American\"\ncalibre = \"caliber\"\n# cancelled = \"canceled\" # winit uses this :(\ncandour = \"candor\"\ncapitalise = \"capitalize\"\ncatalogue = \"catalog\"\ncentre = \"center\"\ncharacterise = \"characterize\"\nchequerboard = \"checkerboard\"\nchequered = \"checkered\"\ncivilise = \"civilize\"\nclamour = \"clamor\"\ncolonise = \"colonize\"\ncolour = \"color\"\ncoloured = \"colored\"\ncosy = \"cozy\"\ncriticise = \"criticize\"\ndefence = \"defense\"\ndemeanour = \"demeanor\"\ndialogue = \"dialog\"\ndistil = \"distill\"\ndoughnut = \"donut\"\ndramatise = \"dramatize\"\ndraught = \"draft\"\nemphasise = \"emphasize\"\nendeavour = \"endeavor\"\nenrol = \"enroll\"\nepilogue = \"epilog\"\nequalise = \"equalize\"\nfavour = \"favor\"\nfavourite = \"favorite\"\nfibre = \"fiber\"\nflavour = \"flavor\"\nfulfil = \"fufill\"\ngaol = \"jail\"\ngrey = \"gray\"\ngreys = \"grays\"\ngreyscale = \"grayscale\"\nharbour = \"habor\"\nhonour = \"honor\"\nhumour = \"humor\"\ninstalment = \"installment\"\ninstil = \"instill\"\njewellery = \"jewelry\"\nkerb = \"curb\"\nlabour = \"labor\"\nlitre = \"liter\"\nlustre = \"luster\"\nmeagre = \"meager\"\nmetre = \"meter\"\nmobilise = \"mobilize\"\nmonologue = \"monolog\"\nnaturalise = \"naturalize\"\nneighbour = \"neighbor\"\nneighbourhood = \"neighborhood\"\nnormalise = \"normalize\"\nnormalised = \"normalized\"\nodour = \"odor\"\noffence = \"offense\"\norganise = \"organize\"\nparlour = \"parlor\"\nplough = \"plow\"\npopularise = \"popularize\"\npretence = \"pretense\"\nprogramme = \"program\"\nprologue = \"prolog\"\nrancour = \"rancor\"\nrealise = \"realize\"\nrecognise = \"recognize\"\nrecognised = \"recognized\"\nrigour = \"rigor\"\nrumour = \"rumor\"\nsabre = \"saber\"\nsatirise = \"satirize\"\nsaviour = \"savior\"\nsavour = \"savor\"\nsceptical = \"skeptical\"\nsceptre = \"scepter\"\nsepulchre = \"sepulcher\"\nserialisation = \"serialization\"\nserialise = \"serialize\"\nserialised = \"serialized\"\nskilful = \"skillful\"\nsombre = \"somber\"\nspecialisation = \"specialization\"\nspecialise = \"specialize\"\nspecialised = \"specialized\"\nsplendour = \"splendor\"\nstandardise = \"standardize\"\nsulphur = \"sulfur\"\nsymbolise = \"symbolize\"\ntheatre = \"theater\"\ntonne = \"ton\"\ntravelogue = \"travelog\"\ntumour = \"tumor\"\nvalour = \"valor\"\nvaporise = \"vaporize\"\nvigour = \"vigor\"\n\n# null-terminated is the name of the wikipedia article!\n# https://en.wikipedia.org/wiki/Null-terminated_string\nnullterminated = \"null-terminated\"\nzeroterminated = \"null-terminated\"\nzero-terminated = \"null-terminated\"\n\n\n[files]\nextend-exclude = [\"web_demo/egui_demo_app.js\"] # auto-generated\n\n[default]\nextend-ignore-re = [\n  \"#\\\\[doc\\\\(alias = .*\", # We suggest \"grey\" in some doc\n]\n"
  },
  {
    "path": "ARCHITECTURE.md",
    "content": "# Architecture\nThis document describes how the crates that make up egui are all connected.\n\nAlso see [`CONTRIBUTING.md`](CONTRIBUTING.md) for what to do before opening a PR.\n\n\n## Crate overview\nThe crates in this repository are: `egui, emath, epaint, epaint_default_fonts, egui_extras, egui-winit, egui_glow, egui-wgpu, egui_demo_lib, egui_demo_app`.\n\n### `egui`: The main GUI library.\nExample code: `if ui.button(\"Click me\").clicked() { … }`\nThis is the crate where the bulk of the code is at. `egui` depends only on `emath` and `epaint`.\n\n### `emath`: minimal 2D math library\nExamples: `Vec2, Pos2, Rect, lerp, remap`\n\n### `epaint`\n2d shapes and text that can be turned into textured triangles.\n\nExample: `Shape::Circle { center, radius, fill, stroke }`\n\nDepends on `emath`. Also depends on `epaint_default_fonts` when the `default_fonts` feature is enabled.\n\n### `epaint_default_fonts`\nEmbedded fonts (using `include_bytes!()`) for use by `epaint` in selecting defaults.\n\nSince the font files themselves are licensed differently from the `epaint` source code, this simplifies licenses for callers who disable the default fonts.\n\n### `egui_extras`\nThis adds additional features on top of `egui`.\n\n### `egui-winit`\nThis crates provides bindings between [`egui`](https://github.com/emilk/egui) and [winit](https://crates.io/crates/winit).\n\nThe library translates winit events to egui, handled copy/paste, updates the cursor, open links clicked in egui, etc.\n\n### `egui_glow`\nPuts an egui app inside a native window on your laptop. Paints the triangles that egui outputs using [glow](https://github.com/grovesNL/glow).\n\n### `egui-wgpu`\nPaints the triangles that egui outputs using [wgpu](https://github.com/grovesNL/wgpu).\n\n### `eframe`\n`eframe` is the official `egui` framework, built so you can compile the same app for either web or native.\n\nThe demo that you can see at <https://www.egui.rs> is using `eframe` to host the `egui`. The demo code is found in:\n\n### `egui_demo_lib`\nDepends on `egui`.\nThis contains a bunch of uses of `egui` and looks like the ui code you would write for an `egui` app.\n\n### `egui_demo_app`\nThin wrapper around `egui_demo_lib` so we can compile it to a web site or a native app executable.\nDepends on `egui_demo_lib` + `eframe`.\n\n### `egui_kittest`\nA test harness for egui based on [kittest](https://github.com/rerun-io/kittest) and [AccessKit](https://github.com/AccessKit/accesskit/).\n\n### Other integrations\n\nThere are also many great integrations for game engines such as `bevy` and `miniquad` which you can find at <https://github.com/emilk/egui#integrations>.\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# egui changelog\nAll notable changes to the `egui` crate will be documented in this file.\n\nThis is just the changelog for the core `egui` crate. Every crate in this repository has their own changelog:\n* [`epaint` changelog](crates/epaint/CHANGELOG.md)\n* [`egui-winit` changelog](crates/egui-winit/CHANGELOG.md)\n* [`egui-wgpu` changelog](crates/egui-wgpu/CHANGELOG.md)\n* [`egui_kittest` changelog](crates/egui_kittest/CHANGELOG.md)\n* [`egui_glow` changelog](crates/egui_glow/CHANGELOG.md)\n* [`ecolor` changelog](crates/ecolor/CHANGELOG.md)\n* [`eframe` changelog](crates/eframe/CHANGELOG.md)\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n## 0.33.3 - 2025-12-11\n* Treat `.` as a word-splitter in text navigation [#7741](https://github.com/emilk/egui/pull/7741) by [@emilk](https://github.com/emilk)\n* Change text color of selected text [#7691](https://github.com/emilk/egui/pull/7691) by [@emilk](https://github.com/emilk)\n\n\n## 0.33.2 - 2025-11-13\n### ⭐ Added\n* Add `Plugin::on_widget_under_pointer` to support widget inspector [#7652](https://github.com/emilk/egui/pull/7652) by [@juancampa](https://github.com/juancampa)\n* Add `Response::total_drag_delta` and `PointerState::total_drag_delta` [#7708](https://github.com/emilk/egui/pull/7708) by [@emilk](https://github.com/emilk)\n\n### 🔧 Changed\n* Improve accessibility and testability of `ComboBox` [#7658](https://github.com/emilk/egui/pull/7658) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🐛 Fixed\n* Fix `profiling::scope` compile error when profiling using `tracing` backend [#7646](https://github.com/emilk/egui/pull/7646) by [@PPakalns](https://github.com/PPakalns)\n* Fix edge cases in \"smart aiming\" in sliders [#7680](https://github.com/emilk/egui/pull/7680) by [@emilk](https://github.com/emilk)\n* Hide scroll bars when dragging other things [#7689](https://github.com/emilk/egui/pull/7689) by [@emilk](https://github.com/emilk)\n* Prevent widgets sometimes appearing to move relative to each other [#7710](https://github.com/emilk/egui/pull/7710) by [@emilk](https://github.com/emilk)\n* Fix `ui.response().interact(Sense::click())` being flakey [#7713](https://github.com/emilk/egui/pull/7713) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n\n## 0.33.0 - 2025-10-09 - `egui::Plugin`, better kerning, kitdiff viewer\nHighlights from this release:\n- `egui::Plugin` a improved way to create and access egui plugins\n- [kitdiff](https://github.com/rerun-io/kitdiff), a viewer for egui_kittest image snapshots (and a general image diff tool)\n- better kerning\n\n\n### Improved kerning\nAs a step towards using [parley](https://github.com/linebender/parley) for font rendering, @valadaptive has refactored the font loading and rendering code. A result of this (next to the font rendering code being much nicer now) is improved kerning.\nNotice how the c moved away from the k:\n\n![Oct-09-2025 16-21-58](https://github.com/user-attachments/assets/d4a17e87-5e98-40db-a85a-fa77fa77aceb)\n\n\n### `egui::Plugin` trait\nWe've added a new trait-based plugin api, meant to replace `Context::on_begin_pass` and `Context::on_end_pass`.\nThis makes it a lot easier to handle state in your plugins. Instead of having to write to egui memory it can live right on your plugin struct.\nThe trait based api also makes easier to add new hooks that plugins can use. In addition to `on_begin_pass` and `on_end_pass`, the `Plugin` trait now has a `input_hook` and `output_hook` which you can use to inspect / modify the `RawInput` / `FullOutput`.\n\n### kitdiff, a image diff viewer\nAt rerun we have a ton of snapshots. Some PRs will change most of them (e.g. [the](https://github.com/rerun-io/rerun/pull/11253/files) [one](https://rerun-io.github.io/kitdiff/?url=https://github.com/rerun-io/rerun/pull/11253/files) that updated egui and introduced the kerning improvements, ~500 snapshots changed!).\nIf you really want to look at every changed snapshot it better be as efficient as possible, and the experience on github, fiddeling with the sliders, is kind of frustrating.\nIn order to fix this, we've made [kitdiff](https://rerun-io.github.io/kitdiff/).\nYou can use it locally via\n- `kitdiff files .` will search for .new.png and .diff.png files\n- `kitdiff git` will compare the current files to the default branch (main/master)\n  Or in the browser via\n- going to https://rerun-io.github.io/kitdiff/ and pasting a PR or github artifact url\n- linking to kitdiff via e.g. a github workflow `https://rerun-io.github.io/kitdiff/?url=<link_to_pr_or_artifact>`\n\nTo install kitdiff run `cargo install --git https://github.com/rerun-io/kitdiff`\n\nHere is a video showing the kerning changes in kitdiff ([try it yourself](https://rerun-io.github.io/kitdiff/?url=https://github.com/rerun-io/rerun/pull/11253/files)):\n\nhttps://github.com/user-attachments/assets/74640af1-09ba-435a-9d0c-2cbeee140c8f\n\n###  Migration guide\n- `egui::Mutex` now has a timeout as a simple deadlock detection\n    - If you use a `egui::Mutex` in some place where it's held for longer than a single frame, you should switch to the std mutex or parking_lot instead (egui mutexes are wrappers around parking lot)\n- `screen_rect` is deprecated\n    - In order to support safe areas, egui now has `viewport_rect` and `content_rect`.\n    - Update all usages of `screen_rect` to `content_rect`, unless you are sure that you want to draw outside the `safe area` (which would mean your Ui may be covered by notches, system ui, etc.)\n\n\n### ⭐ Added\n* New Plugin trait [#7385](https://github.com/emilk/egui/pull/7385) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `Ui::take_available_space()` helper function, which sets the Ui's minimum size to the available space [#7573](https://github.com/emilk/egui/pull/7573) by [@IsseW](https://github.com/IsseW)\n* Add support for the safe area on iOS [#7578](https://github.com/emilk/egui/pull/7578) by [@irh](https://github.com/irh)\n* Add `UiBuilder::global_scope` and `UiBuilder::id` [#7372](https://github.com/emilk/egui/pull/7372) by [@Icekey](https://github.com/Icekey)\n* Add `emath::fast_midpoint` [#7435](https://github.com/emilk/egui/pull/7435) by [@emilk](https://github.com/emilk)\n* Make the `hex_color` macro `const` [#7444](https://github.com/emilk/egui/pull/7444) by [@YgorSouza](https://github.com/YgorSouza)\n* Add `SurrenderFocusOn` option [#7471](https://github.com/emilk/egui/pull/7471) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `Memory::move_focus` [#7476](https://github.com/emilk/egui/pull/7476) by [@darkwater](https://github.com/darkwater)\n* Support on hover tooltip that is noninteractable even with interactable content [#5543](https://github.com/emilk/egui/pull/5543) by [@PPakalns](https://github.com/PPakalns)\n* Add rotation gesture support for trackpad sources [#7453](https://github.com/emilk/egui/pull/7453) by [@thatcomputerguy0101](https://github.com/thatcomputerguy0101)\n\n### 🔧 Changed\n* Document platform compatibility on `viewport::WindowLevel` and dependents [#7432](https://github.com/emilk/egui/pull/7432) by [@lkdm](https://github.com/lkdm)\n* Deprecated `ImageButton` and removed `WidgetType::ImageButton` [#7483](https://github.com/emilk/egui/pull/7483) by [@Stelios-Kourlis](https://github.com/Stelios-Kourlis)\n* More even text kerning [#7431](https://github.com/emilk/egui/pull/7431) by [@valadaptive](https://github.com/valadaptive)\n* Increase default text size from 12.5 to 13.0 [#7521](https://github.com/emilk/egui/pull/7521) by [@emilk](https://github.com/emilk)\n* Update accesskit to 0.21.0 [#7550](https://github.com/emilk/egui/pull/7550) by [@fundon](https://github.com/fundon)\n* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)\n* Group AccessKit nodes by `Ui` [#7386](https://github.com/emilk/egui/pull/7386) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🔥 Removed\n* Remove the `deadlock_detection` feature [#7497](https://github.com/emilk/egui/pull/7497) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Remove deprecated fields from `PlatformOutput` [#7523](https://github.com/emilk/egui/pull/7523) by [@emilk](https://github.com/emilk)\n* Remove `log` feature [#7583](https://github.com/emilk/egui/pull/7583) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Enable `clippy::iter_over_hash_type` lint [#7421](https://github.com/emilk/egui/pull/7421) by [@emilk](https://github.com/emilk)\n* Fixes sense issues in TextEdit when vertical alignment is used [#7436](https://github.com/emilk/egui/pull/7436) by [@RndUsr123](https://github.com/RndUsr123)\n* Fix stuck menu when submenu vanishes [#7589](https://github.com/emilk/egui/pull/7589) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Change Spinner widget to account for width as well as height [#7560](https://github.com/emilk/egui/pull/7560) by [@bryceberger](https://github.com/bryceberger)\n\n\n## 0.32.3 - 2025-09-12\n* Preserve text format in truncated label tooltip [#7514](https://github.com/emilk/egui/pull/7514) [#7535](https://github.com/emilk/egui/pull/7535) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix `TextEdit`'s in RTL layouts [#5547](https://github.com/emilk/egui/pull/5547) by [@zakarumych](https://github.com/zakarumych)\n\n\n## 0.32.2 - 2025-09-04\n* Fix: `SubMenu` should not display when ui is disabled [#7428](https://github.com/emilk/egui/pull/7428) by [@ozwaldorf](https://github.com/ozwaldorf)\n* Remove line breaks when pasting into single line TextEdit [#7441](https://github.com/emilk/egui/pull/7441) by [@YgorSouza](https://github.com/YgorSouza)\n* Panic mutexes that can't lock for 30 seconds, in debug builds [#7468](https://github.com/emilk/egui/pull/7468) by [@emilk](https://github.com/emilk)\n* Add `Ui::place`, to place widgets without changing the cursor [#7359](https://github.com/emilk/egui/pull/7359) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix: prevent calendar popup from closing on dropdown change [#7409](https://github.com/emilk/egui/pull/7409) by [@AStrizh](https://github.com/AStrizh)\n\n\n## 0.32.1 - 2025-08-15 - Misc bug fixes\n### ⭐ Added\n* Add `ComboBox::popup_style` [#7360](https://github.com/emilk/egui/pull/7360) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🐛 Fixed\n* Fix glyph rendering: clamp coverage to [0, 1] [#7415](https://github.com/emilk/egui/pull/7415) by [@emilk](https://github.com/emilk)\n* Fix manual `Popup` not closing [#7383](https://github.com/emilk/egui/pull/7383) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix `WidgetText::Text` ignoring fallback font and overrides [#7361](https://github.com/emilk/egui/pull/7361) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix `override_text_color` priority [#7439](https://github.com/emilk/egui/pull/7439) by [@YgorSouza](https://github.com/YgorSouza)\n* Fix debug-panic in ScrollArea if contents fit without scrolling [#7440](https://github.com/emilk/egui/pull/7440) by [@YgorSouza](https://github.com/YgorSouza)\n\n\n## 0.32.0 - 2025-07-10 - Atoms, popups, and better SVG support\nThis is a big egui release, with several exciting new features!\n\n* _Atoms_ are new layout primitives in egui, for text and images\n* Popups, tooltips and menus have undergone a complete rewrite\n* Much improved SVG support\n* Crisper graphics (especially text!)\n\nLet's dive in!\n\n### ⚛️ Atoms\n\n`egui::Atom` is the new, indivisible building blocks of egui (hence their name).\nAn `Atom` is an `enum` that can be either `WidgetText`, `Image`, or `Custom`.\n\nThe new `AtomLayout` can be used within widgets to do basic layout.\nThe initial implementation is as minimal as possible, doing just enough to implement what `Button` could do before.\nThere is a new `IntoAtoms` trait that works with tuples of `Atom`s. Each atom can be customized with the `AtomExt` trait\nwhich works on everything that implements `Into<Atom>`, so e.g. `RichText` or `Image`.\nSo to create a `Button` with text and image you can now do:\n```rs\nlet image = include_image!(\"my_icon.png\").atom_size(Vec2::splat(12.0));\nui.button((image, \"Click me!\"));\n```\n\nAnywhere you see `impl IntoAtoms` you can add any number of images and text, in any order.\n\nAs of 0.32, we have ported the `Button`, `Checkbox`, `RadioButton` to use atoms\n(meaning they support adding Atoms and are built on top of `AtomLayout`).\nThe `Button` implementation is not only more powerful now, but also much simpler, removing ~130 lines of layout math.\n\nIn combination with `ui.read_response`, custom widgets are really simple now, here is a minimal button implementation:\n\n```rs\npub struct ALButton<'a> {\n    al: AtomLayout<'a>,\n}\n\nimpl<'a> ALButton<'a> {\n    pub fn new(content: impl IntoAtoms<'a>) -> Self {\n        Self {\n            al: AtomLayout::new(content.into_atoms()).sense(Sense::click()),\n        }\n    }\n}\n\nimpl<'a> Widget for ALButton<'a> {\n    fn ui(mut self, ui: &mut Ui) -> Response {\n        let Self { al } = self;\n        let response = ui.ctx().read_response(ui.next_auto_id());\n\n        let visuals = response.map_or(&ui.style().visuals.widgets.inactive, |response| {\n            ui.style().interact(&response)\n        });\n\n        let al = al.frame(\n            Frame::new()\n                .inner_margin(ui.style().spacing.button_padding)\n                .fill(visuals.bg_fill)\n                .stroke(visuals.bg_stroke)\n                .corner_radius(visuals.corner_radius),\n        );\n\n        al.show(ui).response\n    }\n}\n```\n\nYou can even use `Atom::custom` to add custom content to Widgets. Here is a button in a button:\n\nhttps://github.com/user-attachments/assets/8c649784-dcc5-4979-85f8-e735b9cdd090\n\n```rs\nlet custom_button_id = Id::new(\"custom_button\");\nlet response = Button::new((\n    Atom::custom(custom_button_id, Vec2::splat(18.0)),\n    \"Look at my mini button!\",\n))\n.atom_ui(ui);\nif let Some(rect) = response.rect(custom_button_id) {\n    ui.put(rect, Button::new(\"🔎\").frame_when_inactive(false));\n}\n```\nCurrently, you need to use `atom_ui` to get a `AtomResponse` which will have the `Rect` to use, but in the future\nthis could be streamlined, e.g. by adding a `AtomKind::Callback` or by passing the Rects back with `egui::Response`.\n\nBasing our widgets on `AtomLayout` also allowed us to improve `Response::intrinsic_size`, which will now report the\ncorrect size even if widgets are truncated. `intrinsic_size` is the size that a non-wrapped, non-truncated,\nnon-justified version of the widget would have, and can be useful in advanced layout\ncalculations like [egui_flex](https://github.com/lucasmerlin/hello_egui/tree/main/crates/egui_flex).\n\n##### Details\n* Add `AtomLayout`, abstracting layouting within widgets [#5830](https://github.com/emilk/egui/pull/5830) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `Galley::intrinsic_size` and use it in `AtomLayout` [#7146](https://github.com/emilk/egui/pull/7146) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n\n### ❕ Improved popups, tooltips, and menus\n\nIntroduces a new `egui::Popup` api. Checkout the new demo on https://egui.rs:\n\nhttps://github.com/user-attachments/assets/74e45243-7d05-4fc3-b446-2387e1412c05\n\nWe introduced a new `RectAlign` helper to align a rect relative to an other rect. The `Popup` will by default try to find the best `RectAlign` based on the source widgets position (previously submenus would annoyingly overlap if at the edge of the window):\n\nhttps://github.com/user-attachments/assets/0c5adb6b-8310-4e0a-b936-646bb4ec02f7\n\n`Tooltip` and `menu` have been rewritten based on the new `Popup` api. They are now compatible with each other, meaning you can just show a `ui.menu_button()` in any `Popup` to get a sub menu. There are now customizable `MenuButton` and `SubMenuButton` structs, to help with customizing your menu buttons. This means menus now also support `PopupCloseBehavior` so you can remove your `close_menu` calls from your click handlers!\n\nThe old tooltip and popup apis have been ported to the new api so there should be very little breaking changes. The old menu is still around but deprecated. `ui.menu_button` etc now open the new menu, if you can't update to the new one immediately you can use the old buttons from the deprecated `egui::menu` menu.\n\nWe also introduced `ui.close()` which closes the nearest container. So you can now conveniently close `Window`s, `Collapsible`s, `Modal`s and `Popup`s from within. To use this for your own containers, call `UiBuilder::closable` and then check for closing within that ui via `ui.should_close()`.\n\n##### Details\n* Add `Popup` and `Tooltip`, unifying the previous behaviours [#5713](https://github.com/emilk/egui/pull/5713) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `Ui::close` and `Response::should_close` [#5729](https://github.com/emilk/egui/pull/5729) by [@lucasmerlin](https://github.com/lucasmerlin)\n* ⚠️ Improved menu based on `egui::Popup` [#5716](https://github.com/emilk/egui/pull/5716) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add a toggle for the compact menu style [#5777](https://github.com/emilk/egui/pull/5777) by [@s-nie](https://github.com/s-nie)\n* Use the new `Popup` API for the color picker button [#7137](https://github.com/emilk/egui/pull/7137) by [@lucasmerlin](https://github.com/lucasmerlin)\n* ⚠️ Close popup if `Memory::keep_popup_open` isn't called [#5814](https://github.com/emilk/egui/pull/5814) by [@juancampa](https://github.com/juancampa)\n* Fix tooltips sometimes changing position each frame [#7304](https://github.com/emilk/egui/pull/7304) by [@emilk](https://github.com/emilk)\n* Change popup memory to be per-viewport [#6753](https://github.com/emilk/egui/pull/6753) by [@mkalte666](https://github.com/mkalte666)\n* Deprecate `Memory::popup` API in favor of new `Popup` API [#7317](https://github.com/emilk/egui/pull/7317) by [@emilk](https://github.com/emilk)\n\n\n### ▲ Improved SVG support\nYou can render SVG in egui with\n\n```rs\nui.add(egui::Image::new(egui::include_image!(\"icon.svg\"));\n```\n\n(Requires the use of `egui_extras`, with the `svg` feature enabled and a call to [`install_image_loaders`](https://docs.rs/egui_extras/latest/egui_extras/fn.install_image_loaders.html)).\n\nPreviously this would sometimes result in a blurry SVG, epecially if the `Image` was set to be dynamically scale based on the size of the `Ui` that contained it. Now SVG:s are always pixel-perfect, for truly scalable graphics.\n\n![svg-scaling](https://github.com/user-attachments/assets/faf63f0c-0ff7-47a0-a4cb-7210efeccb72)\n\n##### Details\n* Support text in SVGs [#5979](https://github.com/emilk/egui/pull/5979) by [@cernec1999](https://github.com/cernec1999)\n* Fix sometimes blurry SVGs [#7071](https://github.com/emilk/egui/pull/7071) by [@emilk](https://github.com/emilk)\n* Fix incorrect color fringe colors on SVG:s [#7069](https://github.com/emilk/egui/pull/7069) by [@emilk](https://github.com/emilk)\n* Make `Image::paint_at` pixel-perfect crisp for SVG images [#7078](https://github.com/emilk/egui/pull/7078) by [@emilk](https://github.com/emilk)\n\n\n### ✨ Crisper graphics\nNon-SVG icons are also rendered better, and text sharpness has been improved, especially in light mode.\n\n![image](https://github.com/user-attachments/assets/7f370aaf-886a-423c-8391-c378849b63ca)\n\n##### Details\n* Improve text sharpness [#5838](https://github.com/emilk/egui/pull/5838) by [@emilk](https://github.com/emilk)\n* Improve text rendering in light mode [#7290](https://github.com/emilk/egui/pull/7290) by [@emilk](https://github.com/emilk)\n* Improve texture filtering by doing it in gamma space [#7311](https://github.com/emilk/egui/pull/7311) by [@emilk](https://github.com/emilk)\n* Make text underline and strikethrough pixel perfect crisp [#5857](https://github.com/emilk/egui/pull/5857) by [@emilk](https://github.com/emilk)\n\n### Migration guide\nWe have some silently breaking changes (code compiles fine but behavior changed) that require special care:\n\n#### Menus close on click by default\n- previously menus would only close on click outside\n- either\n    - remove the `ui.close_menu()` calls from button click handlers since they are obsolete\n    - if the menu should stay open on clicks, change the `PopupCloseBehavior`:\n      ```rs\n          // Change this\n        ui.menu_button(\"Text\", |ui| { /* Menu Content */ });\n          // To this:\n        MenuButton::new(\"Text\").config(\n            MenuConfig::default().close_behavior(PopupCloseBehavior::CloseOnClickOutside),\n        ).ui(ui, |ui| { /* Menu Content */ });\n        ```\n      You can also change the behavior only for a single SubMenu by using `SubMenuButton`, but by default it should be passed to any submenus when using `MenuButton`.\n\n#### `Memory::is_popup_open` api now requires calls to `Memory::keep_popup_open`\n- The popup will immediately close if `keep_popup_open` is not called.\n- It's recommended to use the new `Popup` api which handles this for you.\n- If you can't switch to the new api for some reason, update the code to call `keep_popup_open`:\n  ```rs\n      if ui.memory(|mem| mem.is_popup_open(popup_id)) {\n        ui.memory_mut(|mem| mem.keep_popup_open(popup_id)); // <- add this line\n        let area_response = Area::new(popup_id).show(...)\n      }\n  ```\n\n### ⭐ Other improvements\n* Add `Label::show_tooltip_when_elided` [#5710](https://github.com/emilk/egui/pull/5710) by [@bryceberger](https://github.com/bryceberger)\n* Deprecate `Ui::allocate_new_ui` in favor of `Ui::scope_builder` [#5764](https://github.com/emilk/egui/pull/5764) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `expand_bg` to customize size of text background [#5365](https://github.com/emilk/egui/pull/5365) by [@MeGaGiGaGon](https://github.com/MeGaGiGaGon)\n* Add assert messages and print bad argument values in asserts [#5216](https://github.com/emilk/egui/pull/5216) by [@bircni](https://github.com/bircni)\n* Use `TextBuffer` for `layouter` in `TextEdit` instead of `&str` [#5712](https://github.com/emilk/egui/pull/5712) by [@kernelkind](https://github.com/kernelkind)\n* Add a `Slider::update_while_editing(bool)` API [#5978](https://github.com/emilk/egui/pull/5978) by [@mbernat](https://github.com/mbernat)\n* Add `Scene::drag_pan_buttons` option. Allows specifying which pointer buttons pan the scene by dragging [#5892](https://github.com/emilk/egui/pull/5892) by [@mitchmindtree](https://github.com/mitchmindtree)\n* Add `Scene::sense` to customize how `Scene` responds to user input [#5893](https://github.com/emilk/egui/pull/5893) by [@mitchmindtree](https://github.com/mitchmindtree)\n* Rework `TextEdit` arrow navigation to handle Unicode graphemes [#5812](https://github.com/emilk/egui/pull/5812) by [@MStarha](https://github.com/MStarha)\n* `ScrollArea` improvements for user configurability [#5443](https://github.com/emilk/egui/pull/5443) by [@MStarha](https://github.com/MStarha)\n* Add `Response::clicked_with_open_in_background` [#7093](https://github.com/emilk/egui/pull/7093) by [@emilk](https://github.com/emilk)\n* Add `Modifiers::matches_any` [#7123](https://github.com/emilk/egui/pull/7123) by [@emilk](https://github.com/emilk)\n* Add `Context::format_modifiers` [#7125](https://github.com/emilk/egui/pull/7125) by [@emilk](https://github.com/emilk)\n* Add `OperatingSystem::is_mac` [#7122](https://github.com/emilk/egui/pull/7122) by [@emilk](https://github.com/emilk)\n* Support vertical-only scrolling by holding down Alt [#7124](https://github.com/emilk/egui/pull/7124) by [@emilk](https://github.com/emilk)\n* Support for back-button on Android [#7073](https://github.com/emilk/egui/pull/7073) by [@ardocrat](https://github.com/ardocrat)\n* Select all text in DragValue when gaining focus via keyboard [#7107](https://github.com/emilk/egui/pull/7107) by [@Azkellas](https://github.com/Azkellas)\n* Add `Context::current_pass_index` [#7276](https://github.com/emilk/egui/pull/7276) by [@emilk](https://github.com/emilk)\n* Add `Context::cumulative_frame_nr` [#7278](https://github.com/emilk/egui/pull/7278) by [@emilk](https://github.com/emilk)\n* Add `Visuals::text_edit_bg_color` [#7283](https://github.com/emilk/egui/pull/7283) by [@emilk](https://github.com/emilk)\n* Add `Visuals::weak_text_alpha` and `weak_text_color` [#7285](https://github.com/emilk/egui/pull/7285) by [@emilk](https://github.com/emilk)\n* Add support for scrolling via accesskit / kittest [#7286](https://github.com/emilk/egui/pull/7286) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Update area struct to allow force resizing [#7114](https://github.com/emilk/egui/pull/7114) by [@blackberryfloat](https://github.com/blackberryfloat)\n* Add `egui::Sides` `shrink_left` / `shrink_right` [#7295](https://github.com/emilk/egui/pull/7295) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Set intrinsic size for Label [#7328](https://github.com/emilk/egui/pull/7328) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🔧 Changed\n* Raise MSRV to 1.85 [#6848](https://github.com/emilk/egui/pull/6848) by [@torokati44](https://github.com/torokati44), [#7279](https://github.com/emilk/egui/pull/7279) by [@emilk](https://github.com/emilk)\n* Set `hint_text` in `WidgetInfo` [#5724](https://github.com/emilk/egui/pull/5724) by [@bircni](https://github.com/bircni)\n* Implement `Default` for `ThemePreference` [#5702](https://github.com/emilk/egui/pull/5702) by [@MichaelGrupp](https://github.com/MichaelGrupp)\n* Align `available_rect` docs with the new reality after #4590 [#5701](https://github.com/emilk/egui/pull/5701) by [@podusowski](https://github.com/podusowski)\n* Clarify platform-specific details for `Viewport` positioning [#5715](https://github.com/emilk/egui/pull/5715) by [@aspiringLich](https://github.com/aspiringLich)\n* Simplify the text cursor API [#5785](https://github.com/emilk/egui/pull/5785) by [@valadaptive](https://github.com/valadaptive)\n* Bump accesskit to 0.19 [#7040](https://github.com/emilk/egui/pull/7040) by [@valadaptive](https://github.com/valadaptive)\n* Better define the meaning of `SizeHint` [#7079](https://github.com/emilk/egui/pull/7079) by [@emilk](https://github.com/emilk)\n* Move all input-related options into `InputOptions` [#7121](https://github.com/emilk/egui/pull/7121) by [@emilk](https://github.com/emilk)\n* `Button` inherits the `alt_text` of the `Image` in it, if any [#7136](https://github.com/emilk/egui/pull/7136) by [@emilk](https://github.com/emilk)\n* Change API of `Tooltip` slightly [#7151](https://github.com/emilk/egui/pull/7151) by [@emilk](https://github.com/emilk)\n* Use Rust edition 2024 [#7280](https://github.com/emilk/egui/pull/7280) by [@emilk](https://github.com/emilk)\n* Change `ui.disable()` to modify opacity [#7282](https://github.com/emilk/egui/pull/7282) by [@emilk](https://github.com/emilk)\n* Make the font atlas use a color image [#7298](https://github.com/emilk/egui/pull/7298) by [@valadaptive](https://github.com/valadaptive)\n* Implement `BitOr` and `BitOrAssign` for `Rect` [#7319](https://github.com/emilk/egui/pull/7319) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🔥 Removed\n* Remove things that have been deprecated for over a year [#7099](https://github.com/emilk/egui/pull/7099) by [@emilk](https://github.com/emilk)\n* Remove `SelectableLabel` [#7277](https://github.com/emilk/egui/pull/7277) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🐛 Fixed\n* `Scene`: make `scene_rect` full size on reset [#5801](https://github.com/emilk/egui/pull/5801) by [@graydenshand](https://github.com/graydenshand)\n* `Scene`: `TextEdit` selection when placed in a `Scene` [#5791](https://github.com/emilk/egui/pull/5791) by [@karhu](https://github.com/karhu)\n* `Scene`: Set transform layer before calling user content [#5884](https://github.com/emilk/egui/pull/5884) by [@mitchmindtree](https://github.com/mitchmindtree)\n* Fix: transform `TextShape` underline width [#5865](https://github.com/emilk/egui/pull/5865) by [@emilk](https://github.com/emilk)\n* Fix missing repaint after `consume_key` [#7134](https://github.com/emilk/egui/pull/7134) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Update `emoji-icon-font` with fix for fullwidth latin characters [#7067](https://github.com/emilk/egui/pull/7067) by [@emilk](https://github.com/emilk)\n* Mark all keys as released if the app loses focus [#5743](https://github.com/emilk/egui/pull/5743) by [@emilk](https://github.com/emilk)\n* Fix scroll handle extending outside of `ScrollArea` [#5286](https://github.com/emilk/egui/pull/5286) by [@gilbertoalexsantos](https://github.com/gilbertoalexsantos)\n* Fix `Response::clicked_elsewhere` not returning `true` sometimes [#5798](https://github.com/emilk/egui/pull/5798) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix kinetic scrolling on touch devices [#5778](https://github.com/emilk/egui/pull/5778) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix `DragValue` expansion when editing [#5809](https://github.com/emilk/egui/pull/5809) by [@MStarha](https://github.com/MStarha)\n* Fix disabled `DragValue` eating focus, causing focus to reset [#5826](https://github.com/emilk/egui/pull/5826) by [@KonaeAkira](https://github.com/KonaeAkira)\n* Fix semi-transparent colors appearing too bright [#5824](https://github.com/emilk/egui/pull/5824) by [@emilk](https://github.com/emilk)\n* Improve drag-to-select text (add margins) [#5797](https://github.com/emilk/egui/pull/5797) by [@hankjordan](https://github.com/hankjordan)\n* Fix bug in pointer movement detection [#5329](https://github.com/emilk/egui/pull/5329) by [@rustbasic](https://github.com/rustbasic)\n* Protect against NaN in hit-test code [#6851](https://github.com/emilk/egui/pull/6851) by [@Skgland](https://github.com/Skgland)\n* Fix image button panicking with tiny `available_space` [#6900](https://github.com/emilk/egui/pull/6900) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix links and text selection in horizontal_wrapped layout [#6905](https://github.com/emilk/egui/pull/6905) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix `leading_space` sometimes being ignored during paragraph splitting [#7031](https://github.com/emilk/egui/pull/7031) by [@afishhh](https://github.com/afishhh)\n* Fix typo in deprecation message for `ComboBox::from_id_source` [#7055](https://github.com/emilk/egui/pull/7055) by [@aelmizeb](https://github.com/aelmizeb)\n* Bug fix: make sure `end_pass` is called for all loaders [#7072](https://github.com/emilk/egui/pull/7072) by [@emilk](https://github.com/emilk)\n* Report image alt text as text if widget contains no other text [#7142](https://github.com/emilk/egui/pull/7142) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Slider: move by at least the next increment when using fixed_decimals [#7066](https://github.com/emilk/egui/pull/7066) by [@0x53A](https://github.com/0x53A)\n* Fix crash when using infinite widgets [#7296](https://github.com/emilk/egui/pull/7296) by [@emilk](https://github.com/emilk)\n* Fix `debug_assert` triggered by `menu`/`intersect_ray` [#7299](https://github.com/emilk/egui/pull/7299) by [@emilk](https://github.com/emilk)\n* Change `Rect::area` to return zero for negative rectangles [#7305](https://github.com/emilk/egui/pull/7305) by [@emilk](https://github.com/emilk)\n\n### 🚀 Performance\n* Optimize editing long text by caching each paragraph [#5411](https://github.com/emilk/egui/pull/5411) by [@afishhh](https://github.com/afishhh)\n* Make `WidgetText` smaller and faster [#6903](https://github.com/emilk/egui/pull/6903) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n\n## 0.31.1 - 2025-03-05\n* Fix sizing bug in `TextEdit::singleline` [#5640](https://github.com/emilk/egui/pull/5640) by [@IaVashik](https://github.com/IaVashik)\n* Fix panic when rendering thin textured rectangles [#5692](https://github.com/emilk/egui/pull/5692) by [@PPakalns](https://github.com/PPakalns)\n\n\n## 0.31.0 - 2025-02-04 - Scene container, improved rendering quality\n\n### Highlights ✨\n\n#### Scene container\nThis release adds the `Scene` container to egui. It is a pannable, zoomable canvas that can contain `Widget`s and child `Ui`s.\nThis will make it easier to e.g. implement a graph editor.\n\n![scene](https://github.com/user-attachments/assets/7dc5e395-a3cb-4bf3-83a3-51a76a48c409)\n\n#### Clearer, pixel perfect rendering\nThe tessellator has been updated for improved rendering quality and better performance. It will produce fewer vertices\nand shapes will have less overdraw. We've also defined what `CornerRadius` (previously `Rounding`) means.\n\nWe've also added a tessellator test to the [demo app](https://www.egui.rs/), where you can play around with different\nvalues to see what's produced:\n\n\nhttps://github.com/user-attachments/assets/adf55e3b-fb48-4df0-aaa2-150ee3163684\n\n\nCheck the [PR](https://github.com/emilk/egui/pull/5669) for more details.\n\n#### `CornerRadius`, `Margin`, `Shadow` size reduction\nIn order to pave the path for more complex and customizable styling solutions, we've reduced the size of\n`CornerRadius`, `Margin` and `Shadow` values to `i8` and `u8`.\n\n\n\n### Migration guide\n- Add a `StrokeKind` to all your `Painter::rect` calls [#5648](https://github.com/emilk/egui/pull/5648)\n- `StrokeKind::default` was removed, since the 'normal' value depends on the context [#5658](https://github.com/emilk/egui/pull/5658)\n  - You probably want to use `StrokeKind::Inside` when drawing rectangles\n  - You probably want to use `StrokeKind::Middle` when drawing open paths\n- Rename `Rounding` to `CornerRadius` [#5673](https://github.com/emilk/egui/pull/5673)\n- `CornerRadius`, `Margin` and `Shadow` have been updated to use `i8` and `u8` [#5563](https://github.com/emilk/egui/pull/5563), [#5567](https://github.com/emilk/egui/pull/5567), [#5568](https://github.com/emilk/egui/pull/5568)\n  - Remove the .0 from your values\n  - Cast dynamic values with `as i8` / `as u8` or `as _` if you want Rust to infer the type\n    - Rust will do a 'saturating' cast, so if your `f32` value is bigger than `127` it will be clamped to `127`\n- `RectShape` parameters changed [#5565](https://github.com/emilk/egui/pull/5565)\n  - Prefer to use the builder methods to create it instead of initializing it directly\n- `Frame` now takes the `Stroke` width into account for its sizing, so check all views of your app to make sure they still look right.\n  Read the [PR](https://github.com/emilk/egui/pull/5575) for more info.\n\n### ⭐ Added\n* Add `egui::Scene` for panning/zooming a `Ui` [#5505](https://github.com/emilk/egui/pull/5505) by [@grtlr](https://github.com/grtlr)\n* Animated WebP support [#5470](https://github.com/emilk/egui/pull/5470) by [@Aely0](https://github.com/Aely0)\n* Improve tessellation quality [#5669](https://github.com/emilk/egui/pull/5669) by [@emilk](https://github.com/emilk)\n* Add `OutputCommand` for copying text and opening URL:s [#5532](https://github.com/emilk/egui/pull/5532) by [@emilk](https://github.com/emilk)\n* Add `Context::copy_image` [#5533](https://github.com/emilk/egui/pull/5533) by [@emilk](https://github.com/emilk)\n* Add `WidgetType::Image` and `Image::alt_text` [#5534](https://github.com/emilk/egui/pull/5534) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `epaint::Brush` for controlling `RectShape` texturing [#5565](https://github.com/emilk/egui/pull/5565) by [@emilk](https://github.com/emilk)\n* Implement `nohash_hasher::IsEnabled` for `Id` [#5628](https://github.com/emilk/egui/pull/5628) by [@emilk](https://github.com/emilk)\n* Add keys for `!`, `{`, `}` [#5548](https://github.com/emilk/egui/pull/5548) by [@Its-Just-Nans](https://github.com/Its-Just-Nans)\n* Add `RectShape::stroke_kind ` to control if stroke is inside/outside/centered [#5647](https://github.com/emilk/egui/pull/5647) by [@emilk](https://github.com/emilk)\n\n### 🔧 Changed\n* ⚠️ `Frame` now includes stroke width as part of padding [#5575](https://github.com/emilk/egui/pull/5575) by [@emilk](https://github.com/emilk)\n* Rename `Rounding` to `CornerRadius` [#5673](https://github.com/emilk/egui/pull/5673) by [@emilk](https://github.com/emilk)\n* Require a `StrokeKind` when painting rectangles with strokes [#5648](https://github.com/emilk/egui/pull/5648) by [@emilk](https://github.com/emilk)\n* Round widget coordinates to even multiple of 1/32 [#5517](https://github.com/emilk/egui/pull/5517) by [@emilk](https://github.com/emilk)\n* Make all lines and rectangles crisp [#5518](https://github.com/emilk/egui/pull/5518) by [@emilk](https://github.com/emilk)\n* Tweak window resize handles [#5524](https://github.com/emilk/egui/pull/5524) by [@emilk](https://github.com/emilk)\n\n### 🔥 Removed\n* Remove `egui::special_emojis::TWITTER` [#5622](https://github.com/emilk/egui/pull/5622) by [@emilk](https://github.com/emilk)\n* Remove `StrokeKind::default` [#5658](https://github.com/emilk/egui/pull/5658) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Use correct minimum version of `profiling` crate [#5494](https://github.com/emilk/egui/pull/5494) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix interactive widgets sometimes being incorrectly marked as hovered [#5523](https://github.com/emilk/egui/pull/5523) by [@emilk](https://github.com/emilk)\n* Fix panic due to non-total ordering in `Area::compare_order()` [#5569](https://github.com/emilk/egui/pull/5569) by [@HactarCE](https://github.com/HactarCE)\n* Fix hovering through custom menu button [#5555](https://github.com/emilk/egui/pull/5555) by [@M4tthewDE](https://github.com/M4tthewDE)\n\n### 🚀 Performance\n* Use `u8` in `CornerRadius`, and introduce `CornerRadiusF32` [#5563](https://github.com/emilk/egui/pull/5563) by [@emilk](https://github.com/emilk)\n* Store `Margin` using `i8` to reduce its size [#5567](https://github.com/emilk/egui/pull/5567) by [@emilk](https://github.com/emilk)\n* Shrink size of `Shadow` by using `i8/u8` instead of `f32` [#5568](https://github.com/emilk/egui/pull/5568) by [@emilk](https://github.com/emilk)\n* Avoid allocations for loader cache lookup [#5584](https://github.com/emilk/egui/pull/5584) by [@mineichen](https://github.com/mineichen)\n* Use bitfield instead of bools in `Response` and `Sense` [#5556](https://github.com/emilk/egui/pull/5556) by [@polwel](https://github.com/polwel)\n\n\n## 0.30.0 - 2024-12-16 - Modals and better layer support\n\n### ✨ Highlights\n* Add `Modal`, a popup that blocks input to the rest of the application ([#5358](https://github.com/emilk/egui/pull/5358) by [@lucasmerlin](https://github.com/lucasmerlin))\n* Improved support for transform layers ([#5465](https://github.com/emilk/egui/pull/5465), [#5468](https://github.com/emilk/egui/pull/5468), [#5429](https://github.com/emilk/egui/pull/5429))\n\n#### `egui_kittest`\nThis release welcomes a new crate to the family: [egui_kittest](https://github.com/emilk/egui/tree/main/crates/egui_kittest).\n`egui_kittest` is a testing framework for egui, allowing you to test both automation (simulated clicks and other events),\nand also do screenshot testing (useful for regression tests).\n`egui_kittest` is built using [`kittest`](https://github.com/rerun-io/kittest), which is a general GUI testing framework that aims to work with any Rust GUI (not just egui!).\n`kittest` uses the accessibility library [`AccessKit`](https://github.com/AccessKit/accesskit/) for automatation and to query the widget tree.\n\n`kittest` and `egui_kittest` are written by [@lucasmerlin](https://github.com/lucasmerlin).\n\nHere's a quick example of how to use `egui_kittest` to test a checkbox:\n\n```rust\nuse egui::accesskit::Toggled;\nuse egui_kittest::{Harness, kittest::Queryable};\n\nfn main() {\n    let mut checked = false;\n    let app = |ui: &mut egui::Ui| {\n        ui.checkbox(&mut checked, \"Check me!\");\n    };\n\n    let mut harness = egui_kittest::Harness::new_ui(app);\n\n    let checkbox = harness.get_by_label(\"Check me!\");\n    assert_eq!(checkbox.toggled(), Some(Toggled::False));\n    checkbox.click();\n\n    harness.run();\n\n    let checkbox = harness.get_by_label(\"Check me!\");\n    assert_eq!(checkbox.toggled(), Some(Toggled::True));\n\n    // You can even render the ui and do image snapshot tests\n    #[cfg(all(feature = \"wgpu\", feature = \"snapshot\"))]\n    harness.wgpu_snapshot(\"readme_example\");\n}\n```\n\n### ⭐ Added\n* Add `Modal` and `Memory::set_modal_layer` [#5358](https://github.com/emilk/egui/pull/5358) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `UiBuilder::layer_id` and remove `layer_id` from `Ui::new` [#5195](https://github.com/emilk/egui/pull/5195) by [@emilk](https://github.com/emilk)\n* Allow easier setting of background color for `TextEdit` [#5203](https://github.com/emilk/egui/pull/5203) by [@bircni](https://github.com/bircni)\n* Set `Response::intrinsic_size` for `TextEdit` [#5266](https://github.com/emilk/egui/pull/5266) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Expose center position in `MultiTouchInfo` [#5247](https://github.com/emilk/egui/pull/5247) by [@lucasmerlin](https://github.com/lucasmerlin)\n* `Context::add_font` [#5228](https://github.com/emilk/egui/pull/5228) by [@frederik-uni](https://github.com/frederik-uni)\n* Impl from `Box<str>` for `WidgetText`, `RichText` [#5309](https://github.com/emilk/egui/pull/5309) by [@dimtpap](https://github.com/dimtpap)\n* Add `Window::scroll_bar_visibility` [#5231](https://github.com/emilk/egui/pull/5231) by [@Zeenobit](https://github.com/Zeenobit)\n* Add `ComboBox::close_behavior` [#5305](https://github.com/emilk/egui/pull/5305) by [@avalsch](https://github.com/avalsch)\n* Add `painter.line()` [#5291](https://github.com/emilk/egui/pull/5291) by [@bircni](https://github.com/bircni)\n* Allow attaching custom user data to a screenshot command [#5416](https://github.com/emilk/egui/pull/5416) by [@emilk](https://github.com/emilk)\n* Add `Button::image_tint_follows_text_color` [#5430](https://github.com/emilk/egui/pull/5430) by [@emilk](https://github.com/emilk)\n* Consume escape keystroke when bailing out from a drag operation [#5433](https://github.com/emilk/egui/pull/5433) by [@abey79](https://github.com/abey79)\n* Add `Context::layer_transform_to_global` & `layer_transform_from_global` [#5465](https://github.com/emilk/egui/pull/5465) by [@emilk](https://github.com/emilk)\n\n### 🔧 Changed\n* Update MSRV to Rust 1.80 [#5421](https://github.com/emilk/egui/pull/5421), [#5457](https://github.com/emilk/egui/pull/5457) by [@emilk](https://github.com/emilk)\n* Expand max font atlas size from 8k to 16k [#5257](https://github.com/emilk/egui/pull/5257) by [@rustbasic](https://github.com/rustbasic)\n* Put font data into `Arc` to reduce memory consumption [#5276](https://github.com/emilk/egui/pull/5276) by [@StarStarJ](https://github.com/StarStarJ)\n* Move `egui::util::cache` to `egui::cache`; add `FramePublisher` [#5426](https://github.com/emilk/egui/pull/5426) by [@emilk](https://github.com/emilk)\n* Remove `Order::PanelResizeLine` [#5455](https://github.com/emilk/egui/pull/5455) by [@emilk](https://github.com/emilk)\n* Drag-and-drop: keep cursor set by user, if any [#5467](https://github.com/emilk/egui/pull/5467) by [@abey79](https://github.com/abey79)\n* Use `profiling` crate to support more profiler backends [#5150](https://github.com/emilk/egui/pull/5150) by [@teddemunnik](https://github.com/teddemunnik)\n* Improve hit-test of thin widgets, and widgets across layers [#5468](https://github.com/emilk/egui/pull/5468) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Update `ScrollArea` drag velocity when drag stopped [#5175](https://github.com/emilk/egui/pull/5175) by [@valadaptive](https://github.com/valadaptive)\n* Fix bug causing wrong-fire of `ViewportCommand::Visible` [#5244](https://github.com/emilk/egui/pull/5244) by [@rustbasic](https://github.com/rustbasic)\n* Fix: `Ui::new_child` does not consider the `sizing_pass` field of `UiBuilder` [#5262](https://github.com/emilk/egui/pull/5262) by [@zhatuokun](https://github.com/zhatuokun)\n* Fix Ctrl+Shift+Z redo shortcut [#5258](https://github.com/emilk/egui/pull/5258) by [@YgorSouza](https://github.com/YgorSouza)\n* Fix: `Window::default_pos` does not work [#5315](https://github.com/emilk/egui/pull/5315) by [@rustbasic](https://github.com/rustbasic)\n* Fix: `Sides` did not apply the layout position correctly [#5303](https://github.com/emilk/egui/pull/5303) by [@zhatuokun](https://github.com/zhatuokun)\n* Respect `Style::override_font_id` in `RichText` [#5310](https://github.com/emilk/egui/pull/5310) by [@MStarha](https://github.com/MStarha)\n* Fix disabled widgets \"eating\" focus [#5370](https://github.com/emilk/egui/pull/5370) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix cursor clipping in `TextEdit` inside a `ScrollArea` [#3660](https://github.com/emilk/egui/pull/3660) by [@juancampa](https://github.com/juancampa)\n* Make text cursor always appear on click  [#5420](https://github.com/emilk/egui/pull/5420) by [@juancampa](https://github.com/juancampa)\n* Fix `on_hover_text_at_pointer` for transformed layers [#5429](https://github.com/emilk/egui/pull/5429) by [@emilk](https://github.com/emilk)\n* Fix: don't interact with `Area` outside its `constrain_rect` [#5459](https://github.com/emilk/egui/pull/5459) by [@MScottMcBee](https://github.com/MScottMcBee)\n* Fix broken images on egui.rs (move from git lfs to normal git) [#5480](https://github.com/emilk/egui/pull/5480) by [@emilk](https://github.com/emilk)\n* Fix: `ui.new_child` should now respect `disabled` [#5483](https://github.com/emilk/egui/pull/5483) by [@emilk](https://github.com/emilk)\n* Fix zero-width strokes still affecting the feathering color of boxes [#5485](https://github.com/emilk/egui/pull/5485) by [@emilk](https://github.com/emilk)\n\n\n## 0.29.1 - 2024-10-01 - Bug fixes\n* Remove debug-assert triggered by `with_layer_id/dnd_drag_source` [#5191](https://github.com/emilk/egui/pull/5191) by [@emilk](https://github.com/emilk)\n* Fix id clash in `Ui::response` [#5192](https://github.com/emilk/egui/pull/5192) by [@emilk](https://github.com/emilk)\n* Do not round panel rectangles to pixel grid [#5196](https://github.com/emilk/egui/pull/5196) by [@emilk](https://github.com/emilk)\n\n\n## 0.29.0 - 2024-09-26 - Multipass, `UiBuilder`, & visual improvements\n### ✨ Highlights\nThis release adds initial support for multi-pass layout, which is a tool to circumvent [a common limitation of immediate mode](https://github.com/emilk/egui#layout).\nYou can use the new `UiBuilder::sizing_pass` ([#4969](https://github.com/emilk/egui/pull/4969)) to instruct the `Ui` and widgets to shrink to their minimum size, then store that size.\nThen call the new `Context::request_discard` ([#5059](https://github.com/emilk/egui/pull/5059)) to discard the visual output and do another _pass_ immediately after the current finishes.\nTogether, this allows more advanced layouts that is normally not possible in immediate mode.\nSo far this is only used by `egui::Grid` to hide the \"first-frame jitters\" that would sometimes happen before, but 3rd party libraries can also use it to do much more advanced things.\n\nThere is also a new `UiBuilder` for more flexible construction of `Ui`s ([#4969](https://github.com/emilk/egui/pull/4969)).\nBy specifying a `sense` for the `Ui` you can make it respond to clicks and drags, reading the result with the new `Ui::response` ([#5054](https://github.com/emilk/egui/pull/5054)).\nAmong other things, you can use this to create buttons that contain arbitrary widgets.\n\n0.29 also adds improve support for automatic switching between light and dark mode.\nYou can now set up a custom `Style` for both dark and light mode, and have egui follow the system preference ([#4744](https://github.com/emilk/egui/pull/4744) [#4860](https://github.com/emilk/egui/pull/4860)).\n\nThere also has been several small improvements to the look of egui:\n* Fix vertical centering of text (e.g. in buttons) ([#5117](https://github.com/emilk/egui/pull/5117))\n* Sharper rendering of lines and outlines ([#4943](https://github.com/emilk/egui/pull/4943))\n* Nicer looking text selection, especially in light mode ([#5017](https://github.com/emilk/egui/pull/5017))\n\n#### The new text selection\n<img width=\"198\" alt=\"New text selection in light mode\" src=\"https://github.com/user-attachments/assets/bd342946-299c-44ab-bc2d-2aa8ddbca8eb\">\n<img width=\"187\" alt=\"New text selection in dark mode\" src=\"https://github.com/user-attachments/assets/352bed32-5150-49b9-a9f9-c7679a0d30b2\">\n\n\n#### What text selection used to look like\n<img width=\"143\" alt=\"Old text selection in light mode\" src=\"https://github.com/user-attachments/assets/f3cbd798-cfed-4ad4-aa3a-d7480efcfa3c\">\n<img width=\"143\" alt=\"Old text selection in dark mode\" src=\"https://github.com/user-attachments/assets/9925d18d-da82-4a44-8a98-ea6857ecc14f\">\n\n### 🧳 Migration\n* `id_source` is now called `id_salt` everywhere ([#5025](https://github.com/emilk/egui/pull/5025))\n* `Ui::new` now takes a `UiBuilder` ([#4969](https://github.com/emilk/egui/pull/4969))\n* Deprecated (replaced with `UiBuilder`):\n\t* `ui.add_visible_ui`\n\t* `ui.allocate_ui_at_rect`\n\t* `ui.child_ui`\n\t* `ui.child_ui_with_id_source`\n\t* `ui.push_stack_info`\n\n### ⭐ Added\n* Create a `UiBuilder` for building `Ui`s [#4969](https://github.com/emilk/egui/pull/4969) by [@emilk](https://github.com/emilk)\n* Add `egui::Sides` for  adding UI on left and right sides [#5036](https://github.com/emilk/egui/pull/5036) by [@emilk](https://github.com/emilk)\n* Make light & dark visuals customizable when following the system theme [#4744](https://github.com/emilk/egui/pull/4744) [#4860](https://github.com/emilk/egui/pull/4860) by [@bash](https://github.com/bash)\n* Interactive `Ui`:s: add `UiBuilder::sense` and `Ui::response` [#5054](https://github.com/emilk/egui/pull/5054) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add a menu button with text and image  [#4748](https://github.com/emilk/egui/pull/4748) by [@NicolasBircksZR](https://github.com/NicolasBircksZR)\n* Add `Ui::columns_const()` [#4764](https://github.com/emilk/egui/pull/4764) by [@v0x0g](https://github.com/v0x0g)\n* Add `Slider::max_decimals_opt` [#4953](https://github.com/emilk/egui/pull/4953) by [@bircni](https://github.com/bircni)\n* Add `Label::halign` [#4975](https://github.com/emilk/egui/pull/4975) by [@rustbasic](https://github.com/rustbasic)\n* Add `ui.shrink_clip_rect` [#5068](https://github.com/emilk/egui/pull/5068) by [@emilk](https://github.com/emilk)\n* Add `ScrollArea::scroll_bar_rect` [#5070](https://github.com/emilk/egui/pull/5070) by [@emilk](https://github.com/emilk)\n* Add `Options::input_options` for click-delay etc [#4942](https://github.com/emilk/egui/pull/4942) by [@girtsf](https://github.com/girtsf)\n* Add `WidgetType::RadioGroup` [#5081](https://github.com/emilk/egui/pull/5081) by [@bash](https://github.com/bash)\n* Add return value to `with_accessibility_parent` [#5083](https://github.com/emilk/egui/pull/5083) by [@bash](https://github.com/bash)\n* Add `Ui::with_visual_transform` [#5055](https://github.com/emilk/egui/pull/5055) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Make `Slider` and `DragValue` compatible with `NonZeroUsize` etc [#5105](https://github.com/emilk/egui/pull/5105) by [@emilk](https://github.com/emilk)\n* Add `Context::request_discard` for multi-pass layouts [#5059](https://github.com/emilk/egui/pull/5059) by [@emilk](https://github.com/emilk)\n* Add UI to modify `FontTweak` live [#5125](https://github.com/emilk/egui/pull/5125) by [@emilk](https://github.com/emilk)\n* Add `Response::intrinsic_size` to enable better layout in 3rd party crates [#5082](https://github.com/emilk/egui/pull/5082) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add support for mipmap textures [#5146](https://github.com/emilk/egui/pull/5146) by [@nolanderc](https://github.com/nolanderc)\n* Add `DebugOptions::show_unaligned` [#5165](https://github.com/emilk/egui/pull/5165) by [@emilk](https://github.com/emilk)\n* Add `Slider::clamping` for precise clamp control [#5119](https://github.com/emilk/egui/pull/5119) by [@emilk](https://github.com/emilk)\n\n### 🚀 Performance\n* Optimize `Color32::from_rgba_unmultiplied` with LUT [#5088](https://github.com/emilk/egui/pull/5088) by [@YgorSouza](https://github.com/YgorSouza)\n\n### 🔧 Changed\n* Rename `id_source` to `id_salt` [#5025](https://github.com/emilk/egui/pull/5025) by [@bircni](https://github.com/bircni)\n* Avoid some `Id` clashes by seeding auto-ids with child id [#4840](https://github.com/emilk/egui/pull/4840) by [@ironpeak](https://github.com/ironpeak)\n* Nicer looking text selection, especially in light mode [#5017](https://github.com/emilk/egui/pull/5017) by [@emilk](https://github.com/emilk)\n* Fix blurry lines by aligning to pixel grid [#4943](https://github.com/emilk/egui/pull/4943) by [@juancampa](https://github.com/juancampa)\n* Center-align all text vertically [#5117](https://github.com/emilk/egui/pull/5117) by [@emilk](https://github.com/emilk)\n* Clamp margin values in `Margin::ui` [#4873](https://github.com/emilk/egui/pull/4873) by [@rustbasic](https://github.com/rustbasic)\n* Make `scroll_to_*` animations configurable [#4305](https://github.com/emilk/egui/pull/4305) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Update `Button` to correctly align contained image [#4891](https://github.com/emilk/egui/pull/4891) by [@PrimmR](https://github.com/PrimmR)\n* Deprecate `ahash` re-exports [#4979](https://github.com/emilk/egui/pull/4979) by [@oscargus](https://github.com/oscargus)\n* Fix: Ensures correct IME behavior when the text input area gains or loses focus [#4896](https://github.com/emilk/egui/pull/4896) by [@rustbasic](https://github.com/rustbasic)\n* Enable rustdoc `generate-link-to-definition` feature on docs.rs [#5030](https://github.com/emilk/egui/pull/5030) by [@GuillaumeGomez](https://github.com/GuillaumeGomez)\n* Make some `Memory` methods public [#5046](https://github.com/emilk/egui/pull/5046) by [@bircni](https://github.com/bircni)\n* Deprecate `ui.set_sizing_pass` [#5074](https://github.com/emilk/egui/pull/5074) by [@emilk](https://github.com/emilk)\n* Export module `egui::frame` [#5087](https://github.com/emilk/egui/pull/5087) by [@simgt](https://github.com/simgt)\n* Use `log` crate instead of `eprintln` & remove some unwraps [#5010](https://github.com/emilk/egui/pull/5010) by [@bircni](https://github.com/bircni)\n* Fix: `Event::Copy` and `Event::Cut` behave as if they select the entire text when there is no selection [#5115](https://github.com/emilk/egui/pull/5115) by [@rustbasic](https://github.com/rustbasic)\n\n### 🐛 Fixed\n* Prevent text shrinking in tooltips; round wrap-width to integer [#5161](https://github.com/emilk/egui/pull/5161) by [@emilk](https://github.com/emilk)\n* Fix bug causing tooltips with dynamic content to shrink [#5168](https://github.com/emilk/egui/pull/5168) by [@emilk](https://github.com/emilk)\n* Remove some debug asserts [#4826](https://github.com/emilk/egui/pull/4826) by [@emilk](https://github.com/emilk)\n* Handle the IME event first in `TextEdit` to fix some bugs [#4794](https://github.com/emilk/egui/pull/4794) by [@rustbasic](https://github.com/rustbasic)\n* Slider: round to decimals after applying `step_by` [#4822](https://github.com/emilk/egui/pull/4822) by [@AurevoirXavier](https://github.com/AurevoirXavier)\n* Fix: hint text follows the alignment set on the `TextEdit` [#4889](https://github.com/emilk/egui/pull/4889) by [@PrimmR](https://github.com/PrimmR)\n* Request focus on a `TextEdit` when clicked [#4991](https://github.com/emilk/egui/pull/4991) by [@Zoxc](https://github.com/Zoxc)\n* Fix `Id` clash in `Frame` styling widget [#4967](https://github.com/emilk/egui/pull/4967) by [@YgorSouza](https://github.com/YgorSouza)\n* Prevent `ScrollArea` contents from exceeding the container size [#5006](https://github.com/emilk/egui/pull/5006) by [@DouglasDwyer](https://github.com/DouglasDwyer)\n* Fix bug in size calculation of truncated text [#5076](https://github.com/emilk/egui/pull/5076) by [@emilk](https://github.com/emilk)\n* Fix: Make sure `RawInput::take` clears all events, like it says it does [#5104](https://github.com/emilk/egui/pull/5104) by [@emilk](https://github.com/emilk)\n* Fix `DragValue` range clamping [#5118](https://github.com/emilk/egui/pull/5118) by [@emilk](https://github.com/emilk)\n* Fix: panic when dragging window between monitors of different pixels_per_point [#4868](https://github.com/emilk/egui/pull/4868) by [@rustbasic](https://github.com/rustbasic)\n\n\n## 0.28.1 - 2024-07-05 - Tooltip tweaks\n### ⭐ Added\n* Add `Image::uri()` [#4720](https://github.com/emilk/egui/pull/4720) by [@rustbasic](https://github.com/rustbasic)\n\n### 🔧 Changed\n* Better documentation for `Event::Zoom` [#4778](https://github.com/emilk/egui/pull/4778) by [@emilk](https://github.com/emilk)\n* Hide tooltips when scrolling [#4784](https://github.com/emilk/egui/pull/4784) by [@emilk](https://github.com/emilk)\n* Smoother animations [#4787](https://github.com/emilk/egui/pull/4787) by [@emilk](https://github.com/emilk)\n* Hide tooltip on click [#4789](https://github.com/emilk/egui/pull/4789) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Fix default height of top/bottom panels [#4779](https://github.com/emilk/egui/pull/4779) by [@emilk](https://github.com/emilk)\n* Show the innermost debug rectangle when pressing all modifier keys [#4782](https://github.com/emilk/egui/pull/4782) by [@emilk](https://github.com/emilk)\n* Fix occasional flickering of pointer-tooltips [#4788](https://github.com/emilk/egui/pull/4788) by [@emilk](https://github.com/emilk)\n\n\n## 0.28.0 - 2024-07-03 - Sizing pass, `UiStack` and GIF support\n### ✨ Highlights\n* Automatic sizing of menus/popups/tooltips with no jittering, using new _sizing pass_ [#4557](https://github.com/emilk/egui/pull/4557), [#4579](https://github.com/emilk/egui/pull/4579) by [@emilk](https://github.com/emilk)\n* Support interactive widgets in tooltips [#4596](https://github.com/emilk/egui/pull/4596) by [@emilk](https://github.com/emilk)\n* Add a `ui.stack()` with info about all ancestor `Ui`s, with optional tags [#4588](https://github.com/emilk/egui/pull/4588) by [@abey79](https://github.com/abey79), [#4617](https://github.com/emilk/egui/pull/4617) by [@emilk](https://github.com/emilk)\n* GIF support [#4620](https://github.com/emilk/egui/pull/4620) by [@JustFrederik](https://github.com/JustFrederik)\n* Blinking text cursor in `TextEdit` [#4279](https://github.com/emilk/egui/pull/4279) by [@emilk](https://github.com/emilk)\n\n### 🧳 Migration\n* Update MSRV to 1.76 ([#4411](https://github.com/emilk/egui/pull/4411))\n* The `wrap/truncate` functions on `Label/Button/ComboBox` no longer take bools as arguments. Use `.wrap_mode(…)` instead for more fine control ([#4556](https://github.com/emilk/egui/pull/4556))\n* `Style::wrap` has been deprecated in favor of `Style::wrap_mode` ([#4556](https://github.com/emilk/egui/pull/4556))\n* `Ui::new` and `ui.child_ui` now takes a new parameter for the `UiStack` ([#4588](https://github.com/emilk/egui/pull/4588))\n* The `extra_asserts` and `extra_debug_asserts` feature flags have been removed ([#4478](https://github.com/emilk/egui/pull/4478))\n* Remove `Event::Scroll` and handle it in egui. Use `Event::MouseWheel` instead ([#4524](https://github.com/emilk/egui/pull/4524))\n* `Event::Zoom` is no longer emitted on ctrl+scroll. Use `InputState::smooth_scroll_delta` instead ([#4524](https://github.com/emilk/egui/pull/4524))\n* `ui.set_enabled` and `set_visible` have  been deprecated ([#4614](https://github.com/emilk/egui/pull/4614))\n* `DragValue::clamp_range` renamed to `range` (([#4728](https://github.com/emilk/egui/pull/4728))\n\n### ⭐ Added\n* Overload operators for `Rect + Margin`, `Rect - Margin` etc [#4277](https://github.com/emilk/egui/pull/4277) by [@emilk](https://github.com/emilk)\n* Add `Window::order` [#4301](https://github.com/emilk/egui/pull/4301) by [@alexparlett](https://github.com/alexparlett)\n* Add a way to specify Undoer settings and construct Undoers more easily [#4357](https://github.com/emilk/egui/pull/4357) by [@valadaptive](https://github.com/valadaptive)\n* Add xtask crate [#4293](https://github.com/emilk/egui/pull/4293) by [@YgorSouza](https://github.com/YgorSouza)\n* Add `ViewportCommand::RequestCut`, `RequestCopy` and `RequestPaste` to trigger clipboard actions [#4035](https://github.com/emilk/egui/pull/4035) by [@bu5hm4nn](https://github.com/bu5hm4nn)\n* Added ability to define colors at UV coordinates along a path [#4353](https://github.com/emilk/egui/pull/4353) by [@murl-digital](https://github.com/murl-digital)\n* Add a `Display` impl for `Vec2`, `Pos2`, and `Rect` [#4428](https://github.com/emilk/egui/pull/4428) by [@tgross35](https://github.com/tgross35)\n* Easing functions [#4630](https://github.com/emilk/egui/pull/4630) by [@emilk](https://github.com/emilk)\n* Add `Options::line_scroll_speed` and `scroll_zoom_speed` [#4532](https://github.com/emilk/egui/pull/4532) by [@emilk](https://github.com/emilk)\n* Add `TextEdit::hint_text_font` [#4517](https://github.com/emilk/egui/pull/4517) by [@zaaarf](https://github.com/zaaarf)\n* Add `Options::reduce_texture_memory` to free up RAM [#4431](https://github.com/emilk/egui/pull/4431) by [@varphone](https://github.com/varphone)\n* Add support for text truncation to `egui::Style` [#4556](https://github.com/emilk/egui/pull/4556) by [@abey79](https://github.com/abey79)\n* Add `Response::show_tooltip_ui` and `show_tooltip_text` [#4580](https://github.com/emilk/egui/pull/4580) by [@emilk](https://github.com/emilk)\n* Add `opacity` and `multiply_opacity` functions to `Ui` and `Painter` [#4586](https://github.com/emilk/egui/pull/4586) by [@emilk](https://github.com/emilk)\n* Add `Key::Quote` [#4683](https://github.com/emilk/egui/pull/4683) by [@mkeeter](https://github.com/mkeeter)\n* Improve backtraces when hovering widgets with modifiers pressed [#4696](https://github.com/emilk/egui/pull/4696) by [@emilk](https://github.com/emilk)\n* Add `PopupCloseBehavior` [#4636](https://github.com/emilk/egui/pull/4636) by [@Umatriz](https://github.com/Umatriz)\n* Add basic test for egui accesskit output [#4716](https://github.com/emilk/egui/pull/4716) by [@Wcubed](https://github.com/Wcubed)\n* Add `clamp_to_range` option to DragValue, rename `clamp_range` to `range` (deprecating the former) [#4728](https://github.com/emilk/egui/pull/4728) by [@Wumpf](https://github.com/Wumpf)\n* Add `Style::number_formatter` as the default used by `DragValue` [#4740](https://github.com/emilk/egui/pull/4740) by [@emilk](https://github.com/emilk)\n\n### 🔧 Changed\n* Improve the UI for changing the egui theme [#4257](https://github.com/emilk/egui/pull/4257) by [@emilk](https://github.com/emilk)\n* Change the resize cursor when you reach the resize limit [#4275](https://github.com/emilk/egui/pull/4275) by [@emilk](https://github.com/emilk)\n* Make `TextEdit` an atomic widget [#4276](https://github.com/emilk/egui/pull/4276) by [@emilk](https://github.com/emilk)\n* Rename `fn scroll2` to `fn scroll` [#4282](https://github.com/emilk/egui/pull/4282) by [@emilk](https://github.com/emilk)\n* Change `Frame::multiply_with_opacity` to multiply in gamma space [#4283](https://github.com/emilk/egui/pull/4283) by [@emilk](https://github.com/emilk)\n* Use parent `Ui`s style for popups [#4325](https://github.com/emilk/egui/pull/4325) by [@alexparlett](https://github.com/alexparlett)\n* Take `rounding` into account when using `Slider::trailing_fill` [#4308](https://github.com/emilk/egui/pull/4308) by [@rustbasic](https://github.com/rustbasic)\n* Allow users to create viewports larger than monitor on Windows & macOS [#4337](https://github.com/emilk/egui/pull/4337) by [@lopo12123](https://github.com/lopo12123)\n* Improve `ViewportBuilder::with_icon()` documentation [#4408](https://github.com/emilk/egui/pull/4408) by [@roccoblues](https://github.com/roccoblues)\n* `include_image!` now accepts expressions [#4521](https://github.com/emilk/egui/pull/4521) by [@YgorSouza](https://github.com/YgorSouza)\n* Remove scroll latency for smooth trackpads [#4526](https://github.com/emilk/egui/pull/4526) by [@emilk](https://github.com/emilk)\n* Smooth out zooming with discreet scroll wheel [#4530](https://github.com/emilk/egui/pull/4530) by [@emilk](https://github.com/emilk)\n* Make `TextEdit::return_key` optional [#4543](https://github.com/emilk/egui/pull/4543) by [@doonv](https://github.com/doonv)\n* Better spacing and sizes for (menu) buttons [#4558](https://github.com/emilk/egui/pull/4558) by [@emilk](https://github.com/emilk)\n* `ComboBox`: fix justified layout of popup if wider than parent button [#4570](https://github.com/emilk/egui/pull/4570) by [@emilk](https://github.com/emilk)\n* Make `Area` state public [#4576](https://github.com/emilk/egui/pull/4576) by [@emilk](https://github.com/emilk)\n* Don't persist `Area` size [#4749](https://github.com/emilk/egui/pull/4749) by [@emilk](https://github.com/emilk)\n* Round text galley sizes to nearest UI point size [#4578](https://github.com/emilk/egui/pull/4578) by [@emilk](https://github.com/emilk)\n* Once you have waited for a tooltip to show, show the next one right away [#4585](https://github.com/emilk/egui/pull/4585) by [@emilk](https://github.com/emilk)\n* Fade in windows, tooltips, popups, etc [#4587](https://github.com/emilk/egui/pull/4587) by [@emilk](https://github.com/emilk)\n* Make `egu::menu` types public [#4544](https://github.com/emilk/egui/pull/4544) by [@sor-ca](https://github.com/sor-ca)\n* The default constrain rect for `Area/Window` is now `ctx.screen_rect` [#4590](https://github.com/emilk/egui/pull/4590) by [@emilk](https://github.com/emilk)\n* Constrain `Area`s to screen by default [#4591](https://github.com/emilk/egui/pull/4591) by [@emilk](https://github.com/emilk)\n* `Grid`: set the `sizing_pass` flag during the initial sizing pass [#4612](https://github.com/emilk/egui/pull/4612) by [@emilk](https://github.com/emilk)\n* Remove special case for 0 in DragValue default formatter [#4639](https://github.com/emilk/egui/pull/4639) by [@YgorSouza](https://github.com/YgorSouza)\n* Abort drags when pressing escape key [#4678](https://github.com/emilk/egui/pull/4678) by [@emilk](https://github.com/emilk)\n* Allow setting a layer as a sublayer of another [#4690](https://github.com/emilk/egui/pull/4690) by [@YgorSouza](https://github.com/YgorSouza)\n* Close context menus with Escape [#4711](https://github.com/emilk/egui/pull/4711) by [@emilk](https://github.com/emilk)\n* Cancel DragValue edit if Escape is pressed [#4713](https://github.com/emilk/egui/pull/4713) by [@YgorSouza](https://github.com/YgorSouza)\n* The default parser for `DragValue` and `Slider` now ignores whitespace [#4739](https://github.com/emilk/egui/pull/4739) by [@emilk](https://github.com/emilk)\n* Disabled widgets are now also disabled in the accesskit output [#4750](https://github.com/emilk/egui/pull/4750) by [@Wcubed](https://github.com/Wcubed)\n* Make it easier to grab the handle of a floating scroll bar [#4754](https://github.com/emilk/egui/pull/4754) by [@emilk](https://github.com/emilk)\n* When debugging widget rects on hover, show width and height [#4762](https://github.com/emilk/egui/pull/4762) by [@emilk](https://github.com/emilk)\n* Make sure all tooltips close if you open a menu in the same layer [#4766](https://github.com/emilk/egui/pull/4766) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Fix wrong replacement function in deprecation notice of `drag_released*` [#4314](https://github.com/emilk/egui/pull/4314) by [@sornas](https://github.com/sornas)\n* Consider layer transform when positioning text agent [#4319](https://github.com/emilk/egui/pull/4319) by [@juancampa](https://github.com/juancampa)\n* Fix incorrect line breaks [#4377](https://github.com/emilk/egui/pull/4377) by [@juancampa](https://github.com/juancampa)\n* Fix `hex_color!` macro by re-exporting `color_hex` crate from `ecolor` [#4372](https://github.com/emilk/egui/pull/4372) by [@dataphract](https://github.com/dataphract)\n* Change `Ui::allocate_painter` to inherit properties from `Ui` [#4343](https://github.com/emilk/egui/pull/4343) by [@varphone](https://github.com/varphone)\n* Fix `Panel` incorrect size [#4351](https://github.com/emilk/egui/pull/4351) by [@zhatuokun](https://github.com/zhatuokun)\n* Improve IME support with new `Event::Ime` [#4358](https://github.com/emilk/egui/pull/4358) by [@rustbasic](https://github.com/rustbasic)\n* Disable interaction for `ScrollArea` and `Plot` when UI is disabled [#4457](https://github.com/emilk/egui/pull/4457) by [@varphone](https://github.com/varphone)\n* Don't panic when replacement glyph is not found [#4542](https://github.com/emilk/egui/pull/4542) by [@RyanBluth](https://github.com/RyanBluth)\n* Fix `Ui::scroll_with_delta` only scrolling if the `ScrollArea` is focused [#4303](https://github.com/emilk/egui/pull/4303) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Handle tooltips so large that they cover the widget [#4623](https://github.com/emilk/egui/pull/4623) by [@emilk](https://github.com/emilk)\n* ScrollArea: Prevent drag interaction outside the area [#4611](https://github.com/emilk/egui/pull/4611) by [@s-nie](https://github.com/s-nie)\n* Fix buggy interaction with widgets outside of clip rect [#4675](https://github.com/emilk/egui/pull/4675) by [@emilk](https://github.com/emilk)\n* Make sure contents of a panel don't overflow [#4676](https://github.com/emilk/egui/pull/4676) by [@emilk](https://github.com/emilk)\n* Fix: `Response::hover_pos` returns incorrect positions with layer transforms [#4679](https://github.com/emilk/egui/pull/4679) by [@Creative0708](https://github.com/Creative0708)\n* Fix: Menu popups and tooltips don't respect layer transforms [#4708](https://github.com/emilk/egui/pull/4708) by [@Creative0708](https://github.com/Creative0708)\n* Bug fix: report latest area size in `Area::show` response [#4710](https://github.com/emilk/egui/pull/4710) by [@emilk](https://github.com/emilk)\n* Ensure `Window` scroll bars are at the window edges [#4733](https://github.com/emilk/egui/pull/4733) by [@emilk](https://github.com/emilk)\n* Prevent `TextEdit` widgets from sending fake primary clicks [#4751](https://github.com/emilk/egui/pull/4751) by [@Aliremu](https://github.com/Aliremu)\n* Fix text selection when there's multiple viewports [#4760](https://github.com/emilk/egui/pull/4760) by [@emilk](https://github.com/emilk)\n* Use correct cursor icons when resizing panels too wide or narrow [#4769](https://github.com/emilk/egui/pull/4769) by [@emilk](https://github.com/emilk)\n\n\n## 0.27.2 - 2024-04-02\n### 🐛 Fixed\n* Fix tooltips for non-interactive widgets [#4291](https://github.com/emilk/egui/pull/4291)\n* Fix problem clicking the edge of a `TextEdit` [#4272](https://github.com/emilk/egui/pull/4272)\n* Fix: `Response::clicked_elsewhere` takes clip rect into account [#4274](https://github.com/emilk/egui/pull/4274)\n* Fix incorrect `Response::interact_rect` for `Area/Window` [#4273](https://github.com/emilk/egui/pull/4273)\n\n### ⭐ Added\n* Allow disabling animations on a `ScrollArea` [#4309](https://github.com/emilk/egui/pull/4309) (thanks [@lucasmerlin](https://github.com/lucasmerlin)!)\n\n\n## 0.27.1 - 2024-03-29\n### 🐛 Fixed\n* Fix visual glitch on the right side of highly rounded rectangles [#4244](https://github.com/emilk/egui/pull/4244)\n* Prevent visual glitch when shadow blur width is very high [#4245](https://github.com/emilk/egui/pull/4245)\n* Fix `InputState::any_touches` and add `InputState::has_touch_screen` [#4247](https://github.com/emilk/egui/pull/4247)\n* Fix `Context::repaint_causes` returning no causes [#4248](https://github.com/emilk/egui/pull/4248)\n* Fix touch-and-hold to open context menu [#4249](https://github.com/emilk/egui/pull/4249)\n* Hide shortcut text on zoom buttons if `zoom_with_keyboard` is false [#4262](https://github.com/emilk/egui/pull/4262)\n\n### 🔧 Changed\n* Don't apply a clip rect to the contents of an `Area` or `Window` [#4258](https://github.com/emilk/egui/pull/4258)\n\n\n## 0.27.0 - 2024-03-26 - Nicer menus and new hit test logic\nThe hit test logic (what is the user clicking on?) has been completely rewritten, and should now be much more accurate and helpful.\nThe hit test and interaction logic is run at the start of the frame, using the widgets rects from the previous frame, but the latest mouse coordinates.\nIt enabled getting a `Response` for a widget _before_ creating it using `Context::read_response`.\nThis will in the future unlock more powerful widget styling options.\nThe new hit test also allows clicking slightly outside a button and still hit it, improving the support for touch screens.\n\nThe menus have also been improved so that they both act and feel better, with no change in API.\nIncluded in this is much nicer looking shadows, supporting an offset.\n\n<img width=\"580\" alt=\"Screenshot 2024-03-26 at 17 00 23\" src=\"https://github.com/emilk/egui/assets/1148717/f1eea39f-17a7-41ca-a983-ee142b04ef26\">\n\n\n### ⚠️ BREAKING\n* `Response::clicked*` and `Response::dragged*` may lock the `Context`, so don't call it from a `Context`-locking closure.\n* `Response::clicked_by` will no longer be true if clicked with keyboard. Use `Response::clicked` instead.\n* `Memory::focus` has been renamed `Memory::focused`\n* `Area::new` now takes an `Id` by argument [#4115](https://github.com/emilk/egui/pull/4115)\n* Change the definition of `clicked_by` [#4192](https://github.com/emilk/egui/pull/4192)\n\n### ☰ Menu related improvements\n* Add some distance between parent menu and submenu [#4230](https://github.com/emilk/egui/pull/4230)\n* Add `Area::sense` and improve hit-testing of buttons in menus [#4234](https://github.com/emilk/egui/pull/4234)\n* Improve logic for when submenus are kept open [#4166](https://github.com/emilk/egui/pull/4166)\n* Better align menus with the button that opened them [#4233](https://github.com/emilk/egui/pull/4233)\n* Hide hover UI when showing the context menu [#4138](https://github.com/emilk/egui/pull/4138) (thanks [@abey79](https://github.com/abey79)!)\n* CSS-like `Shadow` with offset, spread, and blur [#4232](https://github.com/emilk/egui/pull/4232)\n* On touch screens, press-and-hold equals a secondary click [#4195](https://github.com/emilk/egui/pull/4195)\n\n### ⭐ Added\n* Add with_taskbar to viewport builder [#3958](https://github.com/emilk/egui/pull/3958) (thanks [@AnotherNathan](https://github.com/AnotherNathan)!)\n* Add F21 to F35 key bindings [#4004](https://github.com/emilk/egui/pull/4004) (thanks [@oscargus](https://github.com/oscargus)!)\n* Add `Options::debug_paint_interactive_widgets` [#4018](https://github.com/emilk/egui/pull/4018)\n* Add `Ui::set_opacity` [#3965](https://github.com/emilk/egui/pull/3965) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Add `Response::paint_debug_info()` to make it easy to visualize a widget's id and state [#4056](https://github.com/emilk/egui/pull/4056) (thanks [@abey79](https://github.com/abey79)!)\n* Add layer transforms, interaction in layer [#3906](https://github.com/emilk/egui/pull/3906) (thanks [@Tweoss](https://github.com/Tweoss)!)\n* Add `ColorImage::from_gray_iter` [#3536](https://github.com/emilk/egui/pull/3536) (thanks [@wangxiaochuTHU](https://github.com/wangxiaochuTHU)!)\n* Add API for raw mouse motion [#4063](https://github.com/emilk/egui/pull/4063) (thanks [@GiantBlargg](https://github.com/GiantBlargg)!)\n* Add accessibility to `ProgressBar` and `Spinner` [#4139](https://github.com/emilk/egui/pull/4139) (thanks [@DataTriny](https://github.com/DataTriny)!)\n* Add x11 window type settings to viewport builder [#4175](https://github.com/emilk/egui/pull/4175) (thanks [@psethwick](https://github.com/psethwick)!)\n* Add an API for customizing the return key in TextEdit [#4085](https://github.com/emilk/egui/pull/4085) (thanks [@lemon-sh](https://github.com/lemon-sh)!)\n* Convenience `const fn` for `Margin`, `Rounding` and `Shadow` [#4080](https://github.com/emilk/egui/pull/4080) (thanks [@0Qwel](https://github.com/0Qwel)!)\n* Serde feature: add serde derives to input related structs [#4100](https://github.com/emilk/egui/pull/4100) (thanks [@gweisert](https://github.com/gweisert)!)\n* Give each menu `Area` an id distinct from the id of what was clicked  [#4114](https://github.com/emilk/egui/pull/4114)\n* `epaint`: Added `Shape::{scale,translate}` wrappers [#4090](https://github.com/emilk/egui/pull/4090) (thanks [@varphone](https://github.com/varphone)!)\n* A `Window` can now be resizable in only one direction [#4155](https://github.com/emilk/egui/pull/4155)\n* Add `EllipseShape` [#4122](https://github.com/emilk/egui/pull/4122) (thanks [@TheTacBanana](https://github.com/TheTacBanana)!)\n* Adjustable Slider rail height [#4092](https://github.com/emilk/egui/pull/4092) (thanks [@rustbasic](https://github.com/rustbasic)!)\n* Expose state override for `HeaderResponse` [#4200](https://github.com/emilk/egui/pull/4200) (thanks [@Zeenobit](https://github.com/Zeenobit)!)\n\n### 🔧 Changed\n* `TextEdit`: Change `margin` property to `egui::Margin` type [#3993](https://github.com/emilk/egui/pull/3993) (thanks [@bu5hm4nn](https://github.com/bu5hm4nn)!)\n* New widget interaction logic [#4026](https://github.com/emilk/egui/pull/4026)\n* `ui.dnd_drop_zone()` now returns `InnerResponse`. [#4079](https://github.com/emilk/egui/pull/4079) (thanks [@sowbug](https://github.com/sowbug)!)\n* Support interacting with the background of a `Ui` [#4074](https://github.com/emilk/egui/pull/4074)\n* Quickly animate scroll when calling `ui.scroll_to_cursor` etc  [#4119](https://github.com/emilk/egui/pull/4119)\n* Don't clear modifier state on focus change [#4157](https://github.com/emilk/egui/pull/4157) (thanks [@ming08108](https://github.com/ming08108)!)\n* Prevent `egui::Window` from becoming larger than viewport [#4199](https://github.com/emilk/egui/pull/4199) (thanks [@rustbasic](https://github.com/rustbasic)!)\n* Don't show URLs when hovering hyperlinks [#4218](https://github.com/emilk/egui/pull/4218)\n\n### 🐛 Fixed\n* Fix incorrect handling of item spacing in `Window` title bar [#3995](https://github.com/emilk/egui/pull/3995) (thanks [@varphone](https://github.com/varphone)!)\n* Make `on_disabled_hover_ui` respect `tooltip_delay` [#4012](https://github.com/emilk/egui/pull/4012) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Fix `TextEdit` being too short whenever there is horizontal margin [#4005](https://github.com/emilk/egui/pull/4005) (thanks [@gweisert](https://github.com/gweisert)!)\n* Fix `Response::interact` and `Ui:interact_with_hovered` [#4013](https://github.com/emilk/egui/pull/4013)\n* Fix: `Response.interact_pointer_pos` is `Some` on click and drag released [#4014](https://github.com/emilk/egui/pull/4014)\n* Fix custom `Window` `Frame`s [#4009](https://github.com/emilk/egui/pull/4009) (thanks [@varphone](https://github.com/varphone)!)\n* Fix: images with background color now respects rounding [#4029](https://github.com/emilk/egui/pull/4029) (thanks [@vincent-sparks](https://github.com/vincent-sparks)!)\n* Fixed the incorrect display of the `Window` frame with a wide border or large rounding [#4032](https://github.com/emilk/egui/pull/4032) (thanks [@varphone](https://github.com/varphone)!)\n* TextEdit: fix crash when hitting SHIFT + TAB around non-ASCII text [#3984](https://github.com/emilk/egui/pull/3984) (thanks [@rustbasic](https://github.com/rustbasic)!)\n* Fix two `ScrollArea` bugs: leaking scroll target and broken animation to target offset [#4174](https://github.com/emilk/egui/pull/4174) (thanks [@abey79](https://github.com/abey79)!)\n* Fix bug in `Context::parent_viewport_id` [#4190](https://github.com/emilk/egui/pull/4190) (thanks [@rustbasic](https://github.com/rustbasic)!)\n* Remove unnecessary allocation in `RepaintCause::new` [#4146](https://github.com/emilk/egui/pull/4146) (thanks [@valsteen](https://github.com/valsteen)!)\n\n\n## 0.26.2 - 2024-02-14\n* Avoid interacting twice when not required [#4041](https://github.com/emilk/egui/pull/4041) (thanks [@abey79](https://github.com/abey79)!)\n\n\n## 0.26.1 - 2024-02-11\n* Fix `Window` title bar incorrect handling spacing [#3995](https://github.com/emilk/egui/pull/3995) (thanks [@varphone](https://github.com/varphone)!)\n* Make `on_disabled_hover_ui` respect `tooltip_delay` [#4012](https://github.com/emilk/egui/pull/4012) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Fix `TextEdit` being too short whenever there is horizontal margin [#4005](https://github.com/emilk/egui/pull/4005) (thanks [@gweisert](https://github.com/gweisert)!)\n* Fix `Response::interact` and `Ui:interact_with_hovered` [#4013](https://github.com/emilk/egui/pull/4013)\n* Fix: `Response.interact_pointer_pos` is `Some` on click and drag released [#4014](https://github.com/emilk/egui/pull/4014)\n* Fix custom `Window` `Frame`s [#4009](https://github.com/emilk/egui/pull/4009) (thanks [@varphone](https://github.com/varphone)!)\n\n\n## 0.26.0 - 2024-02-05 - Text selection in labels\n\n### ⚠️ BREAKING\n* Always set `response.hovered` to false when dragging another widget [#3860](https://github.com/emilk/egui/pull/3860)\n* `InputState::scroll_delta` has been replaced by `InputState::raw_scroll_delta` and `InputState::smooth_scroll_delta` [#3884](https://github.com/emilk/egui/pull/3884)\n* Improve `Response.dragged`, `drag_started` and `clicked` [#3888](https://github.com/emilk/egui/pull/3888)\n\n### ⭐ Added\n* Selectable text in Labels [#3814](https://github.com/emilk/egui/pull/3814) [#3870](https://github.com/emilk/egui/pull/3870)\n* Add some drag-and-drop-related APIs in `Response` and `Memory` [#3876](https://github.com/emilk/egui/pull/3876) (thanks [@abey79](https://github.com/abey79)!)\n* Add drag-and-drop APIs with payloads storage [#3887](https://github.com/emilk/egui/pull/3887)\n* `ComboBox`: add builder method for height [#3001](https://github.com/emilk/egui/pull/3001) (thanks [@hinto-janai](https://github.com/hinto-janai)!)\n* Add keys `?`, `/`, `|` [#3820](https://github.com/emilk/egui/pull/3820)\n* Add `Response::contains_pointer` [#3859](https://github.com/emilk/egui/pull/3859)\n* Add `Align2::anchor_size` [#3863](https://github.com/emilk/egui/pull/3863)\n* Add `Context::debug_text` [#3864](https://github.com/emilk/egui/pull/3864)\n* Allow read access to shapes added to painter this frame [#3866](https://github.com/emilk/egui/pull/3866) (thanks [@brunizzl](https://github.com/brunizzl)!)\n* Register callbacks with `Context::on_begin_frame` and `on_end_frame` [#3886](https://github.com/emilk/egui/pull/3886)\n* Improve `Frame` API to allow picking color until after adding content [#3889](https://github.com/emilk/egui/pull/3889)\n* Add opacity factor to `TextShape` [#3916](https://github.com/emilk/egui/pull/3916) (thanks [@StratusFearMe21](https://github.com/StratusFearMe21)!)\n* `Context::repaint_causes`: `file:line` of what caused a repaint [#3949](https://github.com/emilk/egui/pull/3949)\n* Add `TextureOptions::wrap_mode` [#3954](https://github.com/emilk/egui/pull/3954) (thanks [@CodedNil](https://github.com/CodedNil)!)\n* Add `Spacing::menu_width` [#3973](https://github.com/emilk/egui/pull/3973)\n\n### 🔧 Changed\n* Move text selection logic to own module [#3843](https://github.com/emilk/egui/pull/3843)\n* Smooth scrolling [#3884](https://github.com/emilk/egui/pull/3884)\n* Turn off text wrapping by default in combo-box popups [#3912](https://github.com/emilk/egui/pull/3912)\n* `Response.context_menu` now returns the response of the context menu, if open [#3904](https://github.com/emilk/egui/pull/3904) (thanks [@AufarZakiev](https://github.com/AufarZakiev)!)\n* Update to puffin 0.19 [#3940](https://github.com/emilk/egui/pull/3940)\n* Wait with showing tooltip until mouse has been still for 300ms [#3977](https://github.com/emilk/egui/pull/3977)\n\n### 🐛 Fixed\n* Fix: dragging to above/below a `TextEdit` or `Label` will select text to begin/end [#3858](https://github.com/emilk/egui/pull/3858)\n* Fix clickable widgets blocking scrolling on touch screens [#3815](https://github.com/emilk/egui/pull/3815) (thanks [@lucasmerlin](https://github.com/lucasmerlin)!)\n* Fix `stable_dt` [#3832](https://github.com/emilk/egui/pull/3832)\n* Bug Fix : `Response::is_pointer_button_down_on` is now false the frame the button is released [#3833](https://github.com/emilk/egui/pull/3833) (thanks [@rustbasic](https://github.com/rustbasic)!)\n* Use runtime knowledge of OS for OS-specific text editing [#3840](https://github.com/emilk/egui/pull/3840)\n* Fix calling `request_repaint_after` every frame causing immediate repaint [#3978](https://github.com/emilk/egui/pull/3978)\n\n### 🚀 Performance\n* Niche-optimize `Id` so that `Option<Id>` is the same size as `Id` [#3932](https://github.com/emilk/egui/pull/3932)\n* Parallel tessellation with opt-in `rayon` feature [#3934](https://github.com/emilk/egui/pull/3934)\n\n\n\n## 0.25.0 - 2024-01-08 - Better keyboard input\n\n### ⚠️ BREAKING\n* Ignore extra SHIFT and ALT when matching modifiers [#3769](https://github.com/emilk/egui/pull/3769)\n* Replace `Key::PlusEquals` with `Key::Plus` and `Key::Equals` [#3769](https://github.com/emilk/egui/pull/3769)\n* Removed `WidgetTextGalley`, `WidgetTextJob`, `RichText::into_text_job`, `WidgetText::into_text_job` [#3727](https://github.com/emilk/egui/pull/3727)\n* Rename `TextBuffer::replace` to `replace_with` [#3751](https://github.com/emilk/egui/pull/3751)\n\n### ⭐ Added\n* Replace a special `Color32::PLACEHOLDER` with widget fallback color [#3727](https://github.com/emilk/egui/pull/3727)\n* Add `Key`s for `Cut` `Copy` `Paste` `[` `]` `,` `\\` `:` `.` `;` `+` `=`  [#3725](https://github.com/emilk/egui/pull/3725) [#3373](https://github.com/emilk/egui/pull/3373) [#3649](https://github.com/emilk/egui/pull/3649) [#3769](https://github.com/emilk/egui/pull/3769) (thanks [@MarijnS95](https://github.com/MarijnS95) and [@mkrueger](https://github.com/mkrueger)!)\n* Add `Key::from_name`, `Key::ALL` [#3649](https://github.com/emilk/egui/pull/3649)\n* Add `Event::Key::physical_key` [#3649](https://github.com/emilk/egui/pull/3649)\n* Add indeterminate state to checkbox [#3605](https://github.com/emilk/egui/pull/3605) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Add `Color32::from_hex` and `Color32::to_hex` [#3570](https://github.com/emilk/egui/pull/3570) [#3777](https://github.com/emilk/egui/pull/3777) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Add `DragValue`s for RGB(A) in the color picker [#2734](https://github.com/emilk/egui/pull/2734) (thanks [@IVAN-MK7](https://github.com/IVAN-MK7)!)\n* Add option to customize progress bar rounding [#2881](https://github.com/emilk/egui/pull/2881) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Add methods to load/store `TextEditState` undoer [#3479](https://github.com/emilk/egui/pull/3479) (thanks [@LoganDark](https://github.com/LoganDark)!)\n* `ScrollArea`: Add option to always scroll the only enabled direction [#3710](https://github.com/emilk/egui/pull/3710) (thanks [@untbu](https://github.com/untbu)!)\n\n### 🔧 Changed\n* `Grid` now follows `style.visuals.striped` if not explicitly overwritten [#3723](https://github.com/emilk/egui/pull/3723) (thanks [@Wcubed](https://github.com/Wcubed)!)\n* Allow arrow keys to move away focus from a Slider [#3641](https://github.com/emilk/egui/pull/3641) (thanks [@fornwall](https://github.com/fornwall)!)\n* Keep submenus open until another one is hovered [#3055](https://github.com/emilk/egui/pull/3055) (thanks [@DannyStoll1](https://github.com/DannyStoll1)!)\n* Highlight the header of the topmost `Window`, controlled by `Visuals.window_highlight_topmost` [#3515](https://github.com/emilk/egui/pull/3515) (thanks [@GuillaumeSchmid](https://github.com/GuillaumeSchmid)!)\n\n### 🐛 Fixed\n* Derive `serde` `Serialize` and `Deserialize` for `KeyboardShortcut` [#3694](https://github.com/emilk/egui/pull/3694) (thanks [@zeozeozeo](https://github.com/zeozeozeo)!)\n* Fix `Window` positioning bug when bad `pivot` is stored in app data [#3721](https://github.com/emilk/egui/pull/3721) (thanks [@abey79](https://github.com/abey79)!)\n* Impl `Clone` for `Fonts` [#3737](https://github.com/emilk/egui/pull/3737)\n* Add missing `ResizeDirection::East` [#3749](https://github.com/emilk/egui/pull/3749) (thanks [@dbuch](https://github.com/dbuch)!)\n* Fix: don't open context menu on drag [#3767](https://github.com/emilk/egui/pull/3767)\n* Fix IME input of `CompositionEnd` without a `CompositionStart` [#3768](https://github.com/emilk/egui/pull/3768) (thanks [@FrankLeeC](https://github.com/FrankLeeC)!)\n* Fix: allow using the full Private Use Area for custom fonts [#3509](https://github.com/emilk/egui/pull/3509) (thanks [@varphone](https://github.com/varphone)!)\n* Fix: apply edited `DragValue` when it looses focus [#3776](https://github.com/emilk/egui/pull/3776)\n* Fix: Non-resizable `Area`s now ignore mouse input outside their bounds [#3039](https://github.com/emilk/egui/pull/3039) (thanks [@fleabitdev](https://github.com/fleabitdev)!)\n* Highlight submenu buttons when hovered and open [#3780](https://github.com/emilk/egui/pull/3780)\n* Invalidate font atlas on any change to `pixels_per_point`, not matter how small [#3698](https://github.com/emilk/egui/pull/3698) (thanks [@StarStarJ](https://github.com/StarStarJ)!)\n* Fix zoom-in shortcut (`Cmd +`) on non-English keyboards [#3769](https://github.com/emilk/egui/pull/3769)\n\n\n## 0.24.1 - 2023-11-30 - Bug fixes\n* Fix buggy text with multiple viewports on monitors with different scales [#3666](https://github.com/emilk/egui/pull/3666)\n\n\n## 0.24.0 - 2023-11-23 - Multi-viewport\n\n### ✨ Highlights\nYou can now spawn multiple native windows on supported backends (e.g. `eframe`), using [the new `viewport` API](https://docs.rs/egui/latest/egui/viewport/index.html) ([#3172](https://github.com/emilk/egui/pull/3172)).\n\nYou can easily zoom any egui app using Cmd+Plus, Cmd+Minus or Cmd+0, just like in a browser ([#3608](https://github.com/emilk/egui/pull/3608)).\n\nScrollbars are now hidden by default until you hover the `ScrollArea` ([#3539](https://github.com/emilk/egui/pull/3539)).\n\n### ⭐ Added\n* Multiple viewports/windows [#3172](https://github.com/emilk/egui/pull/3172) (thanks [@konkitoman](https://github.com/konkitoman)!)\n* Introduce global `zoom_factor` [#3608](https://github.com/emilk/egui/pull/3608)\n* Floating scroll bars [#3539](https://github.com/emilk/egui/pull/3539)\n* Add redo support to `Undoer` [#3478](https://github.com/emilk/egui/pull/3478) (thanks [@LoganDark](https://github.com/LoganDark)!)\n* Add `egui::Vec2b` [#3543](https://github.com/emilk/egui/pull/3543)\n* Add max `Window` size & other size helpers [#3537](https://github.com/emilk/egui/pull/3537) (thanks [@arduano](https://github.com/arduano)!)\n* Allow changing shape of slider handle [#3429](https://github.com/emilk/egui/pull/3429) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* `RawInput::viewports` contains a list of all viewports. Access the current one with `ctx.input(|i| i.viewport())`\n\n### 🔧 Changed\n* Replace `Id::null()` with `Id::NULL` [#3544](https://github.com/emilk/egui/pull/3544)\n* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)\n* Update puffin to 0.18 [#3600](https://github.com/emilk/egui/pull/3600)\n\n### 🐛 Fixed\n* Fix upside down slider in the vertical orientation [#3424](https://github.com/emilk/egui/pull/3424) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Make slider step account for range start [#3488](https://github.com/emilk/egui/pull/3488) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Fix rounding of `ImageButton` [#3531](https://github.com/emilk/egui/pull/3531) (thanks [@chriscate](https://github.com/chriscate)!)\n* Fix naming: `constraint_to` -> `constrain_to` [#3438](https://github.com/emilk/egui/pull/3438) (thanks [@rinde](https://github.com/rinde)!)\n* Fix Shift+Tab behavior when no widget is focused [#3498](https://github.com/emilk/egui/pull/3498) (thanks [@DataTriny](https://github.com/DataTriny)!)\n* Fix scroll not sticking when scrollbar is hidden [#3434](https://github.com/emilk/egui/pull/3434) (thanks [@LoganDark](https://github.com/LoganDark)!)\n* Add `#[inline]` to all builder-pattern functions [#3557](https://github.com/emilk/egui/pull/3557)\n* Properly reverse bool animation if value changes before it's finished [#3577](https://github.com/emilk/egui/pull/3577) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n\n\n### ⚠️ BREAKING\n* `egui::gui_zoom::zoom_with_keyboard_shortcuts` is gone, replaced with `Options::zoom_with_keyboard`, which is `true` by default\n* `Spacing::scroll_bar_X` has been moved to `Spacing::scroll_bar.X`\n* `Context::set_pixels_per_point` now calls `Context::set_zoom_level`, and it may make sense for you to call that directly instead\n* If you are using `eframe`, check out the breaking changes in [the `eframe` changelog](crates/eframe/CHANGELOG.md)\n\n#### For integrations\nThere are several changes relevant to integrations.\n\n* Added `crate::RawInput::viewports` with information about all active viewports\n* The repaint callback set by `Context::set_request_repaint_callback` now points to which viewport should be repainted\n* `Context::run` now returns a list of `ViewportOutput` in `FullOutput` which should result in their own independent windows\n* There is a new `Context::set_immediate_viewport_renderer` for setting up the immediate viewport integration\n* If you support viewports, you need to call `Context::set_embed_viewports(false)`, or all new viewports will be embedded (the default behavior)\n\n\n## 0.23.0 - 2023-09-27 - New image API\nThis release contains a simple and powerful image API:\n\n```rs\n// Load from web:\nui.image(\"https://www.example.com/some_image.png\");\n\n// Include image in the binary using `include_bytes`:\nui.image(egui::include_image!(\"../assets/ferris.svg\"));\n\n// With options:\nui.add(\n    egui::Image::new(\"file://path/to/image.jpg\")\n        .max_width(200.0)\n        .rounding(10.0),\n);\n```\n\nThe API is based on a plugin-system, where you can tell `egui` how to load the images, and from where.\n\n`egui_extras` comes with loaders for you, so all you need to do is add the following to your `Cargo.toml`:\n\n```toml\negui_extras = { version = \"0.23\", features = [\"all_loaders\"] }\nimage = { version = \"0.24\", features = [\"jpeg\", \"png\"] } # Add the types you want support for\n```\n\nAnd this to your code:\n\n```rs\negui_extras::install_image_loaders(egui_ctx);\n```\n\n### ⚠️ BREAKING\n* Update MSRV to Rust 1.70.0 [#3310](https://github.com/emilk/egui/pull/3310)\n* Break out plotting to own crate `egui_plot` [#3282](https://github.com/emilk/egui/pull/3282)\n\n### ⭐ Added\n* A new image API [#3297](https://github.com/emilk/egui/pull/3297) [#3315](https://github.com/emilk/egui/pull/3315) [#3328](https://github.com/emilk/egui/pull/3328) [#3338](https://github.com/emilk/egui/pull/3338) [#3342](https://github.com/emilk/egui/pull/3342) [#3343](https://github.com/emilk/egui/pull/3343) [#3402](https://github.com/emilk/egui/pull/3402) (thanks [@jprochazk](https://github.com/jprochazk)!)\n* Add option to truncate text at some width [#3244](https://github.com/emilk/egui/pull/3244)\n* Add control of line height and letter spacing [#3302](https://github.com/emilk/egui/pull/3302)\n* Support images with rounded corners [#3257](https://github.com/emilk/egui/pull/3257)\n* Change focused widget with arrow keys [#3272](https://github.com/emilk/egui/pull/3272) (thanks [@TimonPost](https://github.com/TimonPost)!)\n* Add opt-in `puffin` feature to egui [#3298](https://github.com/emilk/egui/pull/3298)\n* Add debug-option to show a callstack to the widget under the mouse and removed the `trace!` macro as this is more useful [#3391](https://github.com/emilk/egui/pull/3391)\n* Add `Context::open_url` and `Context::copy_text` [#3380](https://github.com/emilk/egui/pull/3380)\n* Add  `Area::constrain_to` and `Window::constrain_to` [#3396](https://github.com/emilk/egui/pull/3396)\n* Add `Memory::area_rect` [#3161](https://github.com/emilk/egui/pull/3161) (thanks [@tosti007](https://github.com/tosti007)!)\n* Add `Margin::expand_rect` and `shrink_rect` [#3214](https://github.com/emilk/egui/pull/3214)\n* Provide `into_inner()` for `egui::mutex::{Mutex, RwLock}` [#3110](https://github.com/emilk/egui/pull/3110) (thanks [@KmolYuan](https://github.com/KmolYuan)!)\n* Support multi-threaded Wasm [#3236](https://github.com/emilk/egui/pull/3236)\n* Change touch force to be `Option<f32>` instead of `f32` [#3240](https://github.com/emilk/egui/pull/3240) (thanks [@lucasmerlin](https://github.com/lucasmerlin)!)\n* Add option to always open hyperlink in a new browser tab [#3242](https://github.com/emilk/egui/pull/3242) (thanks [@FreddyFunk](https://github.com/FreddyFunk)!)\n* Add `Window::drag_to_scroll` [#3118](https://github.com/emilk/egui/pull/3118) (thanks [@KYovchevski](https://github.com/KYovchevski)!)\n* Add `CollapsingState::remove` to clear stored state [#3252](https://github.com/emilk/egui/pull/3252) (thanks [@dmackdev](https://github.com/dmackdev)!)\n* Add tooltip_delay option [#3245](https://github.com/emilk/egui/pull/3245) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Added `Context::is_context_menu_open()` [#3267](https://github.com/emilk/egui/pull/3267) (thanks [@dmlary](https://github.com/dmlary)!)\n* Add `mime` field to `DroppedFile` [#3273](https://github.com/emilk/egui/pull/3273) (thanks [@abey79](https://github.com/abey79)!)\n* Allow setting the progress bar height [#3183](https://github.com/emilk/egui/pull/3183) (thanks [@s-nie](https://github.com/s-nie)!)\n* Add `scroll_area::State::velocity` [#3300](https://github.com/emilk/egui/pull/3300) (thanks [@Barugon](https://github.com/Barugon)!)\n* Add `Visuals::interact_cursor` [#3312](https://github.com/emilk/egui/pull/3312) (thanks [@zkldi](https://github.com/zkldi)!)\n* Add method to `RichText` making it easier to construct layout jobs [#3319](https://github.com/emilk/egui/pull/3319) (thanks [@OmegaJak](https://github.com/OmegaJak)!)\n* Add `Context::style_mut` [#3359](https://github.com/emilk/egui/pull/3359)\n* `std::borrow::Cow<'_, str>` now implements `TextBuffer` [#3164](https://github.com/emilk/egui/pull/3164) (thanks [@burtonageo](https://github.com/burtonageo)!)\n\n### 🔧 Changed\n* Separate text cursor from selection visuals [#3181](https://github.com/emilk/egui/pull/3181) (thanks [@lampsitter](https://github.com/lampsitter)!)\n* `DragValue`: update value on each key press by default [#2880](https://github.com/emilk/egui/pull/2880) (thanks [@Barugon](https://github.com/Barugon)!)\n* Replace uses of `RangeInclusive<f32>` with `emath::Rangef` [#3221](https://github.com/emilk/egui/pull/3221)\n* Implement `Send + Sync` for `ColorPickerFn` and `Ui` (#3148) [#3233](https://github.com/emilk/egui/pull/3233) (thanks [@idanarye](https://github.com/idanarye)!)\n* Use the minus character instead of \"dash\" [#3271](https://github.com/emilk/egui/pull/3271)\n* Changing `menu_image_button` to use `ImageButton` builder [#3288](https://github.com/emilk/egui/pull/3288) (thanks [@v-kat](https://github.com/v-kat)!)\n* Prune old egui memory data when reaching some limit [#3299](https://github.com/emilk/egui/pull/3299)\n\n### 🐛 Fixed\n* Fix TextEdit's character limit [#3173](https://github.com/emilk/egui/pull/3173) (thanks [@Serverator](https://github.com/Serverator)!)\n* Set the correct unicode character for \"ctrl\" shortcuts [#3186](https://github.com/emilk/egui/pull/3186) (thanks [@abey79](https://github.com/abey79)!)\n* Fix crash in `DragValue` when only setting `min_decimals` [#3231](https://github.com/emilk/egui/pull/3231)\n* Fix clipping issued with `ScrollArea` [#2860](https://github.com/emilk/egui/pull/2860) (thanks [@Barugon](https://github.com/Barugon)!)\n* Fix moving slider with arrow keys [#3354](https://github.com/emilk/egui/pull/3354)\n* Fix problems with tabs in text [#3355](https://github.com/emilk/egui/pull/3355)\n* Fix interaction with moved color-picker [#3395](https://github.com/emilk/egui/pull/3395)\n\n\n\n## 0.22.0 - 2023-05-23 - A plethora of small improvements\n### ⭐ Added\n* Scroll bar visibility options [#2729](https://github.com/emilk/egui/pull/2729) (thanks [@IVAN-MK7](https://github.com/IVAN-MK7)!)\n* Add `Grid::with_row_color` [#2519](https://github.com/emilk/egui/pull/2519) (thanks [@imgurbot12](https://github.com/imgurbot12)!)\n* Add raw mouse wheel event [#2782](https://github.com/emilk/egui/pull/2782) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Improved plot groups and bounds handling [#2410](https://github.com/emilk/egui/pull/2410) (thanks [@s-nie](https://github.com/s-nie)!)\n* Return plot transforms [#2935](https://github.com/emilk/egui/pull/2935)\n* Add `Pointer::is_decidedly_dragging` and `could_any_button_be_click` [#2979](https://github.com/emilk/egui/pull/2979)\n* Plot widget - allow disabling zoom and drag for x and y separately [#2901](https://github.com/emilk/egui/pull/2901) (thanks [@OmegaJak](https://github.com/OmegaJak)!)\n* Add character limit to `TextEdit` [#2816](https://github.com/emilk/egui/pull/2816) (thanks [@wzid](https://github.com/wzid)!)\n* Add `egui::Modifiers::contains` [#2989](https://github.com/emilk/egui/pull/2989) (thanks [@Wumpf](https://github.com/Wumpf)!)\n\n### 🔧 Changed\n* Improve vertical alignment of fonts [#2724](https://github.com/emilk/egui/pull/2724) (thanks [@lictex](https://github.com/lictex)!)\n* Transpose the value/satuation panel of the color picker [#2727](https://github.com/emilk/egui/pull/2727) (thanks [@IVAN-MK7](https://github.com/IVAN-MK7)!)\n* Replace `ComboBox::show_index` `String` with `Into<TextWidget>` [#2790](https://github.com/emilk/egui/pull/2790) (thanks [@tosti007](https://github.com/tosti007)!)\n* Replace `tracing` with `log` [#2928](https://github.com/emilk/egui/pull/2928)\n* Only show id clash warnings in debug builds by default [#2930](https://github.com/emilk/egui/pull/2930)\n* ⚠️ BREAKING: `Plot::link_axis` and `Plot::link_cursor` now take the name of the group [#2410](https://github.com/emilk/egui/pull/2410)\n\n### 🐛 Fixed\n* Clear all keys and modifies on focus change, fixing \"stuck keys\" [#2933](https://github.com/emilk/egui/pull/2933)\n* Fix deadlock when using `show_blocking_widget` [#2753](https://github.com/emilk/egui/pull/2753) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Fix the OS check for windows [#2832](https://github.com/emilk/egui/pull/2832) (thanks [@jleibs](https://github.com/jleibs)!)\n* Fix scroll bars not appearing (#2826) [#2827](https://github.com/emilk/egui/pull/2827) (thanks [@lunixbochs](https://github.com/lunixbochs)!)\n* Fix UI `data()` read mutability [#2742](https://github.com/emilk/egui/pull/2742) (thanks [@IS2511](https://github.com/IS2511)!)\n* Menu State rect now uses menu frame rect instead of contents rect [#2886](https://github.com/emilk/egui/pull/2886) (thanks [@hats-np](https://github.com/hats-np)!)\n* Hide `Response::triple_clicked` in docs [#2867](https://github.com/emilk/egui/pull/2867) (thanks [@ccaven](https://github.com/ccaven)!)\n* `request_repaint_after` works even when called from background thread [#2939](https://github.com/emilk/egui/pull/2939)\n* Show alt key on Mac as `\"Option\"`, not `\"Alt\"` [#2981](https://github.com/emilk/egui/pull/2981) (thanks [@Wumpf](https://github.com/Wumpf)!)\n* Mention `store` in `TextEditState` doc comment [#2988](https://github.com/emilk/egui/pull/2988) (thanks [@fxdave](https://github.com/fxdave)!)\n* Fix typos [#2866](https://github.com/emilk/egui/pull/2866) (thanks [@fezjo](https://github.com/fezjo)!)\n\n\n### ✨ Examples\n* Fix resizable columns option in the table demo [#2780](https://github.com/emilk/egui/pull/2780) (thanks [@Bobo1239](https://github.com/Bobo1239)!)\n* Update serial window example [#2756](https://github.com/emilk/egui/pull/2756) (thanks [@c-git](https://github.com/c-git)!)\n* Demo app: use `enum` instead of strings for demo-selector anchor [#2781](https://github.com/emilk/egui/pull/2781) (thanks [@XyLyXyRR](https://github.com/XyLyXyRR)!)\n* Use `env_logger` in all examples [#2934](https://github.com/emilk/egui/pull/2934)\n* Rename `examples/user_attention/README.mg` to `README.md` [#2948](https://github.com/emilk/egui/pull/2948) (thanks [@MAlba124](https://github.com/MAlba124)!)\n* egui_demo_app: add some native window info [b5c24d6](https://github.com/emilk/egui/commit/b5c24d6ec83112440f1a807d5ec79241ea8b40fe)\n\n\n\n## 0.21.0 - 2023-02-08 - Deadlock fix and style customizability\n* ⚠️ BREAKING: `egui::Context` now use closures for locking ([#2625](https://github.com/emilk/egui/pull/2625)):\n  * `ctx.input().key_pressed(Key::A)` -> `ctx.input(|i| i.key_pressed(Key::A))`\n  * `ui.memory().toggle_popup(popup_id)` -> `ui.memory_mut(|mem| mem.toggle_popup(popup_id))`\n\n### ⭐ Added\n* Add `Response::drag_started_by` and `Response::drag_released_by` for convenience, similar to `dragged` and `dragged_by` ([#2507](https://github.com/emilk/egui/pull/2507)).\n* Add `PointerState::*_pressed` to check if the given button was pressed in this frame ([#2507](https://github.com/emilk/egui/pull/2507)).\n* `Event::Key` now has a `repeat` field that is set to `true` if the event was the result of a key-repeat ([#2435](https://github.com/emilk/egui/pull/2435)).\n* Add `Slider::drag_value_speed`, which lets you ask for finer precision when dragging the slider value rather than the actual slider.\n* Add `Memory::any_popup_open`, which returns true if any popup is currently open ([#2464](https://github.com/emilk/egui/pull/2464)).\n* Add `Plot::clamp_grid` to only show grid where there is data ([#2480](https://github.com/emilk/egui/pull/2480)).\n* Add `ScrollArea::drag_to_scroll` if you want to turn off that feature.\n* Add `Response::on_hover_and_drag_cursor`.\n* Add `Window::default_open` ([#2539](https://github.com/emilk/egui/pull/2539)).\n* Add `ProgressBar::fill` if you want to set the fill color manually. ([#2618](https://github.com/emilk/egui/pull/2618)).\n* Add `Button::rounding` to enable round buttons ([#2616](https://github.com/emilk/egui/pull/2616)).\n* Add `WidgetVisuals::optional_bg_color` - set it to `Color32::TRANSPARENT` to hide button backgrounds ([#2621](https://github.com/emilk/egui/pull/2621)).\n* Add `Context::screen_rect` and `Context::set_cursor_icon` ([#2625](https://github.com/emilk/egui/pull/2625)).\n* You can turn off the vertical line left of indented regions with `Visuals::indent_has_left_vline` ([#2636](https://github.com/emilk/egui/pull/2636)).\n* Add `Response.highlight` to highlight a widget ([#2632](https://github.com/emilk/egui/pull/2632)).\n* Add `Separator::grow` and `Separator::shrink` ([#2665](https://github.com/emilk/egui/pull/2665)).\n* Add `Slider::trailing_fill` for trailing color behind the circle like a `ProgressBar` ([#2660](https://github.com/emilk/egui/pull/2660)).\n\n### 🔧 Changed\n* Improved plot grid appearance ([#2412](https://github.com/emilk/egui/pull/2412)).\n* Improved the algorithm for picking the number of decimals to show when hovering values in the `Plot`.\n* Default `ComboBox` is now controlled with `Spacing::combo_width` ([#2621](https://github.com/emilk/egui/pull/2621)).\n* `DragValue` and `Slider` now use the proportional font ([#2638](https://github.com/emilk/egui/pull/2638)).\n* `ScrollArea` is less aggressive about clipping its contents ([#2665](https://github.com/emilk/egui/pull/2665)).\n* Updated to be compatible with a major breaking change in AccessKit that drastically reduces memory usage when accessibility is enabled ([#2678](https://github.com/emilk/egui/pull/2678)).\n* Improve `DragValue` behavior ([#2649](https://github.com/emilk/egui/pull/2649), [#2650](https://github.com/emilk/egui/pull/2650), [#2688](https://github.com/emilk/egui/pull/2688), [#2638](https://github.com/emilk/egui/pull/2638)).\n\n### 🐛 Fixed\n* Trigger `PointerEvent::Released` for drags ([#2507](https://github.com/emilk/egui/pull/2507)).\n* Expose `TextEdit`'s multiline flag to AccessKit ([#2448](https://github.com/emilk/egui/pull/2448)).\n* Don't render `\\r` (Carriage Return) ([#2452](https://github.com/emilk/egui/pull/2452)).\n* The `button_padding` style option works closer as expected with image+text buttons now ([#2510](https://github.com/emilk/egui/pull/2510)).\n* Menus are now moved to fit on the screen.\n* Fix `Window::pivot` causing windows to move around ([#2694](https://github.com/emilk/egui/pull/2694)).\n\n\n## 0.20.1 - 2022-12-11 - Fix key-repeat\n### 🔧 Changed\n* `InputState`: all press functions again include key repeats (like in egui 0.19) ([#2429](https://github.com/emilk/egui/pull/2429)).\n* Improve the look of thin white lines ([#2437](https://github.com/emilk/egui/pull/2437)).\n\n### 🐛 Fixed\n* Fix key-repeats for `TextEdit`, `Slider`s, etc ([#2429](https://github.com/emilk/egui/pull/2429)).\n\n\n## 0.20.0 - 2022-12-08 - AccessKit, prettier text, overlapping widgets\n* MSRV (Minimum Supported Rust Version) is now `1.65.0` ([#2314](https://github.com/emilk/egui/pull/2314)).\n* ⚠️ BREAKING: egui now expects integrations to do all color blending in gamma space ([#2071](https://github.com/emilk/egui/pull/2071)).\n* ⚠️ BREAKING: if you have overlapping interactive widgets, only the top widget (last added) will be interactive ([#2244](https://github.com/emilk/egui/pull/2244)).\n\n### ⭐ Added\n* Added helper functions for animating panels that collapse/expand ([#2190](https://github.com/emilk/egui/pull/2190)).\n* Added `Context::os/Context::set_os` to query/set what operating system egui believes it is running on ([#2202](https://github.com/emilk/egui/pull/2202)).\n* Added `Button::shortcut_text` for showing keyboard shortcuts in menu buttons ([#2202](https://github.com/emilk/egui/pull/2202)).\n* Added `egui::KeyboardShortcut` for showing keyboard shortcuts in menu buttons ([#2202](https://github.com/emilk/egui/pull/2202)).\n* Texture loading now takes a `TextureOptions` with minification and magnification filters ([#2224](https://github.com/emilk/egui/pull/2224)).\n* Added `Key::Minus` and `Key::Equals` ([#2239](https://github.com/emilk/egui/pull/2239)).\n* Added `egui::gui_zoom` module with helpers for scaling the whole GUI of an app ([#2239](https://github.com/emilk/egui/pull/2239)).\n* You can now put one interactive widget on top of another, and only one will get interaction at a time ([#2244](https://github.com/emilk/egui/pull/2244)).\n* Added `spacing.menu_margin` for customizing menu spacing ([#2036](https://github.com/emilk/egui/pull/2036))\n* Added possibility to enable text wrap for the selected text of `egui::ComboBox` ([#2272](https://github.com/emilk/egui/pull/2272))\n* Added `Area::constrain` and `Window::constrain` which constrains area to the screen bounds ([#2270](https://github.com/emilk/egui/pull/2270)).\n* Added `Area::pivot` and `Window::pivot` which controls what part of the window to position ([#2303](https://github.com/emilk/egui/pull/2303)).\n* Added support for [thin space](https://en.wikipedia.org/wiki/Thin_space).\n* Added optional integration with [AccessKit](https://accesskit.dev/) for implementing platform accessibility APIs ([#2294](https://github.com/emilk/egui/pull/2294)).\n* Added `panel_fill`, `window_fill` and `window_stroke` to `Visuals` for your theming pleasure ([#2406](https://github.com/emilk/egui/pull/2406)).\n* Plots:\n  * Allow linking plot cursors ([#1722](https://github.com/emilk/egui/pull/1722)).\n  * Added `Plot::auto_bounds_x/y` and `Plot::reset` ([#2029](https://github.com/emilk/egui/pull/2029)).\n  * Added `PlotUi::translate_bounds` ([#2145](https://github.com/emilk/egui/pull/2145)).\n  * Added `PlotUi::set_plot_bounds` ([#2320](https://github.com/emilk/egui/pull/2320)).\n  * Added `PlotUi::plot_secondary_clicked` ([#2318](https://github.com/emilk/egui/pull/2318)).\n\n### 🔧 Changed\n* Panels always have a separator line, but no stroke on other sides. Their spacing has also changed slightly ([#2261](https://github.com/emilk/egui/pull/2261)).\n* Tooltips are only shown when mouse pointer is still ([#2263](https://github.com/emilk/egui/pull/2263)).\n* Make it slightly easier to click buttons ([#2304](https://github.com/emilk/egui/pull/2304)).\n* `egui::color` has been renamed `egui::ecolor` ([#2399](https://github.com/emilk/egui/pull/2399)).\n\n### 🐛 Fixed\n* ⚠️ BREAKING: Fix text being too small ([#2069](https://github.com/emilk/egui/pull/2069)).\n* Improve mixed CJK/Latin line-breaking ([#1986](https://github.com/emilk/egui/pull/1986)).\n* Improved text rendering ([#2071](https://github.com/emilk/egui/pull/2071)).\n* Constrain menu popups to the screen ([#2191](https://github.com/emilk/egui/pull/2191)).\n* Less jitter when calling `Context::set_pixels_per_point` ([#2239](https://github.com/emilk/egui/pull/2239)).\n* Fixed popups and color edit going outside the screen.\n* Fixed keyboard support in `DragValue` ([#2342](https://github.com/emilk/egui/pull/2342)).\n* If you nest `ScrollAreas` inside each other, the inner area will now move its scroll bar so it is always visible ([#2371](https://github.com/emilk/egui/pull/2371)).\n* Ignore key-repeats for `input.key_pressed` ([#2334](https://github.com/emilk/egui/pull/2334), [#2389](https://github.com/emilk/egui/pull/2389)).\n* Fixed issue with calling `set_pixels_per_point` each frame ([#2352](https://github.com/emilk/egui/pull/2352)).\n* Fix bug in `ScrollArea::show_rows` ([#2258](https://github.com/emilk/egui/pull/2258)).\n* Fix bug in `plot::Line::fill` ([#2275](https://github.com/emilk/egui/pull/2275)).\n* Only emit `changed` events in `radio_value` and `selectable_value` if the value actually changed ([#2343](https://github.com/emilk/egui/pull/2343)).\n* Fixed sizing bug in `Grid` ([#2384](https://github.com/emilk/egui/pull/2384)).\n* `ComboBox::width` now correctly sets the outer width ([#2406](https://github.com/emilk/egui/pull/2406)).\n\n\n## 0.19.0 - 2022-08-20\n### ⭐ Added\n* Added `*_released` & `*_clicked` methods for `PointerState` ([#1582](https://github.com/emilk/egui/pull/1582)).\n* Added `PointerButton::Extra1` and `PointerButton::Extra2` ([#1592](https://github.com/emilk/egui/pull/1592)).\n* Added `egui::hex_color!` to create `Color32`'s from hex strings under the `color-hex` feature ([#1596](https://github.com/emilk/egui/pull/1596)).\n* Optimized painting of filled circles (e.g. for scatter plots) by 10x or more ([#1616](https://github.com/emilk/egui/pull/1616)).\n* Added opt-in feature `deadlock_detection` to detect double-lock of mutexes on the same thread ([#1619](https://github.com/emilk/egui/pull/1619)).\n* Added `InputState::stable_dt`: a more stable estimate for the delta-time in reactive mode ([#1625](https://github.com/emilk/egui/pull/1625)).\n* You can now specify a texture filter for your textures ([#1636](https://github.com/emilk/egui/pull/1636)).\n* Added functions keys in `egui::Key` ([#1665](https://github.com/emilk/egui/pull/1665)).\n* Added support for using `PaintCallback` shapes with the WGPU backend ([#1684](https://github.com/emilk/egui/pull/1684)).\n* Added `Context::request_repaint_after` ([#1694](https://github.com/emilk/egui/pull/1694)).\n* `ctrl-h` now acts like backspace in `TextEdit` ([#1812](https://github.com/emilk/egui/pull/1812)).\n* Added `custom_formatter` method for `Slider` and `DragValue` ([#1851](https://github.com/emilk/egui/issues/1851)).\n* Added `RawInput::has_focus` which backends can set to indicate whether the UI as a whole has the keyboard focus ([#1859](https://github.com/emilk/egui/pull/1859)).\n* Added `PointerState::button_double_clicked()` and `PointerState::button_triple_clicked()` ([#1906](https://github.com/emilk/egui/issues/1906)).\n* Added `custom_formatter`, `binary`, `octal`, and `hexadecimal` to `DragValue` and `Slider` ([#1953](https://github.com/emilk/egui/issues/1953))\n\n### 🔧 Changed\n* MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)).\n* `PaintCallback` shapes now require the whole callback to be put in an `Arc<dyn Any>` with the value being a backend-specific callback type ([#1684](https://github.com/emilk/egui/pull/1684)).\n* Replaced `needs_repaint` in `FullOutput` with `repaint_after`. Used to force repaint after the set duration in reactive mode ([#1694](https://github.com/emilk/egui/pull/1694)).\n* `Layout::left_to_right` and `Layout::right_to_left` now takes the vertical align as an argument. Previous default was `Align::Center`.\n* Improved ergonomics of adding plot items. All plot items that take a series of 2D coordinates can now be created directly from `Vec<[f64; 2]>`. The `Value` and `Values` types were removed in favor of `PlotPoint` and `PlotPoints` respectively ([#1816](https://github.com/emilk/egui/pull/1816)).\n* `TextBuffer` no longer needs to implement `AsRef<str>` ([#1824](https://github.com/emilk/egui/pull/1824)).\n\n### 🐛 Fixed\n* Fixed `Response::changed` for `ui.toggle_value` ([#1573](https://github.com/emilk/egui/pull/1573)).\n* Fixed `ImageButton`'s changing background padding on hover ([#1595](https://github.com/emilk/egui/pull/1595)).\n* Fixed `Plot` auto-bounds bug ([#1599](https://github.com/emilk/egui/pull/1599)).\n* Fixed dead-lock when alt-tabbing while also showing a tooltip ([#1618](https://github.com/emilk/egui/pull/1618)).\n* Fixed `ScrollArea` scrolling when editing an unrelated `TextEdit` ([#1779](https://github.com/emilk/egui/pull/1779)).\n* Fixed `Slider` not always generating events on change ([#1854](https://github.com/emilk/egui/pull/1854)).\n* Fixed jitter of anchored windows for the first frame ([#1856](https://github.com/emilk/egui/pull/1856)).\n* Fixed focus behavior when pressing Tab in a UI with no focused widget ([#1861](https://github.com/emilk/egui/pull/1861)).\n* Fixed automatic plot bounds ([#1865](https://github.com/emilk/egui/pull/1865)).\n\n\n## 0.18.1 - 2022-05-01\n* Change `Shape::Callback` from `&dyn Any` to `&mut dyn Any` to support more backends.\n\n\n## 0.18.0 - 2022-04-30\n\n### ⭐ Added\n* Added `Shape::Callback` for backend-specific painting, [with an example](https://github.com/emilk/egui/tree/main/examples/custom_3d_glow) ([#1351](https://github.com/emilk/egui/pull/1351)).\n* Added `Frame::canvas` ([#1362](https://github.com/emilk/egui/pull/1362)).\n* `Context::request_repaint` will now wake up UI thread, if integrations has called `Context::set_request_repaint_callback` ([#1366](https://github.com/emilk/egui/pull/1366)).\n* Added `Plot::allow_scroll`, `Plot::allow_zoom` no longer affects scrolling ([#1382](https://github.com/emilk/egui/pull/1382)).\n* Added `Ui::push_id` to resolve id clashes ([#1374](https://github.com/emilk/egui/pull/1374)).\n* Added `ComboBox::icon` ([#1405](https://github.com/emilk/egui/pull/1405)).\n* Added `Ui::scroll_with_delta`.\n* Added `Frame::outer_margin`.\n* Added `Painter::hline` and `Painter::vline`.\n* Added `Link` and `ui.link` ([#1506](https://github.com/emilk/egui/pull/1506)).\n* Added triple-click support; triple-clicking a TextEdit field will select the whole paragraph ([#1512](https://github.com/emilk/egui/pull/1512)).\n* Added `Plot::x_grid_spacer` and `Plot::y_grid_spacer` for custom grid spacing ([#1180](https://github.com/emilk/egui/pull/1180)).\n* Added `Ui::spinner()` shortcut method ([#1494](https://github.com/emilk/egui/pull/1494)).\n* Added `CursorIcon`s for resizing columns, rows, and the eight cardinal directions.\n* Added `Ui::toggle_value`.\n* Added ability to add any widgets to the header of a collapsing region ([#1538](https://github.com/emilk/egui/pull/1538)).\n\n### 🔧 Changed\n* MSRV (Minimum Supported Rust Version) is now `1.60.0` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* `ClippedMesh` has been replaced with `ClippedPrimitive` ([#1351](https://github.com/emilk/egui/pull/1351)).\n* Renamed `Frame::margin` to `Frame::inner_margin`.\n* Renamed `AlphaImage` to `FontImage` to discourage any other use for it ([#1412](https://github.com/emilk/egui/pull/1412)).\n* Warnings will be painted on screen when there is an `Id` clash for `Grid`, `Plot` or `ScrollArea` ([#1452](https://github.com/emilk/egui/pull/1452)).\n* `Checkbox` and `RadioButton` with an empty label (`\"\"`) will now take up much less space ([#1456](https://github.com/emilk/egui/pull/1456)).\n* Replaced `Memory::top_most_layer` with more flexible `Memory::layer_ids`.\n* Renamed the feature `convert_bytemuck` to `bytemuck` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Renamed the feature `serialize` to `serde` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Renamed `Painter::sub_region` to `Painter::with_clip_rect`.\n\n### 🐛 Fixed\n* Fixed `ComboBox`es always being rendered left-aligned ([#1304](https://github.com/emilk/egui/pull/1304)).\n* Fixed ui code that could lead to a deadlock ([#1380](https://github.com/emilk/egui/pull/1380)).\n* Text is darker and more readable in bright mode ([#1412](https://github.com/emilk/egui/pull/1412)).\n* Fixed a lot of broken/missing doclinks  ([#1419](https://github.com/emilk/egui/pull/1419)).\n* Fixed `Ui::add_visible` sometimes leaving the `Ui` in a disabled state ([#1436](https://github.com/emilk/egui/issues/1436)).\n* Added line breaking rules for Japanese text ([#1498](https://github.com/emilk/egui/pull/1498)).\n\n### ☢️ Deprecated\n* Deprecated `CollapsingHeader::selectable` ([#1538](https://github.com/emilk/egui/pull/1538)).\n\n### 🔥 Removed\n* Removed the `single_threaded/multi_threaded` flags - egui is now always thread-safe ([#1390](https://github.com/emilk/egui/pull/1390)).\n\n### Contributors 🙏\n* [4JX](https://github.com/4JX)\n* [a-liashenko](https://github.com/a-liashenko)\n* [ascclemens](https://github.com/ascclemens)\n* [awaken1ng](https://github.com/awaken1ng)\n* [bigfarts](https://github.com/bigfarts)\n* [bobyclaws](https://github.com/bobyclaws)\n* [Bromeon](https://github.com/Bromeon)\n* [cloudhead](https://github.com/cloudhead)\n* [collin-kemper](https://github.com/collin-kemper)\n* [cpterry](https://github.com/cpterry)\n* [dbuch](https://github.com/dbuch)\n* [DusterTheFirst](https://github.com/DusterTheFirst)\n* [Edgeworth ](https://github.com/Edgeworth )\n* [elwerene](https://github.com/elwerene)\n* [follower](https://github.com/follower)\n* [Friz64](https://github.com/Friz64)\n* [Hunter522 ](https://github.com/Hunter522 )\n* [Jake-Shadle](https://github.com/Jake-Shadle)\n* [jean-airoldie ](https://github.com/jean-airoldie )\n* [JelNiSlaw](https://github.com/JelNiSlaw)\n* [juancampa](https://github.com/juancampa)\n* [LU15W1R7H](https://github.com/LU15W1R7H)\n* [mbillingr](https://github.com/mbillingr)\n* [nicklasmoeller](https://github.com/nicklasmoeller)\n* [rukai](https://github.com/rukai)\n* [tami5](https://github.com/tami5)\n* [Titaniumtown](https://github.com/Titaniumtown)\n* [trevyn](https://github.com/trevyn)\n* [waynr](https://github.com/waynr)\n* [zam-5 ](https://github.com/zam-5 )\n\n\n## 0.17.0 - 2022-02-22 - Improved font selection and image handling\n\n### ⭐ Added\n* Much improved font selection ([#1154](https://github.com/emilk/egui/pull/1154)):\n  * You can now select any font size and family using `RichText::size` amd `RichText::family` and the new `FontId`.\n  * Easily change text styles with `Style::text_styles`.\n  * Added `Ui::text_style_height`.\n  * Added `TextStyle::resolve`.\n  * Made the v-align and scale of user fonts tweakable ([#1241](https://github.com/emilk/egui/pull/1027)).\n* Plot:\n  * Added `Plot::x_axis_formatter` and `Plot::y_axis_formatter` for custom axis labels ([#1130](https://github.com/emilk/egui/pull/1130)).\n  * Added `Plot::allow_boxed_zoom()`, `Plot::boxed_zoom_pointer()` for boxed zooming on plots ([#1188](https://github.com/emilk/egui/pull/1188)).\n  * Added plot pointer coordinates with `Plot::coordinates_formatter` ([#1235](https://github.com/emilk/egui/pull/1235)).\n  * Added linked axis support for plots via `plot::LinkedAxisGroup` ([#1184](https://github.com/emilk/egui/pull/1184)).\n* `Context::load_texture` to convert an image into a texture which can be displayed using e.g. `ui.image(texture, size)` ([#1110](https://github.com/emilk/egui/pull/1110)).\n* `Ui::input_mut` to modify how subsequent widgets see the `InputState` and a convenience method `InputState::consume_key` for shortcuts or hotkeys ([#1212](https://github.com/emilk/egui/pull/1212)).\n* Added `Ui::add_visible` and `Ui::add_visible_ui`.\n* Added `CollapsingHeader::icon` to override the default open/close icon using a custom function. ([1147](https://github.com/emilk/egui/pull/1147)).\n* Added `ui.data()`, `ctx.data()`, `ctx.options()` and `ctx.tessellation_options()` ([#1175](https://github.com/emilk/egui/pull/1175)).\n* Added `Response::on_hover_text_at_pointer` as a convenience akin to `Response::on_hover_text` ([1179](https://github.com/emilk/egui/pull/1179)).\n* Opt-in dependency on `tracing` crate for logging warnings ([#1192](https://github.com/emilk/egui/pull/1192)).\n* Added `ui.weak(text)`.\n* Added `Slider::step_by` ([1225](https://github.com/emilk/egui/pull/1225)).\n* Added `Context::move_to_top` and `Context::top_most_layer` for managing the layer on the top ([#1242](https://github.com/emilk/egui/pull/1242)).\n* Support a subset of macOS' emacs input field keybindings in `TextEdit` ([#1243](https://github.com/emilk/egui/pull/1243)).\n* Added ability to scroll a UI into view without specifying an alignment ([1247](https://github.com/emilk/egui/pull/1247)).\n* Added `Ui::scroll_to_rect` ([1252](https://github.com/emilk/egui/pull/1252)).\n\n### 🔧 Changed\n* ⚠️ `Context::input` and `Ui::input` now locks a mutex. This can lead to a dead-lock is used in an `if let` binding!\n  * `if let Some(pos) = ui.input().pointer.latest_pos()` and similar must now be rewritten on two lines.\n  * Search for this problem in your code using the regex `if let .*input`.\n* Better contrast in the default light mode style ([#1238](https://github.com/emilk/egui/pull/1238)).\n* Renamed `CtxRef` to `Context` ([#1050](https://github.com/emilk/egui/pull/1050)).\n* `Context` can now be cloned and stored between frames ([#1050](https://github.com/emilk/egui/pull/1050)).\n* Renamed `Ui::visible` to `Ui::is_visible`.\n* Split `Event::Text` into `Event::Text` and `Event::Paste` ([#1058](https://github.com/emilk/egui/pull/1058)).\n* Replaced `Style::body_text_style` with more generic `Style::text_styles` ([#1154](https://github.com/emilk/egui/pull/1154)).\n* `TextStyle` is no longer `Copy` ([#1154](https://github.com/emilk/egui/pull/1154)).\n* Replaced `TextEdit::text_style` with `TextEdit::font` ([#1154](https://github.com/emilk/egui/pull/1154)).\n* `Plot::highlight` now takes a `bool` argument ([#1159](https://github.com/emilk/egui/pull/1159)).\n* `ScrollArea::show` now returns a `ScrollAreaOutput`, so you might need to add `.inner` after the call to it ([#1166](https://github.com/emilk/egui/pull/1166)).\n* Replaced `corner_radius: f32` with `rounding: Rounding`, allowing per-corner rounding settings ([#1206](https://github.com/emilk/egui/pull/1206)).\n* Replaced Frame's `margin: Vec2` with `margin: Margin`, allowing for different margins on opposing sides ([#1219](https://github.com/emilk/egui/pull/1219)).\n* Renamed `Plot::custom_label_func` to `Plot::label_formatter` ([#1235](https://github.com/emilk/egui/pull/1235)).\n* `Areas::layer_id_at` ignores non-interatable layers (i.e. Tooltips) ([#1240](https://github.com/emilk/egui/pull/1240)).\n* `ScrollArea`s will not shrink below a certain minimum size, set by `min_scrolled_width/min_scrolled_height` ([1255](https://github.com/emilk/egui/pull/1255)).\n* For integrations:\n  * `Output` has now been renamed `PlatformOutput` and `Context::run` now returns the new `FullOutput` ([#1292](https://github.com/emilk/egui/pull/1292)).\n  * `FontImage` has been replaced by `TexturesDelta` (found in `FullOutput`), describing what textures were loaded and freed each frame ([#1110](https://github.com/emilk/egui/pull/1110)).\n  * The painter must support partial texture updates ([#1149](https://github.com/emilk/egui/pull/1149)).\n  * Added `RawInput::max_texture_side` which should be filled in with e.g. `GL_MAX_TEXTURE_SIZE` ([#1154](https://github.com/emilk/egui/pull/1154)).\n\n### 🐛 Fixed\n* Plot `Orientation` was not public, although fields using this type were ([#1130](https://github.com/emilk/egui/pull/1130)).\n* Context menus now respects the theme ([#1043](https://github.com/emilk/egui/pull/1043)).\n* Calling `Context::set_pixels_per_point` before the first frame will now work.\n* Tooltips that don't fit the window don't flicker anymore ([#1240](https://github.com/emilk/egui/pull/1240)).\n* Scroll areas now follow text cursor ([#1252](https://github.com/emilk/egui/pull/1252)).\n* Slider: correctly respond with drag and focus events when interacting with the value directly ([1270](https://github.com/emilk/egui/pull/1270)).\n\n### Contributors 🙏\n* [4JX](https://github.com/4JX)\n* [55nknown](https://github.com/55nknown)\n* [AlanRace](https://github.com/AlanRace)\n* [a-liashenko](https://github.com/a-liashenko)\n* [awaken1ng](https://github.com/awaken1ng)\n* [BctfN0HUK7Yg](https://github.com/BctfN0HUK7Yg)\n* [Bromeon](https://github.com/Bromeon)\n* [cat-state](https://github.com/cat)\n* [danielkeller](https://github.com/danielkeller)\n* [dvec](https://github.com/dvec)\n* [Friz64](https://github.com/Friz64)\n* [Gordon01](https://github.com/Gordon01)\n* [HackerFoo](https://github.com/HackerFoo)\n* [juancampa](https://github.com/juancampa)\n* [justinj](https://github.com/justinj)\n* [lampsitter](https://github.com/lampsitter)\n* [LordMZTE](https://github.com/LordMZTE)\n* [manuel-i](https://github.com/manuel)\n* [Mingun](https://github.com/Mingun)\n* [niklaskorz](https://github.com/niklaskorz)\n* [nongiach](https://github.com/nongiach)\n* [parasyte](https://github.com/parasyte)\n* [psiphi75](https://github.com/psiphi75)\n* [s-nie](https://github.com/s)\n* [t18b219k](https://github.com/t18b219k)\n* [terhechte](https://github.com/terhechte)\n* [xudesheng](https://github.com/xudesheng)\n* [yusdacra](https://github.com/yusdacra)\n\n\n## 0.16.1 - 2021-12-31 - Add back `CtxRef::begin_frame,end_frame`\n\n### ⭐ Added\n* Added back `CtxRef::begin_frame,end_frame` as an alternative to `CtxRef::run`.\n\n\n## 0.16.0 - 2021-12-29 - Context menus and rich text\n\n### ⭐ Added\n* Added context menus: See `Ui::menu_button` and `Response::context_menu` ([#543](https://github.com/emilk/egui/pull/543)).\n* Most widgets containing text (`Label`, `Button` etc) now supports rich text ([#855](https://github.com/emilk/egui/pull/855)).\n* Plots:\n  * Added bar charts and box plots ([#863](https://github.com/emilk/egui/pull/863)).\n  * You can now query information about the plot (e.g. get the mouse position in plot coordinates, or the plot\n    bounds) while adding items. `Plot` ([#766](https://github.com/emilk/egui/pull/766) and\n    [#892](https://github.com/emilk/egui/pull/892)).\n* You can now read and write the cursor of a `TextEdit` ([#848](https://github.com/emilk/egui/pull/848)).\n* When using a custom font you can now specify a font index ([#873](https://github.com/emilk/egui/pull/873)).\n* Added vertical sliders with `Slider::new(…).vertical()` ([#875](https://github.com/emilk/egui/pull/875)).\n* Added `Button::image_and_text` ([#832](https://github.com/emilk/egui/pull/832)).\n* Added `CollapsingHeader::open` to control if it is open or collapsed ([#1006](https://github.com/emilk/egui/pull/1006)).\n* Added `egui::widgets::color_picker::color_picker_color32` to show the color picker.\n\n### 🔧 Changed\n* MSRV (Minimum Supported Rust Version) is now `1.56.0`.\n* `ui.add(Button::new(\"…\").text_color(…))` is now `ui.button(RichText::new(\"…\").color(…))` (same for `Label` )([#855](https://github.com/emilk/egui/pull/855)).\n* Plots now provide a `show` method that has to be used to add items to and show the plot ([#766](https://github.com/emilk/egui/pull/766)).\n* `menu::menu(ui, ...)` is now `ui.menu_button(...)` ([#543](https://github.com/emilk/egui/pull/543))\n* Replaced `CtxRef::begin_frame` and `end_frame` with `CtxRef::run` ([#872](https://github.com/emilk/egui/pull/872)).\n* Replaced `scroll_delta` and `zoom_delta` in `RawInput` with `Event::Scroll` and `Event::Zoom`.\n* Unified the four `Memory` data buckets (`data`, `data_temp`, `id_data` and `id_data_temp`) into a single `Memory::data`, with a new interface ([#836](https://github.com/emilk/egui/pull/836)).\n* Replaced `Ui::__test` with `egui::__run_test_ui` ([#872](https://github.com/emilk/egui/pull/872)).\n\n### 🐛 Fixed\n* Fixed `ComboBox` and other popups getting clipped to parent window ([#885](https://github.com/emilk/egui/pull/885)).\n* The color picker is now better at keeping the same hue even when saturation goes to zero ([#886](https://github.com/emilk/egui/pull/886)).\n\n### 🔥 Removed\n* Removed `egui::math` (use `egui::emath` instead).\n* Removed `egui::paint` (use `egui::epaint` instead).\n\n### Contributors 🙏\n* [5225225](https://github.com/5225225): [#849](https://github.com/emilk/egui/pull/849).\n* [aevyrie](https://github.com/aevyrie): [#966](https://github.com/emilk/egui/pull/966).\n* [B-Reif](https://github.com/B-Reif): [#875](https://github.com/emilk/egui/pull/875).\n* [Bromeon](https://github.com/Bromeon): [#863](https://github.com/emilk/egui/pull/863), [#918](https://github.com/emilk/egui/pull/918).\n* [d10sfan](https://github.com/d10sfan): [#832](https://github.com/emilk/egui/pull/832).\n* [EmbersArc](https://github.com/EmbersArc): [#766](https://github.com/emilk/egui/pull/766), [#892](https://github.com/emilk/egui/pull/892).\n* [Hperigo](https://github.com/Hperigo): [#905](https://github.com/emilk/egui/pull/905).\n* [isegal](https://github.com/isegal): [#934](https://github.com/emilk/egui/pull/934).\n* [mankinskin](https://github.com/mankinskin): [#543](https://github.com/emilk/egui/pull/543).\n* [niladic](https://github.com/niladic): [#499](https://github.com/emilk/egui/pull/499), [#863](https://github.com/emilk/egui/pull/863).\n* [singalen](https://github.com/singalen): [#973](https://github.com/emilk/egui/pull/973).\n* [sumibi-yakitori](https://github.com/sumibi-yakitori): [#830](https://github.com/emilk/egui/pull/830), [#870](https://github.com/emilk/egui/pull/870).\n* [t18b219k](https://github.com/t18b219k): [#868](https://github.com/emilk/egui/pull/868), [#888](https://github.com/emilk/egui/pull/888).\n\n\n## 0.15.0 - 2021-10-24 - Syntax highlighting and hscroll\n\n<img src=\"media/egui-0.15-code-editor.gif\">\n\n### ⭐ Added\n* Added horizontal scrolling support to `ScrollArea` and `Window` (opt-in).\n* `TextEdit::layouter`: Add custom text layout for e.g. syntax highlighting or WYSIWYG.\n* `Fonts::layout_job`: New text layout engine allowing mixing fonts, colors and styles, with underlining and strikethrough.\n* Added `ui.add_enabled(bool, widget)` to easily add a possibly disabled widget.\n* Added `ui.add_enabled_ui(bool, |ui| …)` to create a possibly disabled UI section.\n* Added feature `\"serialize\"` separately from `\"persistence\"`.\n* Added `egui::widgets::global_dark_light_mode_buttons` to easily add buttons for switching the egui theme.\n* `TextEdit` can now be used to show text which can be selected and copied, but not edited.\n* Added `Memory::caches` for caching things from one frame to the next.\n\n### 🔧 Changed\n* Change the default monospace font to [Hack](https://github.com/source-foundry/Hack).\n* Label text will now be centered, right-aligned and/or justified based on the layout of the `Ui` it is in.\n* `Hyperlink` will now word-wrap just like a `Label`.\n* All `Ui`s must now have a finite `max_rect`.\n  * Deprecated: `max_rect_finite`, `available_size_before_wrap_finite` and `available_rect_before_wrap_finite`.\n* `Painter`/`Fonts`: text layout now expect a color when creating a `Galley`. You may override that color with `Painter::galley_with_color`.\n* MSRV (Minimum Supported Rust Version) is now `1.54.0`.\n* By default, `DragValue`s no longer show a tooltip when hovered. Change with `Style::explanation_tooltips`.\n* Smaller and nicer color picker.\n* `ScrollArea` will auto-shrink to content size unless told otherwise using `ScrollArea::auto_shrink`.\n* By default, `Slider`'s `clamp_to_range` is set to true.\n* Renamed `TextEdit::enabled` to `TextEdit::interactive`.\n* `ui.label` (and friends) now take `impl ToString` as argument instead of `impl Into<Label>`.\n\n### 🐛 Fixed\n* Fixed wrongly sized multiline `TextEdit` in justified layouts.\n* Fixed clip rectangle of windows that don't fit the central area.\n* Show tooltips above widgets on touch screens.\n* Fixed popups sometimes getting clipped by panels.\n\n### 🔥 Removed\n* Replace `Button::enabled` with `ui.add_enabled`.\n\n### Contributors 🙏\n* [AlexApps99](https://github.com/AlexApps99)\n* [baysmith](https://github.com/baysmith)\n* [bpostlethwaite](https://github.com/bpostlethwaite)\n* [cwfitzgerald](https://github.com/cwfitzgerald)\n* [DrOptix](https://github.com/DrOptix)\n* [JerzySpendel](https://github.com/JerzySpendel)\n* [NiceneNerd](https://github.com/NiceneNerd)\n* [parasyte](https://github.com/parasyte)\n* [spersson](https://github.com/spersson)\n* [Stock84-dev](https://github.com/Stock84-dev)\n* [sumibi-yakitori](https://github.com/sumibi-yakitori)\n* [t18b219k](https://github.com/t18b219k)\n* [TobTobXX](https://github.com/TobTobXX)\n* [zu1k](https://github.com/zu1k)\n\n\n## 0.14.2 - 2021-08-28 - Window resize fix\n\n### 🐛 Fixed\n* Fixed window resize bug introduced in `0.14.1`.\n\n\n## 0.14.1 - 2021-08-28 - Layout bug fixes\n\n### ⭐ Added\n* Added `Ui::horizontal_top`.\n\n### 🐛 Fixed\n* Fixed `set_width/set_min_width/set_height/set_min_height/expand_to_include_x/expand_to_include_y`.\n* Make minimum grid column width propagate properly.\n* Make sure `TextEdit` contents expand to fill width if applicable.\n* `ProgressBar`: add a minimum width and fix for having it in an infinite layout.\n* Fixed sometimes not being able to click inside a combo box or popup menu.\n\n\n## 0.14.0 - 2021-08-24 - Ui panels and bug fixes\n\n### ⭐ Added\n* Panels can now be added to any `Ui`.\n* Plot:\n  * [Line styles](https://github.com/emilk/egui/pull/482).\n  * Added `show_background` and `show_axes` methods to `Plot`.\n* [Progress bar](https://github.com/emilk/egui/pull/519).\n* `Grid::num_columns`: allow the last column to take up the rest of the space of the parent `Ui`.\n* Added an API for dropping files into egui (see `RawInput`).\n* `CollapsingHeader` can now optionally be selectable.\n\n### 🔧 Changed\n* A single-line `TextEdit` will now clip text that doesn't fit in it, and scroll.\n* Return closure return value from `Area::show`, `ComboBox::show_ui`, `ComboBox::combo_box_with_label`, `Window::show`, `popup::*`, `menu::menu`.\n* Only move/resize windows with primary mouse button.\n* Tooltips are now moved to not cover the widget they are attached to.\n\n### 🐛 Fixed\n* Fixed custom font definitions getting replaced when `pixels_per_point` is changed.\n* Fixed `lost_focus` for `TextEdit`.\n* Clicking the edge of a menu button will now properly open the menu.\n* Fixed hover detection close to an `Area`.\n* Fixed case where `Plot`'s `min_auto_bounds` could be ignored after the first call to `Plot::ui`.\n* Fixed slow startup when using large font files.\n\n### Contributors 🙏\n* [barrowsys](https://github.com/barrowsys)\n* [EmbersArc](https://github.com/EmbersArc)\n* [gents83](https://github.com/gents83 )\n* [lucaspoffo](https://github.com/lucaspoffo)\n* [mankinskin](https://github.com/mankinskin)\n* [mental32](https://github.com/mental32)\n* [mitchmindtree](https://github.com/mitchmindtree)\n* [parasyte](https://github.com/parasyte)\n* [rekka](https://github.com/rekka)\n* [zu1k](https://github.com/zu1k)\n\n\n## 0.13.1 - 2021-06-28 - Plot fixes\n\n### ⭐ Added\n* Plot: you can now set the stroke of a `HLine/VLine`.\n\n### 🔧 Changed\n* `Plot::new` now takes an `id_source: impl Hash` instead of a `name: impl ToString`. Functionally it is the same.\n\n\n## 0.13.0 - 2021-06-24 - Better panels, plots and new visual style\n\n### ⭐ Added\n* Plot:\n  * [More plot items: Arrows, Polygons, Text, Images](https://github.com/emilk/egui/pull/471).\n  * [Plot legend improvements](https://github.com/emilk/egui/pull/410).\n  * [Line markers for plots](https://github.com/emilk/egui/pull/363).\n* Panels:\n  * Added right and bottom panels (`SidePanel::right` and `Panel::bottom`).\n  * Panels can now be resized.\n  * Added an option to overwrite frame of a `Panel`.\n* [Improve accessibility / screen reader](https://github.com/emilk/egui/pull/412).\n* Added `ScrollArea::show_rows` for efficient scrolling of huge UI:s.\n* Added `ScrollArea::enable_scrolling` to allow freezing scrolling when editing TextEdit widgets within it\n* Added `Ui::set_visible` as a way to hide widgets.\n* Added `Style::override_text_style` to easily change the text style of everything in a `Ui` (or globally).\n* You can now change `TextStyle` on checkboxes, radio buttons and `SelectableLabel`.\n* Added support for [cint](https://crates.io/crates/cint) under `cint` feature.\n* Added features `extra_asserts` and `extra_debug_asserts` to enable additional checks.\n* `TextEdit` now supports edits on a generic buffer using `TextBuffer`.\n* Added `Context::set_debug_on_hover` and `egui::trace!(ui)`\n\n### 🔧 Changed\n* Minimum Rust version is now 1.51 (used to be 1.52)\n* [Tweaked the default visuals style](https://github.com/emilk/egui/pull/450).\n* Plot: Renamed `Curve` to `Line`.\n* `TopPanel::top` is now `TopBottomPanel::top`.\n* `SidePanel::left` no longer takes the default width by argument, but by a builder call.\n* `SidePanel::left` is resizable by default.\n\n### 🐛 Fixed\n* Fixed uneven lettering on non-integral device scales (\"extortion lettering\").\n* Fixed invisible scroll bar when native window is too narrow for egui.\n\n\n## 0.12.0 - 2021-05-10 - Multitouch, user memory, window pivots, and improved plots\n\n### ⭐ Added\n* Added anchors to windows and areas so you can put a window in e.g. the top right corner.\n* Make labels interactive with `Label::sense(Sense::click())`.\n* Added `Response::request_focus` and `Response::surrender_focus`.\n* Added `TextEdit::code_editor` (VERY basic).\n* [Pan and zoom plots](https://github.com/emilk/egui/pull/317).\n* [Add plot legends](https://github.com/emilk/egui/pull/349).\n* [Users can now store custom state in `egui::Memory`](https://github.com/emilk/egui/pull/257).\n* Added `Response::on_disabled_hover_text` to show tooltip for disabled widgets.\n* Zoom input: ctrl-scroll and (on `eframe` web) trackpad-pinch gesture.\n* Support for raw [multi touch](https://github.com/emilk/egui/pull/306) events,\n  enabling zoom, rotate, and more. Works with `eframe` web on mobile devices,\n  and should work with `egui_glium` for certain touch devices/screens.\n* Added (optional) compatibility with [mint](https://docs.rs/mint).\n\n### 🔧 Changed\n* Make `Memory::has_focus` public (again).\n* `Plot` must now be given a name that is unique within its scope.\n* Tab only selects labels if the `screen_reader` option is turned on.\n* Renamed `ui.wrap` to `ui.scope`.\n\n### 🐛 Fixed\n* Fixed [defocus-bug on touch screens](https://github.com/emilk/egui/issues/288).\n* Fixed bug with the layout of wide `DragValue`s.\n\n### 🔥 Removed\n* Moved experimental markup language to `egui_demo_lib`\n\n\n## 0.11.0 - 2021-04-05 - Optimization, screen reader & new layout logic\n\n### ⭐ Added\n* You can now give focus to any clickable widget with tab/shift-tab.\n  * Use space or enter to click the selected widget.\n  * Use arrow keys to adjust sliders and `DragValue`s.\n* egui will now output events when widgets gain keyboard focus.\n  * This can be hooked up to a screen reader to aid the visually impaired\n* Added the option to restrict the dragging bounds of `Window` and `Area` to a specified area using `drag_bounds(rect)`.\n* Added support for small and raised text.\n* Added `ui.set_row_height`.\n* Added `DebugOptions::show_widgets` to debug layouting by hovering widgets.\n* Added `ComboBox` to more easily customize combo boxes.\n* Added `Slider::new` and `DragValue::new` to replace old type-specific constructors.\n* Added `TextEdit::password` to hide input characters.\n\n### 🔧 Changed\n* `ui.advance_cursor` is now called `ui.add_space`.\n* `kb_focus` is now just called `focus`.\n\n### 🐛 Fixed\n* Fixed some bugs related to centered layouts.\n* Fixed secondary-click to open a menu.\n* [Fix panic for zero-range sliders and zero-speed drag values](https://github.com/emilk/egui/pull/216).\n* Fixed false id clash error for wrapping text.\n* Fixed bug that would close a popup (e.g. the color picker) when clicking inside of it.\n\n### ☢️ Deprecated\n* Deprecated `combo_box_with_label` in favor of new `ComboBox`.\n* Deprecated type-specific constructors for `Slider` and `DragValue` (`Slider::f32`, `DragValue::usize` etc).\n\n\n## 0.10.0 - 2021-02-28 - Plot and polish\n\n<img src=\"media/egui-0.10-plot.gif\" width=\"50%\">\n\n### ⭐ Added\n* Added `egui::plot::Plot` to plot some 2D data.\n* Added `Ui::hyperlink_to(label, url)`.\n* Sliders can now have a value prefix and suffix (e.g. the suffix `\"°\"` works like a unit).\n* `Context::set_pixels_per_point` to control the scale of the UI.\n* Added `Response::changed()` to query if e.g. a slider was dragged, text was entered or a checkbox was clicked.\n* Added support for all integers in `DragValue` and `Slider` (except 128-bit).\n\n### 🔧 Changed\n* Improve the positioning of tooltips.\n* Only show tooltips if mouse is still.\n* `Slider` will now show the value display by default, unless turned off with `.show_value(false)`.\n* The `Slider` value is now a `DragValue` which when dragged can pick values outside of the slider range (unless `clamp_to_range` is set).\n\n\n## 0.9.0 - 2021-02-07 - Light Mode and much more\n\n<img src=\"media/0.9.0-disabled.gif\" width=\"50%\">\n\n### ⭐ Added\n* Added support for secondary and middle mouse buttons.\n* Added `Label` methods for code, strong, strikethrough, underline and italics.\n* Added `ui.group(|ui| { … })` to visually group some widgets within a frame.\n* Added `Ui` helpers for doing manual layout (`ui.put`, `ui.allocate_ui_at_rect` and more).\n* Added `ui.set_enabled(false)` to disable all widgets in a `Ui` (grayed out and non-interactive).\n* Added `TextEdit::hint_text` for showing a weak hint text when empty.\n* `egui::popup::popup_below_widget`: show a popup area below another widget.\n* Added `Slider::clamp_to_range(bool)`: if set, clamp the incoming and outgoing values to the slider range.\n* Add: `ui.spacing()`, `ui.spacing_mut()`, `ui.visuals()`, `ui.visuals_mut()`.\n* Add: `ctx.set_visuals()`.\n* You can now control text wrapping with `Style::wrap`.\n* Added `Grid::max_col_width`.\n\n### 🔧 Changed\n* Text will now wrap at newlines, spaces, dashes, punctuation or in the middle of a words if necessary, in that order of priority.\n* Widgets will now always line break at `\\n` characters.\n* Widgets will now more intelligently choose whether or not to wrap text.\n* `mouse` has been renamed `pointer` everywhere (to make it clear it includes touches too).\n* Most parts of `Response` are now methods, so `if ui.button(\"…\").clicked {` is now `if ui.button(\"…\").clicked() {`.\n* `Response::active` is now gone. You can use `response.dragged()` or `response.clicked()` instead.\n* Backend: pointer (mouse/touch) position and buttons are now passed to egui in the event stream.\n* `DragValue::range` is now called `clamp_range` and also clamps incoming values.\n* Renamed `Triangles` to `Mesh`.\n* The tessellator now wraps the clip rectangle and mesh in `struct ClippedMesh(Rect, Mesh)`.\n* `Mesh::split_to_u16` now returns a 16-bit indexed `Mesh16`.\n\n### 🐛 Fixed\n* It is now possible to click widgets even when FPS is very low.\n* Tessellator: handle sharp path corners better (switch to bevel instead of miter joints for > 90°).\n\n\n## 0.8.0 - 2021-01-17 - Grid layout & new visual style\n\n<img src=\"media/widget_gallery_0.8.0.gif\" width=\"50%\">\n\n### ⭐ Added\n* Added a simple grid layout (`Grid`).\n* Added `ui.allocate_at_least` and `ui.allocate_exact_size`.\n* Added function `InputState::key_down`.\n* Added `Window::current_pos` to position a window.\n\n### 🔧 Changed\n* New simpler and sleeker look!\n* Renamed `PaintCmd` to `Shape`.\n* Replace tuple `(Rect, Shape)` with tuple-struct `ClippedShape`.\n* Renamed feature `\"serde\"` to `\"persistence\"`.\n* Break out the modules `math` and `paint` into separate crates `emath` and `epaint`.\n\n### 🐛 Fixed\n* Fixed a bug that would sometimes trigger a \"Mismatching panels\" panic in debug builds.\n* `Image` and `ImageButton` will no longer stretch to fill a justified layout.\n\n\n## 0.7.0 - 2021-01-04\n\n### ⭐ Added\n* Added `ui.scroll_to_cursor` and `response.scroll_to_me` ([#81](https://github.com/emilk/egui/pull/81) by [lucaspoffo](https://github.com/lucaspoffo)).\n* Added `window.id(…)` and `area.id(…)` for overriding the default `Id`.\n\n### 🔧 Changed\n* Renamed `Srgba` to `Color32`.\n* All color constructors now starts with `from_`, e.g. `Color32::from_rgb`.\n* Renamed `FontFamily::VariableWidth` to `FontFamily::Proportional`.\n* Removed `pixels_per_point` from `FontDefinitions`.\n\n### 🐛 Fixed\n* `RepaintSignal` now implements `Sync` so it can be sent to a background thread.\n* `TextEdit` widgets are now slightly larger to accommodate their frames.\n\n### ☢️ Deprecated\n* Deprecated `color::srgba`.\n\n\n## 0.6.0 - 2020-12-26\n\n### ⭐ Added\n* Turn off `Window` title bars with `window.title_bar(false)`.\n* `ImageButton` - `ui.add(ImageButton::new(…))`.\n* `ui.vertical_centered` and `ui.vertical_centered_justified`.\n* `ui.allocate_painter` helper.\n* Mouse-over explanation to duplicate ID warning.\n* You can now easily constrain egui to a portion of the screen using `RawInput::screen_rect`.\n* You can now control the minimum and maixumum number of decimals to show in a `Slider` or `DragValue`.\n* Added `egui::math::Rot2`: rotation helper.\n* `Response` now contains the `Id` of the widget it pertains to.\n* `ui.allocate_response` that allocates space and checks for interactions.\n* Added `response.interact(sense)`, e.g. to check for clicks on labels.\n\n### 🔧 Changed\n* `ui.allocate_space` now returns an `(Id, Rect)` tuple.\n* `Arc<Context>` has been replaced with `CtxRef` everywhere.\n* Slight tweak of the default `Style` and font sizes.\n* `SidePanel::left` and `TopPanel::top` now takes `impl Hash` as first argument.\n* A `Window` may now cover an existing `CentralPanel`.\n* `ui.image` now takes `impl Into<Vec2>` as a `size` argument.\n* Made some more fields of `RawInput` optional.\n* `Slider` and `DragValue` uses fewer decimals by default. See the full precision by hovering over the value.\n* `egui::App`: added `fn name(&self)` and `fn clear_color(&self)`.\n* Combo boxes has scroll bars when needed.\n* Expand `Window` + `Resize` containers to be large enough for last frames content\n* `ui.columns`: Columns now defaults to justified top-to-down layouts.\n* Renamed `Sense::nothing()` to  `Sense::hover()`.\n* Replaced `parking_lot` dependency with `atomic_refcell` by default.\n\n### 🐛 Fixed\n* The background for `CentralPanel` will now cover unused space too.\n* `ui.columns`: Improve allocated size estimation.\n\n### ☢️ Deprecated\n* `RawInput::screen_size` - use `RawInput::screen_rect` instead.\n* left/centered/right column functions on `Ui`.\n* `ui.interact_hover` and `ui.hovered`.\n\n\n## 0.5.0 - 2020-12-13\n\n### ⭐ Added\n* Emoji support: 1216 different emojis that work in any text.\n  * The Demo app comes with a Font Book to explore the available glyphs.\n* `ui.horizontal_wrapped(|ui| …)`: Add widgets on a row but wrap at `max_size`.\n* `ui.horizontal_wrapped_for_text`: Like `ui.horizontal_wrapped`, but with spacing made for embedding text.\n* `ui.horizontal_for_text`: Like `ui.horizontal`, but with spacing made for embedding text.\n* `egui::Layout` now supports justified layouts where contents is _also_ centered, right-aligned, etc.\n* `ui.allocate_ui(size, |ui| …)`: Easily create a child-`Ui` of a given size.\n* `SelectableLabel` (`ui.selectable_label` and `ui.selectable_value`): A text-button that can be selected.\n* `ui.small_button`: A smaller button that looks good embedded in text.\n* `ui.drag_angle_tau`: For those who want to specify angles as fractions of τ (a full turn).\n* Added `Resize::id_source` and `ScrollArea::id_source` to let the user avoid Id clashes.\n\n### 🔧 Changed\n* New default font: [Ubuntu-Light](https://fonts.google.com/specimen/Ubuntu).\n* Make it simpler to override fonts in `FontDefinitions`.\n* Remove minimum button width.\n* Refactor `egui::Layout` substantially, changing its interface.\n* Calling `on_hover_text`/`on_hover_ui` multiple times will stack tooltips underneath the previous ones.\n* Text wrapping on labels, buttons, checkboxes and radio buttons is now based on the layout.\n\n### 🔥 Removed\n\n* Removed the `label!` macro.\n\n\n## 0.4.0 - 2020-11-28\n\n### ⭐ Added\n* `TextEdit` improvements:\n  * Much improved text editing, with better navigation and selection.\n  * Move focus between `TextEdit` widgets with tab and shift-tab.\n  * Undo edtis in a `TextEdit`.\n  * You can now check if a `TextEdit` lost keyboard focus with `response.lost_focus`.\n  * Added `ui.text_edit_singleline` and `ui.text_edit_multiline`.\n* You can now debug why your `Ui` is unexpectedly wide with `ui.style_mut().debug.show_expand_width = true;`\n\n### 🔧 Changed\n* Pressing enter in a single-line `TextEdit` will now surrender keyboard focus for it.\n* You must now be explicit when creating a `TextEdit` if you want it to be singeline or multiline.\n* Improved automatic `Id` generation, making `Id` clashes less likely.\n* egui now requires modifier key state from the integration\n* Added, renamed and removed some keys in the `Key` enum.\n* Fixed incorrect text wrapping width on radio buttons\n\n### 🐛 Fixed\n* Fixed bug where a lost widget could still retain keyboard focus.\n\n\n## 0.3.0 - 2020-11-07\n\n### ⭐ Added\n* Panels: you can now create panels using `SidePanel`, `TopPanel` and `CentralPanel`.\n* You can now override the default egui fonts.\n* Added ability to override text color with `visuals.override_text_color`.\n* The demo now includes a simple drag-and-drop example.\n* The demo app now has a slider to scale all of egui.\n\n### 🔧 Changed\n* `ui.horizontal(…)` etc returns `Response`.\n* Refactored the interface for `egui::app::App`.\n* Windows are now constrained to the screen.\n* `Context::begin_frame()` no longer returns a `Ui`. Instead put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.\n* `Context::end_frame()` now returns shapes that need to be converted to triangles with `Context::tessellate()`.\n* Anti-aliasing is now off by default in debug builds.\n\n### 🔥 Removed\n* You can no longer throw windows.\n\n### 🐛 Fixed\n* Fixed a bug where some regions would slowly grow for non-integral scales (`pixels_per_point`).\n\n\n## 0.2.0 - 2020-10-10\n\n* Color picker\n* Unicode characters in labels (limited by [what the default font supports](https://fonts.google.com/specimen/Comfortaa#glyphs))\n* Simple drop-down combo box menu\n* Logarithmic sliders\n* Optimization: coarse culling in the tessellator\n* CHANGED: switch argument order of `ui.checkbox` and `ui.radio`\n\n\n## 0.1.4 - 2020-09-08\n\nThis is when I started the CHANGELOG.md, after almost two years of development. Better late than never.\n\n* Widgets: label, text button, hyperlink, checkbox, radio button, slider, draggable value, text editing\n* Layouts: horizontal, vertical, columns\n* Text input: very basic, multiline, copy/paste\n* Windows: move, resize, name, minimize and close. Automatically sized and positioned.\n* Regions: resizing, vertical scrolling, collapsing headers (sections)\n* Rendering: Anti-aliased rendering of lines, circles, text and convex polygons.\n* Tooltips on hover\n\n\n## Earlier:\n\n* 2020-08-10: renamed the project to \"egui\"\n* 2020-05-30: first release on crates.io (0.1.0)\n* 2020-04-01: serious work starts (pandemic project)\n* 2019-03-12: gave a talk about what would later become egui: https://www.youtube.com/watch?v=-pmwLHw5Gbs\n* 2018-12-23: [initial commit](https://github.com/emilk/egui/commit/856bbf4dae4a69693a0324da34e8b0dd3754dfdf)\n* 2018-11-04: started tinkering on a train\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "/crates/egui_kittest @lucasmerlin\n/crates/egui-wgpu @Wumpf\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the overall\n  community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or advances of\n  any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email address,\n  without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n[the egui discord](https://discord.gg/JFcEma9bJq).\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of\nactions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within the\ncommunity.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.1, available at\n[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].\n\nCommunity Impact Guidelines were inspired by\n[Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n\nFor answers to common questions about this code of conduct, see the FAQ at\n[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at\n[https://www.contributor-covenant.org/translations][translations].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[Mozilla CoC]: https://github.com/mozilla/diversity\n[FAQ]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations\n\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contribution Guidelines\n\n## Introduction\n\n`egui` has been an on-and-off weekend project of mine since late 2018. I am grateful to any help I can get, but bear in mind that sometimes I can be slow to respond because I am busy with other things!\n\n/ Emil\n\n## How to contribute to egui\nYou want to contribute to egui, but don't know how? First of all: thank you! I created a special issue just for that: <https://github.com/emilk/egui/issues/3742>. But make sure you still read this file first :)\n\n## Discussion\n\nYou can ask questions, share screenshots and more at [GitHub Discussions](https://github.com/emilk/egui/discussions).\n\nThere is an `egui` discord at <https://discord.gg/vbuv9Xan65>.\n\n\n## Filing an issue\n\n[Issues](https://github.com/emilk/egui/issues) are for bug reports and feature requests. Issues are not for asking questions (use [Discussions](https://github.com/emilk/egui/discussions) or [Discord](https://discord.gg/vbuv9Xan65) for that).\n\nAlways make sure there is not already a similar issue to the one you are creating.\n\nIf you are filing a bug, please provide a way to reproduce it.\n\n\n## Making a PR\n\nFor small things, just go ahead an open a PR. For bigger things, please file an issue first (or find an existing one) and announce that you plan to work on something. That way we will avoid having several people doing double work, and you might get useful feedback on the issue before you start working.\n\nBrowse through [`ARCHITECTURE.md`](ARCHITECTURE.md) to get a sense of how all pieces connects.\n\nYou can test your code locally by running `./scripts/check.sh`.\nThere are snapshots test that might need to be updated.\nRun the tests with `UPDATE_SNAPSHOTS=true cargo test --workspace --all-features` to update all of them.\nIf CI keeps complaining about snapshots (which could happen if you don't use macOS, snapshots in CI are currently\nrendered with macOS), you can instead run `./scripts/update_snapshots_from_ci.sh` to update your local snapshots from\nthe last CI run of your PR (which will download the `test_results` artifact).\nFor more info about the tests see [egui_kittest](./crates/egui_kittest/README.md).\nSnapshots and other big files are stored with git lfs. See [Working with git lfs](#working-with-git-lfs) for more info.\nIf you see an `InvalidSignature` error when running snapshot tests, it's probably a problem related to git-lfs.\n\nWhen you have something that works, open a draft PR. You may get some helpful feedback early!\nWhen you feel the PR is ready to go, do a self-review of the code, and then open it for review.\n\nDon't worry about having many small commits in the PR - they will be squashed to one commit once merged.\n\nPlease keep pull requests small and focused. The smaller it is, the more likely it is to get merged.\n\n## Working with git lfs\n\nWe use [git-lfs](https://git-lfs.com/) to store big files in the repository.\nMake sure you have it installed (running `git lfs ls-files` from the repository root should list some files).\nDon't forget to run `git lfs install` in this repo after installing the git-lfs binary.\nYou need to add any .png images to `git lfs` (see the .gitattributes file for rules and exclusions).\nIf the CI complains about lfs, try running `git add --renormalize .`.\n\nCommon git-lfs commands:\n```bash\n# Install git-lfs in the repo (installs git hooks)\ngit lfs install\n\n# Move a file to git lfs\ngit lfs track \"path/to/file/or/pattern\" # OR manually edit .gitattributes\ngit add --renormalize . # Moves already added files to lfs (according to .gitattributes)\n\n# Move a file from lfs to regular git\ngit lfs untrack \"path/to/file/or/pattern\" # OR manually edit .gitattributes\ngit add --renormalize . # Moves already added files to regular git (according to .gitattributes)\n\n# Push to a contributor remote (see https://github.com/cli/cli/discussions/8794#discussioncomment-8695076)\ngit push --no-verify\n\n# Push git lfs files to contributor remote:\ngit push origin $(git branch --show-current) && git push --no-verify && git push origin --delete $(git branch --show-current)\n```\n\n## PR review\n\nMost PR reviews are done by me, Emil, but I very much appreciate any help I can get reviewing PRs!\n\nIt is very easy to add complexity to a project, but remember that each line of code added is code that needs to be maintained in perpetuity, so we have a high bar on what get merged!\n\nWhen reviewing, we look for:\n* The PR title and description should be helpful\n* Breaking changes are documented in the PR description\n* The code should be readable\n* The code should have helpful docstrings\n* The code should follow the [Code Style](CONTRIBUTING.md#code-style)\n\nNote that each new egui release have some breaking changes, so we don't mind having a few of those in a PR. Of course, we still try to avoid them if we can, and if we can't we try to first deprecate old code using the `#[deprecated]` attribute.\n\n## Creating an integration for egui\n\nSee <https://docs.rs/egui/latest/egui/#integrating-with-egui> for how to write your own egui integration.\n\nIf you make an integration for `egui` for some engine or renderer, please share it with the world!\nMake a PR to add it as a link to [`README.md`](README.md#integrations) so others can easily find it.\n\n\n## Testing the web viewer\n* Build with `scripts/build_demo_web.sh`\n* Host with `scripts/start_server.sh`\n* Open <http://localhost:8765/index.html>\n\n\n## Code Style\nWhile using an immediate mode gui is simple, implementing one is a lot more tricky. There are many subtle corner-case you need to think through. The `egui` source code is a bit messy, partially because it is still evolving.\n\n* Read some code before writing your own\n* Leave the code cleaner than how you found it\n* Write idiomatic rust\n* Follow the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/)\n* Add blank lines around all `fn`, `struct`, `enum`, etc\n* `// Comment like this.` and not `//like this`\n* Use `TODO` instead of `FIXME`\n* Add your github handle to the `TODO`:s you write, e.g: `TODO(emilk): clean this up`\n* Avoid `unsafe`\n* Avoid `unwrap` and any other code that can cause panics\n* Use good names for everything\n* Add docstrings to types, `struct` fields and all `pub fn`\n* Add some example code (doc-tests)\n* Before making a function longer, consider adding a helper function\n* If you are only using it in one function, put the `use` statement in that function. This improves locality, making it easier to read and move the code\n* When importing a `trait` to use it's trait methods, do this: `use Trait as _;`. That lets the reader know why you imported it, even though it seems unused\n* Avoid double negatives\n* Flip `if !condition {} else {}`\n* Sets of things should be lexicographically sorted (e.g. crate dependencies in `Cargo.toml`)\n* Put each type in their own file, unless they are trivial (e.g. a `struct` with no `impl`)\n* Put most generic arguments first (e.g. `Context`), and most specific last\n* Break the above rules when it makes sense\n\n\n### Good:\n``` rust\n/// The name of the thing.\npub fn name(&self) -> &str {\n    &self.name\n}\n\nfn foo(&self) {\n    // TODO(emilk): this can be optimized\n}\n```\n\n### Bad:\n``` rust\n//gets the name\npub fn get_name(&self) -> &str {\n    &self.name\n}\nfn foo(&self) {\n    //FIXME: this can be optimized\n}\n```\n\n### Coordinate system\nThe left-top corner of the screen is `(0.0, 0.0)`,\nwith `Vec2::X` increasing to the right and `Vec2::Y` increasing downwards.\n\n`egui` uses logical _points_ as its coordinate system.\nThose related to physical _pixels_ by the `pixels_per_point` scale factor.\nFor example, a high-dpi screen can have `pixels_per_point = 2.0`,\nmeaning there are two physical screen pixels for each logical point.\n\nAngles are in radians, and are measured clockwise from the X-axis, which has angle=0.\n\n\n### Avoid `unwrap`, `expect` etc.\nThe code should never panic or crash, which means that any instance of `unwrap` or `expect` is a potential time-bomb. Even if you structured your code to make them impossible, any reader will have to read the code very carefully to prove to themselves that an `unwrap` won't panic. Often you can instead rewrite your code so as to avoid it. The same goes for indexing into a slice (which will panic on out-of-bounds) - it is often preferable to use `.get()`.\n\nFor instance:\n\n``` rust\nlet first = if vec.is_empty() {\n    return;\n} else {\n    vec[0]\n};\n```\ncan be better written as:\n\n``` rust\nlet Some(first) = vec.first() else {\n    return;\n};\n```\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n  \"crates/ecolor\",\n  \"crates/egui_demo_app\",\n  \"crates/egui_demo_lib\",\n  \"crates/egui_extras\",\n  \"crates/egui_glow\",\n  \"crates/egui_kittest\",\n  \"crates/egui-wgpu\",\n  \"crates/egui-winit\",\n  \"crates/egui\",\n  \"crates/emath\",\n  \"crates/epaint\",\n  \"crates/epaint_default_fonts\",\n\n  \"examples/*\",\n  \"tests/*\",\n\n  \"xtask\",\n]\n\n[workspace.package]\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\nrust-version = \"1.92\"\nversion = \"0.33.3\"\n\n\n[profile.release]\n# lto = true # VERY slightly smaller wasm\n# opt-level = 's' # 10-20% smaller wasm compared to `opt-level = 3`\n# opt-level = 1 # very slow and big wasm. Don't do this.\nopt-level = 2 # fast and small wasm, basically same as `opt-level = 's'`\n# opt-level = 3 # unnecessarily large wasm for no performance gain\n\n# debug = true # include debug symbols, useful when profiling wasm\n\npanic = \"abort\" # This leads to better optimizations and smaller binaries (and is the default in Wasm anyways).\n\n\n[profile.dev]\n# Can't leave this on by default, because it breaks the Windows build. Related: https://github.com/rust-lang/cargo/issues/4897\n# split-debuginfo = \"unpacked\" # faster debug builds on mac\n# opt-level = 1                # Make debug builds run faster\n\n# panic = \"abort\" leads to better optimizations and smaller binaries (and is the default in Wasm anyways),\n# but it also means backtraces don't work with the `backtrace` library (https://github.com/rust-lang/backtrace-rs/issues/397).\n# egui has a feature where if you hold down all modifiers keys on your keyboard and hover any UI widget,\n# you will see the backtrace to that widget, and we don't want to break that feature in dev builds.\n\n[profile.dev.package.\"*\"]\n# Optimize all dependencies even in debug builds (does not affect workspace packages):\nopt-level = 2\n\n\n[workspace.dependencies]\nemath = { version = \"0.33.3\", path = \"crates/emath\", default-features = false }\necolor = { version = \"0.33.3\", path = \"crates/ecolor\", default-features = false }\nepaint = { version = \"0.33.3\", path = \"crates/epaint\", default-features = false }\nepaint_default_fonts = { version = \"0.33.3\", path = \"crates/epaint_default_fonts\" }\negui = { version = \"0.33.3\", path = \"crates/egui\", default-features = false }\negui-winit = { version = \"0.33.3\", path = \"crates/egui-winit\", default-features = false }\negui_extras = { version = \"0.33.3\", path = \"crates/egui_extras\", default-features = false }\negui-wgpu = { version = \"0.33.3\", path = \"crates/egui-wgpu\", default-features = false }\negui_demo_lib = { version = \"0.33.3\", path = \"crates/egui_demo_lib\", default-features = false }\negui_glow = { version = \"0.33.3\", path = \"crates/egui_glow\", default-features = false }\negui_kittest = { version = \"0.33.3\", path = \"crates/egui_kittest\", default-features = false }\neframe = { version = \"0.33.3\", path = \"crates/eframe\", default-features = false }\n\naccesskit = \"0.24.0\"\naccesskit_consumer = \"0.35.0\"\naccesskit_winit = \"0.32.0\"\nahash = { version = \"0.8.12\", default-features = false, features = [\n  \"no-rng\", # we don't need DOS-protection, so we let users opt-in to it instead\n  \"std\",\n] }\nandroid_logger = \"0.15.1\"\narboard = { version = \"3.6.1\", default-features = false }\nbacktrace = \"0.3.76\"\nbitflags = \"2.9.4\"\nbytemuck = \"1.24.0\"\nchrono = { version = \"0.4.42\", default-features = false }\ncint = \"0.3.1\"\ncolor-hex = \"0.2.0\"\ncriterion = { version = \"0.7.0\", default-features = false }\ndify = { version = \"0.8\", default-features = false }\ndirectories = \"6.0.0\"\ndocument-features = \"0.2.11\"\nehttp = { version = \"0.6.0\", default-features = false }\nenum-map = \"2.7.3\"\nenv_logger = { version = \"0.11.8\", default-features = false }\nfont-types = { version = \"0.11.0\", default-features = false, features = [\"std\"] }\nglow = \"0.16.0\"\nglutin = { version = \"0.32.3\", default-features = false }\nglutin-winit = { version = \"0.5.0\", default-features = false }\nhome = \"0.5.9\"\nimage = { version = \"0.25.6\", default-features = false }\njs-sys = \"0.3.77\"\nkittest = { version = \"0.3.0\" }\nlog = { version = \"0.4.28\", features = [\"std\"] }\nmemoffset = \"0.9.1\"\nmimalloc = \"0.1.48\"\nmime_guess2 = { version = \"2.3.1\", default-features = false }\nmint = \"0.5.9\"\nnohash-hasher = \"0.2.0\"\nobjc2 = \"0.5.2\"\nobjc2-app-kit = { version = \"0.2.2\", default-features = false }\nobjc2-foundation = { version = \"0.2.2\", default-features = false }\nobjc2-ui-kit = { version = \"0.2.2\", default-features = false }\nopen = \"5.3.2\"\nparking_lot = \"0.12.5\"\npercent-encoding = \"2.3.2\"\npoll-promise = { version = \"0.3.0\", default-features = false }\npollster = \"0.4.0\"\nprofiling = { version = \"1.0.17\", default-features = false }\npuffin = \"0.19.1\"\npuffin_http = \"0.16.1\"\nrand = \"0.9.2\"\nraw-window-handle = \"0.6.2\"\nrayon = \"1.11.0\"\nresvg = { version = \"0.45.1\", default-features = false }\nrfd = \"0.17.2\"\nron = \"0.11.0\"\nself_cell = \"1.2.1\"\nserde = { version = \"1.0.228\", features = [\"derive\"] }\nsimilar-asserts = \"1.7.0\"\nskrifa = { version = \"0.40.0\", default-features = false, features = [\"std\", \"autohint_shaping\"] }\nsmallvec = \"1.15.1\"\nsmithay-clipboard = \"0.7.2\"\nstatic_assertions = \"1.1.0\"\nsyntect = { version = \"5.3.0\", default-features = false }\ntempfile = \"3.23.0\"\nthiserror = \"2.0.17\"\ntokio = \"1.49\"\ntoml = {version = \"1\", default-features = false }\ntype-map = \"0.5.1\"\nunicode_names2 = { version = \"2.0.0\", default-features = false }\nunicode-segmentation = \"1.12.0\"\nvello_cpu = { version = \"0.0.6\", default-features = false, features = [\"std\", \"u8_pipeline\", \"f32_pipeline\"] }\nwasm-bindgen = \"0.2.100\" # Keep wasm-bindgen version in sync in: setup_web.sh, Cargo.toml, Cargo.lock, rust.yml\nwasm-bindgen-futures = \"0.4.0\"\nwayland-cursor = { version = \"0.31.11\", default-features = false }\nweb-sys = \"0.3.77\"\nweb-time = \"1.1.0\" # Timekeeping for native and web\nwebbrowser = \"1.0.5\"\nwgpu = { version = \"28.0.0\", default-features = false, features = [\"std\"] }\nwindows-sys = \"0.61.2\"\nwinit = { version = \"0.30.12\", default-features = false }\n\n[patch.crates-io]\nkittest = { git = \"https://github.com/rerun-io/kittest\", branch = \"main\" }\n\n[workspace.lints.rust]\nunsafe_code = \"deny\"\n\nelided_lifetimes_in_paths = \"warn\"\nfuture_incompatible = { level = \"warn\", priority = -1 }\nnonstandard_style = { level = \"warn\", priority = -1 }\nrust_2018_idioms = { level = \"warn\", priority = -1 }\nrust_2021_prelude_collisions = \"warn\"\nsemicolon_in_expressions_from_macros = \"warn\"\ntrivial_numeric_casts = \"warn\"\nunexpected_cfgs = \"warn\"\nunsafe_op_in_unsafe_fn = \"warn\" # `unsafe_op_in_unsafe_fn` may become the default in future Rust versions: https://github.com/rust-lang/rust/issues/71668\nunused_extern_crates = \"warn\"\nunused_import_braces = \"warn\"\nunused_lifetimes = \"warn\"\n\ntrivial_casts = \"allow\"\nunused_qualifications = \"allow\"\n\n[workspace.lints.rustdoc]\nall = \"warn\"\nmissing_crate_level_docs = \"warn\"\nbroken_intra_doc_links = \"warn\"\n\n# See also clippy.toml\n[workspace.lints.clippy]\nall = { level = \"warn\", priority = -1 }\n\nallow_attributes = \"warn\"\nas_ptr_cast_mut = \"warn\"\nawait_holding_lock = \"warn\"\nbool_to_int_with_if = \"warn\"\nbranches_sharing_code = \"warn\"\nchar_lit_as_u8 = \"warn\"\nchecked_conversions = \"warn\"\nclear_with_drain = \"warn\"\nclone_on_ref_ptr = \"warn\"\ncloned_instead_of_copied = \"warn\"\ncoerce_container_to_any = \"warn\"\ndbg_macro = \"warn\"\ndebug_assert_with_mut_call = \"warn\"\ndefault_union_representation = \"warn\"\nderive_partial_eq_without_eq = \"warn\"\ndisallowed_macros = \"warn\" # See clippy.toml\ndisallowed_methods = \"warn\" # See clippy.toml\ndisallowed_names = \"warn\" # See clippy.toml\ndisallowed_script_idents = \"warn\" # See clippy.toml\ndisallowed_types = \"warn\" # See clippy.toml\ndoc_broken_link = \"warn\"\ndoc_comment_double_space_linebreaks = \"warn\"\ndoc_include_without_cfg = \"warn\"\ndoc_link_with_quotes = \"warn\"\ndoc_markdown = \"warn\"\nelidable_lifetime_names = \"warn\"\nempty_enum = \"warn\"\nempty_enum_variants_with_brackets = \"warn\"\nempty_line_after_outer_attr = \"warn\"\nenum_glob_use = \"warn\"\nequatable_if_let = \"warn\"\nexit = \"warn\"\nexpl_impl_clone_on_copy = \"warn\"\nexplicit_deref_methods = \"warn\"\nexplicit_into_iter_loop = \"warn\"\nexplicit_iter_loop = \"warn\"\nfallible_impl_from = \"warn\"\nfilter_map_next = \"warn\"\nflat_map_option = \"warn\"\nfloat_cmp_const = \"warn\"\nfn_params_excessive_bools = \"warn\"\nfn_to_numeric_cast_any = \"warn\"\nfrom_iter_instead_of_collect = \"warn\"\nget_unwrap = \"warn\"\nif_let_mutex = \"warn\"\nignore_without_reason = \"warn\"\nimplicit_clone = \"warn\"\nimplied_bounds_in_impls = \"warn\"\nimprecise_flops = \"warn\"\ninconsistent_struct_constructor = \"warn\"\nindex_refutable_slice = \"warn\"\ninefficient_to_string = \"warn\"\ninfinite_loop = \"warn\"\ninto_iter_without_iter = \"warn\"\ninvalid_upcast_comparisons = \"warn\"\nip_constant = \"warn\"\niter_filter_is_ok = \"warn\"\niter_filter_is_some = \"warn\"\niter_not_returning_iterator = \"warn\"\niter_on_empty_collections = \"warn\"\niter_on_single_items = \"warn\"\niter_over_hash_type = \"warn\"\niter_without_into_iter = \"warn\"\nlarge_digit_groups = \"warn\"\nlarge_futures = \"warn\"\nlarge_include_file = \"warn\"\nlarge_stack_arrays = \"warn\"\nlarge_stack_frames = \"warn\"\nlarge_types_passed_by_value = \"warn\"\nlet_unit_value = \"warn\"\nlinkedlist = \"warn\"\nliteral_string_with_formatting_args = \"warn\"\nlossy_float_literal = \"warn\"\nmacro_use_imports = \"warn\"\nmanual_assert = \"warn\"\nmanual_clamp = \"warn\"\nmanual_instant_elapsed = \"warn\"\nmanual_is_power_of_two = \"warn\"\nmanual_is_variant_and = \"warn\"\nmanual_let_else = \"warn\"\nmanual_midpoint = \"warn\" # NOTE `midpoint` is often a lot slower for floats, so we have our own `emath::fast_midpoint` function.\nmanual_ok_or = \"warn\"\nmanual_string_new = \"warn\"\nmap_err_ignore = \"warn\"\nmap_flatten = \"warn\"\nmatch_bool = \"warn\"\nmatch_same_arms = \"warn\"\nmatch_wild_err_arm = \"warn\"\nmatch_wildcard_for_single_variants = \"warn\"\nmem_forget = \"warn\"\nmismatching_type_param_order = \"warn\"\nmissing_assert_message = \"warn\"\nmissing_enforced_import_renames = \"warn\"\nmissing_errors_doc = \"warn\"\nmissing_safety_doc = \"warn\"\nmixed_attributes_style = \"warn\"\nmut_mut = \"warn\"\nmutex_integer = \"warn\"\nneedless_borrow = \"warn\"\nneedless_continue = \"warn\"\nneedless_for_each = \"warn\"\nneedless_pass_by_ref_mut = \"warn\"\nneedless_pass_by_value = \"warn\"\nnegative_feature_names = \"warn\"\nnon_std_lazy_statics = \"warn\"\nnon_zero_suggestions = \"warn\"\nnonstandard_macro_braces = \"warn\"\noption_as_ref_cloned = \"warn\"\noption_option = \"warn\"\nor_fun_call = \"warn\"\npath_buf_push_overwrite = \"warn\"\npathbuf_init_then_push = \"warn\"\nprecedence_bits = \"warn\"\nprint_stderr = \"warn\"\nprint_stdout = \"warn\"\nptr_as_ptr = \"warn\"\nptr_cast_constness = \"warn\"\npub_underscore_fields = \"warn\"\npub_without_shorthand = \"warn\"\nrc_mutex = \"warn\"\nreadonly_write_lock = \"warn\"\nredundant_type_annotations = \"warn\"\nref_as_ptr = \"warn\"\nref_option_ref = \"warn\"\nref_patterns = \"warn\"\nrest_pat_in_fully_bound_structs = \"warn\"\nreturn_and_then = \"warn\"\nsame_functions_in_if_condition = \"warn\"\nself_only_used_in_recursion = \"warn\"\nsemicolon_if_nothing_returned = \"warn\"\nset_contains_or_insert = \"warn\"\nsingle_char_pattern = \"warn\"\nsingle_match_else = \"warn\"\nsingle_option_map = \"warn\"\nstr_split_at_newline = \"warn\"\nstr_to_string = \"warn\"\nstring_add = \"warn\"\nstring_add_assign = \"warn\"\nstring_lit_as_bytes = \"warn\"\nstring_lit_chars_any = \"warn\"\nsuspicious_command_arg_space = \"warn\"\nsuspicious_xor_used_as_pow = \"warn\"\ntodo = \"warn\"\ntoo_long_first_doc_paragraph = \"warn\"\ntrailing_empty_array = \"warn\"\ntrait_duplication_in_bounds = \"warn\"\ntransmute_ptr_to_ptr = \"warn\"\ntuple_array_conversions = \"warn\"\nunchecked_time_subtraction = \"warn\"\nundocumented_unsafe_blocks = \"warn\"\nunimplemented = \"warn\"\nuninhabited_references = \"warn\"\nuninlined_format_args = \"warn\"\nunnecessary_box_returns = \"warn\"\nunnecessary_debug_formatting = \"warn\"\nunnecessary_literal_bound = \"warn\"\nunnecessary_safety_comment = \"warn\"\nunnecessary_safety_doc = \"warn\"\nunnecessary_self_imports = \"warn\"\nunnecessary_semicolon = \"warn\"\nunnecessary_struct_initialization = \"warn\"\nunnecessary_wraps = \"warn\"\nunnested_or_patterns = \"warn\"\nunused_async = \"warn\"\nunused_peekable = \"warn\"\nunused_rounding = \"warn\"\nunused_self = \"warn\"\nunused_trait_names = \"warn\"\nunwrap_used = \"warn\"\nuse_self = \"warn\"\nuseless_let_if_seq = \"warn\"\nuseless_transmute = \"warn\"\nverbose_file_reads = \"warn\"\nwildcard_dependencies = \"warn\"\nzero_sized_map_values = \"warn\"\n\n\n# TODO(emilk): maybe enable more of these lints?\ncast_possible_wrap = \"allow\"\ncomparison_chain = \"allow\"\nshould_panic_without_expect = \"allow\"\ntoo_many_lines = \"allow\"\n\n# These are meh:\nassigning_clones = \"allow\" # No please\nlet_underscore_must_use = \"allow\"\nlet_underscore_untyped = \"allow\"\nmanual_range_contains = \"allow\" # this one is just worse imho\nmap_unwrap_or = \"allow\" # so is this one\nself_named_module_files = \"allow\" # Disabled waiting on https://github.com/rust-lang/rust-clippy/issues/9602\nsignificant_drop_tightening = \"allow\" # Too many false positives\nwildcard_imports = \"allow\" # `use crate::*` is useful to avoid merge conflicts when adding/removing imports\n"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Copyright (c) 2018-2021 Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# 🖌 egui: an easy-to-use GUI in pure Rust\n\n[<img alt=\"github\" src=\"https://img.shields.io/badge/github-emilk/egui-8da0cb?logo=github\" height=\"20\">](https://github.com/emilk/egui)\n[![Latest version](https://img.shields.io/crates/v/egui.svg)](https://crates.io/crates/egui)\n[![Documentation](https://docs.rs/egui/badge.svg)](https://docs.rs/egui)\n[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)\n[![Build Status](https://github.com/emilk/egui/workflows/Rust/badge.svg)](https://github.com/emilk/egui/actions/workflows/rust.yml)\n[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/emilk/egui/blob/main/LICENSE-MIT)\n[![Apache](https://img.shields.io/badge/license-Apache-blue.svg)](https://github.com/emilk/egui/blob/main/LICENSE-APACHE)\n[![Discord](https://img.shields.io/discord/900275882684477440?label=egui%20discord)](https://discord.gg/JFcEma9bJq)\n\n\n<div align=\"center\">\n<a href=\"https://www.rerun.io/\"><img src=\"https://github.com/user-attachments/assets/78e79463-4357-461b-bbd1-31aa5ef5e1a2\" width=\"250\"></a>\n\negui development is sponsored by [Rerun](https://www.rerun.io/), a startup building<br>\nan SDK for visualizing streams of multimodal data.\n</div>\n\n---\n\n👉 [Click to run the web demo](https://www.egui.rs/#demo) 👈\n\negui (pronounced \"e-gooey\") is a simple, fast, and highly portable immediate mode GUI library for Rust. egui runs on the web, natively, and [in your favorite game engine](#integrations).\n\negui aims to be the easiest-to-use Rust GUI library, and the simplest way to make a web app in Rust.\n\negui can be used anywhere you can draw textured triangles, which means you can easily integrate it into your game engine of choice.\n\n[`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe) is the official egui framework, which supports writing apps for Web, Linux, Mac, Windows, and Android.\n\n\n## Example\n\n``` rust\nui.heading(\"My egui Application\");\nui.horizontal(|ui| {\n    ui.label(\"Your name: \");\n    ui.text_edit_singleline(&mut name);\n});\nui.add(egui::Slider::new(&mut age, 0..=120).text(\"age\"));\nif ui.button(\"Increment\").clicked() {\n    age += 1;\n}\nui.label(format!(\"Hello '{name}', age {age}\"));\nui.image(egui::include_image!(\"ferris.png\"));\n```\n\n<img alt=\"Dark mode\" src=\"https://github.com/user-attachments/assets/3b446d29-99d8-4c82-86bb-4d8ef0516017\"> &nbsp; &nbsp; <img alt=\"Light mode\" src=\"https://github.com/user-attachments/assets/a5e7da93-89a8-4ba0-86b8-0fa2228a4f62\" height=\"278\">\n\n## Sections:\n\n* [Example](#example)\n* [Quick start](#quick-start)\n* [Demo](#demo)\n* [Goals](#goals)\n* [State / features](#state)\n* [Dependencies](#dependencies)\n* [Who is egui for?](#who-is-egui-for)\n* [Integrations](#integrations)\n* [Why immediate mode](#why-immediate-mode)\n* [FAQ](#faq)\n* [Other](#other)\n* [Credits](#credits)\n\n([egui 的中文翻译文档 / chinese translation](https://github.com/Re-Ch-Love/egui-doc-cn/blob/main/README_zh-hans.md))\n\n\n## Quick start\n\nThere are simple examples in [the `examples/` folder](https://github.com/emilk/egui/blob/main/examples/). If you want to write a web app, then go to <https://github.com/emilk/eframe_template/> and follow the instructions. The official docs are at <https://docs.rs/egui>. For inspiration and more examples, check out the [the egui web demo](https://www.egui.rs/#demo) and follow the links in it to its source code.\n\nIf you want to integrate egui into an existing engine, go to the [Integrations](#integrations) section.\n\nIf you have questions, use [GitHub Discussions](https://github.com/emilk/egui/discussions). There is also [an egui discord server](https://discord.gg/JFcEma9bJq). If you want to contribute to egui, please read the [Contributing Guidelines](https://github.com/emilk/egui/blob/main/CONTRIBUTING.md).\n\n## Demo\n\n[Click to run egui web demo](https://www.egui.rs/#demo) (works in any browser with Wasm and WebGL support). Uses [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe).\n\nTo test the demo app locally, run `cargo run --release -p egui_demo_app`.\n\nThe native backend is [`egui-wgpu`](https://github.com/emilk/egui/tree/main/crates/egui-wgpu) (using [`wgpu`](https://crates.io/crates/wgpu)) and should work out-of-the-box on Mac and Windows, but on Linux you need to first run:\n\n`sudo apt-get install -y libclang-dev libgtk-3-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev`\n\nOn Fedora Rawhide you need to run:\n\n`dnf install clang clang-devel clang-tools-extra libxkbcommon-devel pkg-config openssl-devel libxcb-devel gtk3-devel atk fontconfig-devel`\n\n**NOTE**: This is just for the demo app - egui itself is completely platform agnostic!\n\n## Goals\n\n* The easiest to use GUI library\n* Responsive: target 60 Hz in debug build\n* Friendly: difficult to make mistakes, and shouldn't panic\n* Portable: the same code works on the web and as a native app\n* Easy to integrate into any environment\n* A simple 2D graphics API for custom painting ([`epaint`](https://docs.rs/epaint)).\n* Pure immediate mode: no callbacks\n* Extensible: [easy to write your own widgets for egui](https://github.com/emilk/egui/blob/main/crates/egui_demo_lib/src/demo/toggle_switch.rs)\n* Modular: You should be able to use small parts of egui and combine them in new ways\n* Safe: there is no `unsafe` code in egui\n* Minimal dependencies\n\negui is *not* a framework. egui is a library you call into, not an environment you program for.\n\n**NOTE**: egui does not claim to have reached all these goals yet! egui is still work in progress.\n\n### Non-goals\n\n* Become the most powerful GUI library\n* Native looking interface\n\n## State\n\negui is in active development. It works well for what it does, but it lacks many features and the interfaces are still in flux. New releases will have breaking changes.\n\nStill, egui can be used to create professional looking applications, like [the Rerun Viewer](https://app.rerun.io/).\n\n### Features\n\n* Widgets: label, text button, hyperlink, checkbox, radio button, slider, draggable value, text editing, color picker, spinner\n* Images\n* Layouts: horizontal, vertical, columns, automatic wrapping\n* Text editing: multiline, copy/paste, undo, emoji supports\n* Windows: move, resize, name, minimize and close. Automatically sized and positioned.\n* Regions: resizing, vertical scrolling, collapsing headers (sections), panels\n* Rendering: Anti-aliased rendering of lines, circles, text and convex polygons.\n* Tooltips on hover\n* Accessibility via [AccessKit](https://accesskit.dev/)\n* Label text selection\n* And more!\n\nCheck out the [3rd party egui crates wiki](https://github.com/emilk/egui/wiki/3rd-party-egui-crates) for even more\nwidgets and features, maintained by the community.\n\n<img src=\"https://github.com/user-attachments/assets/13e73b76-e456-42bd-8ec9-220802834268\" width=\"50%\">\n\nLight Theme:\n\n<img src=\"https://github.com/user-attachments/assets/2e38972c-a444-4894-b32f-47a2719cf369\" width=\"50%\">\n\n## Dependencies\n`egui` has a minimal set of default dependencies.\nHeavier dependencies are kept out of `egui`, even as opt-in.\nAll code in `egui` is Wasm-friendly (even outside a browser).\n\nTo load images into `egui` you can use the official [`egui_extras`](https://github.com/emilk/egui/tree/main/crates/egui_extras) crate.\n\n[`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe) on the other hand has a lot of dependencies, including [`winit`](https://crates.io/crates/winit), [`image`](https://crates.io/crates/image), graphics crates, clipboard crates, etc,\n\n## Who is egui for?\n\negui aims to be the best choice when you want a simple way to create a GUI, or you want to add a GUI to a game engine.\n\nIf you are not using Rust, egui is not for you. If you want a GUI that looks native, egui is not for you. If you want something that doesn't break when you upgrade it, egui isn't for you (yet).\n\nBut if you are writing something interactive in Rust that needs a simple GUI, egui may be for you.\n\n\n## Integrations\n\negui is built to be easy to integrate into any existing game engine or platform you are working on.\negui itself doesn't know or care on what OS it is running or how to render things to the screen - that is the job of the egui integration.\n\nAn integration needs to do the following each frame:\n\n* **Input**: Gather input (mouse, touches, keyboard, screen size, etc) and give it to egui\n* Call into the application GUI code\n* **Output**: Handle egui output (cursor changes, paste, texture allocations, …)\n* **Painting**: Render the triangle mesh egui produces (see [OpenGL example](https://github.com/emilk/egui/blob/main/crates/egui_glow/src/painter.rs))\n\n### Official integrations\n\nThese are the official egui integrations:\n\n* [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe) for compiling the same app to web/wasm and desktop/native. Uses `egui-winit` and `egui_glow` or `egui-wgpu`\n* [`egui_glow`](https://github.com/emilk/egui/tree/main/crates/egui_glow) for rendering egui with [glow](https://github.com/grovesNL/glow) on native and web, and for making native apps\n* [`egui-wgpu`](https://github.com/emilk/egui/tree/main/crates/egui-wgpu) for [wgpu](https://crates.io/crates/wgpu) (WebGPU API)\n* [`egui-winit`](https://github.com/emilk/egui/tree/main/crates/egui-winit) for integrating with [winit](https://github.com/rust-windowing/winit)\n\n### 3rd party integrations\n\nCheck the wiki to find [3rd party integrations](https://github.com/emilk/egui/wiki/3rd-party-integrations)\nand [egui crates](https://github.com/emilk/egui/wiki/3rd-party-egui-crates).\n\n### Writing your own egui integration\nMissing an integration for the thing you're working on? Create one, it's easy!\nSee <https://docs.rs/egui/latest/egui/#integrating-with-egui>.\n\n\n## Why immediate mode\n\n`egui` is an [immediate mode GUI library](https://en.wikipedia.org/wiki/Immediate_mode_GUI), as opposed to a *retained mode* GUI library. The difference between retained mode and immediate mode is best illustrated with the example of a button: In a retained GUI you create a button, add it to some UI and install some on-click handler (callback). The button is retained in the UI, and to change the text on it you need to store some sort of reference to it. By contrast, in immediate mode you show the button and interact with it immediately, and you do so every frame (e.g. 60 times per second). This means there is no need for any on-click handler, nor to store any reference to it. In `egui` this looks like this: `if ui.button(\"Save file\").clicked() { save(file); }`.\n\nA more detailed description of immediate mode can be found [in the `egui` docs](https://docs.rs/egui/latest/egui/#understanding-immediate-mode).\n\nThere are advantages and disadvantages to both systems.\n\nThe short of it is this: immediate mode GUI libraries are easier to use, but less powerful.\n\n### Advantages of immediate mode\n#### Usability\nThe main advantage of immediate mode is that the application code becomes vastly simpler:\n\n* You never need to have any on-click handlers and callbacks that disrupts your code flow.\n* You don't have to worry about a lingering callback calling something that is gone.\n* Your GUI code can easily live in a simple function (no need for an object just for the UI).\n* You don't have to worry about app state and GUI state being out-of-sync (i.e. the GUI showing something outdated), because the GUI isn't storing any state - it is showing the latest state *immediately*.\n\nIn other words, a whole lot of code, complexity and bugs are gone, and you can focus your time on something more interesting than writing GUI code.\n\n### Disadvantages of immediate mode\n\n#### Layout\nThe main disadvantage of immediate mode is it makes layout more difficult. Say you want to show a small dialog window in the center of the screen. To position the window correctly the GUI library must first know the size of it. To know the size of the window the GUI library must first layout the contents of the window. In retained mode this is easy: the GUI library does the window layout, positions the window, then checks for interaction (\"was the OK button clicked?\").\n\nIn immediate mode you run into a paradox: to know the size of the window, we must do the layout, but the layout code also checks for interaction (\"was the OK button clicked?\") and so it needs to know the window position *before* showing the window contents. This means we must decide where to show the window *before* we know its size!\n\nThis is a fundamental shortcoming of immediate mode GUIs, and any attempt to resolve it comes with its own downsides.\n\nOne workaround is to store the size and use it the next frame. This produces a frame-delay for the correct layout, producing occasional flickering the first frame something shows up. `egui` does this for some things such as windows and grid layouts.\n\nThe \"first-frame jitter\" can be covered up with an extra _pass_, which egui supports via `Context::request_discard`.\nThe downside of this is the added CPU cost of a second pass, so egui only does this in very rare circumstances (the majority of frames are single-pass).\n\nFor \"atomic\" widgets (e.g. a button) `egui` knows the size before showing it, so centering buttons, labels etc is possible in `egui` without any special workarounds.\n\nSee [this issue](https://github.com/emilk/egui/issues/4378) for more.\n\n#### CPU usage\nSince an immediate mode GUI does a full layout each frame, the layout code needs to be quick. If you have a very complex GUI this can tax the CPU. In particular, having a very large UI in a scroll area (with very long scrollback) can be slow, as the content needs to be laid out each frame.\n\nIf you design the GUI with this in mind and refrain from huge scroll areas (or only lay out the part that is in view) then the performance hit is generally pretty small. For most cases you can expect `egui` to take up 1-2 ms per frame, but `egui` still has a lot of room for optimization (it's not something I've focused on yet). `egui` only repaints when there is interaction (e.g. mouse movement) or an animation, so if your app is idle, no CPU is wasted.\n\nIf your GUI is highly interactive, then immediate mode may actually be more performant compared to retained mode. Go to any web page and resize the browser window, and you'll notice that the browser is very slow to do the layout and eats a lot of CPU doing it. Resize a window in `egui` by contrast, and you'll get smooth 60 FPS at no extra CPU cost.\n\n\n#### IDs\nThere are some GUI state that you want the GUI library to retain, even in an immediate mode library such as `egui`. This includes position and sizes of windows and how far the user has scrolled in some UI. In these cases you need to provide `egui` with a seed of a unique identifier (unique within the parent UI). For instance: by default `egui` uses the window titles as unique IDs to store window positions. If you want two windows with the same name (or one window with a dynamic name) you must provide some other ID source to `egui` (some unique integer or string).\n\n`egui` also needs to track which widget is being interacted with (e.g. which slider is being dragged). `egui` uses unique IDs for this as well, but in this case the IDs are automatically generated, so there is no need for the user to worry about it. In particular, having two buttons with the same name is no problem (this is in contrast with [`Dear ImGui`](https://github.com/ocornut/imgui)).\n\nOverall, ID handling is a rare inconvenience, and not a big disadvantage.\n\n\n## FAQ\n\nAlso see [GitHub Discussions](https://github.com/emilk/egui/discussions/categories/q-a).\n\n### Can I use `egui` with non-latin characters?\nYes! But you need to install your own font (`.ttf` or `.otf`) using [`Context::set_fonts`](https://docs.rs/egui/latest/egui/struct.Context.html#method.set_fonts).\n\n### Can I customize the look of egui?\nYes! You can customize the colors, spacing, fonts and sizes of everything using `Context::set_style`.\n\nThis is not yet as powerful as say CSS, [but this is going to improve](https://github.com/emilk/egui/issues/3284).\n\nHere is an example (from https://github.com/a-liashenko/TinyPomodoro):\n\n<img src=\"https://github.com/user-attachments/assets/e6107237-2547-41d6-996b-9a20ae0345ab\" width=\"50%\">\n\n### How do I use egui with `async`?\nIf you call `.await` in your GUI code, the UI will freeze, which is very bad UX. Instead, keep the GUI thread non-blocking and communicate with any concurrent tasks (`async` tasks or other threads) with something like:\n* Channels (e.g. [`std::sync::mpsc::channel`](https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html)). Make sure to use [`try_recv`](https://doc.rust-lang.org/std/sync/mpsc/struct.Receiver.html#method.try_recv) so you don't block the gui thread!\n* `Arc<Mutex<Value>>` (background thread sets a value; GUI thread reads it)\n* [`poll_promise::Promise`](https://docs.rs/poll-promise)\n* [`eventuals::Eventual`](https://docs.rs/eventuals/latest/eventuals/struct.Eventual.html)\n* [`tokio::sync::watch::channel`](https://docs.rs/tokio/latest/tokio/sync/watch/fn.channel.html)\n\n### How do I create a file dialog?\n\nThe async version of [rfd](https://docs.rs/rfd/latest/rfd/) supports both native and Wasm. See example app here https://github.com/woelper/egui_pick_file which also has a demo available via [gitub pages](https://woelper.github.io/egui_pick_file/).\n\n### What about accessibility, such as screen readers?\negui includes optional support for [AccessKit](https://accesskit.dev/), which currently implements the native accessibility APIs on Windows and macOS. This feature is enabled by default in eframe. For platforms that AccessKit doesn't yet support, including web, there is an experimental built-in screen reader; in [the web demo](https://www.egui.rs/#demo) you can enable it in the \"Backend\" tab.\n\nThe original discussion of accessibility in egui is at <https://github.com/emilk/egui/issues/167>. Now that AccessKit support is merged, providing a strong foundation for future accessibility work, please open new issues on specific accessibility problems.\n\n### What is the difference between [egui](https://docs.rs/egui) and [eframe](https://github.com/emilk/egui/tree/main/crates/eframe)?\n\n`egui` is a 2D user interface library for laying out and interacting with buttons, sliders, etc.\n`egui` has no idea if it is running on the web or natively, and does not know how to collect input or show things on screen.\nThat is the job of *the integration* or *backend*.\n\nIt is common to use `egui` from a game engine (using e.g. [`bevy_egui`](https://docs.rs/bevy_egui)),\nbut you can also use `egui` stand-alone using `eframe`. `eframe` has integration for web and native, and handles input and rendering.\nThe _frame_ in `eframe` stands both for the frame in which your egui app resides and also for \"framework\" (`eframe` is a framework, `egui` is a library).\n\n### How do I render 3D stuff in an egui area?\nThere are multiple ways to combine egui with 3D. The simplest way is to use a 3D library and have egui sit on top of the 3D view. See for instance [`bevy_egui`](https://github.com/mvlabat/bevy_egui) or [`three-d`](https://github.com/asny/three-d).\n\nIf you want to embed 3D into an egui view there are two options:\n\n#### `Shape::Callback`\nExample:\n* <https://github.com/emilk/egui/blob/main/examples/custom_3d_glow/src/main.rs>\n\n`Shape::Callback` will call your code when egui gets painted, to show anything using whatever the background rendering context is. When using [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe) this will be [`glow`](https://github.com/grovesNL/glow). Other integrations will give you other rendering contexts, if they support `Shape::Callback` at all.\n\n#### Render-to-texture\nYou can also render your 3D scene to a texture and display it using [`ui.image(…)`](https://docs.rs/egui/latest/egui/struct.Ui.html#method.image). You first need to convert the native texture to an [`egui::TextureId`](https://docs.rs/egui/latest/egui/enum.TextureId.html), and how to do this depends on the integration you use.\n\nExamples:\n* Using [`egui-miniquad`]( https://github.com/not-fl3/egui-miniquad): https://github.com/not-fl3/egui-miniquad/blob/master/examples/render_to_egui_image.rs\n* Using [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe) + [`VTK (C++)`](https://vtk.org/): https://github.com/Gerharddc/vtk-egui-demo\n\n\n## Other\n\n### Conventions and design choices\n\nAll coordinates are in screen space coordinates, with (0, 0) in the top left corner\n\nAll coordinates are in logical \"points\" which may consist of many physical pixels.\n\nAll colors have premultiplied alpha, unless otherwise stated.\n\negui uses the builder pattern for construction widgets. For instance: `ui.add(Label::new(\"Hello\").text_color(RED));` I am not a big fan of the builder pattern (it is quite verbose both in implementation and in use) but until Rust has named, default arguments it is the best we can do. To alleviate some of the verbosity there are common-case helper functions, like `ui.label(\"Hello\");`.\n\nInstead of using matching `begin/end` style function calls (which can be error prone) egui prefers to use `FnOnce` closures passed to a wrapping function. Lambdas are a bit ugly though, so I'd like to find a nicer solution to this. More discussion of this at <https://github.com/emilk/egui/issues/1004#issuecomment-1001650754>.\n\negui uses a single `RwLock` for short-time locks on each access of `Context` data. This is to leave implementation simple and transactional and allow users to run their UI logic in parallel. Instead of creating mutex guards, egui uses closures passed to a wrapping function, e.g. `ctx.input(|i| i.key_down(Key::A))`. This is to make it less likely that a user would accidentally double-lock the `Context`, which would lead to a deadlock.\n\n### Inspiration\n\nThe one and only [Dear ImGui](https://github.com/ocornut/imgui) is a great Immediate Mode GUI for C++ which works with many backends. That library revolutionized how I think about GUI code and turned GUI programming from something I hated to do to something I now enjoy.\n\n### Name\n\nThe name of the library and the project is \"egui\" and pronounced as \"e-gooey\". Please don't write it as \"EGUI\".\n\nThe library was originally called \"Emigui\", but was renamed to \"egui\" in 2020.\n\n## Credits\n\negui author and maintainer: Emil Ernerfeldt ([@emilk](https://github.com/emilk)).\n\nNotable contributions by:\n\n* [@n2](https://github.com/n2): [Mobile web input and IME support](https://github.com/emilk/egui/pull/253)\n* [@optozorax](https://github.com/optozorax): [Arbitrary widget data storage](https://github.com/emilk/egui/pull/257)\n* [@quadruple-output](https://github.com/quadruple-output): [Multitouch](https://github.com/emilk/egui/pull/306)\n* [@EmbersArc](https://github.com/EmbersArc): [Plots](https://github.com/emilk/egui/pulls?q=+is%3Apr+author%3AEmbersArc)\n* [@AsmPrgmC3](https://github.com/AsmPrgmC3): [Proper sRGBA blending for web](https://github.com/emilk/egui/pull/650)\n* [@AlexApps99](https://github.com/AlexApps99): [`egui_glow`](https://github.com/emilk/egui/pull/685)\n* [@mankinskin](https://github.com/mankinskin): [Context menus](https://github.com/emilk/egui/pull/543)\n* [@KentaTheBugMaker](https://github.com/KentaTheBugMaker): [Port glow painter to web](https://github.com/emilk/egui/pull/868)\n* [@danielkeller](https://github.com/danielkeller): [`Context` refactor](https://github.com/emilk/egui/pull/1050)\n* [@MaximOsipenko](https://github.com/MaximOsipenko): [`Context` lock refactor](https://github.com/emilk/egui/pull/2625)\n* [@mwcampbell](https://github.com/mwcampbell): [AccessKit](https://github.com/AccessKit/accesskit) [integration](https://github.com/emilk/egui/pull/2294)\n* [@hasenbanck](https://github.com/hasenbanck), [@s-nie](https://github.com/s-nie), [@Wumpf](https://github.com/Wumpf): [`egui-wgpu`](https://github.com/emilk/egui/tree/main/crates/egui-wgpu)\n* [@jprochazk](https://github.com/jprochazk): [egui image API](https://github.com/emilk/egui/issues/3291)\n* And [many more](https://github.com/emilk/egui/graphs/contributors?type=a).\n\negui is licensed under [MIT](LICENSE-MIT) OR [Apache-2.0](LICENSE-APACHE).\n\n* The flattening algorithm for the cubic bezier curve and quadratic bezier curve is from [lyon_geom](https://docs.rs/lyon_geom/latest/lyon_geom/)\n\nDefault fonts:\n\n* `emoji-icon-font.ttf`: [Copyright (c) 2014 John Slegers](https://github.com/jslegers/emoji-icon-font) , MIT License\n* `Hack-Regular.ttf`: <https://github.com/source-foundry/Hack>, [MIT Licence](https://github.com/source-foundry/Hack/blob/master/LICENSE.md)\n* `NotoEmoji-Regular.ttf`: [google.com/get/noto](https://google.com/get/noto), [SIL Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL)\n* `Ubuntu-Light.ttf` by [Dalton Maag](http://www.daltonmaag.com/): [Ubuntu font licence](https://ubuntu.com/legal/font-licence)\n\n---\n\n<div align=\"center\">\n<a href=\"https://www.rerun.io/\"><img src=\"https://github.com/user-attachments/assets/78e79463-4357-461b-bbd1-31aa5ef5e1a2\" width=\"440\"></a>\n\negui development is sponsored by [Rerun](https://www.rerun.io/), a startup building<br>\nan SDK for visualizing streams of multimodal data.\n</div>\n"
  },
  {
    "path": "RELEASES.md",
    "content": "# Releases\n## Cadence\nWe don't have a regular cadence, but there is usually a new major release every two months or so.\n\nOften a major release is followed by one or two patch releases within a week or two.\n\n## Versioning\nAll crates under the [`crates/`](crates/) folder are published in lock-step, with the same version number. This means that we won't publish a new breaking change of a single crate without also publishing all other crates. This also means we sometimes do a new release of a crate even though there are no changes to that crate.\n\nThe only exception to this are patch releases, where we sometimes only patch a single crate.\n\nThe egui version in egui `main` is always the version of the last published crates. This is so that users can easily patch their egui crates to egui `main` if they want to.\n\n## Governance\nReleases are generally done by [emilk](https://github.com/emilk/), but the [rerun-io](https://github.com/rerun-io/) organization (where emilk is CTO) also has publish rights to all the crates.\n\n\n## Rust version policy\nOur Minimum Supported Rust Version (MSRV) is always _at least_ two minor release behind the latest Rust version. This means users of egui aren't forced to update to the very latest Rust version.\n\nWe don't update the MSRV in a patch release, unless we really, really need to.\n\n\n# Release process\n* [ ] copy this checklist to a new egui issue, called \"Release 0.xx.y\"\n* [ ] close all issues in the milestone for this release\n\n## Special steps for patch release\n* [ ] make a branch off of the _latest_ release\n* [ ] cherry-pick what you want to release\n* [ ] run `cargo semver-checks`\n\n## Optional polish before a major release\n* [ ] improve the demo a bit\n* [ ] see if you can make web demo WASM smaller\n* [ ] `./scripts/docs.sh`: read and improve documentation of new stuff\n* [ ] `cargo update`\n* [ ] `cargo outdated` (or manually look for outdated crates in each `Cargo.toml`)\n\n## Release testing\n* [ ] `cargo r -p egui_demo_app` and click around for while\n* [ ] update `eframe_template` and test\n* [ ] update `egui_plot` and test\n* [ ] update `egui_table` and test\n* [ ] update `egui_tiles` and test\n* [ ] test with Rerun\n* [ ] `./scripts/check.sh`\n* [ ] check that CI is green\n\n## Preparation\n* [ ] make sure there are no important unmerged PRs\n* [ ] Ensure we don't have any patch/git dependencies (e.g. kittest)\n* [ ] Create a branch called `release-0.xx.0` and open a PR for it\n* [ ] run `scripts/generate_example_screenshots.sh` if needed\n* [ ] write a short release note that fits in a bluesky post\n* [ ] record gif for `CHANGELOG.md` release note (and later bluesky post)\n* [ ] update changelogs\n  * [ ] run `scripts/generate_changelog.py --version 0.x.0 --write`\n  * [ ] read changelogs and clean them up if needed\n  * [ ] write a good intro with highlight for the main changelog\n* [ ] run `typos`\n\n## Actual release\n* [ ] bump version numbers in workspace `Cargo.toml`\n* [ ] check that CI for the PR is green\n* [ ] publish the crates by running `scripts/publish_crates.sh`\n* [ ] `git tag -a 0.x.0 -m 'Release 0.x.0 - <release title>'`\n* [ ] `git pull --tags ; git tag -d latest && git tag -a latest -m 'Latest release' && git push --tags origin latest --force ; git push --tags`\n* [ ] merge release PR as `Release 0.x.0 - <release title>`\n* [ ] check that CI for `main` is green\n* [ ] do a GitHub release: https://github.com/emilk/egui/releases/new\n  * follow the format of the last release\n* [ ] wait for  the documentation build to finish: https://docs.rs/releases/queue\n  * [ ] https://docs.rs/egui/ works\n  * [ ] https://docs.rs/eframe/ works\n\n\n## Announcements\n* [ ] [Bluesky](https://bsky.app/profile/ernerfeldt.bsky.social)\n* [ ] egui discord\n* [ ] [r/rust](https://www.reddit.com/r/rust/comments/1bocr5s/announcing_egui_027_with_improved_menus_and/)\n* [ ] [r/programming](https://www.reddit.com/r/programming/comments/1bocsf6/announcing_egui_027_an_easytouse_crossplatform/)\n* [ ] [This Week in Rust](https://github.com/rust-lang/this-week-in-rust/pull/5167)\n\n\n## After release\n* [ ] update `eframe_template`\n* [ ] publish new `egui_plot`\n* [ ] publish new `egui_table`\n* [ ] publish new `egui_tiles`\n* [ ] make a PR to `egui_commonmark`\n* [ ] make a PR to `rerun`\n\n\n## Finally\n* [ ] close the milestone\n* [ ] close this issue\n* [ ] improve `RELEASES.md` with what you learned this time around\n"
  },
  {
    "path": "clippy.toml",
    "content": "# There is also a scripts/clippy_wasm/clippy.toml which forbids some methods that are not available in wasm.\n\n# -----------------------------------------------------------------------------\n# Section identical to scripts/clippy_wasm/clippy.toml:\n\nmsrv = \"1.92\"\n\nallow-unwrap-in-tests = true\n\n# https://doc.rust-lang.org/nightly/clippy/lint_configuration.html#avoid-breaking-exported-api\n# We want suggestions, even if it changes public API.\navoid-breaking-exported-api = false\n\nmax-fn-params-bools = 2 # TODO(emilk): decrease this to 1\n\n# https://rust-lang.github.io/rust-clippy/master/index.html#/large_include_file\nmax-include-file-size = 1000000\n\n# https://rust-lang.github.io/rust-clippy/master/index.html#/type_complexity\ntype-complexity-threshold = 350\n\n# -----------------------------------------------------------------------------\n\n# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros\ndisallowed-macros = [\n  'std::dbg',\n  'std::unimplemented',\n\n  # TODO(emilk): consider forbidding these to encourage the use of proper log stream, and then explicitly allow legitimate uses\n  # 'std::eprint',\n  # 'std::eprintln',\n  # 'std::print',\n  # 'std::println',\n]\n\n# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods\ndisallowed-methods = [\n  # NOTE: There are many things that aren't allowed on wasm,\n  # but we cannot disable them all here (because of e.g. https://github.com/rust-lang/rust-clippy/issues/10406)\n  # so we do that in `clipppy_wasm.toml` instead.\n\n  { path = \"std::env::temp_dir\", reason = \"Use the tempfile crate instead\" },\n  { path = \"std::panic::catch_unwind\", reason = \"We compile with `panic = abort\" },\n  { path = \"std::thread::spawn\", reason = \"Use `std::thread::Builder` and name the thread\" },\n]\n\n# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names\ndisallowed-names = []\n\n# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types\ndisallowed-types = [\n  { path = \"std::sync::Condvar\", reason = \"Use parking_lot instead\" },\n  { path = \"std::sync::Mutex\", reason = \"Use epaint::mutex instead\" },\n  { path = \"std::sync::RwLock\", reason = \"Use epaint::mutex instead\" },\n  { path = \"winit::dpi::LogicalPosition\", reason = \"We do our own pixels<->point conversion, taking `egui_ctx.zoom_factor` into account\" },\n  { path = \"winit::dpi::LogicalSize\", reason = \"We do our own pixels<->point conversion, taking `egui_ctx.zoom_factor` into account\" },\n  # \"std::sync::Once\",  # enabled for now as the `log_once` macro uses it internally\n]\n\n# -----------------------------------------------------------------------------\n\n# Allow-list of words for markdown in docstrings https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown\ndoc-valid-idents = [\n  # You must also update the same list in `scripts/clippy_wasm/clippy.toml`!\n  \"AccessKit\",\n  \"WebGL\",\n  \"WebGL1\",\n  \"WebGL2\",\n  \"WebGPU\",\n  \"VirtualBox\",\n  \"..\",\n]\n"
  },
  {
    "path": "crates/ecolor/CHANGELOG.md",
    "content": "# Changelog for ecolor\nAll notable changes to the `ecolor` crate will be noted in this file.\n\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n## 0.33.3 - 2025-12-11\nNothing new\n\n\n## 0.33.2 - 2025-11-13\nNothing new\n\n\n## 0.33.0 - 2025-10-09\n* Align `Color32` to 4 bytes [#7318](https://github.com/emilk/egui/pull/7318) by [@anti-social](https://github.com/anti-social)\n* Make the `hex_color` macro `const` [#7444](https://github.com/emilk/egui/pull/7444) by [@YgorSouza](https://github.com/YgorSouza)\n* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)\n\n\n## 0.32.3 - 2025-09-12\nNothing new\n\n\n## 0.32.2 - 2025-09-04\nNothing new\n\n\n## 0.32.1 - 2025-08-15\nNothing new\n\n\n## 0.32.0 - 2025-07-10\n* Fix semi-transparent colors appearing too bright [#5824](https://github.com/emilk/egui/pull/5824) by [@emilk](https://github.com/emilk)\n* Remove things that have been deprecated for over a year [#7099](https://github.com/emilk/egui/pull/7099) by [@emilk](https://github.com/emilk)\n* Make `Hsva` derive serde [#7132](https://github.com/emilk/egui/pull/7132) by [@bircni](https://github.com/bircni)\n\n\n## 0.31.1 - 2025-03-05\nNothing new\n\n\n## 0.31.0 - 2025-02-04\n* Add `Color32::CYAN` and `Color32::MAGENTA` [#5663](https://github.com/emilk/egui/pull/5663) by [@juancampa](https://github.com/juancampa)\n\n\n## 0.30.0 - 2024-12-16\n* Use boxed slice for lookup table to avoid stack overflow [#5212](https://github.com/emilk/egui/pull/5212) by [@YgorSouza](https://github.com/YgorSouza)\n* Add `Color32::mul` [#5437](https://github.com/emilk/egui/pull/5437) by [@emilk](https://github.com/emilk)\n\n\n## 0.29.1 - 2024-10-01\nNothing new\n\n\n## 0.29.0 - 2024-09-26\n* Document the fact that the `hex_color!` macro is not `const` [#5169](https://github.com/emilk/egui/pull/5169) by [@YgorSouza](https://github.com/YgorSouza)\n\n\n## 0.28.1 - 2024-07-05\nNothing new\n\n\n## 0.28.0 - 2024-07-03\n* Fix `hex_color!` macro by re-exporting `color_hex` crate from `ecolor` [#4372](https://github.com/emilk/egui/pull/4372) by [@dataphract](https://github.com/dataphract)\n* Remove `extra_asserts` and `extra_debug_asserts` feature flags [#4478](https://github.com/emilk/egui/pull/4478) by [@emilk](https://github.com/emilk)\n* Add `Color32::lerp_to_gamma` [#4627](https://github.com/emilk/egui/pull/4627) by [@abey79](https://github.com/abey79)\n\n\n## 0.27.2 - 2024-04-02\n* Nothing new\n\n\n## 0.27.1 - 2024-03-29\n* Nothing new\n\n\n## 0.27.0 - 2024-03-26\n* Nothing new\n\n\n## 0.26.2 - 2024-02-14\n* Nothing new\n\n\n## 0.26.1 - 2024-02-11\n* Nothing new\n\n\n## 0.26.0 - 2024-02-05\n* Nothing new\n\n\n## 0.25.0 - 2024-01-08\n* Add `Color32::from_hex` and `Color32::to_hex` [#3570](https://github.com/emilk/egui/pull/3570) [#3777](https://github.com/emilk/egui/pull/3777) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n\n\n## 0.24.1 - 2023-11-30\n* Optimize color conversions [#3666](https://github.com/emilk/egui/pull/3666)\n\n\n## 0.24.0 - 2023-11-23\n* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)\n* Add `#[inline]` to all color-related function [38b4234](https://github.com/emilk/egui/commit/38b4234c3282a7c044c18b77234ee8c204efe171)\n\n\n## 0.22.0 - 2023-05-23\n* Nothing new\n\n\n## 0.21.0 - 2023-02-08\n* Add `Color32::gamma_multiply` ([#2437](https://github.com/emilk/egui/pull/2437)).\n\n\n## 0.20.0 - 2022-12-08\n* Split out `ecolor` crate from `epaint`\n"
  },
  {
    "path": "crates/ecolor/Cargo.toml",
    "content": "[package]\nname = \"ecolor\"\nversion.workspace = true\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\", \"Andreas Reich <reichandreas@gmx.de>\"]\ndescription = \"Color structs and color conversion utilities\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui\"\nlicense.workspace = true\nreadme = \"README.md\"\nrepository = \"https://github.com/emilk/egui\"\ncategories = [\"mathematics\", \"encoding\"]\nkeywords = [\"gui\", \"color\", \"conversion\", \"gamedev\", \"images\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"Cargo.toml\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[lib]\n\n\n[features]\ndefault = []\n\n\n[dependencies]\nemath.workspace = true\n\n#! ### Optional dependencies\n\n## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast `ecolor` types to `&[u8]`.\nbytemuck = { workspace = true, optional = true, features = [\"derive\"] }\n\n## [`cint`](https://docs.rs/cint) enables interoperability with other color libraries.\ncint = { workspace = true, optional = true }\n\n## Enable the [`hex_color`] macro.\ncolor-hex = { workspace = true, optional = true }\n\n## Enable this when generating docs.\ndocument-features = { workspace = true, optional = true }\n\n## Allow serialization using [`serde`](https://docs.rs/serde).\nserde = { workspace = true, optional = true }\n"
  },
  {
    "path": "crates/ecolor/README.md",
    "content": "# ecolor - egui color library\n\n[![Latest version](https://img.shields.io/crates/v/ecolor.svg)](https://crates.io/crates/ecolor)\n[![Documentation](https://docs.rs/ecolor/badge.svg)](https://docs.rs/ecolor)\n[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\nA simple color storage and conversion library.\n\nThis crate is built for the wants and needs of [`egui`](https://github.com/emilk/egui/).\n\nIf you want an actual _good_ color crate, use [`color`](https://crates.io/crates/color) instead.\n"
  },
  {
    "path": "crates/ecolor/src/cint_impl.rs",
    "content": "use super::{Color32, Hsva, HsvaGamma, Rgba, linear_f32_from_linear_u8, linear_u8_from_linear_f32};\nuse cint::{Alpha, ColorInterop, EncodedSrgb, Hsv, LinearSrgb, PremultipliedAlpha};\n\n// ---- Color32 ----\n\nimpl From<Alpha<EncodedSrgb<u8>>> for Color32 {\n    fn from(srgba: Alpha<EncodedSrgb<u8>>) -> Self {\n        let Alpha {\n            color: EncodedSrgb { r, g, b },\n            alpha: a,\n        } = srgba;\n\n        Self::from_rgba_unmultiplied(r, g, b, a)\n    }\n}\n\n// No From<Color32> for Alpha<_> because Color32 is premultiplied\n\nimpl From<PremultipliedAlpha<EncodedSrgb<u8>>> for Color32 {\n    fn from(srgba: PremultipliedAlpha<EncodedSrgb<u8>>) -> Self {\n        let PremultipliedAlpha {\n            color: EncodedSrgb { r, g, b },\n            alpha: a,\n        } = srgba;\n\n        Self::from_rgba_premultiplied(r, g, b, a)\n    }\n}\n\nimpl From<Color32> for PremultipliedAlpha<EncodedSrgb<u8>> {\n    fn from(col: Color32) -> Self {\n        let (r, g, b, a) = col.to_tuple();\n\n        Self {\n            color: EncodedSrgb { r, g, b },\n            alpha: a,\n        }\n    }\n}\n\nimpl From<PremultipliedAlpha<EncodedSrgb<f32>>> for Color32 {\n    fn from(srgba: PremultipliedAlpha<EncodedSrgb<f32>>) -> Self {\n        let PremultipliedAlpha {\n            color: EncodedSrgb { r, g, b },\n            alpha: a,\n        } = srgba;\n\n        // This is a bit of an abuse of the function name but it does what we want.\n        let r = linear_u8_from_linear_f32(r);\n        let g = linear_u8_from_linear_f32(g);\n        let b = linear_u8_from_linear_f32(b);\n        let a = linear_u8_from_linear_f32(a);\n\n        Self::from_rgba_premultiplied(r, g, b, a)\n    }\n}\n\nimpl From<Color32> for PremultipliedAlpha<EncodedSrgb<f32>> {\n    fn from(col: Color32) -> Self {\n        let (r, g, b, a) = col.to_tuple();\n\n        // This is a bit of an abuse of the function name but it does what we want.\n        let r = linear_f32_from_linear_u8(r);\n        let g = linear_f32_from_linear_u8(g);\n        let b = linear_f32_from_linear_u8(b);\n        let a = linear_f32_from_linear_u8(a);\n\n        Self {\n            color: EncodedSrgb { r, g, b },\n            alpha: a,\n        }\n    }\n}\n\nimpl ColorInterop for Color32 {\n    type CintTy = PremultipliedAlpha<EncodedSrgb<u8>>;\n}\n\n// ---- Rgba ----\n\nimpl From<PremultipliedAlpha<LinearSrgb<f32>>> for Rgba {\n    fn from(srgba: PremultipliedAlpha<LinearSrgb<f32>>) -> Self {\n        let PremultipliedAlpha {\n            color: LinearSrgb { r, g, b },\n            alpha: a,\n        } = srgba;\n\n        Self([r, g, b, a])\n    }\n}\n\nimpl From<Rgba> for PremultipliedAlpha<LinearSrgb<f32>> {\n    fn from(col: Rgba) -> Self {\n        let (r, g, b, a) = col.to_tuple();\n\n        Self {\n            color: LinearSrgb { r, g, b },\n            alpha: a,\n        }\n    }\n}\n\nimpl ColorInterop for Rgba {\n    type CintTy = PremultipliedAlpha<LinearSrgb<f32>>;\n}\n\n// ---- Hsva ----\n\nimpl From<Alpha<Hsv<f32>>> for Hsva {\n    fn from(srgba: Alpha<Hsv<f32>>) -> Self {\n        let Alpha {\n            color: Hsv { h, s, v },\n            alpha: a,\n        } = srgba;\n\n        Self::new(h, s, v, a)\n    }\n}\n\nimpl From<Hsva> for Alpha<Hsv<f32>> {\n    fn from(col: Hsva) -> Self {\n        let Hsva { h, s, v, a } = col;\n\n        Self {\n            color: Hsv { h, s, v },\n            alpha: a,\n        }\n    }\n}\n\nimpl ColorInterop for Hsva {\n    type CintTy = Alpha<Hsv<f32>>;\n}\n\n// ---- HsvaGamma ----\n\nimpl ColorInterop for HsvaGamma {\n    type CintTy = Alpha<Hsv<f32>>;\n}\n\nimpl From<Alpha<Hsv<f32>>> for HsvaGamma {\n    fn from(srgba: Alpha<Hsv<f32>>) -> Self {\n        let Alpha {\n            color: Hsv { h, s, v },\n            alpha: a,\n        } = srgba;\n\n        Hsva::new(h, s, v, a).into()\n    }\n}\n\nimpl From<HsvaGamma> for Alpha<Hsv<f32>> {\n    fn from(col: HsvaGamma) -> Self {\n        let Hsva { h, s, v, a } = col.into();\n\n        Self {\n            color: Hsv { h, s, v },\n            alpha: a,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/ecolor/src/color32.rs",
    "content": "use crate::{Rgba, fast_round, linear_f32_from_linear_u8};\n\n/// This format is used for space-efficient color representation (32 bits).\n///\n/// Instead of manipulating this directly it is often better\n/// to first convert it to either [`Rgba`] or [`crate::Hsva`].\n///\n/// Internally this uses 0-255 gamma space `sRGBA` color with _premultiplied alpha_.\n///\n/// It's the non-linear (\"gamma\") values that are multiplied with the alpha.\n///\n/// Premultiplied alpha means that the color values have been pre-multiplied with the alpha (opacity).\n/// This is in contrast with \"normal\" RGBA, where the alpha is _separate_ (or \"unmultiplied\").\n/// Using premultiplied alpha has some advantages:\n/// * It allows encoding additive colors\n/// * It is the better way to blend colors, e.g. when filtering texture colors\n/// * Because the above, it is the better way to encode colors in a GPU texture\n///\n/// The color space is assumed to be [sRGB](https://en.wikipedia.org/wiki/SRGB).\n///\n/// All operations on `Color32` are done in \"gamma space\" (see <https://en.wikipedia.org/wiki/SRGB>).\n/// This is not physically correct, but it is fast and sometimes more perceptually even than linear space.\n/// If you instead want to perform these operations in linear-space color, use [`Rgba`].\n///\n/// An `alpha=0` means the color is to be treated as an additive color.\n#[repr(C)]\n#[repr(align(4))]\n#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct Color32(pub(crate) [u8; 4]);\n\nimpl std::fmt::Debug for Color32 {\n    /// Prints the contents with premultiplied alpha!\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let [r, g, b, a] = self.0;\n        write!(f, \"#{r:02X}_{g:02X}_{b:02X}_{a:02X}\")\n    }\n}\n\nimpl std::ops::Index<usize> for Color32 {\n    type Output = u8;\n\n    #[inline]\n    fn index(&self, index: usize) -> &u8 {\n        &self.0[index]\n    }\n}\n\nimpl std::ops::IndexMut<usize> for Color32 {\n    #[inline]\n    fn index_mut(&mut self, index: usize) -> &mut u8 {\n        &mut self.0[index]\n    }\n}\n\nimpl Color32 {\n    // Mostly follows CSS names:\n\n    pub const TRANSPARENT: Self = Self::from_rgba_premultiplied(0, 0, 0, 0);\n    pub const BLACK: Self = Self::from_rgb(0, 0, 0);\n    #[doc(alias = \"DARK_GREY\")]\n    pub const DARK_GRAY: Self = Self::from_rgb(96, 96, 96);\n    #[doc(alias = \"GREY\")]\n    pub const GRAY: Self = Self::from_rgb(160, 160, 160);\n    #[doc(alias = \"LIGHT_GREY\")]\n    pub const LIGHT_GRAY: Self = Self::from_rgb(220, 220, 220);\n    pub const WHITE: Self = Self::from_rgb(255, 255, 255);\n\n    pub const BROWN: Self = Self::from_rgb(165, 42, 42);\n    pub const DARK_RED: Self = Self::from_rgb(0x8B, 0, 0);\n    pub const RED: Self = Self::from_rgb(255, 0, 0);\n    pub const LIGHT_RED: Self = Self::from_rgb(255, 128, 128);\n\n    pub const CYAN: Self = Self::from_rgb(0, 255, 255);\n    pub const MAGENTA: Self = Self::from_rgb(255, 0, 255);\n    pub const YELLOW: Self = Self::from_rgb(255, 255, 0);\n\n    pub const ORANGE: Self = Self::from_rgb(255, 165, 0);\n    pub const LIGHT_YELLOW: Self = Self::from_rgb(255, 255, 0xE0);\n    pub const KHAKI: Self = Self::from_rgb(240, 230, 140);\n\n    pub const DARK_GREEN: Self = Self::from_rgb(0, 0x64, 0);\n    pub const GREEN: Self = Self::from_rgb(0, 255, 0);\n    pub const LIGHT_GREEN: Self = Self::from_rgb(0x90, 0xEE, 0x90);\n\n    pub const DARK_BLUE: Self = Self::from_rgb(0, 0, 0x8B);\n    pub const BLUE: Self = Self::from_rgb(0, 0, 255);\n    pub const LIGHT_BLUE: Self = Self::from_rgb(0xAD, 0xD8, 0xE6);\n\n    pub const PURPLE: Self = Self::from_rgb(0x80, 0, 0x80);\n\n    pub const GOLD: Self = Self::from_rgb(255, 215, 0);\n\n    pub const DEBUG_COLOR: Self = Self::from_rgba_premultiplied(0, 200, 0, 128);\n\n    /// An ugly color that is planned to be replaced before making it to the screen.\n    ///\n    /// This is an invalid color, in that it does not correspond to a valid multiplied color,\n    /// nor to an additive color.\n    ///\n    /// This is used as a special color key,\n    /// i.e. often taken to mean \"no color\".\n    pub const PLACEHOLDER: Self = Self::from_rgba_premultiplied(64, 254, 0, 128);\n\n    /// From RGB with alpha of 255 (opaque).\n    #[inline]\n    pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {\n        Self([r, g, b, 255])\n    }\n\n    /// From RGB into an additive color (will make everything it blend with brighter).\n    #[inline]\n    pub const fn from_rgb_additive(r: u8, g: u8, b: u8) -> Self {\n        Self([r, g, b, 0])\n    }\n\n    /// From `sRGBA` with premultiplied alpha.\n    ///\n    /// You likely want to use [`Self::from_rgba_unmultiplied`] instead.\n    #[inline]\n    pub const fn from_rgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {\n        Self([r, g, b, a])\n    }\n\n    /// From `sRGBA` with separate alpha.\n    ///\n    /// This is a \"normal\" RGBA value that you would find in a color picker or a table somewhere.\n    ///\n    /// You can use [`Self::to_srgba_unmultiplied`] to get back these values,\n    /// but for transparent colors what you get back might be slightly different (rounding errors).\n    #[inline]\n    pub fn from_rgba_unmultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {\n        use std::sync::OnceLock;\n        match a {\n            // common-case optimization:\n            0 => Self::TRANSPARENT,\n\n            // common-case optimization:\n            255 => Self::from_rgb(r, g, b),\n\n            a => {\n                static LOOKUP_TABLE: OnceLock<Box<[u8]>> = OnceLock::new();\n                let lut = LOOKUP_TABLE.get_or_init(|| {\n                    (0..=u16::MAX)\n                        .map(|i| {\n                            let [value, alpha] = i.to_ne_bytes();\n                            fast_round(value as f32 * linear_f32_from_linear_u8(alpha))\n                        })\n                        .collect()\n                });\n\n                let [r, g, b] =\n                    [r, g, b].map(|value| lut[usize::from(u16::from_ne_bytes([value, a]))]);\n                Self::from_rgba_premultiplied(r, g, b, a)\n            }\n        }\n    }\n\n    /// Same as [`Self::from_rgba_unmultiplied`], but can be used in a const context.\n    ///\n    /// It is slightly slower when operating on non-const data.\n    #[inline]\n    pub const fn from_rgba_unmultiplied_const(r: u8, g: u8, b: u8, a: u8) -> Self {\n        match a {\n            // common-case optimization:\n            0 => Self::TRANSPARENT,\n\n            // common-case optimization:\n            255 => Self::from_rgb(r, g, b),\n\n            a => {\n                let r = fast_round(r as f32 * linear_f32_from_linear_u8(a));\n                let g = fast_round(g as f32 * linear_f32_from_linear_u8(a));\n                let b = fast_round(b as f32 * linear_f32_from_linear_u8(a));\n                Self::from_rgba_premultiplied(r, g, b, a)\n            }\n        }\n    }\n\n    /// Opaque gray.\n    #[doc(alias = \"from_grey\")]\n    #[inline]\n    pub const fn from_gray(l: u8) -> Self {\n        Self([l, l, l, 255])\n    }\n\n    /// Black with the given opacity.\n    #[inline]\n    pub const fn from_black_alpha(a: u8) -> Self {\n        Self([0, 0, 0, a])\n    }\n\n    /// White with the given opacity.\n    #[inline]\n    pub fn from_white_alpha(a: u8) -> Self {\n        Self([a, a, a, a])\n    }\n\n    /// Additive white.\n    #[inline]\n    pub const fn from_additive_luminance(l: u8) -> Self {\n        Self([l, l, l, 0])\n    }\n\n    #[inline]\n    pub const fn is_opaque(&self) -> bool {\n        self.a() == 255\n    }\n\n    /// Red component multiplied by alpha.\n    #[inline]\n    pub const fn r(&self) -> u8 {\n        self.0[0]\n    }\n\n    /// Green component multiplied by alpha.\n    #[inline]\n    pub const fn g(&self) -> u8 {\n        self.0[1]\n    }\n\n    /// Blue component multiplied by alpha.\n    #[inline]\n    pub const fn b(&self) -> u8 {\n        self.0[2]\n    }\n\n    /// Alpha (opacity).\n    #[inline]\n    pub const fn a(&self) -> u8 {\n        self.0[3]\n    }\n\n    /// Returns an opaque version of self\n    #[inline]\n    pub fn to_opaque(self) -> Self {\n        Rgba::from(self).to_opaque().into()\n    }\n\n    /// Returns an additive version of self\n    #[inline]\n    pub const fn additive(self) -> Self {\n        let [r, g, b, _] = self.to_array();\n        Self([r, g, b, 0])\n    }\n\n    /// Is the alpha=0 ?\n    #[inline]\n    pub fn is_additive(self) -> bool {\n        self.a() == 0\n    }\n\n    /// Premultiplied RGBA\n    #[inline]\n    pub const fn to_array(&self) -> [u8; 4] {\n        [self.r(), self.g(), self.b(), self.a()]\n    }\n\n    /// Premultiplied RGBA\n    #[inline]\n    pub const fn to_tuple(&self) -> (u8, u8, u8, u8) {\n        (self.r(), self.g(), self.b(), self.a())\n    }\n\n    /// Convert to a normal \"unmultiplied\" RGBA color (i.e. with separate alpha).\n    ///\n    /// This will unmultiply the alpha.\n    ///\n    /// This is the inverse of [`Self::from_rgba_unmultiplied`],\n    /// but due to precision problems it may return slightly different values for transparent colors.\n    #[inline]\n    pub fn to_srgba_unmultiplied(&self) -> [u8; 4] {\n        let [r, g, b, a] = self.to_array();\n        match a {\n            // Common-case optimization.\n            0 | 255 => self.to_array(),\n            a => {\n                let factor = 255.0 / a as f32;\n                let r = fast_round(factor * r as f32);\n                let g = fast_round(factor * g as f32);\n                let b = fast_round(factor * b as f32);\n                [r, g, b, a]\n            }\n        }\n    }\n\n    /// Multiply with 0.5 to make color half as opaque, perceptually.\n    ///\n    /// Fast multiplication in gamma-space.\n    ///\n    /// This is perceptually even, and faster that [`Self::linear_multiply`].\n    #[inline]\n    pub fn gamma_multiply(self, factor: f32) -> Self {\n        debug_assert!(\n            0.0 <= factor && factor.is_finite(),\n            \"factor should be finite, but was {factor}\"\n        );\n        let Self([r, g, b, a]) = self;\n        Self([\n            (r as f32 * factor + 0.5) as u8,\n            (g as f32 * factor + 0.5) as u8,\n            (b as f32 * factor + 0.5) as u8,\n            (a as f32 * factor + 0.5) as u8,\n        ])\n    }\n\n    /// Multiply with 127 to make color half as opaque, perceptually.\n    ///\n    /// Fast multiplication in gamma-space.\n    ///\n    /// This is perceptually even, and faster that [`Self::linear_multiply`].\n    #[inline]\n    pub fn gamma_multiply_u8(self, factor: u8) -> Self {\n        let Self([r, g, b, a]) = self;\n        let factor = factor as u32;\n        Self([\n            ((r as u32 * factor + 127) / 255) as u8,\n            ((g as u32 * factor + 127) / 255) as u8,\n            ((b as u32 * factor + 127) / 255) as u8,\n            ((a as u32 * factor + 127) / 255) as u8,\n        ])\n    }\n\n    /// Multiply with 0.5 to make color half as opaque in linear space.\n    ///\n    /// This is using linear space, which is not perceptually even.\n    /// You likely want to use [`Self::gamma_multiply`] instead.\n    #[inline]\n    pub fn linear_multiply(self, factor: f32) -> Self {\n        debug_assert!(\n            0.0 <= factor && factor.is_finite(),\n            \"factor should be finite, but was {factor}\"\n        );\n        // As an unfortunate side-effect of using premultiplied alpha\n        // we need a somewhat expensive conversion to linear space and back.\n        Rgba::from(self).multiply(factor).into()\n    }\n\n    /// Converts to floating point values in the range 0-1 without any gamma space conversion.\n    ///\n    /// Use this with great care! In almost all cases, you want to convert to [`crate::Rgba`] instead\n    /// in order to obtain linear space color values.\n    #[inline]\n    pub fn to_normalized_gamma_f32(self) -> [f32; 4] {\n        let Self([r, g, b, a]) = self;\n        [\n            r as f32 / 255.0,\n            g as f32 / 255.0,\n            b as f32 / 255.0,\n            a as f32 / 255.0,\n        ]\n    }\n\n    /// Lerp this color towards `other` by `t` in gamma space.\n    pub fn lerp_to_gamma(&self, other: Self, t: f32) -> Self {\n        use emath::lerp;\n\n        Self::from_rgba_premultiplied(\n            fast_round(lerp((self[0] as f32)..=(other[0] as f32), t)),\n            fast_round(lerp((self[1] as f32)..=(other[1] as f32), t)),\n            fast_round(lerp((self[2] as f32)..=(other[2] as f32), t)),\n            fast_round(lerp((self[3] as f32)..=(other[3] as f32), t)),\n        )\n    }\n\n    /// Blend two colors in gamma space, so that `self` is behind the argument.\n    pub fn blend(self, on_top: Self) -> Self {\n        self.gamma_multiply_u8(255 - on_top.a()) + on_top\n    }\n\n    /// Intensity of the color.\n    ///\n    /// Returns a value in the range 0-1.\n    /// The brighter the color, the closer to 1.\n    pub fn intensity(&self) -> f32 {\n        (self.r() as f32 * 0.299 + self.g() as f32 * 0.587 + self.b() as f32 * 0.114) / 255.0\n    }\n}\n\nimpl std::ops::Mul for Color32 {\n    type Output = Self;\n\n    /// Fast gamma-space multiplication.\n    #[inline]\n    fn mul(self, other: Self) -> Self {\n        Self([\n            fast_round(self[0] as f32 * other[0] as f32 / 255.0),\n            fast_round(self[1] as f32 * other[1] as f32 / 255.0),\n            fast_round(self[2] as f32 * other[2] as f32 / 255.0),\n            fast_round(self[3] as f32 * other[3] as f32 / 255.0),\n        ])\n    }\n}\n\nimpl std::ops::Add for Color32 {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, other: Self) -> Self {\n        Self([\n            self[0].saturating_add(other[0]),\n            self[1].saturating_add(other[1]),\n            self[2].saturating_add(other[2]),\n            self[3].saturating_add(other[3]),\n        ])\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    fn test_rgba() -> impl Iterator<Item = [u8; 4]> {\n        [\n            [0, 0, 0, 0],\n            [0, 0, 0, 255],\n            [10, 0, 30, 0],\n            [10, 0, 30, 40],\n            [10, 100, 200, 0],\n            [10, 100, 200, 100],\n            [10, 100, 200, 200],\n            [10, 100, 200, 255],\n            [10, 100, 200, 40],\n            [10, 20, 0, 0],\n            [10, 20, 0, 255],\n            [10, 20, 30, 255],\n            [10, 20, 30, 40],\n            [255, 255, 255, 0],\n            [255, 255, 255, 255],\n        ]\n        .into_iter()\n    }\n\n    #[test]\n    fn test_color32_additive() {\n        let opaque = Color32::from_rgb(40, 50, 60);\n        let additive = Color32::from_rgb(255, 127, 10).additive();\n        assert_eq!(additive.blend(opaque), opaque, \"opaque on top of additive\");\n        assert_eq!(\n            opaque.blend(additive),\n            Color32::from_rgb(255, 177, 70),\n            \"additive on top of opaque\"\n        );\n    }\n\n    #[test]\n    fn test_color32_blend_vs_gamma_blend() {\n        let opaque = Color32::from_rgb(0x60, 0x60, 0x60);\n        let transparent = Color32::from_rgba_unmultiplied(168, 65, 65, 79);\n        assert_eq!(\n            transparent.blend(opaque),\n            opaque,\n            \"Opaque on top of transparent\"\n        );\n        // Blending in gamma-space is the de-facto standard almost everywhere.\n        // Browsers and most image editors do it, and so it is what users expect.\n        assert_eq!(\n            opaque.blend(transparent),\n            Color32::from_rgb(\n                blend(0x60, 168, 79),\n                blend(0x60, 65, 79),\n                blend(0x60, 65, 79)\n            ),\n            \"Transparent on top of opaque\"\n        );\n\n        fn blend(dest: u8, src: u8, alpha: u8) -> u8 {\n            let src = src as f32 / 255.0;\n            let dest = dest as f32 / 255.0;\n            let alpha = alpha as f32 / 255.0;\n            fast_round((src * alpha + dest * (1.0 - alpha)) * 255.0)\n        }\n    }\n\n    #[test]\n    fn color32_unmultiplied_round_trip() {\n        for in_rgba in test_rgba() {\n            let [r, g, b, a] = in_rgba;\n            if a == 0 {\n                continue;\n            }\n\n            let c = Color32::from_rgba_unmultiplied(r, g, b, a);\n            let out_rgba = c.to_srgba_unmultiplied();\n\n            if a == 255 {\n                assert_eq!(in_rgba, out_rgba);\n            } else {\n                // There will be small rounding errors whenever the alpha is not 0 or 255,\n                // because we multiply and then unmultiply the alpha.\n                for (&a, &b) in in_rgba.iter().zip(out_rgba.iter()) {\n                    assert!(a.abs_diff(b) <= 3, \"{in_rgba:?} != {out_rgba:?}\");\n                }\n            }\n        }\n    }\n\n    #[test]\n    fn from_black_white_alpha() {\n        for a in 0..=255 {\n            assert_eq!(\n                Color32::from_white_alpha(a),\n                Color32::from_rgba_unmultiplied(255, 255, 255, a)\n            );\n            assert_eq!(\n                Color32::from_white_alpha(a),\n                Color32::WHITE.gamma_multiply_u8(a)\n            );\n\n            assert_eq!(\n                Color32::from_black_alpha(a),\n                Color32::from_rgba_unmultiplied(0, 0, 0, a)\n            );\n            assert_eq!(\n                Color32::from_black_alpha(a),\n                Color32::BLACK.gamma_multiply_u8(a)\n            );\n        }\n    }\n\n    #[test]\n    fn to_from_rgba() {\n        for [r, g, b, a] in test_rgba() {\n            let original = Color32::from_rgba_unmultiplied(r, g, b, a);\n            let constfn = Color32::from_rgba_unmultiplied_const(r, g, b, a);\n            let rgba = Rgba::from(original);\n            let back = Color32::from(rgba);\n            assert_eq!(back, original);\n            assert_eq!(constfn, original);\n        }\n\n        assert_eq!(\n            Color32::from(Rgba::from_rgba_unmultiplied(1.0, 0.0, 0.0, 0.5)),\n            Color32::from_rgba_unmultiplied(255, 0, 0, 128)\n        );\n    }\n}\n"
  },
  {
    "path": "crates/ecolor/src/hex_color_macro.rs",
    "content": "/// Construct a [`crate::Color32`] from a hex RGB or RGBA string literal.\n///\n/// Requires the \"color-hex\" feature.\n///\n/// The string is checked at compile time. If the format is invalid, compilation fails. The valid\n/// format is the one described in <https://drafts.csswg.org/css-color-4/#hex-color>. Only 6 (RGB) or 8 (RGBA)\n/// digits are supported, and the leading `#` character is optional.\n///\n/// Note that despite being checked at compile-time, this macro is not usable in `const` contexts\n/// because creating the [`crate::Color32`] instance requires floating-point arithmetic.\n///\n/// See also [`crate::Color32::from_hex`] and [`crate::Color32::to_hex`].\n///\n/// # Examples\n///\n/// ```\n/// # use ecolor::{hex_color, Color32};\n/// assert_eq!(hex_color!(\"#202122\"), Color32::from_hex(\"#202122\").unwrap());\n/// assert_eq!(hex_color!(\"#202122\"), Color32::from_rgb(0x20, 0x21, 0x22));\n/// assert_eq!(hex_color!(\"#202122\"), hex_color!(\"202122\"));\n/// assert_eq!(hex_color!(\"#abcdef12\"), Color32::from_rgba_unmultiplied(0xab, 0xcd, 0xef, 0x12));\n/// ```\n///\n/// If the literal string has the wrong format, the code does not compile.\n///\n/// ```compile_fail\n/// let _ = ecolor::hex_color!(\"#abc\");\n/// ```\n///\n/// ```compile_fail\n/// let _ = ecolor::hex_color!(\"#20212x\");\n/// ```\n///\n/// The macro can be used in a `const` context.\n///\n/// ```\n/// const COLOR: ecolor::Color32 = ecolor::hex_color!(\"#202122\");\n/// assert_eq!(COLOR, ecolor::Color32::from_rgb(0x20, 0x21, 0x22));\n/// ```\n#[macro_export]\nmacro_rules! hex_color {\n    ($s:literal) => {{\n        let array = $crate::color_hex::color_from_hex!($s);\n        match array.as_slice() {\n            [r, g, b] => $crate::Color32::from_rgb(*r, *g, *b),\n            [r, g, b, a] => $crate::Color32::from_rgba_unmultiplied_const(*r, *g, *b, *a),\n            _ => panic!(\"Invalid hex color length: expected 3 (RGB) or 4 (RGBA) bytes\"),\n        }\n    }};\n}\n\n#[test]\nfn test_from_rgb_hex() {\n    assert_eq!(\n        crate::Color32::from_rgb(0x20, 0x21, 0x22),\n        hex_color!(\"#202122\")\n    );\n    assert_eq!(\n        crate::Color32::from_rgb_additive(0x20, 0x21, 0x22),\n        hex_color!(\"#202122\").additive()\n    );\n}\n\n#[test]\nfn test_from_rgba_hex() {\n    assert_eq!(\n        crate::Color32::from_rgba_unmultiplied(0x20, 0x21, 0x22, 0x50),\n        hex_color!(\"20212250\")\n    );\n}\n"
  },
  {
    "path": "crates/ecolor/src/hex_color_runtime.rs",
    "content": "//! Convert colors to and from the hex-color string format at runtime\n//!\n//! Supports the 3, 4, 6, and 8-digit formats, according to the specification in\n//! <https://drafts.csswg.org/css-color-4/#hex-color>\n\nuse std::{fmt::Display, str::FromStr};\n\nuse crate::Color32;\n\n#[repr(C)]\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n/// A wrapper around Color32 that converts to and from a hex-color string\n///\n/// Implements [`Display`] and [`FromStr`] to convert to and from the hex string.\npub enum HexColor {\n    /// 3 hexadecimal digits, one for each of the r, g, b channels\n    Hex3(Color32),\n\n    /// 4 hexadecimal digits, one for each of the r, g, b, a channels\n    Hex4(Color32),\n\n    /// 6 hexadecimal digits, two for each of the r, g, b channels\n    Hex6(Color32),\n\n    /// 8 hexadecimal digits, one for each of the r, g, b, a channels\n    Hex8(Color32),\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum ParseHexColorError {\n    MissingHash,\n    InvalidLength,\n    InvalidInt(std::num::ParseIntError),\n}\n\nimpl FromStr for HexColor {\n    type Err = ParseHexColorError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        s.strip_prefix('#')\n            .ok_or(ParseHexColorError::MissingHash)\n            .and_then(Self::from_str_without_hash)\n    }\n}\n\nimpl Display for HexColor {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Hex3(color) => {\n                let [r, g, b, _] = color.to_srgba_unmultiplied().map(|u| u >> 4);\n                f.write_fmt(format_args!(\"#{r:x}{g:x}{b:x}\"))\n            }\n            Self::Hex4(color) => {\n                let [r, g, b, a] = color.to_srgba_unmultiplied().map(|u| u >> 4);\n                f.write_fmt(format_args!(\"#{r:x}{g:x}{b:x}{a:x}\"))\n            }\n            Self::Hex6(color) => {\n                let [r, g, b, _] = color.to_srgba_unmultiplied();\n                let u = u32::from_be_bytes([0, r, g, b]);\n                f.write_fmt(format_args!(\"#{u:06x}\"))\n            }\n            Self::Hex8(color) => {\n                let [r, g, b, a] = color.to_srgba_unmultiplied();\n                let u = u32::from_be_bytes([r, g, b, a]);\n                f.write_fmt(format_args!(\"#{u:08x}\"))\n            }\n        }\n    }\n}\n\nimpl HexColor {\n    /// Retrieves the inner [`Color32`]\n    #[inline]\n    pub fn color(&self) -> Color32 {\n        match self {\n            Self::Hex3(color) | Self::Hex4(color) | Self::Hex6(color) | Self::Hex8(color) => *color,\n        }\n    }\n\n    /// Parses a string as a hex color without the leading `#` character\n    ///\n    /// # Errors\n    /// Returns an error if the length of the string does not correspond to one of the standard\n    /// formats (3, 4, 6, or 8), or if it contains non-hex characters.\n    #[inline]\n    pub fn from_str_without_hash(s: &str) -> Result<Self, ParseHexColorError> {\n        match s.len() {\n            3 => {\n                let [r, gb] = u16::from_str_radix(s, 16)\n                    .map_err(ParseHexColorError::InvalidInt)?\n                    .to_be_bytes();\n                let [r, g, b] = [r, gb >> 4, gb & 0x0f].map(|u| (u << 4) | u);\n                Ok(Self::Hex3(Color32::from_rgb(r, g, b)))\n            }\n            4 => {\n                let [r_g, b_a] = u16::from_str_radix(s, 16)\n                    .map_err(ParseHexColorError::InvalidInt)?\n                    .to_be_bytes();\n                let [r, g, b, a] =\n                    [r_g >> 4, r_g & 0x0f, b_a >> 4, b_a & 0x0f].map(|u| (u << 4) | u);\n                Ok(Self::Hex4(Color32::from_rgba_unmultiplied(r, g, b, a)))\n            }\n            6 => {\n                let [_, r, g, b] = u32::from_str_radix(s, 16)\n                    .map_err(ParseHexColorError::InvalidInt)?\n                    .to_be_bytes();\n                Ok(Self::Hex6(Color32::from_rgb(r, g, b)))\n            }\n            8 => {\n                let [r, g, b, a] = u32::from_str_radix(s, 16)\n                    .map_err(ParseHexColorError::InvalidInt)?\n                    .to_be_bytes();\n                Ok(Self::Hex8(Color32::from_rgba_unmultiplied(r, g, b, a)))\n            }\n            _ => Err(ParseHexColorError::InvalidLength)?,\n        }\n    }\n}\n\nimpl Color32 {\n    /// Parses a color from a hex string.\n    ///\n    /// Supports the 3, 4, 6, and 8-digit formats, according to the specification in\n    /// <https://drafts.csswg.org/css-color-4/#hex-color>\n    ///\n    /// To parse hex colors from string literals with compile-time checking, use the macro\n    /// [`crate::hex_color!`] instead.\n    ///\n    /// # Example\n    /// ```rust\n    /// use ecolor::Color32;\n    /// assert_eq!(Ok(Color32::RED), Color32::from_hex(\"#ff0000\"));\n    /// assert_eq!(Ok(Color32::GREEN), Color32::from_hex(\"#00ff00ff\"));\n    /// assert_eq!(Ok(Color32::BLUE), Color32::from_hex(\"#00f\"));\n    /// assert_eq!(Ok(Color32::TRANSPARENT), Color32::from_hex(\"#0000\"));\n    /// ```\n    ///\n    /// # Errors\n    /// Returns an error if the string doesn't start with the hash `#` character, if the remaining\n    /// length does not correspond to one of the standard formats (3, 4, 6, or 8), if it contains\n    /// non-hex characters.\n    pub fn from_hex(hex: &str) -> Result<Self, ParseHexColorError> {\n        HexColor::from_str(hex).map(|h| h.color())\n    }\n\n    /// Formats the color as a hex string.\n    ///\n    /// # Example\n    /// ```rust\n    /// use ecolor::Color32;\n    /// assert_eq!(Color32::RED.to_hex(), \"#ff0000ff\");\n    /// assert_eq!(Color32::GREEN.to_hex(), \"#00ff00ff\");\n    /// assert_eq!(Color32::BLUE.to_hex(), \"#0000ffff\");\n    /// assert_eq!(Color32::TRANSPARENT.to_hex(), \"#00000000\");\n    /// ```\n    ///\n    /// Uses the 8-digit format described in <https://drafts.csswg.org/css-color-4/#hex-color>,\n    /// as that is the only format that is lossless.\n    /// For other formats, see [`HexColor`].\n    #[inline]\n    pub fn to_hex(&self) -> String {\n        HexColor::Hex8(*self).to_string()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn hex_string_formats() {\n        use Color32 as C;\n        use HexColor as H;\n        let cases = [\n            (H::Hex3(C::RED), \"#f00\"),\n            (H::Hex4(C::RED), \"#f00f\"),\n            (H::Hex6(C::RED), \"#ff0000\"),\n            (H::Hex8(C::RED), \"#ff0000ff\"),\n            (H::Hex3(C::GREEN), \"#0f0\"),\n            (H::Hex4(C::GREEN), \"#0f0f\"),\n            (H::Hex6(C::GREEN), \"#00ff00\"),\n            (H::Hex8(C::GREEN), \"#00ff00ff\"),\n            (H::Hex3(C::BLUE), \"#00f\"),\n            (H::Hex4(C::BLUE), \"#00ff\"),\n            (H::Hex6(C::BLUE), \"#0000ff\"),\n            (H::Hex8(C::BLUE), \"#0000ffff\"),\n            (H::Hex3(C::WHITE), \"#fff\"),\n            (H::Hex4(C::WHITE), \"#ffff\"),\n            (H::Hex6(C::WHITE), \"#ffffff\"),\n            (H::Hex8(C::WHITE), \"#ffffffff\"),\n            (H::Hex3(C::BLACK), \"#000\"),\n            (H::Hex4(C::BLACK), \"#000f\"),\n            (H::Hex6(C::BLACK), \"#000000\"),\n            (H::Hex8(C::BLACK), \"#000000ff\"),\n            (H::Hex4(C::TRANSPARENT), \"#0000\"),\n            (H::Hex8(C::TRANSPARENT), \"#00000000\"),\n        ];\n        for (color, string) in cases {\n            assert_eq!(color.to_string(), string, \"{color:?} <=> {string}\");\n            assert_eq!(\n                H::from_str(string).unwrap(),\n                color,\n                \"{color:?} <=> {string}\"\n            );\n        }\n    }\n\n    #[test]\n    fn hex_string_round_trip() {\n        let cases = [\n            [0, 20, 30, 0],\n            [10, 0, 30, 40],\n            [10, 100, 200, 0],\n            [10, 100, 200, 100],\n            [10, 100, 200, 200],\n            [10, 100, 200, 255],\n            [10, 100, 200, 40],\n            [10, 20, 0, 255],\n            [10, 20, 30, 0],\n            [10, 20, 30, 255],\n            [10, 20, 30, 40],\n        ];\n        for [r, g, b, a] in cases {\n            let color = Color32::from_rgba_unmultiplied(r, g, b, a);\n            assert_eq!(Color32::from_hex(color.to_hex().as_str()), Ok(color));\n        }\n    }\n}\n"
  },
  {
    "path": "crates/ecolor/src/hsva.rs",
    "content": "use crate::{\n    Color32, Rgba, gamma_u8_from_linear_f32, linear_f32_from_gamma_u8, linear_u8_from_linear_f32,\n};\n\n/// Hue, saturation, value, alpha. All in the range [0, 1].\n/// No premultiplied alpha.\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\npub struct Hsva {\n    /// hue 0-1\n    pub h: f32,\n\n    /// saturation 0-1\n    pub s: f32,\n\n    /// value 0-1\n    pub v: f32,\n\n    /// alpha 0-1. A negative value signifies an additive color (and alpha is ignored).\n    pub a: f32,\n}\n\nimpl Hsva {\n    #[inline]\n    pub fn new(h: f32, s: f32, v: f32, a: f32) -> Self {\n        Self { h, s, v, a }\n    }\n\n    /// From `sRGBA` with premultiplied alpha\n    #[inline]\n    pub fn from_srgba_premultiplied([r, g, b, a]: [u8; 4]) -> Self {\n        Self::from(Color32::from_rgba_premultiplied(r, g, b, a))\n    }\n\n    /// From `sRGBA` without premultiplied alpha\n    #[inline]\n    pub fn from_srgba_unmultiplied([r, g, b, a]: [u8; 4]) -> Self {\n        Self::from(Color32::from_rgba_unmultiplied(r, g, b, a))\n    }\n\n    /// From linear RGBA with premultiplied alpha\n    #[inline]\n    pub fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {\n        #![expect(clippy::many_single_char_names)]\n        if a <= 0.0 {\n            if r == 0.0 && b == 0.0 && a == 0.0 {\n                Self::default()\n            } else {\n                Self::from_additive_rgb([r, g, b])\n            }\n        } else {\n            let (h, s, v) = hsv_from_rgb([r / a, g / a, b / a]);\n            Self { h, s, v, a }\n        }\n    }\n\n    /// From linear RGBA without premultiplied alpha\n    #[inline]\n    pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {\n        #![expect(clippy::many_single_char_names)]\n        let (h, s, v) = hsv_from_rgb([r, g, b]);\n        Self { h, s, v, a }\n    }\n\n    #[inline]\n    pub fn from_additive_rgb(rgb: [f32; 3]) -> Self {\n        let (h, s, v) = hsv_from_rgb(rgb);\n        Self {\n            h,\n            s,\n            v,\n            a: -0.5, // anything negative is treated as additive\n        }\n    }\n\n    #[inline]\n    pub fn from_additive_srgb([r, g, b]: [u8; 3]) -> Self {\n        Self::from_additive_rgb([\n            linear_f32_from_gamma_u8(r),\n            linear_f32_from_gamma_u8(g),\n            linear_f32_from_gamma_u8(b),\n        ])\n    }\n\n    #[inline]\n    pub fn from_rgb(rgb: [f32; 3]) -> Self {\n        let (h, s, v) = hsv_from_rgb(rgb);\n        Self { h, s, v, a: 1.0 }\n    }\n\n    #[inline]\n    pub fn from_srgb([r, g, b]: [u8; 3]) -> Self {\n        Self::from_rgb([\n            linear_f32_from_gamma_u8(r),\n            linear_f32_from_gamma_u8(g),\n            linear_f32_from_gamma_u8(b),\n        ])\n    }\n\n    // ------------------------------------------------------------------------\n\n    #[inline]\n    pub fn to_opaque(self) -> Self {\n        Self { a: 1.0, ..self }\n    }\n\n    #[inline]\n    pub fn to_rgb(&self) -> [f32; 3] {\n        rgb_from_hsv((self.h, self.s, self.v))\n    }\n\n    #[inline]\n    pub fn to_srgb(&self) -> [u8; 3] {\n        let [r, g, b] = self.to_rgb();\n        [\n            gamma_u8_from_linear_f32(r),\n            gamma_u8_from_linear_f32(g),\n            gamma_u8_from_linear_f32(b),\n        ]\n    }\n\n    #[inline]\n    pub fn to_rgba_premultiplied(&self) -> [f32; 4] {\n        let [r, g, b, a] = self.to_rgba_unmultiplied();\n        let additive = a < 0.0;\n        if additive {\n            [r, g, b, 0.0]\n        } else {\n            [a * r, a * g, a * b, a]\n        }\n    }\n\n    /// To linear space rgba in 0-1 range.\n    ///\n    /// Represents additive colors using a negative alpha.\n    #[inline]\n    pub fn to_rgba_unmultiplied(&self) -> [f32; 4] {\n        let Self { h, s, v, a } = *self;\n        let [r, g, b] = rgb_from_hsv((h, s, v));\n        [r, g, b, a]\n    }\n\n    #[inline]\n    pub fn to_srgba_premultiplied(&self) -> [u8; 4] {\n        Color32::from(*self).to_array()\n    }\n\n    /// To gamma-space 0-255.\n    #[inline]\n    pub fn to_srgba_unmultiplied(&self) -> [u8; 4] {\n        let [r, g, b, a] = self.to_rgba_unmultiplied();\n        [\n            gamma_u8_from_linear_f32(r),\n            gamma_u8_from_linear_f32(g),\n            gamma_u8_from_linear_f32(b),\n            linear_u8_from_linear_f32(a.abs()),\n        ]\n    }\n}\n\nimpl From<Hsva> for Rgba {\n    #[inline]\n    fn from(hsva: Hsva) -> Self {\n        Self(hsva.to_rgba_premultiplied())\n    }\n}\n\nimpl From<Rgba> for Hsva {\n    #[inline]\n    fn from(rgba: Rgba) -> Self {\n        Self::from_rgba_premultiplied(rgba.0[0], rgba.0[1], rgba.0[2], rgba.0[3])\n    }\n}\n\nimpl From<Hsva> for Color32 {\n    #[inline]\n    fn from(hsva: Hsva) -> Self {\n        Self::from(Rgba::from(hsva))\n    }\n}\n\nimpl From<Color32> for Hsva {\n    #[inline]\n    fn from(srgba: Color32) -> Self {\n        Self::from(Rgba::from(srgba))\n    }\n}\n\n/// All ranges in 0-1, rgb is linear.\n#[inline]\npub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) {\n    #![expect(clippy::many_single_char_names)]\n    let min = r.min(g.min(b));\n    let max = r.max(g.max(b)); // value\n\n    let range = max - min;\n\n    let h = if max == min {\n        0.0 // hue is undefined\n    } else if max == r {\n        (g - b) / (6.0 * range)\n    } else if max == g {\n        (b - r) / (6.0 * range) + 1.0 / 3.0\n    } else {\n        // max == b\n        (r - g) / (6.0 * range) + 2.0 / 3.0\n    };\n    let h = (h + 1.0).fract(); // wrap\n    let s = if max == 0.0 { 0.0 } else { 1.0 - min / max };\n    (h, s, max)\n}\n\n/// All ranges in 0-1, rgb is linear.\n#[inline]\npub fn rgb_from_hsv((h, s, v): (f32, f32, f32)) -> [f32; 3] {\n    #![expect(clippy::many_single_char_names)]\n    let h = (h.fract() + 1.0).fract(); // wrap\n    let s = s.clamp(0.0, 1.0);\n\n    let f = h * 6.0 - (h * 6.0).floor();\n    let p = v * (1.0 - s);\n    let q = v * (1.0 - f * s);\n    let t = v * (1.0 - (1.0 - f) * s);\n\n    match (h * 6.0).floor() as i32 % 6 {\n        0 => [v, t, p],\n        1 => [q, v, p],\n        2 => [p, v, t],\n        3 => [p, q, v],\n        4 => [t, p, v],\n        5 => [v, p, q],\n        _ => unreachable!(),\n    }\n}\n\n#[test]\n#[ignore = \"too expensive\"]\nfn test_hsv_roundtrip() {\n    for r in 0..=255 {\n        for g in 0..=255 {\n            for b in 0..=255 {\n                let srgba = Color32::from_rgb(r, g, b);\n                let hsva = Hsva::from(srgba);\n                assert_eq!(srgba, Color32::from(hsva));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/ecolor/src/hsva_gamma.rs",
    "content": "use crate::{Color32, Hsva, Rgba, gamma_from_linear, linear_from_gamma};\n\n/// Like Hsva but with the `v` value (brightness) being gamma corrected\n/// so that it is somewhat perceptually even.\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\npub struct HsvaGamma {\n    /// hue 0-1\n    pub h: f32,\n\n    /// saturation 0-1\n    pub s: f32,\n\n    /// value 0-1, in gamma-space (~perceptually even)\n    pub v: f32,\n\n    /// alpha 0-1. A negative value signifies an additive color (and alpha is ignored).\n    pub a: f32,\n}\n\nimpl From<HsvaGamma> for Rgba {\n    fn from(hsvag: HsvaGamma) -> Self {\n        Hsva::from(hsvag).into()\n    }\n}\n\nimpl From<HsvaGamma> for Color32 {\n    fn from(hsvag: HsvaGamma) -> Self {\n        Rgba::from(hsvag).into()\n    }\n}\n\nimpl From<HsvaGamma> for Hsva {\n    fn from(hsvag: HsvaGamma) -> Self {\n        let HsvaGamma { h, s, v, a } = hsvag;\n        Self {\n            h,\n            s,\n            v: linear_from_gamma(v),\n            a,\n        }\n    }\n}\n\nimpl From<Rgba> for HsvaGamma {\n    fn from(rgba: Rgba) -> Self {\n        Hsva::from(rgba).into()\n    }\n}\n\nimpl From<Color32> for HsvaGamma {\n    fn from(srgba: Color32) -> Self {\n        Hsva::from(srgba).into()\n    }\n}\n\nimpl From<Hsva> for HsvaGamma {\n    fn from(hsva: Hsva) -> Self {\n        let Hsva { h, s, v, a } = hsva;\n        Self {\n            h,\n            s,\n            v: gamma_from_linear(v),\n            a,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/ecolor/src/lib.rs",
    "content": "//! Color conversions and types.\n//!\n//! This crate is built for the wants and needs of [`egui`](https://github.com/emilk/egui/).\n//!\n//! If you want an actual _good_ color crate, use [`color`](https://crates.io/crates/color) instead.\n//!\n//! If you want a compact color representation, use [`Color32`].\n//! If you want to manipulate RGBA colors in linear space use [`Rgba`].\n//! If you want to manipulate colors in a way closer to how humans think about colors, use [`HsvaGamma`].\n//!\n//! ## Conventions\n//! The word \"gamma\" or \"srgb\" is used to refer to values in the non-linear space defined by\n//! [the sRGB transfer function](https://en.wikipedia.org/wiki/SRGB).\n//! We use `u8` for anything in the \"gamma\" space.\n//!\n//! We use `f32` in 0-1 range for anything in the linear space.\n//!\n//! ## Feature flags\n#![cfg_attr(feature = \"document-features\", doc = document_features::document_features!())]\n//!\n\n#![expect(clippy::wrong_self_convention)]\n\n#[cfg(feature = \"cint\")]\nmod cint_impl;\n\nmod color32;\npub use color32::*;\n\nmod hsva_gamma;\npub use hsva_gamma::*;\n\nmod hsva;\npub use hsva::*;\n\n#[cfg(feature = \"color-hex\")]\nmod hex_color_macro;\n#[cfg(feature = \"color-hex\")]\n#[doc(hidden)]\npub use color_hex;\n\nmod rgba;\npub use rgba::*;\n\nmod hex_color_runtime;\npub use hex_color_runtime::*;\n\n// ----------------------------------------------------------------------------\n// Color conversion:\n\nimpl From<Color32> for Rgba {\n    fn from(srgba: Color32) -> Self {\n        let [r, g, b, a] = srgba.to_array();\n        if a == 0 {\n            // Additive, or completely transparent\n            Self([\n                linear_f32_from_gamma_u8(r),\n                linear_f32_from_gamma_u8(g),\n                linear_f32_from_gamma_u8(b),\n                0.0,\n            ])\n        } else {\n            let a = linear_f32_from_linear_u8(a);\n            Self([\n                linear_from_gamma(r as f32 / (255.0 * a)) * a,\n                linear_from_gamma(g as f32 / (255.0 * a)) * a,\n                linear_from_gamma(b as f32 / (255.0 * a)) * a,\n                a,\n            ])\n        }\n    }\n}\n\nimpl From<Rgba> for Color32 {\n    fn from(rgba: Rgba) -> Self {\n        let [r, g, b, a] = rgba.to_array();\n        if a == 0.0 {\n            // Additive, or completely transparent\n            Self([\n                gamma_u8_from_linear_f32(r),\n                gamma_u8_from_linear_f32(g),\n                gamma_u8_from_linear_f32(b),\n                0,\n            ])\n        } else {\n            Self([\n                fast_round(gamma_u8_from_linear_f32(r / a) as f32 * a),\n                fast_round(gamma_u8_from_linear_f32(g / a) as f32 * a),\n                fast_round(gamma_u8_from_linear_f32(b / a) as f32 * a),\n                linear_u8_from_linear_f32(a),\n            ])\n        }\n    }\n}\n\n/// gamma [0, 255] -> linear [0, 1].\npub fn linear_f32_from_gamma_u8(s: u8) -> f32 {\n    if s <= 10 {\n        s as f32 / 3294.6\n    } else {\n        ((s as f32 + 14.025) / 269.025).powf(2.4)\n    }\n}\n\n/// linear [0, 255] -> linear [0, 1].\n/// Useful for alpha-channel.\n#[inline(always)]\npub const fn linear_f32_from_linear_u8(a: u8) -> f32 {\n    a as f32 / 255.0\n}\n\n/// linear [0, 1] -> gamma [0, 255] (clamped).\n/// Values outside this range will be clamped to the range.\npub fn gamma_u8_from_linear_f32(l: f32) -> u8 {\n    if l <= 0.0 {\n        0\n    } else if l <= 0.0031308 {\n        fast_round(3294.6 * l)\n    } else if l <= 1.0 {\n        fast_round(269.025 * l.powf(1.0 / 2.4) - 14.025)\n    } else {\n        255\n    }\n}\n\n/// linear [0, 1] -> linear [0, 255] (clamped).\n/// Useful for alpha-channel.\n#[inline(always)]\npub fn linear_u8_from_linear_f32(a: f32) -> u8 {\n    fast_round(a * 255.0)\n}\n\nconst fn fast_round(r: f32) -> u8 {\n    (r + 0.5) as _ // rust does a saturating cast since 1.45\n}\n\n#[test]\npub fn test_srgba_conversion() {\n    for b in 0..=255 {\n        let l = linear_f32_from_gamma_u8(b);\n        assert!(0.0 <= l && l <= 1.0);\n        assert_eq!(gamma_u8_from_linear_f32(l), b);\n    }\n}\n\n/// gamma [0, 1] -> linear [0, 1] (not clamped).\n/// Works for numbers outside this range (e.g. negative numbers).\npub fn linear_from_gamma(gamma: f32) -> f32 {\n    if gamma < 0.0 {\n        -linear_from_gamma(-gamma)\n    } else if gamma <= 0.04045 {\n        gamma / 12.92\n    } else {\n        ((gamma + 0.055) / 1.055).powf(2.4)\n    }\n}\n\n/// linear [0, 1] -> gamma [0, 1] (not clamped).\n/// Works for numbers outside this range (e.g. negative numbers).\npub fn gamma_from_linear(linear: f32) -> f32 {\n    if linear < 0.0 {\n        -gamma_from_linear(-linear)\n    } else if linear <= 0.0031308 {\n        12.92 * linear\n    } else {\n        1.055 * linear.powf(1.0 / 2.4) - 0.055\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Cheap and ugly.\n/// Made for graying out disabled `Ui`s.\npub fn tint_color_towards(color: Color32, target: Color32) -> Color32 {\n    let [mut r, mut g, mut b, mut a] = color.to_array();\n\n    if a == 0 {\n        r /= 2;\n        g /= 2;\n        b /= 2;\n    } else if a < 170 {\n        // Cheapish and looks ok.\n        // Works for e.g. grid stripes.\n        let div = (2 * 255 / a as i32) as u8;\n        r = r / 2 + target.r() / div;\n        g = g / 2 + target.g() / div;\n        b = b / 2 + target.b() / div;\n        a /= 2;\n    } else {\n        r = r / 2 + target.r() / 2;\n        g = g / 2 + target.g() / 2;\n        b = b / 2 + target.b() / 2;\n    }\n    Color32::from_rgba_premultiplied(r, g, b, a)\n}\n"
  },
  {
    "path": "crates/ecolor/src/rgba.rs",
    "content": "use crate::Color32;\n\n/// 0-1 linear space `RGBA` color with premultiplied alpha.\n///\n/// See [`crate::Color32`] for explanation of what \"premultiplied alpha\" means.\n#[repr(C)]\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct Rgba(pub(crate) [f32; 4]);\n\nimpl std::ops::Index<usize> for Rgba {\n    type Output = f32;\n\n    #[inline]\n    fn index(&self, index: usize) -> &f32 {\n        &self.0[index]\n    }\n}\n\nimpl std::ops::IndexMut<usize> for Rgba {\n    #[inline]\n    fn index_mut(&mut self, index: usize) -> &mut f32 {\n        &mut self.0[index]\n    }\n}\n\n/// Deterministically hash an `f32`, treating all NANs as equal, and ignoring the sign of zero.\n#[inline]\npub(crate) fn f32_hash<H: std::hash::Hasher>(state: &mut H, f: f32) {\n    if f == 0.0 {\n        state.write_u8(0);\n    } else if f.is_nan() {\n        state.write_u8(1);\n    } else {\n        use std::hash::Hash as _;\n        f.to_bits().hash(state);\n    }\n}\n\nimpl std::hash::Hash for Rgba {\n    #[inline]\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        crate::f32_hash(state, self.0[0]);\n        crate::f32_hash(state, self.0[1]);\n        crate::f32_hash(state, self.0[2]);\n        crate::f32_hash(state, self.0[3]);\n    }\n}\n\nimpl Rgba {\n    pub const TRANSPARENT: Self = Self::from_rgba_premultiplied(0.0, 0.0, 0.0, 0.0);\n    pub const BLACK: Self = Self::from_rgb(0.0, 0.0, 0.0);\n    pub const WHITE: Self = Self::from_rgb(1.0, 1.0, 1.0);\n    pub const RED: Self = Self::from_rgb(1.0, 0.0, 0.0);\n    pub const GREEN: Self = Self::from_rgb(0.0, 1.0, 0.0);\n    pub const BLUE: Self = Self::from_rgb(0.0, 0.0, 1.0);\n\n    #[inline]\n    pub const fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {\n        Self([r, g, b, a])\n    }\n\n    #[inline]\n    pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {\n        Self([r * a, g * a, b * a, a])\n    }\n\n    #[inline]\n    pub fn from_srgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {\n        Self::from(Color32::from_rgba_premultiplied(r, g, b, a))\n    }\n\n    #[inline]\n    pub fn from_srgba_unmultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {\n        Self::from(Color32::from_rgba_unmultiplied(r, g, b, a))\n    }\n\n    #[inline]\n    pub const fn from_rgb(r: f32, g: f32, b: f32) -> Self {\n        Self([r, g, b, 1.0])\n    }\n\n    #[doc(alias = \"from_grey\")]\n    #[inline]\n    pub const fn from_gray(l: f32) -> Self {\n        Self([l, l, l, 1.0])\n    }\n\n    #[inline]\n    pub fn from_luminance_alpha(l: f32, a: f32) -> Self {\n        debug_assert!(\n            0.0 <= l && l <= 1.0,\n            \"l should be in the range [0, 1], but was {l}\"\n        );\n        debug_assert!(\n            0.0 <= a && a <= 1.0,\n            \"a should be in the range [0, 1], but was {a}\"\n        );\n        Self([l * a, l * a, l * a, a])\n    }\n\n    /// Transparent black\n    #[inline]\n    pub fn from_black_alpha(a: f32) -> Self {\n        debug_assert!(\n            0.0 <= a && a <= 1.0,\n            \"a should be in the range [0, 1], but was {a}\"\n        );\n        Self([0.0, 0.0, 0.0, a])\n    }\n\n    /// Transparent white\n    #[inline]\n    pub fn from_white_alpha(a: f32) -> Self {\n        debug_assert!(0.0 <= a && a <= 1.0, \"a: {a}\");\n        Self([a, a, a, a])\n    }\n\n    /// Return an additive version of this color (alpha = 0)\n    #[inline]\n    pub fn additive(self) -> Self {\n        let [r, g, b, _] = self.0;\n        Self([r, g, b, 0.0])\n    }\n\n    /// Is the alpha=0 ?\n    #[inline]\n    pub fn is_additive(self) -> bool {\n        self.a() == 0.0\n    }\n\n    /// Multiply with e.g. 0.5 to make us half transparent\n    #[inline]\n    pub fn multiply(self, alpha: f32) -> Self {\n        Self([\n            alpha * self[0],\n            alpha * self[1],\n            alpha * self[2],\n            alpha * self[3],\n        ])\n    }\n\n    #[inline]\n    pub fn r(&self) -> f32 {\n        self.0[0]\n    }\n\n    #[inline]\n    pub fn g(&self) -> f32 {\n        self.0[1]\n    }\n\n    #[inline]\n    pub fn b(&self) -> f32 {\n        self.0[2]\n    }\n\n    #[inline]\n    pub fn a(&self) -> f32 {\n        self.0[3]\n    }\n\n    /// How perceptually intense (bright) is the color?\n    #[inline]\n    pub fn intensity(&self) -> f32 {\n        0.3 * self.r() + 0.59 * self.g() + 0.11 * self.b()\n    }\n\n    /// Returns an opaque version of self\n    #[inline]\n    pub fn to_opaque(&self) -> Self {\n        if self.a() == 0.0 {\n            // Additive or fully transparent black.\n            Self::from_rgb(self.r(), self.g(), self.b())\n        } else {\n            // un-multiply alpha:\n            Self::from_rgb(\n                self.r() / self.a(),\n                self.g() / self.a(),\n                self.b() / self.a(),\n            )\n        }\n    }\n\n    /// Premultiplied RGBA\n    #[inline]\n    pub fn to_array(&self) -> [f32; 4] {\n        [self.r(), self.g(), self.b(), self.a()]\n    }\n\n    /// Premultiplied RGBA\n    #[inline]\n    pub fn to_tuple(&self) -> (f32, f32, f32, f32) {\n        (self.r(), self.g(), self.b(), self.a())\n    }\n\n    /// unmultiply the alpha\n    #[inline]\n    pub fn to_rgba_unmultiplied(&self) -> [f32; 4] {\n        let a = self.a();\n        if a == 0.0 {\n            // Additive, let's assume we are black\n            self.0\n        } else {\n            [self.r() / a, self.g() / a, self.b() / a, a]\n        }\n    }\n\n    /// unmultiply the alpha\n    #[inline]\n    pub fn to_srgba_unmultiplied(&self) -> [u8; 4] {\n        crate::Color32::from(*self).to_srgba_unmultiplied()\n    }\n\n    /// Blend two colors in linear space, so that `self` is behind the argument.\n    pub fn blend(self, on_top: Self) -> Self {\n        self.multiply(1.0 - on_top.a()) + on_top\n    }\n}\n\nimpl std::ops::Add for Rgba {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, rhs: Self) -> Self {\n        Self([\n            self[0] + rhs[0],\n            self[1] + rhs[1],\n            self[2] + rhs[2],\n            self[3] + rhs[3],\n        ])\n    }\n}\n\nimpl std::ops::Mul for Rgba {\n    type Output = Self;\n\n    #[inline]\n    fn mul(self, other: Self) -> Self {\n        Self([\n            self[0] * other[0],\n            self[1] * other[1],\n            self[2] * other[2],\n            self[3] * other[3],\n        ])\n    }\n}\n\nimpl std::ops::Mul<f32> for Rgba {\n    type Output = Self;\n\n    #[inline]\n    fn mul(self, factor: f32) -> Self {\n        Self([\n            self[0] * factor,\n            self[1] * factor,\n            self[2] * factor,\n            self[3] * factor,\n        ])\n    }\n}\n\nimpl std::ops::Mul<Rgba> for f32 {\n    type Output = Rgba;\n\n    #[inline]\n    fn mul(self, rgba: Rgba) -> Rgba {\n        Rgba([\n            self * rgba[0],\n            self * rgba[1],\n            self * rgba[2],\n            self * rgba[3],\n        ])\n    }\n}\n\n#[cfg(test)]\nmod test {\n\n    use super::*;\n\n    fn test_rgba() -> impl Iterator<Item = [u8; 4]> {\n        [\n            [0, 0, 0, 0],\n            [0, 0, 0, 255],\n            [10, 0, 30, 0],\n            [10, 0, 30, 40],\n            [10, 100, 200, 0],\n            [10, 100, 200, 100],\n            [10, 100, 200, 200],\n            [10, 100, 200, 255],\n            [10, 100, 200, 40],\n            [10, 20, 0, 0],\n            [10, 20, 0, 255],\n            [10, 20, 30, 255],\n            [10, 20, 30, 40],\n            [255, 255, 255, 0],\n            [255, 255, 255, 255],\n        ]\n        .into_iter()\n    }\n\n    #[test]\n    fn test_rgba_blend() {\n        let opaque = Rgba::from_rgb(0.4, 0.5, 0.6);\n        let transparent = Rgba::from_rgb(1.0, 0.5, 0.0).multiply(0.3);\n        assert_eq!(\n            transparent.blend(opaque),\n            opaque,\n            \"Opaque on top of transparent\"\n        );\n        assert_eq!(\n            opaque.blend(transparent),\n            Rgba::from_rgb(\n                0.7 * 0.4 + 0.3 * 1.0,\n                0.7 * 0.5 + 0.3 * 0.5,\n                0.7 * 0.6 + 0.3 * 0.0\n            ),\n            \"Transparent on top of opaque\"\n        );\n    }\n\n    #[test]\n    fn test_rgba_roundtrip() {\n        for in_rgba in test_rgba() {\n            let [r, g, b, a] = in_rgba;\n            if a == 0 {\n                continue;\n            }\n            let rgba = Rgba::from_srgba_unmultiplied(r, g, b, a);\n            let out_rgba = rgba.to_srgba_unmultiplied();\n\n            if a == 255 {\n                assert_eq!(in_rgba, out_rgba);\n            } else {\n                // There will be small rounding errors whenever the alpha is not 0 or 255,\n                // because we multiply and then unmultiply the alpha.\n                for (&a, &b) in in_rgba.iter().zip(out_rgba.iter()) {\n                    assert!(a.abs_diff(b) <= 3, \"{in_rgba:?} != {out_rgba:?}\");\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/eframe/CHANGELOG.md",
    "content": "# Changelog for eframe\nAll notable changes to the `eframe` crate.\n\nNOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glow`](../egui_glow/CHANGELOG.md),and [`egui-wgpu`](../egui-wgpu/CHANGELOG.md) have their own changelogs!\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n## 0.33.3 - 2025-12-11\nNothing new\n\n\n## 0.33.2 - 2025-11-13\n* Fix jittering during window resize on MacOS for WGPU/Metal [#7641](https://github.com/emilk/egui/pull/7641) by [@aspcartman](https://github.com/aspcartman)\n* Make sure `native_pixels_per_point` is set during app creation [#7683](https://github.com/emilk/egui/pull/7683) by [@emilk](https://github.com/emilk)\n\n\n## 0.33.0 - 2025-10-09\n### ⭐ Added\n* Add an option to limit the repaint rate in the web runner [#7482](https://github.com/emilk/egui/pull/7482) by [@s-nie](https://github.com/s-nie)\n* Add rotation gesture support for trackpad sources [#7453](https://github.com/emilk/egui/pull/7453) by [@thatcomputerguy0101](https://github.com/thatcomputerguy0101)\n* Add support for the safe area on iOS [#7578](https://github.com/emilk/egui/pull/7578) by [@irh](https://github.com/irh)\n\n### 🔧 Changed\n* Replace `winapi` with `windows-sys` crate [#7416](https://github.com/emilk/egui/pull/7416) by [@unlimitedsola](https://github.com/unlimitedsola)\n* Prevent default action on command-comma in eframe web [#7547](https://github.com/emilk/egui/pull/7547) by [@emilk](https://github.com/emilk)\n* Warn if `DYLD_LIBRARY_PATH` is set and we find no wgpu adapter [#7572](https://github.com/emilk/egui/pull/7572) by [@emilk](https://github.com/emilk)\n* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)\n\n### 🐛 Fixed\n* Properly end winit event loop [#7565](https://github.com/emilk/egui/pull/7565) by [@tye-exe](https://github.com/tye-exe)\n* Fix eframe window not being focused on mac on startup [#7593](https://github.com/emilk/egui/pull/7593) by [@emilk](https://github.com/emilk)\n* Fix black flash on start in glow eframe backend [#7616](https://github.com/emilk/egui/pull/7616) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n\n## 0.32.3 - 2025-09-12\nNothing new\n\n\n## 0.32.2 - 2025-09-04\nNothing new\n\n\n## 0.32.1 - 2025-08-15\n* Enable wgpu default features in eframe / egui_wgpu default features [#7344](https://github.com/emilk/egui/pull/7344) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Request a redraw when the url change through the `popstate` event listener [#7403](https://github.com/emilk/egui/pull/7403) by [@irevoire](https://github.com/irevoire)\n\n\n## 0.32.0 - 2025-07-10\n### ⭐ Added\n* Add pointer events and focus handling for apps run in a Shadow DOM [#5627](https://github.com/emilk/egui/pull/5627) by [@xxvvii](https://github.com/xxvvii)\n* MacOS: Add `movable_by_window_background` option to viewport [#5412](https://github.com/emilk/egui/pull/5412) by [@jim-ec](https://github.com/jim-ec)\n* Add macOS-specific `has_shadow` and `with_has_shadow` to ViewportBuilder [#6850](https://github.com/emilk/egui/pull/6850) by [@gaelanmcmillan](https://github.com/gaelanmcmillan)\n* Add external eventloop support [#6750](https://github.com/emilk/egui/pull/6750) by [@wpbrown](https://github.com/wpbrown)\n\n### 🔧 Changed\n* Update MSRV to 1.85 [#7279](https://github.com/emilk/egui/pull/7279) by [@emilk](https://github.com/emilk)\n* Use Rust edition 2024 [#7280](https://github.com/emilk/egui/pull/7280) by [@emilk](https://github.com/emilk)\n* Rename `should_propagate_event` and add `should_prevent_default` [#5779](https://github.com/emilk/egui/pull/5779) by [@th0rex](https://github.com/th0rex)\n* Clarify platform-specific details for `Viewport` positioning [#5715](https://github.com/emilk/egui/pull/5715) by [@aspiringLich](https://github.com/aspiringLich)\n* Enhance stability on Windows [#5723](https://github.com/emilk/egui/pull/5723) by [@rustbasic](https://github.com/rustbasic)\n* Set `web-sys` min version to `0.3.73` [#5862](https://github.com/emilk/egui/pull/5862) by [@wareya](https://github.com/wareya)\n* Bump `ron` to `0.10.1` [#6861](https://github.com/emilk/egui/pull/6861) by [@torokati44](https://github.com/torokati44)\n* Disallow `accesskit` on Android NativeActivity, making `hello_android` working again [#6855](https://github.com/emilk/egui/pull/6855) by [@podusowski](https://github.com/podusowski)\n* Respect and detect `prefers-color-scheme: no-preference` [#7293](https://github.com/emilk/egui/pull/7293) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Mark all keys as up if the app loses focus [#5743](https://github.com/emilk/egui/pull/5743) by [@emilk](https://github.com/emilk)\n* Fix text input on Android [#5759](https://github.com/emilk/egui/pull/5759) by [@StratusFearMe21](https://github.com/StratusFearMe21)\n* Fix text distortion on mobile devices/browsers with `glow` backend [#6893](https://github.com/emilk/egui/pull/6893) by [@wareya](https://github.com/wareya)\n* Workaround libpng crash on macOS by not creating `NSImage` from png data [#7252](https://github.com/emilk/egui/pull/7252) by [@Wumpf](https://github.com/Wumpf)\n* Fix incorrect window sizes for non-resizable windows on Wayland [#7103](https://github.com/emilk/egui/pull/7103) by [@GoldsteinE](https://github.com/GoldsteinE)\n* Web: only consume copy/cut events if the canvas has focus [#7270](https://github.com/emilk/egui/pull/7270) by [@emilk](https://github.com/emilk)\n\n\n## 0.31.1 - 2025-03-05\nNothing new\n\n\n## 0.31.0 - 2025-02-04\n* Web: Fix incorrect scale when moving to screen with new DPI [#5631](https://github.com/emilk/egui/pull/5631) by [@emilk](https://github.com/emilk)\n* Re-enable IME support on Linux [#5198](https://github.com/emilk/egui/pull/5198) by [@YgorSouza](https://github.com/YgorSouza)\n* Serialize window maximized state in `WindowSettings` [#5554](https://github.com/emilk/egui/pull/5554) by [@landaire](https://github.com/landaire)\n* Save state on suspend on Android and iOS [#5601](https://github.com/emilk/egui/pull/5601) by [@Pandicon](https://github.com/Pandicon)\n* Eframe web: forward cmd-S/O to egui app (stop default browser action) [#5655](https://github.com/emilk/egui/pull/5655) by [@emilk](https://github.com/emilk)\n\n\n## 0.30.0 - 2024-12-16 - Android support\nNOTE: you now need to enable the `wayland` or `x11` features to get Linux support, including getting it to work on most CI systems.\n\n### ⭐ Added\n* Support `ViewportCommand::Screenshot` on web [#5438](https://github.com/emilk/egui/pull/5438) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🔧 Changed\n* Android support [#5318](https://github.com/emilk/egui/pull/5318) by [@parasyte](https://github.com/parasyte)\n* Update MSRV to 1.80 [#5457](https://github.com/emilk/egui/pull/5457) by [@emilk](https://github.com/emilk)\n* Use `profiling` crate to support more profiler backends [#5150](https://github.com/emilk/egui/pull/5150) by [@teddemunnik](https://github.com/teddemunnik)\n* Update glow to 0.16 [#5395](https://github.com/emilk/egui/pull/5395) by [@sagudev](https://github.com/sagudev)\n* Forward `x11` and `wayland` features to `glutin` [#5391](https://github.com/emilk/egui/pull/5391) by [@e00E](https://github.com/e00E)\n\n### 🐛 Fixed\n* iOS: Support putting UI next to the dynamic island [#5211](https://github.com/emilk/egui/pull/5211) by [@frederik-uni](https://github.com/frederik-uni)\n* Prevent panic when copying text outside of a secure context [#5326](https://github.com/emilk/egui/pull/5326) by [@YgorSouza](https://github.com/YgorSouza)\n* Fix accidental change of `FallbackEgl` to `PreferEgl` [#5408](https://github.com/emilk/egui/pull/5408) by [@e00E](https://github.com/e00E)\n\n\n## 0.29.1 - 2024-10-01 - Fix backspace/arrow keys on X11\n* Linux: Disable IME to fix backspace/arrow keys [#5188](https://github.com/emilk/egui/pull/5188) by [@emilk](https://github.com/emilk)\n\n\n## 0.29.0 - 2024-09-26 - `winit` 0.30 & fix mobile text input\n### ✨ Highlights\n* Upgrade winit to 0.30 ([#4849](https://github.com/emilk/egui/pull/4849) [#4939](https://github.com/emilk/egui/pull/4939))\n* Fix virtual keyboard on (mobile) web ([#4848](https://github.com/emilk/egui/pull/4848) [#4855](https://github.com/emilk/egui/pull/4855))\n\n### 🧳 Migration\n* `WebRunner::start` now expects a `HtmlCanvasElement` rather than the id of it ([#4780](https://github.com/emilk/egui/pull/4780))\n* `NativeOptions::follow_system_theme` and `default_theme` is gone, and is now in `egui::Options` instead ([#4860](https://github.com/emilk/egui/pull/4860) by [@bash](https://github.com/bash))\n\n### ⭐ Added\n* Conditionally propagate web events using a filter in WebOptions [#5056](https://github.com/emilk/egui/pull/5056) by [@liamrosenfeld](https://github.com/liamrosenfeld)\n\n### 🔧 Changed\n* Pass `HtmlCanvasElement ` element directly in `WebRunner::start` [#4780](https://github.com/emilk/egui/pull/4780) by [@jprochazk](https://github.com/jprochazk)\n* Upgrade winit to 0.30.2 [#4849](https://github.com/emilk/egui/pull/4849) [#4939](https://github.com/emilk/egui/pull/4939) by [@ArthurBrussee](https://github.com/ArthurBrussee)\n* Allow non-`static` `eframe::App` lifetime [#5060](https://github.com/emilk/egui/pull/5060) by [@timstr](https://github.com/timstr)\n* Improve `glow` context switching [#4814](https://github.com/emilk/egui/pull/4814) by [@rustbasic](https://github.com/rustbasic)\n* Ignore viewport size/position on iOS [#4922](https://github.com/emilk/egui/pull/4922) by [@frederik-uni](https://github.com/frederik-uni)\n* Update `web-sys` & `wasm-bindgen` [#4980](https://github.com/emilk/egui/pull/4980) by [@bircni](https://github.com/bircni)\n* Remove the need for setting `web_sys_unstable_apis` [#5000](https://github.com/emilk/egui/pull/5000) by [@emilk](https://github.com/emilk)\n* Remove the `directories` dependency [#4904](https://github.com/emilk/egui/pull/4904) by [@YgorSouza](https://github.com/YgorSouza)\n\n### 🐛 Fixed\n* Fix: call `save` when hiding web tab, and `update` when focusing it [#5114](https://github.com/emilk/egui/pull/5114) by [@emilk](https://github.com/emilk)\n* Force canvas/text input focus on touch for iOS web browsers [#4848](https://github.com/emilk/egui/pull/4848) by [@BKSalman](https://github.com/BKSalman)\n* Fix virtual keyboard on (mobile) web [#4855](https://github.com/emilk/egui/pull/4855) by [@micmonay](https://github.com/micmonay)\n* Fix: Backspace not working after IME input [#4912](https://github.com/emilk/egui/pull/4912) by [@rustbasic](https://github.com/rustbasic)\n* Fix iOS build, and add iOS step to CI [#4898](https://github.com/emilk/egui/pull/4898) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Fix iOS compilation of eframe [#4851](https://github.com/emilk/egui/pull/4851) by [@ardocrat](https://github.com/ardocrat)\n* Fix crash when changing viewport settings [#4862](https://github.com/emilk/egui/pull/4862) by [@pm100](https://github.com/pm100)\n* Fix eframe centering on multiple monitor systems [#4919](https://github.com/emilk/egui/pull/4919) by [@VinTarZ](https://github.com/VinTarZ)\n* Fix viewport not working when minimized [#5042](https://github.com/emilk/egui/pull/5042) by [@rustbasic](https://github.com/rustbasic)\n* Clarified `eframe::run_simple_native()` persistence [#4846](https://github.com/emilk/egui/pull/4846) by [@tpstevens](https://github.com/tpstevens)\n\n\n## 0.28.1 - 2024-07-05\n* Web: only capture clicks/touches when actually over canvas [#4775](https://github.com/emilk/egui/pull/4775) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n\n## 0.28.0 - 2024-07-03 - Better integration of a eframe in a bigger website\n### ✨ Highlights\nThe eframe web canvas now works properly when its a small part of a larger web page.\nPreviously this caused a lot of weird bugs, such as the eframe canvas stealing focus, and resizing the canvas in annoying ways.\nNow it should all work seamlessly to have an eframe canvas as part of a web page, including having multiple different eframe apps next to each other.\nAs part of that the eframe canvas can now be focused (or not), just like an `<input>` HTML element.\n\nWe've also implemented a better method for sizing and positioning the canvas so that it yields pixel-perfect rendering on all known browsers except for Desktop Safari.\nWhat this means is that text is much less likely to be blurry on web for users ([#4536](https://github.com/emilk/egui/pull/4536) by [@jprochazk](https://github.com/jprochazk)).\n\n### ⭐ Added\n* Add `register_native_texture` in `eframe::Frame` [#4246](https://github.com/emilk/egui/pull/4246) by [@Chaojimengnan](https://github.com/Chaojimengnan)\n* Add `NativeOptions::persistence_path` [#4423](https://github.com/emilk/egui/pull/4423) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Make sure to call `raw_input_hook` on web [#4646](https://github.com/emilk/egui/pull/4646) by [@owen-d](https://github.com/owen-d)\n\n### 🔧 Changed\n* Early-out from context switching the `glow` backend [#4284](https://github.com/emilk/egui/pull/4284), [#4296](https://github.com/emilk/egui/pull/4296) by [@emilk](https://github.com/emilk)\n* Allow users to create viewports larger than monitor on Windows & macOS [#4337](https://github.com/emilk/egui/pull/4337) by [@lopo12123](https://github.com/lopo12123)\n* Use `objc2` and its framework crates [#4395](https://github.com/emilk/egui/pull/4395) by [@madsmtm](https://github.com/madsmtm)\n* Emit physical key presses when a non-Latin layout is active [#4461](https://github.com/emilk/egui/pull/4461) by [@TicClick](https://github.com/TicClick)\n* Clamp window size to monitor size by default on all platforms [#4410](https://github.com/emilk/egui/pull/4410) by [@rustbasic](https://github.com/rustbasic)\n* Ignore synthetic key presses [#4514](https://github.com/emilk/egui/pull/4514) by [@hut](https://github.com/hut)\n* Use `ResizeObserver` instead of `resize` event [#4536](https://github.com/emilk/egui/pull/4536) by [@jprochazk](https://github.com/jprochazk)\n* Make pinch-to-zoom more responsive on web [#4621](https://github.com/emilk/egui/pull/4621) by [@emilk](https://github.com/emilk)\n* Move first `request_animation_frame` into resize observer [#4628](https://github.com/emilk/egui/pull/4628) by [@jprochazk](https://github.com/jprochazk)\n* Replace `directories-next` dependency with `directories` [#4661](https://github.com/emilk/egui/pull/4661) by [@crumblingstatue](https://github.com/crumblingstatue)\n* `eframe::Result` is now short for `eframe::Result<()>` [#4706](https://github.com/emilk/egui/pull/4706) by [@emilk](https://github.com/emilk)\n* Ignore keyboard events unless canvas has focus [#4718](https://github.com/emilk/egui/pull/4718) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Fix `ViewportCommand::InnerSize` not resizing viewport on Wayland (#4211) [#4211](https://github.com/emilk/egui/pull/4211) by [@rustbasic](https://github.com/rustbasic)\n* Improve IME support with new `Event::Ime` [#4358](https://github.com/emilk/egui/pull/4358) by [@rustbasic](https://github.com/rustbasic)\n* IME for chinese [#4436](https://github.com/emilk/egui/pull/4436) by [@rustbasic](https://github.com/rustbasic)\n* Fix: Window position creeps between executions on scaled monitors [#4443](https://github.com/emilk/egui/pull/4443) by [@avery-radmacher](https://github.com/avery-radmacher)\n* Fix: still track mouse when dragging outside web canvas [#4522](https://github.com/emilk/egui/pull/4522) by [@emilk](https://github.com/emilk)\n* Fix: Don't `.forget()` RAF closure [#4551](https://github.com/emilk/egui/pull/4551) by [@jprochazk](https://github.com/jprochazk)\n* Improve web text agent [#4561](https://github.com/emilk/egui/pull/4561) by [@jprochazk](https://github.com/jprochazk)\n* Fix broken mouse coordinates when there's padding on the canvas element [#4729](https://github.com/emilk/egui/pull/4729) by [@emilk](https://github.com/emilk)\n* Only repaint on cursor movements of area, or if dragging outside [#4730](https://github.com/emilk/egui/pull/4730) by [@emilk](https://github.com/emilk)\n* Fix drag-and-drop file preview/hover [#4732](https://github.com/emilk/egui/pull/4732) by [@emilk](https://github.com/emilk)\n* Fix stuck keys after pressing ctrl+C, cmd+A, etc [#4731](https://github.com/emilk/egui/pull/4731) by [@emilk](https://github.com/emilk)\n\n### 🧳 Migration\n* Update MSRV to 1.76 [#4411](https://github.com/emilk/egui/pull/4411) by [@emilk](https://github.com/emilk)\n\n#### Wrap app creator in a `Result`\nApplications can now return an error during the app creation ([#4565](https://github.com/emilk/egui/pull/4565) by [@emilk](https://github.com/emilk)), so you now need to wrap your `Box<dyn App>` in a `Result` like so:\n\n\n``` diff\n- eframe::run_native(\"My App\", options, Box::new(|cc| Box::new(MyApp::new(cc))));\n+ eframe::run_native(\"My App\", options, Box::new(|cc| Ok(Box::new(MyApp::new(cc)))));\n```\n\n#### Change web CSS\nTo make the eframe canvas fill the entire web browser, set its CSS to:\n\n``` css\ntop: 0;\nleft: 0;\nwidth: 100%;\nheight: 100%;\n```\n\nSee [`index.html`](https://github.com/emilk/egui/blob/a489374ca63f0d1ae983bb21d8bb766b2d68737b/web_demo/index.html#L30-L50) and [#4536](https://github.com/emilk/egui/pull/4536) for details.\n\n#### Web canvas focus\nIf you are using eframe for a fullscreen app, you should call `.focus()` on your canvas during startup:\n```js\ndocument.getElementById(\"the_canvas_id\").focus();\n```\n\n\n## 0.27.2 - 2024-04-02\n#### Desktop/Native\n* Fix continuous repaint on Wayland when TextEdit is focused or IME output is set [#4269](https://github.com/emilk/egui/pull/4269) (thanks [@white-axe](https://github.com/white-axe)!)\n* Remove a bunch of `unwrap()` [#4285](https://github.com/emilk/egui/pull/4285)\n\n#### Web\n* Fix blurry rendering in some browsers [#4299](https://github.com/emilk/egui/pull/4299)\n* Correctly identify if the browser tab has focus [#4280](https://github.com/emilk/egui/pull/4280)\n\n\n## 0.27.1 - 2024-03-29\n* Web: repaint if the `#hash` in the URL changes [#4261](https://github.com/emilk/egui/pull/4261)\n* Add web support for `zoom_factor` [#4260](https://github.com/emilk/egui/pull/4260) (thanks [@justusdieckmann](https://github.com/justusdieckmann)!)\n\n\n## 0.27.0 - 2024-03-26\n* Update to document-features 0.2.8 [#4003](https://github.com/emilk/egui/pull/4003)\n* Added `App::raw_input_hook` allows for the manipulation or filtering of raw input events [#4008](https://github.com/emilk/egui/pull/4008) (thanks [@varphone](https://github.com/varphone)!)\n\n#### Desktop/Native\n* Add with_taskbar to viewport builder [#3958](https://github.com/emilk/egui/pull/3958) (thanks [@AnotherNathan](https://github.com/AnotherNathan)!)\n* Add `winuser` feature to `winapi` to fix unresolved import [#4037](https://github.com/emilk/egui/pull/4037) (thanks [@varphone](https://github.com/varphone)!)\n* Add `get_proc_address` in CreationContext [#4145](https://github.com/emilk/egui/pull/4145) (thanks [@Chaojimengnan](https://github.com/Chaojimengnan)!)\n* Don't clear modifier state on focus change [#4157](https://github.com/emilk/egui/pull/4157) (thanks [@ming08108](https://github.com/ming08108)!)\n* Add x11 window type settings to viewport builder [#4175](https://github.com/emilk/egui/pull/4175) (thanks [@psethwick](https://github.com/psethwick)!)\n\n#### Web\n* Add `webgpu` feature by default to wgpu [#4124](https://github.com/emilk/egui/pull/4124) (thanks [@ctaggart](https://github.com/ctaggart)!)\n* Update kb modifiers from web mouse events [#4156](https://github.com/emilk/egui/pull/4156) (thanks [@ming08108](https://github.com/ming08108)!)\n* Fix crash on `request_animation_frame` when destroying web runner [#4169](https://github.com/emilk/egui/pull/4169) (thanks [@jprochazk](https://github.com/jprochazk)!)\n* Fix bug parsing url query with escaped & or = [#4172](https://github.com/emilk/egui/pull/4172)\n* `Location::query_map`: support repeated key [#4183](https://github.com/emilk/egui/pull/4183)\n\n\n## 0.26.2 - 2024-02-14\n* Add `winuser` feature to `winapi` to fix unresolved import [#4037](https://github.com/emilk/egui/pull/4037) (thanks [@varphone](https://github.com/varphone)!)\n\n\n## 0.26.1 - 2024-02-11\n* Fix high CPU usage on Windows when app is minimized [#3985](https://github.com/emilk/egui/pull/3985) (thanks [@rustbasic](https://github.com/rustbasic)!)\n* Update to document-features 0.2.8 [#4003](https://github.com/emilk/egui/pull/4003)\n\n\n## 0.26.0 - 2024-02-05\n* Update `wgpu` to 0.19 [#3824](https://github.com/emilk/egui/pull/3824)\n* Disable the default features of `wgpu` [#3875](https://github.com/emilk/egui/pull/3875)\n* Much more accurate `cpu_usage` timing [#3913](https://github.com/emilk/egui/pull/3913)\n* Update to puffin 0.19 [#3940](https://github.com/emilk/egui/pull/3940)\n\n#### Desktop/Native\n* Keep `ViewportInfo::maximized` and `minimized` up-to-date on Windows [#3831](https://github.com/emilk/egui/pull/3831) (thanks [@rustbasic](https://github.com/rustbasic)!)\n* Handle `IconData::default()` without crashing [#3842](https://github.com/emilk/egui/pull/3842)\n* Fix Android crash on resume [#3847](https://github.com/emilk/egui/pull/3847) [#3867](https://github.com/emilk/egui/pull/3867) (thanks [@Garoven](https://github.com/Garoven)!)\n* Add `WgpuConfiguration::desired_maximum_frame_latency` [#3874](https://github.com/emilk/egui/pull/3874)\n* Don't call `App::update` on minimized windows [#3877](https://github.com/emilk/egui/pull/3877) (thanks [@rustbasic](https://github.com/rustbasic)!)\n\n#### Web\n* When using `wgpu` on web, `eframe` will try to use WebGPU if available, then fall back to WebGL [#3824](https://github.com/emilk/egui/pull/3824) [#3895](https://github.com/emilk/egui/pull/3895) (thanks [@Wumpf](https://github.com/Wumpf)!)\n\n\n## 0.25.0 - 2024-01-08\n* If both `glow` and `wgpu` features are enabled, default to `wgpu` [#3717](https://github.com/emilk/egui/pull/3717)\n\n#### Desktop/Native\n* Update to winit 0.29 [#3649](https://github.com/emilk/egui/pull/3649) (thanks [@fornwall](https://github.com/fornwall)!)\n* Make glow `Send + Sync` again [#3646](https://github.com/emilk/egui/pull/3646) (thanks [@surban](https://github.com/surban)!)\n* Bug fix: framebuffer clear when using glow with multi-viewports [#3713](https://github.com/emilk/egui/pull/3713)\n* Fix: Let `accesskit` process window events [#3733](https://github.com/emilk/egui/pull/3733) (thanks [@DataTriny](https://github.com/DataTriny)!)\n\n#### Web\n* Fix building the `wasm32` docs for `docs.rs` [#3757](https://github.com/emilk/egui/pull/3757)\n\n\n## 0.24.1 - 2023-11-30\n#### Desktop/Native\n* Fix window flashing white on launch [#3631](https://github.com/emilk/egui/pull/3631) (thanks [@zeozeozeo](https://github.com/zeozeozeo)!)\n* Fix windowing problems when using the `x11` feature on Linux [#3643](https://github.com/emilk/egui/pull/3643)\n* Fix bugs when there are multiple monitors with different scales [#3663](https://github.com/emilk/egui/pull/3663)\n* `glow` backend: clear framebuffer color before calling `App::update` [#3665](https://github.com/emilk/egui/pull/3665)\n\n#### Web\n* Fix click-to-copy on Safari [#3621](https://github.com/emilk/egui/pull/3621)\n* Don't throw away frames on click/copy/cut [#3623](https://github.com/emilk/egui/pull/3623)\n* Remove dependency on `tts` [#3651](https://github.com/emilk/egui/pull/3651)\n\n\n## 0.24.0 - 2023-11-23\n* Multiple viewports/windows [#3172](https://github.com/emilk/egui/pull/3172) (thanks [@konkitoman](https://github.com/konkitoman)!)\n* Replace `eframe::Frame` commands and `WindowInfo` with egui [#3564](https://github.com/emilk/egui/pull/3564)\n* Use `egui::ViewportBuilder` in `eframe::NativeOptions` [#3572](https://github.com/emilk/egui/pull/3572)\n* Remove warm-starting [#3574](https://github.com/emilk/egui/pull/3574)\n* Fix copy and cut on Safari [#3513](https://github.com/emilk/egui/pull/3513) (thanks [@lunixbochs](https://github.com/lunixbochs)!)\n* Update puffin to 0.18 [#3600](https://github.com/emilk/egui/pull/3600)\n* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)\n\n### Breaking changes:\nMost settings in `NativeOptions` have been moved to `NativeOptions::viewport`, which uses the new `egui::ViewportBuilder`:\n\n```diff\n let native_options = eframe::nativeOptions {\n-    initial_window_size: Some(egui::vec2(320.0, 240.0)),\n-    drag_and_drop_support: true,\n+    viewport: egui::ViewportBuilder::default()\n+        .with_inner_size([320.0, 240.0])\n+        .with_drag_and_drop(true),\n     ..Default::default()\n };\n```\n\n`NativeOptions::fullsize_content` has been replaced with four settings: `ViewportBuilder::with_fullsize_content_view`, `with_title_shown`, `with_titlebar_shown`, `with_titlebar_buttons_shown`\n\n`frame.info().window_info` is gone, replaced with `ctx.input(|i| i.viewport())`.\n\n`frame.info().native_pixels_per_point` is replaced with `ctx.input(|i| i.raw.native_pixels_per_point)`.\n\nMost commands in `eframe::Frame` has been replaced with `egui::ViewportCommand`, so So `frame.close()` becomes `ctx.send_viewport_cmd(ViewportCommand::Close)`, etc.\n\n`App::on_close_event` has been replaced with `ctx.input(|i| i.viewport().close_requested())` and `ctx.send_viewport_cmd(ViewportCommand::CancelClose)`.\n\n`eframe::IconData` is now `egui::IconData`.\n\n`eframe::IconData::try_from_png_bytes` is now `eframe::icon_data::from_png_bytes`.\n\n`App::post_rendering` is gone. Screenshots are taken with `ctx.send_viewport_cmd(ViewportCommand::Screenshots)` and are returned in `egui::Event` which you can check with:\n``` rust\nui.input(|i| {\n    for event in &i.raw.events {\n        if let egui::Event::Screenshot { viewport_id, image } = event {\n            // handle it here\n        }\n    }\n});\n```\n\n\n## 0.23.0 - 2023-09-27\n* Update MSRV to Rust 1.70.0 [#3310](https://github.com/emilk/egui/pull/3310)\n* Update to puffin 0.16 [#3144](https://github.com/emilk/egui/pull/3144)\n* Update to `wgpu` 0.17.0 [#3170](https://github.com/emilk/egui/pull/3170) (thanks [@Aaron1011](https://github.com/Aaron1011)!)\n* Improved wgpu callbacks [#3253](https://github.com/emilk/egui/pull/3253) (thanks [@Wumpf](https://github.com/Wumpf)!)\n* Improve documentation of `eframe`, especially for wasm32 [#3295](https://github.com/emilk/egui/pull/3295)\n* `eframe::Frame::info` returns a reference [#3301](https://github.com/emilk/egui/pull/3301) (thanks [@Barugon](https://github.com/Barugon)!)\n* Move `App::persist_window` to `NativeOptions` and `App::max_size_points` to `WebOptions` [#3397](https://github.com/emilk/egui/pull/3397)\n\n#### Desktop/Native\n* Only show on-screen-keyboard and IME when editing text [#3362](https://github.com/emilk/egui/pull/3362) (thanks [@Barugon](https://github.com/Barugon)!)\n* Add `eframe::storage_dir` [#3286](https://github.com/emilk/egui/pull/3286)\n* Add `NativeOptions::window_builder` for more customization [#3390](https://github.com/emilk/egui/pull/3390) (thanks [@twop](https://github.com/twop)!)\n* Better restore Window position on Mac when on secondary monitor [#3239](https://github.com/emilk/egui/pull/3239)\n* Fix iOS support in `eframe` [#3241](https://github.com/emilk/egui/pull/3241) (thanks [@lucasmerlin](https://github.com/lucasmerlin)!)\n* Speed up `eframe` state storage [#3353](https://github.com/emilk/egui/pull/3353) (thanks [@sebbert](https://github.com/sebbert)!)\n* Allow users to opt-out of default `winit` features [#3228](https://github.com/emilk/egui/pull/3228)\n* Expose Raw Window and Display Handles [#3073](https://github.com/emilk/egui/pull/3073) (thanks [@bash](https://github.com/bash)!)\n* Use window title as fallback when app_id is not set [#3107](https://github.com/emilk/egui/pull/3107) (thanks [@jacekpoz](https://github.com/jacekpoz)!)\n* Sleep a bit only when minimized [#3139](https://github.com/emilk/egui/pull/3139) (thanks [@icedrocket](https://github.com/icedrocket)!)\n* Prevent text from being cleared when selected due to winit IME [#3376](https://github.com/emilk/egui/pull/3376) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Fix android app quit on resume with glow backend [#3080](https://github.com/emilk/egui/pull/3080) (thanks [@tkkcc](https://github.com/tkkcc)!)\n* Fix panic with persistence without window [#3167](https://github.com/emilk/egui/pull/3167) (thanks [@sagebind](https://github.com/sagebind)!)\n* Only call `run_return` twice on Windows [#3053](https://github.com/emilk/egui/pull/3053) (thanks [@pan93412](https://github.com/pan93412)!)\n* Gracefully catch error saving state to disk [#3230](https://github.com/emilk/egui/pull/3230)\n* Recognize numpad enter/plus/minus [#3285](https://github.com/emilk/egui/pull/3285)\n* Add more puffin profile scopes to `eframe` [#3330](https://github.com/emilk/egui/pull/3330) [#3332](https://github.com/emilk/egui/pull/3332)\n\n#### Web\n* Update to wasm-bindgen 0.2.87 [#3237](https://github.com/emilk/egui/pull/3237)\n* Remove `Function()` invocation from eframe text_agent to bypass \"unsafe-eval\" restrictions in Chrome browser extensions. [#3349](https://github.com/emilk/egui/pull/3349) (thanks [@aspect](https://github.com/aspect)!)\n* Fix docs about web [#3026](https://github.com/emilk/egui/pull/3026) (thanks [@kerryeon](https://github.com/kerryeon)!)\n\n\n## 0.22.0 - 2023-05-23\n* Fix: `request_repaint_after` works even when called from background thread [#2939](https://github.com/emilk/egui/pull/2939)\n* Clear all keys and modifies on focus change [#2857](https://github.com/emilk/egui/pull/2857) [#2933](https://github.com/emilk/egui/pull/2933)\n* Remove dark-light dependency [#2929](https://github.com/emilk/egui/pull/2929)\n* Replace `tracing` with `log` [#2928](https://github.com/emilk/egui/pull/2928)\n* Update accesskit to 0.11 [#3012](https://github.com/emilk/egui/pull/3012)\n\n#### Desktop/Native\n* Automatically change theme when system dark/light mode changes [#2750](https://github.com/emilk/egui/pull/2750) (thanks [@bash](https://github.com/bash)!)\n* Enabled wayland feature for winit when running native [#2751](https://github.com/emilk/egui/pull/2751) (thanks [@ItsEthra](https://github.com/ItsEthra)!)\n* Fix eframe window position bug (pixels vs points) [#2763](https://github.com/emilk/egui/pull/2763) (thanks [@get200](https://github.com/get200)!)\n* Add `Frame::request_screenshot` and `Frame::screenshot` to communicate to the backend that a screenshot of the current frame should be exposed by `Frame` during `App::post_rendering` ([#2676](https://github.com/emilk/egui/pull/2676)).\n* Add `eframe::run_simple_native` * a simple API for simple apps ([#2453](https://github.com/emilk/egui/pull/2453)).\n* Add `NativeOptions::app_id` which allows to set the Wayland application ID under Linux ([#1600](https://github.com/emilk/egui/issues/1600)).\n* Add `NativeOptions::active` [#2813](https://github.com/emilk/egui/pull/2813) (thanks [@Dixeran](https://github.com/Dixeran)!)\n* Remove `android-activity` dependency + add `Activity` backend features [#2863](https://github.com/emilk/egui/pull/2863) (thanks [@rib](https://github.com/rib)!)\n* Fix bug where the eframe window is never destroyed on Linux when using `run_and_return` ([#2892](https://github.com/emilk/egui/issues/2892))\n* Fix state persisting when exiting on Linux [#2895](https://github.com/emilk/egui/pull/2895) (thanks [@flukejones](https://github.com/flukejones)!)\n* Allow for requesting the user's attention to the window [#2905](https://github.com/emilk/egui/pull/2905) (thanks [@TicClick](https://github.com/TicClick)!)\n* Read and request window focus [#2900](https://github.com/emilk/egui/pull/2900) (thanks [@TicClick](https://github.com/TicClick)!)\n* Set app icon on Mac and Windows [#2940](https://github.com/emilk/egui/pull/2940)\n* Set a default icon for all eframe apps: a white `e` on black background [#2996](https://github.com/emilk/egui/pull/2996)\n* Add `NativeOptions::app_id` for the persistence location [#3014](https://github.com/emilk/egui/pull/3014) and for Wayland [#3007](https://github.com/emilk/egui/pull/3007) (thanks [@thomaskrause](https://github.com/thomaskrause)!)\n* capture a screenshot using `Frame::request_screenshot` [870264b](https://github.com/emilk/egui/commit/870264b00577a95d3fd9bdf36efaf87fd351de62)\n\n\n#### Web\n* ⚠️ BREAKING: `eframe::start_web` has been replaced with `eframe::WebRunner`, which also installs a nice panic hook (no need for `console_error_panic_hook`).\n* ⚠️ BREAKING: WebGPU is now the default web renderer when using the `wgpu` feature of `eframe`. To use WebGL with `wgpu`, you need to add `wgpu = { version = \"0.16.0\", features = [\"webgl\"] }` to your own `Cargo.toml`. ([#2945](https://github.com/emilk/egui/pull/2945))\n* Add `eframe::WebLogger` for redirecting `log` calls to the web console (`console.log`).\n* Prefer the client width/height for the canvas parent [#2804](https://github.com/emilk/egui/pull/2804) (thanks [@samitbasu](https://github.com/samitbasu)!)\n* eframe web: Persist app state to local storage when leaving site [#2927](https://github.com/emilk/egui/pull/2927)\n* Better panic handling [#2942](https://github.com/emilk/egui/pull/2942) [#2992](https://github.com/emilk/egui/pull/2992)\n* Update wasm-bindgen to 0.2.86 [#2995](https://github.com/emilk/egui/pull/2995)\n* Properly unsubscribe from events on destroy [4d360f6](https://github.com/emilk/egui/commit/4d360f67a4ae2314fbc8b83b01b701ec8e9cea5b)\n\n\n## 0.21.3 - 2023-02-15\n* Fix typing the letter 'P' on web ([#2740](https://github.com/emilk/egui/pull/2740)).\n\n\n## 0.21.2 - 2023-02-12\n* Allow compiling `eframe` with `--no-default-features` ([#2728](https://github.com/emilk/egui/pull/2728)).\n\n\n## 0.21.1 - 2023-02-12\n* Fixed crash when native window position is in an invalid state, which could happen e.g. due to changes in monitor size or DPI ([#2722](https://github.com/emilk/egui/issues/2722)).\n\n\n## 0.21.0 - 2023-02-08 - Update to `winit` 0.28\n* ⚠️ BREAKING: `App::clear_color` now expects you to return a raw float array ([#2666](https://github.com/emilk/egui/pull/2666)).\n* The `screen_reader` feature has now been renamed `web_screen_reader` and only work on web. On other platforms, use the `accesskit` feature flag instead ([#2669](https://github.com/emilk/egui/pull/2669)).\n\n#### Desktop/Native\n* `eframe::run_native` now returns a `Result` ([#2433](https://github.com/emilk/egui/pull/2433)).\n* Update to `winit` 0.28, adding support for mac trackpad zoom ([#2654](https://github.com/emilk/egui/pull/2654)).\n* Fix bug where the cursor could get stuck using the wrong icon.\n* `NativeOptions::transparent` now works with the wgpu backend ([#2684](https://github.com/emilk/egui/pull/2684)).\n* Add `Frame::set_minimized` and `set_maximized` ([#2292](https://github.com/emilk/egui/pull/2292), [#2672](https://github.com/emilk/egui/pull/2672)).\n* Fixed persistence of native window position on Windows OS ([#2583](https://github.com/emilk/egui/issues/2583)).\n\n#### Web\n* Prevent ctrl-P/cmd-P from opening the print dialog ([#2598](https://github.com/emilk/egui/pull/2598)).\n\n\n## 0.20.1 - 2022-12-11\n* Fix [docs.rs](https://docs.rs/eframe) build ([#2420](https://github.com/emilk/egui/pull/2420)).\n\n\n## 0.20.0 - 2022-12-08 - AccessKit integration and `wgpu` web support\n* MSRV (Minimum Supported Rust Version) is now `1.65.0` ([#2314](https://github.com/emilk/egui/pull/2314)).\n* Allow empty textures with the glow renderer.\n\n#### Desktop/Native\n* Don't repaint when just moving window ([#1980](https://github.com/emilk/egui/pull/1980)).\n* Added `NativeOptions::event_loop_builder` hook for apps to change platform specific event loop options ([#1952](https://github.com/emilk/egui/pull/1952)).\n* Enabled deferred render state initialization to support Android ([#1952](https://github.com/emilk/egui/pull/1952)).\n* Added `shader_version` to `NativeOptions` for cross compiling support on different target OpenGL | ES versions (on native `glow` renderer only) ([#1993](https://github.com/emilk/egui/pull/1993)).\n* Fix: app state is now saved when user presses Cmd-Q on Mac ([#2013](https://github.com/emilk/egui/pull/2013)).\n* Added `center` to `NativeOptions` and `monitor_size` to `WindowInfo` on desktop ([#2035](https://github.com/emilk/egui/pull/2035)).\n* Improve IME support ([#2046](https://github.com/emilk/egui/pull/2046)).\n* Added mouse-passthrough option ([#2080](https://github.com/emilk/egui/pull/2080)).\n* Added `NativeOptions::fullsize_content` option on Mac to build titlebar-less windows with floating window controls ([#2049](https://github.com/emilk/egui/pull/2049)).\n* Wgpu device/adapter/surface creation has now various configuration options exposed via `NativeOptions/WebOptions::wgpu_options` ([#2207](https://github.com/emilk/egui/pull/2207)).\n* Fix: Make sure that `native_pixels_per_point` is updated ([#2256](https://github.com/emilk/egui/pull/2256)).\n* Added optional, but enabled by default, integration with [AccessKit](https://accesskit.dev/) for implementing platform accessibility APIs ([#2294](https://github.com/emilk/egui/pull/2294)).\n* Fix: Less flickering on resize on Windows ([#2280](https://github.com/emilk/egui/pull/2280)).\n\n#### Web\n* ⚠️ BREAKING: `start_web` is a now `async` ([#2107](https://github.com/emilk/egui/pull/2107)).\n* Web: You can now use WebGL on top of `wgpu` by enabling the `wgpu` feature (and disabling `glow` via disabling default features) ([#2107](https://github.com/emilk/egui/pull/2107)).\n* Web: Add `WebInfo::user_agent` ([#2202](https://github.com/emilk/egui/pull/2202)).\n* Web: you can access your application from JS using `AppRunner::app_mut`. See `crates/egui_demo_app/src/lib.rs` ([#1886](https://github.com/emilk/egui/pull/1886)).\n\n\n## 0.19.0 - 2022-08-20\n* MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)).\n* Added `wgpu` rendering backed ([#1564](https://github.com/emilk/egui/pull/1564)):\n  * Added features `wgpu` and `glow`.\n  * Added `NativeOptions::renderer` to switch between the rendering backends.\n* `egui_glow`: remove calls to `gl.get_error` in release builds to speed up rendering ([#1583](https://github.com/emilk/egui/pull/1583)).\n* Added `App::post_rendering` for e.g. reading the framebuffer ([#1591](https://github.com/emilk/egui/pull/1591)).\n* Use `Arc` for `glow::Context` instead of `Rc` ([#1640](https://github.com/emilk/egui/pull/1640)).\n* Fixed bug where the result returned from `App::on_exit_event` would sometimes be ignored ([#1696](https://github.com/emilk/egui/pull/1696)).\n* Added `NativeOptions::follow_system_theme` and `NativeOptions::default_theme` ([#1726](https://github.com/emilk/egui/pull/1726)).\n* Selectively expose parts of the API based on target arch (`wasm32` or not) ([#1867](https://github.com/emilk/egui/pull/1867)).\n\n#### Desktop/Native\n* Fixed clipboard on Wayland ([#1613](https://github.com/emilk/egui/pull/1613)).\n* Added ability to read window position and size with `frame.info().window_info` ([#1617](https://github.com/emilk/egui/pull/1617)).\n* Allow running on native without hardware accelerated rendering. Change with `NativeOptions::hardware_acceleration` ([#1681](https://github.com/emilk/egui/pull/1681), [#1693](https://github.com/emilk/egui/pull/1693)).\n* Fixed window position persistence ([#1745](https://github.com/emilk/egui/pull/1745)).\n* Fixed mouse cursor change on Linux ([#1747](https://github.com/emilk/egui/pull/1747)).\n* Added `Frame::set_visible` ([#1808](https://github.com/emilk/egui/pull/1808)).\n* Added fullscreen support ([#1866](https://github.com/emilk/egui/pull/1866)).\n* You can now continue execution after closing the native desktop window ([#1889](https://github.com/emilk/egui/pull/1889)).\n* `Frame::quit` has been renamed to `Frame::close` and `App::on_exit_event` is now `App::on_close_event` ([#1943](https://github.com/emilk/egui/pull/1943)).\n\n#### Web\n* Added ability to stop/re-run web app from JavaScript. ⚠️ You need to update your CSS with `html, body: { height: 100%; width: 100%; }` ([#1803](https://github.com/emilk/egui/pull/1650)).\n* Added `WebOptions::follow_system_theme` and `WebOptions::default_theme` ([#1726](https://github.com/emilk/egui/pull/1726)).\n* Added option to select WebGL version ([#1803](https://github.com/emilk/egui/pull/1803)).\n\n\n## 0.18.0 - 2022-04-30\n* MSRV (Minimum Supported Rust Version) is now `1.60.0` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Removed `eframe::epi` - everything is now in `eframe` (`eframe::App`, `eframe::Frame` etc) ([#1545](https://github.com/emilk/egui/pull/1545)).\n* Removed `Frame::request_repaint` - just call `egui::Context::request_repaint` for the same effect ([#1366](https://github.com/emilk/egui/pull/1366)).\n* Changed app creation/setup ([#1363](https://github.com/emilk/egui/pull/1363)):\n  * Removed `App::setup` and `App::name`.\n  * Provide `CreationContext` when creating app with egui context, storage, integration info and glow context.\n  * Change interface of `run_native` and `start_web`.\n* Added `Frame::storage()` and `Frame::storage_mut()` ([#1418](https://github.com/emilk/egui/pull/1418)).\n  * You can now load/save state in `App::update`\n  * Changed `App::update` to take `&mut Frame` instead of `&Frame`.\n  * `Frame` is no longer `Clone` or `Sync`.\n* Added `glow` (OpenGL) context to `Frame` ([#1425](https://github.com/emilk/egui/pull/1425)).\n\n#### Desktop/Native\n* Remove the `egui_glium` feature. `eframe` will now always use `egui_glow` as the native backend ([#1357](https://github.com/emilk/egui/pull/1357)).\n* Change default for `NativeOptions::drag_and_drop_support` to `true` ([#1329](https://github.com/emilk/egui/pull/1329)).\n* Added new `NativeOptions`: `vsync`, `multisampling`, `depth_buffer`, `stencil_buffer`.\n* `dark-light` (dark mode detection) is now an opt-in feature ([#1437](https://github.com/emilk/egui/pull/1437)).\n* Fixed potential scale bug when DPI scaling changes (e.g. when dragging a  window between different displays) ([#1441](https://github.com/emilk/egui/pull/1441)).\n* Added new feature `puffin` to add [`puffin profiler`](https://github.com/EmbarkStudios/puffin) scopes ([#1483](https://github.com/emilk/egui/pull/1483)).\n* Moved app persistence to a background thread, allowing for smoother frame rates (on native).\n* Added `Frame::set_window_pos` ([#1505](https://github.com/emilk/egui/pull/1505)).\n\n#### Web\n* Use full browser width by default ([#1378](https://github.com/emilk/egui/pull/1378)).\n* egui code will no longer be called after panic ([#1306](https://github.com/emilk/egui/pull/1306)).\n\n\n## 0.17.0 - 2022-02-22\n* Removed `Frame::alloc_texture`. Use `egui::Context::load_texture` instead ([#1110](https://github.com/emilk/egui/pull/1110)).\n* Shift-scroll will now result in horizontal scrolling on all platforms ([#1136](https://github.com/emilk/egui/pull/1136)).\n* Log using the `tracing` crate. Log to stdout by adding `tracing_subscriber::fmt::init();` to your `main` ([#1192](https://github.com/emilk/egui/pull/1192)).\n\n#### Desktop/Native\n* The default native backend is now `egui_glow` (instead of `egui_glium`) ([#1020](https://github.com/emilk/egui/pull/1020)).\n* Automatically detect and apply dark or light mode from system ([#1045](https://github.com/emilk/egui/pull/1045)).\n* Fixed horizontal scrolling direction on Linux.\n* Added `App::on_exit_event` ([#1038](https://github.com/emilk/egui/pull/1038))\n* Added `NativeOptions::initial_window_pos`.\n* Fixed `enable_drag` for Windows OS ([#1108](https://github.com/emilk/egui/pull/1108)).\n\n#### Web\n* The default web painter is now `egui_glow` (instead of WebGL) ([#1020](https://github.com/emilk/egui/pull/1020)).\n* Fixed glow failure on Chromium ([#1092](https://github.com/emilk/egui/pull/1092)).\n* Updated `eframe::IntegrationInfo::web_location_hash` on `hashchange` event ([#1140](https://github.com/emilk/egui/pull/1140)).\n* Expose all parts of the location/url in `frame.info().web_info` ([#1258](https://github.com/emilk/egui/pull/1258)).\n\n\n## 0.16.0 - 2021-12-29\n* `Frame` can now be cloned, saved, and passed to background threads ([#999](https://github.com/emilk/egui/pull/999)).\n* Added `Frame::request_repaint` to replace `repaint_signal` ([#999](https://github.com/emilk/egui/pull/999)).\n* Added `Frame::alloc_texture/free_texture` to replace `tex_allocator` ([#999](https://github.com/emilk/egui/pull/999)).\n\n#### Web\n* Fixed [dark rendering in WebKitGTK](https://github.com/emilk/egui/issues/794) ([#888](https://github.com/emilk/egui/pull/888/)).\n* Added feature `glow` to switch to a [`glow`](https://github.com/grovesNL/glow) based painter ([#868](https://github.com/emilk/egui/pull/868)).\n\n\n## 0.15.0 - 2021-10-24\n* `Frame` now provides `set_window_title` to set window title dynamically ([#828](https://github.com/emilk/egui/pull/828)).\n* `Frame` now provides `set_decorations` to set whether to show window decorations.\n* Remove \"http\" feature (use https://github.com/emilk/ehttp instead!).\n* Added `App::persist_native_window` and `App::persist_egui_memory` to control what gets persisted.\n\n#### Desktop/Native\n* Increase native scroll speed.\n* Added new backend `egui_glow` as an alternative to `egui_glium`. Enable with `default-features = false, features = [\"default_fonts\", \"egui_glow\"]`.\n\n#### Web\n* Implement `eframe::NativeTexture` trait for the WebGL painter.\n* Deprecate `Painter::register_webgl_texture.\n* Fixed multiline paste.\n* Fixed painting with non-opaque backgrounds.\n* Improve text input on mobile and for IME.\n\n\n## 0.14.0 - 2021-08-24\n* Added dragging and dropping files into egui.\n* Improve http fetch API.\n* `run_native` now returns when the app is closed.\n* Web: Made text thicker and less pixelated.\n\n\n## 0.13.1 - 2021-06-24\n* Fixed `http` feature flag and docs\n\n\n## 0.13.0 - 2021-06-24\n* `App::setup` now takes a `Frame` and `Storage` by argument.\n* `App::load` has been removed. Implement `App::setup` instead.\n* Web: Default to light visuals unless the system reports a preference for dark mode.\n* Web: Improve alpha blending, making fonts look much better (especially in light mode)\n* Web: Fix double-paste bug\n\n\n## 0.12.0 - 2021-05-10\n* Moved options out of `trait App` into new `NativeOptions`.\n* Added option for `always_on_top`.\n* Web: Scroll faster when scrolling with mouse wheel.\n\n\n## 0.11.0 - 2021-04-05\n* You can now turn your window transparent with the `App::transparent` option.\n* You can now disable window decorations with the `App::decorated` option.\n* Web: [Fix mobile and IME text input](https://github.com/emilk/egui/pull/253)\n* Web: Hold down a modifier key when clicking a link to open it in a new tab.\n\nContributors: [n2](https://github.com/n2)\n\n\n## 0.10.0 - 2021-02-28\n* [You can now set your own app icons](https://github.com/emilk/egui/pull/193).\n* You can control the initial size of the native window with `App::initial_window_size`.\n* You can control the maximum egui web canvas size with `App::max_size_points`.\n* `Frame::tex_allocator()` no longer returns an `Option` (there is always a texture allocator).\n\n\n## 0.9.0 - 2021-02-07\n* [Added support for HTTP body](https://github.com/emilk/egui/pull/139).\n* Web: Right-clicks will no longer open browser context menu.\n* Web: Fix a bug where one couldn't select items in a combo box on a touch screen.\n\n\n## 0.8.0 - 2021-01-17\n* Simplify `TextureAllocator` interface.\n* WebGL2 is now supported, with improved texture sampler. WebGL1 will be used as a fallback.\n* Web: Slightly improved alpha-blending (work-around for non-existing linear-space blending).\n* Web: Call `prevent_default` for arrow keys when entering text\n\n\n## 0.7.0 - 2021-01-04\n* Initial release of `eframe`\n"
  },
  {
    "path": "crates/eframe/Cargo.toml",
    "content": "[package]\nname = \"eframe\"\nversion.workspace = true\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\ndescription = \"egui framework - write GUI apps that compiles to web and/or natively\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui/tree/main/crates/eframe\"\nlicense.workspace = true\nreadme = \"README.md\"\nrepository = \"https://github.com/emilk/egui/tree/main/crates/eframe\"\ncategories = [\"gui\", \"game-development\"]\nkeywords = [\"egui\", \"gui\", \"gamedev\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"Cargo.toml\", \"data/icon.png\"]\n\n[package.metadata.docs.rs]\nall-features = true\ntargets = [\"x86_64-unknown-linux-gnu\", \"wasm32-unknown-unknown\"]\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[lints]\nworkspace = true\n\n[lib]\n\n\n[features]\ndefault = [\n  \"accesskit\",\n  \"default_fonts\",\n  \"wayland\", # Required for Linux support (including CI!)\n  \"web_screen_reader\",\n  \"wgpu\",\n  \"winit/default\",\n  \"x11\",\n]\n\n## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).\naccesskit = [\"egui-winit/accesskit\"]\n\n# Allow crates to choose an android-activity backend via Winit\n# - It's important that most applications should not have to depend on android-activity directly, and can\n#   rely on Winit to pull in a suitable version (unlike most Rust crates, any version conflicts won't link)\n# - It's also important that we don't impose an android-activity backend by taking this choice away from applications.\n\n## Enable the `game-activity` backend via `egui-winit` on Android\nandroid-game-activity = [\"egui-winit/android-game-activity\"]\n## Enable the `native-activity` backend via `egui-winit` on Android\nandroid-native-activity = [\"egui-winit/android-native-activity\"]\n\n## If set, egui will use `include_bytes!` to bundle some fonts.\n## If you plan on specifying your own fonts you may disable this feature.\ndefault_fonts = [\"egui/default_fonts\"]\n\n## Enable [`glow`](https://github.com/grovesNL/glow) for painting, via [`egui_glow`](https://github.com/emilk/egui/tree/main/crates/egui_glow).\n##\n## There is generally no need to enable both the `wgpu` and `glow` features,\n## but if you do you can pick the renderer to use with [`NativeOptions::renderer`]\n## and `WebOptions::renderer`.\nglow = [\"dep:egui_glow\", \"dep:glow\", \"dep:glutin-winit\", \"dep:glutin\"]\n\n## Enable saving app state to disk.\npersistence = [\"dep:home\", \"egui-winit/serde\", \"egui/persistence\", \"ron\", \"serde\"]\n\n## Enables wayland support and fixes clipboard issue.\n##\n## If you are compiling for Linux (or want to test on a CI system using Linux), you should enable this feature.\nwayland = [\n  \"egui-winit/wayland\",\n  \"egui-wgpu?/wayland\",\n  \"egui_glow?/wayland\",\n  \"glutin?/wayland\",\n  \"glutin-winit?/wayland\",\n]\n\n## Enable screen reader support (requires `ctx.options_mut(|o| o.screen_reader = true);`) on web.\n##\n## For other platforms, use the `accesskit` feature instead.\nweb_screen_reader = [\"web-sys/SpeechSynthesis\", \"web-sys/SpeechSynthesisUtterance\"]\n\n## Enable [`wgpu`](https://docs.rs/wgpu) for painting (via [`egui-wgpu`](https://github.com/emilk/egui/tree/main/crates/egui-wgpu)).\n##\n## There is generally no need to enable both the `wgpu` and `glow` features,\n## but if you do you can pick the renderer to use with [`NativeOptions::renderer`]\n## and `WebOptions::renderer`.\n##\n## Switching from `wgpu (the default)` to `glow` can significantly reduce your binary size\n## (including the .wasm of a web app).\n## See <https://github.com/emilk/egui/issues/5889> for more details.\n##\n## By default, eframe will prefer WebGPU over WebGL, but\n## you can configure this at run-time with [`NativeOptions::wgpu_options`].\nwgpu = [\"wgpu_no_default_features\", \"egui-wgpu/default\"]\n\n## This is exactly like the `wgpu` feature, but does NOT enable the default features of `wgpu` and `egui-wgpu`.\n##\n## This means that no `wgpu` backends are enabled. You will need to enable them yourself, e.g. like this:\n##\n## ```toml\n## wgpu = { version = \"*\", features = [\"dx12\", \"metal\", \"webgl\"] }\n## ```\nwgpu_no_default_features = [\"dep:wgpu\", \"dep:egui-wgpu\", \"dep:pollster\"]\n\n## Enables compiling for x11.\nx11 = [\n  \"egui-winit/x11\",\n  \"egui-wgpu?/x11\",\n  \"egui_glow?/x11\",\n  \"glutin?/x11\",\n  \"glutin?/glx\",\n  \"glutin-winit?/x11\",\n  \"glutin-winit?/glx\",\n]\n\n## If set, eframe will look for the env-var `EFRAME_SCREENSHOT_TO` and write a screenshot to that location, and then quit.\n## This is used to generate images for examples.\n__screenshot = []\n\n[dependencies]\negui = { workspace = true, default-features = false, features = [\"bytemuck\"] }\n\nahash.workspace = true\ndocument-features.workspace = true\nlog.workspace = true\nparking_lot.workspace = true\nprofiling.workspace = true\nraw-window-handle.workspace = true\nstatic_assertions.workspace = true\nweb-time.workspace = true\n\n# Optional dependencies\n\negui_glow = { workspace = true, optional = true, default-features = false }\nglow = { workspace = true, optional = true }\nron = { workspace = true, optional = true, features = [\"integer128\"] }\nserde = { workspace = true, optional = true }\n\n# -------------------------------------------\n# native:\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\negui-winit = { workspace = true, default-features = false, features = [\"clipboard\", \"links\"] }\nimage = { workspace = true, features = [\"png\"] } # Needed for app icon\nwinit = { workspace = true, default-features = false, features = [\"rwh_06\"] }\n\n# optional native:\negui-wgpu = { workspace = true, optional = true, features = [\n  \"winit\",\n  \"capture\",\n] } # if wgpu is used, use it with winit\npollster = { workspace = true, optional = true } # needed for wgpu\n\nglutin = { workspace = true, optional = true, default-features = false, features = [\"egl\", \"wgl\"] }\nglutin-winit = { workspace = true, optional = true, default-features = false, features = [\n  \"egl\",\n  \"wgl\",\n] }\nhome = { workspace = true, optional = true }\nwgpu = { workspace = true, optional = true }\n\n# mac:\n[target.'cfg(any(target_os = \"macos\"))'.dependencies]\nobjc2.workspace = true\nobjc2-foundation = { workspace = true, default-features = false, features = [\n  \"std\",\n  \"block2\",\n  \"NSData\",\n  \"NSString\",\n] }\nobjc2-app-kit = { workspace = true, default-features = false, features = [\n  \"std\",\n  \"NSApplication\",\n  \"NSImage\",\n  \"NSMenu\",\n  \"NSMenuItem\",\n  \"NSResponder\",\n] }\n\n# windows:\n[target.'cfg(any(target_os = \"windows\"))'.dependencies]\nwindows-sys = { workspace = true, features = [\n  \"Win32_Foundation\",\n  \"Win32_System_Com\",\n  \"Win32_UI_Input_KeyboardAndMouse\",\n  \"Win32_UI_Shell\",\n  \"Win32_UI_WindowsAndMessaging\",\n] }\n\n# -------------------------------------------\n# web:\n[target.'cfg(target_arch = \"wasm32\")'.dependencies]\nbytemuck.workspace = true\nimage = { workspace = true, features = [\"png\"] } # For copying images\njs-sys.workspace = true\npercent-encoding.workspace = true\nwasm-bindgen.workspace = true\nwasm-bindgen-futures.workspace = true\nweb-sys = { workspace = true, features = [\n  \"AddEventListenerOptions\",\n  \"BinaryType\",\n  \"Blob\",\n  \"BlobPropertyBag\",\n  \"Clipboard\",\n  \"ClipboardEvent\",\n  \"ClipboardItem\",\n  \"CompositionEvent\",\n  \"console\",\n  \"CssStyleDeclaration\",\n  \"DataTransfer\",\n  \"DataTransferItem\",\n  \"DataTransferItemList\",\n  \"Document\",\n  \"DomRect\",\n  \"DragEvent\",\n  \"Element\",\n  \"Event\",\n  \"EventListener\",\n  \"EventTarget\",\n  \"ExtSRgb\",\n  \"File\",\n  \"FileList\",\n  \"FocusEvent\",\n  \"HtmlCanvasElement\",\n  \"HtmlElement\",\n  \"HtmlInputElement\",\n  \"InputEvent\",\n  \"KeyboardEvent\",\n  \"Location\",\n  \"MediaQueryList\",\n  \"MediaQueryListEvent\",\n  \"MouseEvent\",\n  \"Navigator\",\n  \"Node\",\n  \"NodeList\",\n  \"Performance\",\n  \"ResizeObserver\",\n  \"ResizeObserverBoxOptions\",\n  \"ResizeObserverEntry\",\n  \"ResizeObserverOptions\",\n  \"ResizeObserverSize\",\n  \"ShadowRoot\",\n  \"Storage\",\n  \"Touch\",\n  \"TouchEvent\",\n  \"PointerEvent\",\n  \"TouchList\",\n  \"WebGl2RenderingContext\",\n  \"WebglDebugRendererInfo\",\n  \"WebGlRenderingContext\",\n  \"WheelEvent\",\n  \"Window\",\n] }\n\n# optional web:\negui-wgpu = { workspace = true, optional = true, features = [\n  # if wgpu is used, use it without (!) winit:\n  \"capture\",\n] }\nwgpu = { workspace = true, optional = true }\n\n# Native dev dependencies for testing\n[target.'cfg(not(target_arch = \"wasm32\"))'.dev-dependencies]\ndirectories.workspace = true\n"
  },
  {
    "path": "crates/eframe/README.md",
    "content": "# eframe: the [`egui`](https://github.com/emilk/egui) framework\n\n[![Latest version](https://img.shields.io/crates/v/eframe.svg)](https://crates.io/crates/eframe)\n[![Documentation](https://docs.rs/eframe/badge.svg)](https://docs.rs/eframe)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\n`eframe` is the official framework library for writing apps using [`egui`](https://github.com/emilk/egui). The app can be compiled both to run natively (for Linux, Mac, Windows, and Android) or as a web app (using [Wasm](https://en.wikipedia.org/wiki/WebAssembly)).\n\nTo get started, see the [examples](https://github.com/emilk/egui/tree/main/examples).\nTo learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!\n\nThere is also a tutorial video at <https://www.youtube.com/watch?v=NtUkr_z7l84>.\n\nFor how to use `egui`, see [the egui docs](https://docs.rs/egui).\n\n---\n\n`eframe` defaults to using [wgpu](https://crates.io/crates/wgpu) for rendering (with an option to change to [glow](https://crates.io/crates/glow)), and on native it uses [`egui-winit`](https://github.com/emilk/egui/tree/main/crates/egui-winit).\n\nTo use on Linux, first run:\n\n```\nsudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev\n```\n\nYou need to either use `edition = \"2024\"`, or set `resolver = \"2\"` in the `[workspace]` section of your to-level `Cargo.toml`. See [this link](https://doc.rust-lang.org/edition-guide/rust-2021/default-cargo-resolver.html) for more info.\n\nYou can opt-in to the using [`egui_glow`](https://github.com/emilk/egui/tree/main/crates/egui_glow) for rendering by enabling the `glow` feature and setting `NativeOptions::renderer` to `Renderer::Glow`.\n\n## Alternatives\n`eframe` is not the only way to write an app using `egui`! You can also try [`egui-miniquad`](https://github.com/not-fl3/egui-miniquad), [`bevy_egui`](https://github.com/mvlabat/bevy_egui), [`egui_sdl2_gl`](https://github.com/ArjunNair/egui_sdl2_gl), and others.\n\nYou can also use `egui_glow` and [`winit`](https://github.com/rust-windowing/winit) to build your own app as demonstrated in <https://github.com/emilk/egui/blob/main/crates/egui_glow/examples/pure_glow.rs>.\n\n\n## Limitations when running egui on the web\n`eframe` and egui compiles to Wasm using either WebGPU (when available) or WebGL2 for rendering, and almost nothing else from the web tech stack. This has some benefits, but also produces some challenges and serious downsides.\n\n* Rendering: Getting pixel-perfect rendering right on the web is very difficult.\n* Search: you cannot search an egui web page like you would a normal web page.\n* Bringing up an on-screen keyboard on mobile: there is no JS function to do this, so `eframe` fakes it by adding some invisible DOM elements. It doesn't always work.\n* Mobile text editing is not as good as for a normal web app.\n* No integration with browser settings for colors and fonts.\n* Accessibility: There is an experimental screen reader for `eframe`, but it has to be enabled explicitly. There is no JS function to ask \"Does the user want a screen reader?\" (and there should probably not be such a function, due to user tracking/integrity concerns). `egui` supports [AccessKit](https://github.com/AccessKit/accesskit), but as of early 2024, AccessKit lacks a Web backend.\n\nIn many ways, `eframe` is trying to make the browser do something it wasn't designed to do (though there are many things browser vendors could do to improve how well libraries like egui work).\n\nThe suggested use for `eframe` are for web apps where performance and responsiveness are more important than accessibility and mobile text editing.\n\n\n## Companion crates\nNot all rust crates work when compiled to Wasm, but here are some useful crates have been designed to work well both natively and as Wasm:\n\n* Audio: [`cpal`](https://github.com/RustAudio/cpal)\n* File dialogs: [rfd](https://docs.rs/rfd/latest/rfd/)\n* HTTP client: [`ehttp`](https://github.com/emilk/ehttp) and [`reqwest`](https://github.com/seanmonstar/reqwest)\n* Time: [`chrono`](https://github.com/chronotope/chrono)\n* WebSockets: [`ewebsock`](https://github.com/rerun-io/ewebsock)\n\n\n## Name\nThe _frame_ in `eframe` stands both for the frame in which your `egui` app resides and also for \"framework\" (`eframe` is a framework, `egui` is a library).\n"
  },
  {
    "path": "crates/eframe/src/epi.rs",
    "content": "//! Platform-agnostic interface for writing apps using [`egui`] (epi = egui programming interface).\n//!\n//! `epi` provides interfaces for window management and serialization.\n//!\n//! Start by looking at the [`App`] trait, and implement [`App::update`].\n\n#![warn(missing_docs)] // Let's keep `epi` well-documented.\n\n#[cfg(target_arch = \"wasm32\")]\nuse std::any::Any;\n\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\npub use crate::native::winit_integration::UserEvent;\n\n#[cfg(not(target_arch = \"wasm32\"))]\nuse raw_window_handle::{\n    DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle,\n    RawWindowHandle, WindowHandle,\n};\n#[cfg(not(target_arch = \"wasm32\"))]\nuse static_assertions::assert_not_impl_any;\n\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\npub use winit::{event_loop::EventLoopBuilder, window::WindowAttributes};\n\n/// Hook into the building of an event loop before it is run\n///\n/// You can configure any platform specific details required on top of the default configuration\n/// done by `EFrame`.\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\npub type EventLoopBuilderHook = Box<dyn FnOnce(&mut EventLoopBuilder<UserEvent>)>;\n\n/// Hook into the building of a the native window.\n///\n/// You can configure any platform specific details required on top of the default configuration\n/// done by `eframe`.\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\npub type WindowBuilderHook = Box<dyn FnOnce(egui::ViewportBuilder) -> egui::ViewportBuilder>;\n\ntype DynError = Box<dyn std::error::Error + Send + Sync>;\n\n/// This is how your app is created.\n///\n/// You can use the [`CreationContext`] to setup egui, restore state, setup OpenGL things, etc.\npub type AppCreator<'app> =\n    Box<dyn 'app + FnOnce(&CreationContext<'_>) -> Result<Box<dyn 'app + App>, DynError>>;\n\n/// Data that is passed to [`AppCreator`] that can be used to setup and initialize your app.\npub struct CreationContext<'s> {\n    /// The egui Context.\n    ///\n    /// You can use this to customize the look of egui, e.g to call [`egui::Context::set_fonts`],\n    /// [`egui::Context::set_visuals_of`] etc.\n    pub egui_ctx: egui::Context,\n\n    /// Information about the surrounding environment.\n    pub integration_info: IntegrationInfo,\n\n    /// You can use the storage to restore app state(requires the \"persistence\" feature).\n    pub storage: Option<&'s dyn Storage>,\n\n    /// The [`glow::Context`] allows you to initialize OpenGL resources (e.g. shaders) that\n    /// you might want to use later from a [`egui::PaintCallback`].\n    ///\n    /// Only available when compiling with the `glow` feature and using [`Renderer::Glow`].\n    #[cfg(feature = \"glow\")]\n    pub gl: Option<std::sync::Arc<glow::Context>>,\n\n    /// The `get_proc_address` wrapper of underlying GL context\n    #[cfg(feature = \"glow\")]\n    pub get_proc_address:\n        Option<std::sync::Arc<dyn Fn(&std::ffi::CStr) -> *const std::ffi::c_void + Send + Sync>>,\n\n    /// The underlying WGPU render state.\n    ///\n    /// Only available when compiling with the `wgpu` feature and using [`Renderer::Wgpu`].\n    ///\n    /// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.\n    #[cfg(feature = \"wgpu_no_default_features\")]\n    pub wgpu_render_state: Option<egui_wgpu::RenderState>,\n\n    /// Raw platform window handle\n    #[cfg(not(target_arch = \"wasm32\"))]\n    pub(crate) raw_window_handle: Result<RawWindowHandle, HandleError>,\n\n    /// Raw platform display handle for window\n    #[cfg(not(target_arch = \"wasm32\"))]\n    pub(crate) raw_display_handle: Result<RawDisplayHandle, HandleError>,\n}\n\n#[expect(unsafe_code)]\n#[cfg(not(target_arch = \"wasm32\"))]\nimpl HasWindowHandle for CreationContext<'_> {\n    fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {\n        // Safety: the lifetime is correct.\n        unsafe { Ok(WindowHandle::borrow_raw(self.raw_window_handle.clone()?)) }\n    }\n}\n\n#[expect(unsafe_code)]\n#[cfg(not(target_arch = \"wasm32\"))]\nimpl HasDisplayHandle for CreationContext<'_> {\n    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {\n        // Safety: the lifetime is correct.\n        unsafe { Ok(DisplayHandle::borrow_raw(self.raw_display_handle.clone()?)) }\n    }\n}\n\nimpl CreationContext<'_> {\n    /// Create a new empty [CreationContext] for testing [App]s in kittest.\n    #[doc(hidden)]\n    pub fn _new_kittest(egui_ctx: egui::Context) -> Self {\n        Self {\n            egui_ctx,\n            integration_info: IntegrationInfo::mock(),\n            storage: None,\n            #[cfg(feature = \"glow\")]\n            gl: None,\n            #[cfg(feature = \"glow\")]\n            get_proc_address: None,\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            wgpu_render_state: None,\n            #[cfg(not(target_arch = \"wasm32\"))]\n            raw_window_handle: Err(HandleError::NotSupported),\n            #[cfg(not(target_arch = \"wasm32\"))]\n            raw_display_handle: Err(HandleError::NotSupported),\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Implement this trait to write apps that can be compiled for both web/wasm and desktop/native using [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe).\npub trait App {\n    /// Called once before each call to [`Self::ui`],\n    /// and additionally also called when the UI is hidden, but [`egui::Context::request_repaint`] was called.\n    ///\n    /// You may NOT show any ui or do any painting during the call to [`Self::logic`].\n    ///\n    /// The [`egui::Context`] can be cloned and saved if you like.\n    ///\n    /// To force another call to [`Self::logic`], call [`egui::Context::request_repaint`] at any time (e.g. from another thread).\n    fn logic(&mut self, ctx: &egui::Context, frame: &mut Frame) {\n        _ = (ctx, frame);\n    }\n\n    /// Called each time the UI needs repainting, which may be many times per second.\n    ///\n    /// The given [`egui::Ui`] has no margin or background color.\n    /// You can wrap your UI code in [`egui::CentralPanel`] or a [`egui::Frame::central_panel`] to remedy this.\n    ///\n    /// The [`egui::Ui::ctx`] can be cloned and saved if you like.\n    /// To force a repaint, call [`egui::Context::request_repaint`] at any time (e.g. from another thread).\n    ///\n    /// This is called for the root viewport ([`egui::ViewportId::ROOT`]).\n    /// Use [`egui::Context::show_viewport_deferred`] to spawn additional viewports (windows).\n    /// (A \"viewport\" in egui means an native OS window).\n    fn ui(&mut self, ui: &mut egui::Ui, frame: &mut Frame);\n\n    /// Called each time the UI needs repainting, which may be many times per second.\n    ///\n    /// Put your widgets into a [`egui::Panel`], [`egui::CentralPanel`], [`egui::Window`] or [`egui::Area`].\n    ///\n    /// The [`egui::Context`] can be cloned and saved if you like.\n    ///\n    /// To force a repaint, call [`egui::Context::request_repaint`] at any time (e.g. from another thread).\n    ///\n    /// This is called for the root viewport ([`egui::ViewportId::ROOT`]).\n    /// Use [`egui::Context::show_viewport_deferred`] to spawn additional viewports (windows).\n    /// (A \"viewport\" in egui means an native OS window).\n    #[deprecated = \"Use Self::ui instead\"]\n    fn update(&mut self, ctx: &egui::Context, frame: &mut Frame) {\n        _ = (ctx, frame);\n    }\n\n    /// Get a handle to the app.\n    ///\n    /// Can be used from web to interact or other external context.\n    ///\n    /// You need to implement this if you want to be able to access the application from JS using [`crate::WebRunner::app_mut`].\n    ///\n    /// This is needed because downcasting `Box<dyn App>` -> `Box<dyn Any>` to get &`ConcreteApp` is not simple in current rust.\n    ///\n    /// Just copy-paste this as your implementation:\n    /// ```ignore\n    /// #[cfg(target_arch = \"wasm32\")]\n    /// fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {\n    ///     Some(&mut *self)\n    /// }\n    /// ```\n    #[cfg(target_arch = \"wasm32\")]\n    fn as_any_mut(&mut self) -> Option<&mut dyn Any> {\n        None\n    }\n\n    /// Called on shutdown, and perhaps at regular intervals. Allows you to save state.\n    ///\n    /// Only called when the \"persistence\" feature is enabled.\n    ///\n    /// On web the state is stored to \"Local Storage\".\n    ///\n    /// On native the path is picked using [`crate::storage_dir`].\n    /// The path can be customized via [`NativeOptions::persistence_path`].\n    fn save(&mut self, _storage: &mut dyn Storage) {}\n\n    /// Called once on shutdown, after [`Self::save`].\n    ///\n    /// If you need to abort an exit check `ctx.input(|i| i.viewport().close_requested())`\n    /// and respond with [`egui::ViewportCommand::CancelClose`].\n    ///\n    /// To get a [`glow`] context you need to compile with the `glow` feature flag,\n    /// and run eframe with the glow backend.\n    #[cfg(feature = \"glow\")]\n    fn on_exit(&mut self, _gl: Option<&glow::Context>) {}\n\n    /// Called once on shutdown, after [`Self::save`].\n    ///\n    /// If you need to abort an exit use [`Self::on_close_event`].\n    #[cfg(not(feature = \"glow\"))]\n    fn on_exit(&mut self) {}\n\n    // ---------\n    // Settings:\n\n    /// Time between automatic calls to [`Self::save`]\n    fn auto_save_interval(&self) -> std::time::Duration {\n        std::time::Duration::from_secs(30)\n    }\n\n    /// Background color values for the app, e.g. what is sent to `gl.clearColor`.\n    ///\n    /// This is the background of your windows if you don't set a central panel.\n    ///\n    /// ATTENTION:\n    /// Since these float values go to the render as-is, any color space conversion as done\n    /// e.g. by converting from [`egui::Color32`] to [`egui::Rgba`] may cause incorrect results.\n    /// egui recommends that rendering backends use a normal \"gamma-space\" (non-sRGB-aware) blending,\n    ///  which means the values you return here should also be in `sRGB` gamma-space in the 0-1 range.\n    /// You can use [`egui::Color32::to_normalized_gamma_f32`] for this.\n    fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] {\n        // NOTE: a bright gray makes the shadows of the windows look weird.\n        // We use a bit of transparency so that if the user switches on the\n        // `transparent()` option they get immediate results.\n        egui::Color32::from_rgba_unmultiplied(12, 12, 12, 180).to_normalized_gamma_f32()\n\n        // _visuals.window_fill() would also be a natural choice\n    }\n\n    /// Controls whether or not the egui memory (window positions etc) will be\n    /// persisted (only if the \"persistence\" feature is enabled).\n    fn persist_egui_memory(&self) -> bool {\n        true\n    }\n\n    /// A hook for manipulating or filtering raw input before it is processed by [`Self::update`].\n    ///\n    /// This function provides a way to modify or filter input events before they are processed by egui.\n    ///\n    /// It can be used to prevent specific keyboard shortcuts or mouse events from being processed by egui.\n    ///\n    /// Additionally, it can be used to inject custom keyboard or mouse events into the input stream, which can be useful for implementing features like a virtual keyboard.\n    ///\n    /// # Arguments\n    ///\n    /// * `_ctx` - The context of the egui, which provides access to the current state of the egui.\n    /// * `_raw_input` - The raw input events that are about to be processed. This can be modified to change the input that egui processes.\n    ///\n    /// # Note\n    ///\n    /// This function does not return a value. Any changes to the input should be made directly to `_raw_input`.\n    fn raw_input_hook(&mut self, _ctx: &egui::Context, _raw_input: &mut egui::RawInput) {}\n}\n\n/// Selects the level of hardware graphics acceleration.\n#[cfg(not(target_arch = \"wasm32\"))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\npub enum HardwareAcceleration {\n    /// Require graphics acceleration.\n    Required,\n\n    /// Prefer graphics acceleration, but fall back to software.\n    Preferred,\n\n    /// Do NOT use graphics acceleration.\n    ///\n    /// On some platforms (macOS) this is ignored and treated the same as [`Self::Preferred`].\n    Off,\n}\n\n/// Options controlling the behavior of a native window.\n///\n/// Additional windows can be opened using (egui viewports)[`egui::viewport`].\n///\n/// Set the window title and size using [`Self::viewport`].\n///\n/// ### Application id\n/// [`egui::ViewportBuilder::with_app_id`] is used for determining the folder to persist the app to.\n///\n/// On native the path is picked using [`crate::storage_dir`].\n///\n/// If you don't set an app id, the title argument to [`crate::run_native`]\n/// will be used as app id instead.\n#[cfg(not(target_arch = \"wasm32\"))]\npub struct NativeOptions {\n    /// Controls the native window of the root viewport.\n    ///\n    /// This is where you set things like window title and size.\n    ///\n    /// If you don't set an icon, a default egui icon will be used.\n    /// To avoid this, set the icon to [`egui::IconData::default`].\n    pub viewport: egui::ViewportBuilder,\n\n    /// Turn on vertical syncing, limiting the FPS to the display refresh rate.\n    ///\n    /// The default is `true`.\n    pub vsync: bool,\n\n    /// Set the level of the multisampling anti-aliasing (MSAA).\n    ///\n    /// Must be a power-of-two. Higher = more smooth 3D.\n    ///\n    /// A value of `0` turns it off (default).\n    ///\n    /// `egui` already performs anti-aliasing via \"feathering\"\n    /// (controlled by [`egui::epaint::TessellationOptions`]),\n    /// but if you are embedding 3D in egui you may want to turn on multisampling.\n    pub multisampling: u16,\n\n    /// Sets the number of bits in the depth buffer.\n    ///\n    /// `egui` doesn't need the depth buffer, so the default value is 0.\n    pub depth_buffer: u8,\n\n    /// Sets the number of bits in the stencil buffer.\n    ///\n    /// `egui` doesn't need the stencil buffer, so the default value is 0.\n    pub stencil_buffer: u8,\n\n    /// Specify whether or not hardware acceleration is preferred, required, or not.\n    ///\n    /// Default: [`HardwareAcceleration::Preferred`].\n    pub hardware_acceleration: HardwareAcceleration,\n\n    /// What rendering backend to use.\n    #[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n    pub renderer: Renderer,\n\n    /// This controls what happens when you close the main eframe window.\n    ///\n    /// If `true`, execution will continue after the eframe window is closed.\n    /// If `false`, the app will close once the eframe window is closed.\n    ///\n    /// This is `true` by default, and the `false` option is only there\n    /// so we can revert if we find any bugs.\n    ///\n    /// This feature was introduced in <https://github.com/emilk/egui/pull/1889>.\n    ///\n    /// When `true`, [`winit::platform::run_on_demand::EventLoopExtRunOnDemand`] is used.\n    /// When `false`, [`winit::event_loop::EventLoop::run`] is used.\n    pub run_and_return: bool,\n\n    /// Hook into the building of an event loop before it is run.\n    ///\n    /// Specify a callback here in case you need to make platform specific changes to the\n    /// event loop before it is run.\n    ///\n    /// Note: A [`NativeOptions`] clone will not include any `event_loop_builder` hook.\n    #[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n    pub event_loop_builder: Option<EventLoopBuilderHook>,\n\n    /// Hook into the building of a window.\n    ///\n    /// Specify a callback here in case you need to make platform specific changes to the\n    /// window appearance.\n    ///\n    /// Note: A [`NativeOptions`] clone will not include any `window_builder` hook.\n    #[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n    pub window_builder: Option<WindowBuilderHook>,\n\n    #[cfg(feature = \"glow\")]\n    /// Needed for cross compiling for VirtualBox VMSVGA driver with OpenGL ES 2.0 and OpenGL 2.1 which doesn't support SRGB texture.\n    /// See <https://github.com/emilk/egui/pull/1993>.\n    ///\n    /// For OpenGL ES 2.0: set this to [`egui_glow::ShaderVersion::Es100`] to solve blank texture problem (by using the \"fallback shader\").\n    pub shader_version: Option<egui_glow::ShaderVersion>,\n\n    /// On desktop: make the window position to be centered at initialization.\n    ///\n    /// Platform specific:\n    ///\n    /// Wayland desktop currently not supported.\n    pub centered: bool,\n\n    /// Configures wgpu instance/device/adapter/surface creation and renderloop.\n    #[cfg(feature = \"wgpu_no_default_features\")]\n    pub wgpu_options: egui_wgpu::WgpuConfiguration,\n\n    /// Controls whether or not the native window position and size will be\n    /// persisted (only if the \"persistence\" feature is enabled).\n    pub persist_window: bool,\n\n    /// The folder where `eframe` will store the app state. If not set, eframe will use a default\n    /// data storage path for each target system.\n    pub persistence_path: Option<std::path::PathBuf>,\n\n    /// Controls whether to apply dithering to minimize banding artifacts.\n    ///\n    /// Dithering assumes an sRGB output and thus will apply noise to any input value that lies between\n    /// two 8bit values after applying the sRGB OETF function, i.e. if it's not a whole 8bit value in \"gamma space\".\n    /// This means that only inputs from texture interpolation and vertex colors should be affected in practice.\n    ///\n    /// Defaults to true.\n    pub dithering: bool,\n\n    /// Android application for `winit`'s event loop.\n    ///\n    /// This value is required on Android to correctly create the event loop. See\n    /// [`EventLoopBuilder::build`] and [`with_android_app`] for details.\n    ///\n    /// [`EventLoopBuilder::build`]: winit::event_loop::EventLoopBuilder::build\n    /// [`with_android_app`]: winit::platform::android::EventLoopBuilderExtAndroid::with_android_app\n    #[cfg(target_os = \"android\")]\n    pub android_app: Option<winit::platform::android::activity::AndroidApp>,\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\nimpl Clone for NativeOptions {\n    fn clone(&self) -> Self {\n        Self {\n            viewport: self.viewport.clone(),\n\n            #[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n            event_loop_builder: None, // Skip any builder callbacks if cloning\n\n            #[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n            window_builder: None, // Skip any builder callbacks if cloning\n\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            wgpu_options: self.wgpu_options.clone(),\n\n            persistence_path: self.persistence_path.clone(),\n\n            #[cfg(target_os = \"android\")]\n            android_app: self.android_app.clone(),\n\n            ..*self\n        }\n    }\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\nimpl Default for NativeOptions {\n    fn default() -> Self {\n        Self {\n            viewport: Default::default(),\n\n            vsync: true,\n            multisampling: 0,\n            depth_buffer: 0,\n            stencil_buffer: 0,\n            hardware_acceleration: HardwareAcceleration::Preferred,\n\n            #[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n            renderer: Renderer::default(),\n\n            run_and_return: true,\n\n            #[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n            event_loop_builder: None,\n\n            #[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n            window_builder: None,\n\n            #[cfg(feature = \"glow\")]\n            shader_version: None,\n\n            centered: false,\n\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            wgpu_options: egui_wgpu::WgpuConfiguration::default(),\n\n            persist_window: true,\n\n            persistence_path: None,\n\n            dithering: true,\n\n            #[cfg(target_os = \"android\")]\n            android_app: None,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Options when using `eframe` in a web page.\n#[cfg(target_arch = \"wasm32\")]\npub struct WebOptions {\n    /// What rendering backend to use.\n    #[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n    pub renderer: Renderer,\n\n    /// Sets the number of bits in the depth buffer.\n    ///\n    /// `egui` doesn't need the depth buffer, so the default value is 0.\n    /// Unused by webgl context as of writing.\n    pub depth_buffer: u8,\n\n    /// Which version of WebGL context to select\n    ///\n    /// Default: [`WebGlContextOption::BestFirst`].\n    #[cfg(feature = \"glow\")]\n    pub webgl_context_option: WebGlContextOption,\n\n    /// Configures wgpu instance/device/adapter/surface creation and renderloop.\n    #[cfg(feature = \"wgpu_no_default_features\")]\n    pub wgpu_options: egui_wgpu::WgpuConfiguration,\n\n    /// Controls whether to apply dithering to minimize banding artifacts.\n    ///\n    /// Dithering assumes an sRGB output and thus will apply noise to any input value that lies between\n    /// two 8bit values after applying the sRGB OETF function, i.e. if it's not a whole 8bit value in \"gamma space\".\n    /// This means that only inputs from texture interpolation and vertex colors should be affected in practice.\n    ///\n    /// Defaults to true.\n    pub dithering: bool,\n\n    /// If the web event corresponding to an egui event should be propagated\n    /// to the rest of the web page.\n    ///\n    /// The default is `true`, meaning\n    /// [`stopPropagation`](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation)\n    /// is called on every event, and the event is not propagated to the rest of the web page.\n    pub should_stop_propagation: Box<dyn Fn(&egui::Event) -> bool>,\n\n    /// Whether the web event corresponding to an egui event should have `prevent_default` called\n    /// on it or not.\n    ///\n    /// Defaults to true.\n    pub should_prevent_default: Box<dyn Fn(&egui::Event) -> bool>,\n\n    /// Maximum rate at which to repaint. This can be used to artificially reduce the repaint rate below\n    /// vsync in order to save resources.\n    pub max_fps: Option<u32>,\n}\n\n#[cfg(target_arch = \"wasm32\")]\nimpl Default for WebOptions {\n    fn default() -> Self {\n        Self {\n            #[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n            renderer: Renderer::default(),\n\n            depth_buffer: 0,\n\n            #[cfg(feature = \"glow\")]\n            webgl_context_option: WebGlContextOption::BestFirst,\n\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            wgpu_options: egui_wgpu::WgpuConfiguration::default(),\n\n            dithering: true,\n\n            should_stop_propagation: Box::new(|_| true),\n            should_prevent_default: Box::new(|_| true),\n\n            max_fps: None,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// WebGL Context options\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum WebGlContextOption {\n    /// Force Use WebGL1.\n    WebGl1,\n\n    /// Force use WebGL2.\n    WebGl2,\n\n    /// Use WebGL2 first.\n    BestFirst,\n\n    /// Use WebGL1 first\n    CompatibilityFirst,\n}\n\n// ----------------------------------------------------------------------------\n\n/// What rendering backend to use.\n///\n/// You need to enable the \"glow\" and \"wgpu\" features to have a choice.\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(rename_all = \"snake_case\"))]\npub enum Renderer {\n    /// Use [`egui_glow`] renderer for [`glow`](https://github.com/grovesNL/glow).\n    #[cfg(feature = \"glow\")]\n    Glow,\n\n    /// Use [`egui_wgpu`] renderer for [`wgpu`](https://github.com/gfx-rs/wgpu).\n    #[cfg(feature = \"wgpu_no_default_features\")]\n    Wgpu,\n}\n\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\nimpl Default for Renderer {\n    fn default() -> Self {\n        #[cfg(not(feature = \"glow\"))]\n        #[cfg(not(feature = \"wgpu_no_default_features\"))]\n        compile_error!(\n            \"eframe: you must enable at least one of the rendering backend features: 'glow' or 'wgpu'\"\n        );\n\n        #[cfg(feature = \"glow\")]\n        #[cfg(not(feature = \"wgpu_no_default_features\"))]\n        return Self::Glow;\n\n        #[cfg(not(feature = \"glow\"))]\n        #[cfg(feature = \"wgpu_no_default_features\")]\n        return Self::Wgpu;\n\n        // It's weird that the user has enabled both glow and wgpu,\n        // but let's pick the better of the two (wgpu):\n        #[cfg(feature = \"glow\")]\n        #[cfg(feature = \"wgpu_no_default_features\")]\n        return Self::Wgpu;\n    }\n}\n\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\nimpl std::fmt::Display for Renderer {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            #[cfg(feature = \"glow\")]\n            Self::Glow => \"glow\".fmt(f),\n\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            Self::Wgpu => \"wgpu\".fmt(f),\n        }\n    }\n}\n\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\nimpl std::str::FromStr for Renderer {\n    type Err = String;\n\n    fn from_str(name: &str) -> Result<Self, String> {\n        match name.to_lowercase().as_str() {\n            #[cfg(feature = \"glow\")]\n            \"glow\" => Ok(Self::Glow),\n\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            \"wgpu\" => Ok(Self::Wgpu),\n\n            _ => Err(format!(\n                \"eframe renderer {name:?} is not available. Make sure that the corresponding eframe feature is enabled.\"\n            )),\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Represents the surroundings of your app.\n///\n/// It provides methods to inspect the surroundings (are we on the web?),\n/// access to persistent storage, and access to the rendering backend.\npub struct Frame {\n    /// Information about the integration.\n    pub(crate) info: IntegrationInfo,\n\n    /// A place where you can store custom data in a way that persists when you restart the app.\n    pub(crate) storage: Option<Box<dyn Storage>>,\n\n    /// A reference to the underlying [`glow`] (OpenGL) context.\n    #[cfg(feature = \"glow\")]\n    pub(crate) gl: Option<std::sync::Arc<glow::Context>>,\n\n    /// Used to convert user custom [`glow::Texture`] to [`egui::TextureId`]\n    #[cfg(all(feature = \"glow\", not(target_arch = \"wasm32\")))]\n    pub(crate) glow_register_native_texture:\n        Option<Box<dyn FnMut(glow::Texture) -> egui::TextureId>>,\n\n    /// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.\n    #[cfg(feature = \"wgpu_no_default_features\")]\n    #[doc(hidden)]\n    pub wgpu_render_state: Option<egui_wgpu::RenderState>,\n\n    /// Raw platform window handle\n    #[cfg(not(target_arch = \"wasm32\"))]\n    pub(crate) raw_window_handle: Result<RawWindowHandle, HandleError>,\n\n    /// Raw platform display handle for window\n    #[cfg(not(target_arch = \"wasm32\"))]\n    pub(crate) raw_display_handle: Result<RawDisplayHandle, HandleError>,\n}\n\n// Implementing `Clone` would violate the guarantees of `HasWindowHandle` and `HasDisplayHandle`.\n#[cfg(not(target_arch = \"wasm32\"))]\nassert_not_impl_any!(Frame: Clone);\n\n#[expect(unsafe_code)]\n#[cfg(not(target_arch = \"wasm32\"))]\nimpl HasWindowHandle for Frame {\n    fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {\n        // Safety: the lifetime is correct.\n        unsafe { Ok(WindowHandle::borrow_raw(self.raw_window_handle.clone()?)) }\n    }\n}\n\n#[expect(unsafe_code)]\n#[cfg(not(target_arch = \"wasm32\"))]\nimpl HasDisplayHandle for Frame {\n    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {\n        // Safety: the lifetime is correct.\n        unsafe { Ok(DisplayHandle::borrow_raw(self.raw_display_handle.clone()?)) }\n    }\n}\n\nimpl Frame {\n    /// Create a new empty [Frame] for testing [App]s in kittest.\n    #[doc(hidden)]\n    pub fn _new_kittest() -> Self {\n        Self {\n            #[cfg(feature = \"glow\")]\n            gl: None,\n            #[cfg(all(feature = \"glow\", not(target_arch = \"wasm32\")))]\n            glow_register_native_texture: None,\n            info: IntegrationInfo::mock(),\n            #[cfg(not(target_arch = \"wasm32\"))]\n            raw_display_handle: Err(HandleError::NotSupported),\n            #[cfg(not(target_arch = \"wasm32\"))]\n            raw_window_handle: Err(HandleError::NotSupported),\n            storage: None,\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            wgpu_render_state: None,\n        }\n    }\n\n    /// True if you are in a web environment.\n    ///\n    /// Equivalent to `cfg!(target_arch = \"wasm32\")`\n    #[expect(clippy::unused_self)]\n    pub fn is_web(&self) -> bool {\n        cfg!(target_arch = \"wasm32\")\n    }\n\n    /// Information about the integration.\n    pub fn info(&self) -> &IntegrationInfo {\n        &self.info\n    }\n\n    /// A place where you can store custom data in a way that persists when you restart the app.\n    pub fn storage(&self) -> Option<&dyn Storage> {\n        self.storage.as_deref()\n    }\n\n    /// A place where you can store custom data in a way that persists when you restart the app.\n    pub fn storage_mut(&mut self) -> Option<&mut (dyn Storage + 'static)> {\n        self.storage.as_deref_mut()\n    }\n\n    /// A reference to the underlying [`glow`] (OpenGL) context.\n    ///\n    /// This can be used, for instance, to:\n    /// * Render things to offscreen buffers.\n    /// * Read the pixel buffer from the previous frame (`glow::Context::read_pixels`).\n    /// * Render things behind the egui windows.\n    ///\n    /// Note that all egui painting is deferred to after the call to [`App::update`]\n    /// ([`egui`] only collects [`egui::Shape`]s and then eframe paints them all in one go later on).\n    ///\n    /// To get a [`glow`] context you need to compile with the `glow` feature flag,\n    /// and run eframe using [`Renderer::Glow`].\n    #[cfg(feature = \"glow\")]\n    pub fn gl(&self) -> Option<&std::sync::Arc<glow::Context>> {\n        self.gl.as_ref()\n    }\n\n    /// Register your own [`glow::Texture`],\n    /// and then you can use the returned [`egui::TextureId`] to render your texture with [`egui`].\n    ///\n    /// This function will take the ownership of your [`glow::Texture`], so please do not delete your [`glow::Texture`] after registering.\n    #[cfg(all(feature = \"glow\", not(target_arch = \"wasm32\")))]\n    pub fn register_native_glow_texture(&mut self, native: glow::Texture) -> egui::TextureId {\n        #[expect(clippy::unwrap_used)]\n        self.glow_register_native_texture.as_mut().unwrap()(native)\n    }\n\n    /// The underlying WGPU render state.\n    ///\n    /// Only available when compiling with the `wgpu` feature and using [`Renderer::Wgpu`].\n    ///\n    /// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.\n    #[cfg(feature = \"wgpu_no_default_features\")]\n    pub fn wgpu_render_state(&self) -> Option<&egui_wgpu::RenderState> {\n        self.wgpu_render_state.as_ref()\n    }\n}\n\n/// Information about the web environment (if applicable).\n#[derive(Clone, Debug)]\n#[cfg(target_arch = \"wasm32\")]\npub struct WebInfo {\n    /// The browser user agent.\n    pub user_agent: String,\n\n    /// Information about the URL.\n    pub location: Location,\n}\n\n/// Information about the URL.\n///\n/// Everything has been percent decoded (`%20` -> ` ` etc).\n#[cfg(target_arch = \"wasm32\")]\n#[derive(Clone, Debug)]\npub struct Location {\n    /// The full URL (`location.href`) without the hash, percent-decoded.\n    ///\n    /// Example: `\"http://www.example.com:80/index.html?foo=bar\"`.\n    pub url: String,\n\n    /// `location.protocol`\n    ///\n    /// Example: `\"http:\"`.\n    pub protocol: String,\n\n    /// `location.host`\n    ///\n    /// Example: `\"example.com:80\"`.\n    pub host: String,\n\n    /// `location.hostname`\n    ///\n    /// Example: `\"example.com\"`.\n    pub hostname: String,\n\n    /// `location.port`\n    ///\n    /// Example: `\"80\"`.\n    pub port: String,\n\n    /// The \"#fragment\" part of \"www.example.com/index.html?query#fragment\".\n    ///\n    /// Note that the leading `#` is included in the string.\n    /// Also known as \"hash-link\" or \"anchor\".\n    pub hash: String,\n\n    /// The \"query\" part of \"www.example.com/index.html?query#fragment\".\n    ///\n    /// Note that the leading `?` is NOT included in the string.\n    ///\n    /// Use [`Self::query_map`] to get the parsed version of it.\n    pub query: String,\n\n    /// The parsed \"query\" part of \"www.example.com/index.html?query#fragment\".\n    ///\n    /// \"foo=hello&bar%20&foo=world\" is parsed as `{\"bar \": [\"\"], \"foo\": [\"hello\", \"world\"]}`\n    pub query_map: std::collections::BTreeMap<String, Vec<String>>,\n\n    /// `location.origin`\n    ///\n    /// Example: `\"http://www.example.com:80\"`.\n    pub origin: String,\n}\n\n/// Information about the integration passed to the use app each frame.\n#[derive(Clone, Debug)]\npub struct IntegrationInfo {\n    /// Information about the surrounding web environment.\n    #[cfg(target_arch = \"wasm32\")]\n    pub web_info: WebInfo,\n\n    /// Seconds of cpu usage (in seconds) on the previous frame.\n    ///\n    /// This includes [`App::update`] as well as rendering (except for vsync waiting).\n    ///\n    /// For a more detailed view of cpu usage, connect your preferred profiler by enabling it's feature in [`profiling`](https://crates.io/crates/profiling).\n    ///\n    /// `None` if this is the first frame.\n    pub cpu_usage: Option<f32>,\n}\n\nimpl IntegrationInfo {\n    fn mock() -> Self {\n        Self {\n            #[cfg(target_arch = \"wasm32\")]\n            web_info: WebInfo {\n                user_agent: \"kittest\".to_owned(),\n                location: Location {\n                    url: \"http://localhost\".to_owned(),\n                    protocol: \"http:\".to_owned(),\n                    host: \"localhost\".to_owned(),\n                    hostname: \"localhost\".to_owned(),\n                    port: \"80\".to_owned(),\n                    hash: String::new(),\n                    query: String::new(),\n                    query_map: Default::default(),\n                    origin: \"http://localhost\".to_owned(),\n                },\n            },\n            cpu_usage: None,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A place where you can store custom data in a way that persists when you restart the app.\n///\n/// On the web this is backed by [local storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).\n/// On desktop this is backed by the file system.\n///\n/// See [`CreationContext::storage`] and [`App::save`].\npub trait Storage {\n    /// Get the value for the given key.\n    fn get_string(&self, key: &str) -> Option<String>;\n\n    /// Set the value for the given key.\n    fn set_string(&mut self, key: &str, value: String);\n\n    /// write-to-disk or similar\n    fn flush(&mut self);\n}\n\n/// Get and deserialize the [RON](https://github.com/ron-rs/ron) stored at the given key.\n#[cfg(feature = \"ron\")]\npub fn get_value<T: serde::de::DeserializeOwned>(storage: &dyn Storage, key: &str) -> Option<T> {\n    profiling::function_scope!(key);\n    let value = storage.get_string(key)?;\n    match ron::from_str(&value) {\n        Ok(value) => Some(value),\n        Err(err) => {\n            // This happens on when we break the format, e.g. when updating egui.\n            log::debug!(\"Failed to decode RON: {err}\");\n            None\n        }\n    }\n}\n\n/// Serialize the given value as [RON](https://github.com/ron-rs/ron) and store with the given key.\n#[cfg(feature = \"ron\")]\npub fn set_value<T: serde::Serialize>(storage: &mut dyn Storage, key: &str, value: &T) {\n    profiling::function_scope!(key);\n    match ron::ser::to_string(value) {\n        Ok(string) => storage.set_string(key, string),\n        Err(err) => log::error!(\"eframe failed to encode data using ron: {err}\"),\n    }\n}\n\n/// [`Storage`] key used for app\npub const APP_KEY: &str = \"app\";\n"
  },
  {
    "path": "crates/eframe/src/icon_data.rs",
    "content": "//! Helpers for loading [`egui::IconData`].\n\nuse egui::IconData;\n\n/// Helpers for working with [`IconData`].\npub trait IconDataExt {\n    /// Convert into [`image::RgbaImage`]\n    ///\n    /// # Errors\n    /// If `width*height != 4 * rgba.len()`, or if the image is too big.\n    fn to_image(&self) -> Result<image::RgbaImage, String>;\n\n    /// Encode as PNG.\n    ///\n    /// # Errors\n    /// The image is invalid, or the PNG encoder failed.\n    fn to_png_bytes(&self) -> Result<Vec<u8>, String>;\n}\n\n/// Load the contents of .png file.\n///\n/// # Errors\n/// If this is not a valid png.\npub fn from_png_bytes(png_bytes: &[u8]) -> Result<IconData, image::ImageError> {\n    profiling::function_scope!();\n    let image = image::load_from_memory(png_bytes)?;\n    Ok(from_image(image))\n}\n\nfn from_image(image: image::DynamicImage) -> IconData {\n    let image = image.into_rgba8();\n    IconData {\n        width: image.width(),\n        height: image.height(),\n        rgba: image.into_raw(),\n    }\n}\n\nimpl IconDataExt for IconData {\n    fn to_image(&self) -> Result<image::RgbaImage, String> {\n        profiling::function_scope!();\n        let Self {\n            rgba,\n            width,\n            height,\n        } = self.clone();\n        image::RgbaImage::from_raw(width, height, rgba).ok_or_else(|| \"Invalid IconData\".to_owned())\n    }\n\n    fn to_png_bytes(&self) -> Result<Vec<u8>, String> {\n        profiling::function_scope!();\n        let image = self.to_image()?;\n        let mut png_bytes: Vec<u8> = Vec::new();\n        image\n            .write_to(\n                &mut std::io::Cursor::new(&mut png_bytes),\n                image::ImageFormat::Png,\n            )\n            .map_err(|err| err.to_string())?;\n        Ok(png_bytes)\n    }\n}\n"
  },
  {
    "path": "crates/eframe/src/lib.rs",
    "content": "//! eframe - the [`egui`] framework crate\n//!\n//! If you are planning to write an app for web or native,\n//! and want to use [`egui`] for everything, then `eframe` is for you!\n//!\n//! To get started, see the [examples](https://github.com/emilk/egui/tree/main/examples).\n//! To learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!\n//!\n//! In short, you implement [`App`] (especially [`App::update`]) and then\n//! call [`crate::run_native`] from your `main.rs`, and/or use `eframe::WebRunner` from your `lib.rs`.\n//!\n//! ## Compiling for web\n//! You need to install the `wasm32` target with `rustup target add wasm32-unknown-unknown`.\n//!\n//! Build the `.wasm` using `cargo build --target wasm32-unknown-unknown`\n//! and then use [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) to generate the JavaScript glue code.\n//!\n//! See the [`eframe_template` repository](https://github.com/emilk/eframe_template/) for more.\n//!\n//! ## Simplified usage\n//! If your app is only for native, and you don't need advanced features like state persistence,\n//! then you can use the simpler function [`run_simple_native`].\n//!\n//! ## Usage, native:\n//! ``` no_run\n//! use eframe::egui;\n//!\n//! fn main() {\n//!     let native_options = eframe::NativeOptions::default();\n//!     eframe::run_native(\"My egui App\", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))));\n//! }\n//!\n//! #[derive(Default)]\n//! struct MyEguiApp {}\n//!\n//! impl MyEguiApp {\n//!     fn new(cc: &eframe::CreationContext<'_>) -> Self {\n//!         // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_global_style.\n//!         // Restore app state using cc.storage (requires the \"persistence\" feature).\n//!         // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use\n//!         // for e.g. egui::PaintCallback.\n//!         Self::default()\n//!     }\n//! }\n//!\n//! impl eframe::App for MyEguiApp {\n//!    fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {\n//!        egui::CentralPanel::default().show_inside(ui, |ui| {\n//!            ui.heading(\"Hello World!\");\n//!        });\n//!    }\n//! }\n//! ```\n//!\n//! ## Usage, web:\n//! ``` no_run\n//! # #[cfg(target_arch = \"wasm32\")]\n//! use wasm_bindgen::prelude::*;\n//!\n//! /// Your handle to the web app from JavaScript.\n//! # #[cfg(target_arch = \"wasm32\")]\n//! #[derive(Clone)]\n//! #[wasm_bindgen]\n//! pub struct WebHandle {\n//!     runner: eframe::WebRunner,\n//! }\n//!\n//! # #[cfg(target_arch = \"wasm32\")]\n//! #[wasm_bindgen]\n//! impl WebHandle {\n//!     /// Installs a panic hook, then returns.\n//!     #[expect(clippy::new_without_default)]\n//!     #[wasm_bindgen(constructor)]\n//!     pub fn new() -> Self {\n//!         // Redirect [`log`] message to `console.log` and friends:\n//!         eframe::WebLogger::init(log::LevelFilter::Debug).ok();\n//!\n//!         Self {\n//!             runner: eframe::WebRunner::new(),\n//!         }\n//!     }\n//!\n//!     /// Call this once from JavaScript to start your app.\n//!     #[wasm_bindgen]\n//!     pub async fn start(&self, canvas: web_sys::HtmlCanvasElement) -> Result<(), wasm_bindgen::JsValue> {\n//!         self.runner\n//!             .start(\n//!                 canvas,\n//!                 eframe::WebOptions::default(),\n//!                 Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc))),)\n//!             )\n//!             .await\n//!     }\n//!\n//!     // The following are optional:\n//!\n//!     /// Shut down eframe and clean up resources.\n//!     #[wasm_bindgen]\n//!     pub fn destroy(&self) {\n//!         self.runner.destroy();\n//!     }\n//!\n//!     /// Example on how to call into your app from JavaScript.\n//!     #[wasm_bindgen]\n//!     pub fn example(&self) {\n//!         if let Some(app) = self.runner.app_mut::<MyEguiApp>() {\n//!             app.example();\n//!         }\n//!     }\n//!\n//!     /// The JavaScript can check whether or not your app has crashed:\n//!     #[wasm_bindgen]\n//!     pub fn has_panicked(&self) -> bool {\n//!         self.runner.has_panicked()\n//!     }\n//!\n//!     #[wasm_bindgen]\n//!     pub fn panic_message(&self) -> Option<String> {\n//!         self.runner.panic_summary().map(|s| s.message())\n//!     }\n//!\n//!     #[wasm_bindgen]\n//!     pub fn panic_callstack(&self) -> Option<String> {\n//!         self.runner.panic_summary().map(|s| s.callstack())\n//!     }\n//! }\n//! ```\n//!\n//! ## Feature flags\n#![doc = document_features::document_features!()]\n//!\n//! ## Instrumentation\n//! This crate supports using the [profiling](https://crates.io/crates/profiling) crate for instrumentation.\n//! You can enable features on the profiling crates in your application to add instrumentation for all\n//! crates that support it, including egui. See the profiling crate docs for more information.\n//! ```toml\n//! [dependencies]\n//! profiling = \"1.0\"\n//! [features]\n//! profile-with-puffin = [\"profiling/profile-with-puffin\"]\n//! ```\n//!\n\n#![warn(missing_docs)] // let's keep eframe well-documented\n\n// Limitation imposed by `accesskit_winit`:\n// https://github.com/AccessKit/accesskit/tree/accesskit-v0.18.0/platforms/winit#android-activity-compatibility`\n#[cfg(all(\n    target_os = \"android\",\n    feature = \"accesskit\",\n    feature = \"android-native-activity\"\n))]\ncompile_error!(\"`accesskit` feature is only available with `android-game-activity`\");\n\n// Re-export all useful libraries:\npub use {egui, egui::emath, egui::epaint};\n\n#[cfg(feature = \"glow\")]\npub use {egui_glow, glow};\n\n#[cfg(feature = \"wgpu_no_default_features\")]\npub use {egui_wgpu, wgpu};\n\nmod epi;\n\n// Re-export everything in `epi` so `eframe` users don't have to care about what `epi` is:\npub use epi::*;\n\npub(crate) mod stopwatch;\n\n// ----------------------------------------------------------------------------\n// When compiling for web\n\n#[cfg(target_arch = \"wasm32\")]\npub use wasm_bindgen;\n\n#[cfg(target_arch = \"wasm32\")]\npub use web_sys;\n\n#[cfg(target_arch = \"wasm32\")]\npub mod web;\n\n#[cfg(target_arch = \"wasm32\")]\npub use web::{WebLogger, WebRunner};\n\n// ----------------------------------------------------------------------------\n// When compiling natively\n\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\nmod native;\n\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\npub use native::run::EframeWinitApplication;\n\n#[cfg(not(any(target_arch = \"wasm32\", target_os = \"ios\")))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\npub use native::run::EframePumpStatus;\n\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n#[cfg(feature = \"persistence\")]\npub use native::file_storage::storage_dir;\n\n#[cfg(not(target_arch = \"wasm32\"))]\npub mod icon_data;\n\n/// This is how you start a native (desktop) app.\n///\n/// The first argument is name of your app, which is an identifier\n/// used for the save location of persistence (see [`App::save`]).\n/// It is also used as the application id on wayland.\n/// If you set no title on the viewport, the app id will be used\n/// as the title.\n///\n/// For details about application ID conventions, see the\n/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)\n///\n/// Call from `fn main` like this:\n/// ``` no_run\n/// use eframe::egui;\n///\n/// fn main() -> eframe::Result {\n///     let native_options = eframe::NativeOptions::default();\n///     eframe::run_native(\"MyApp\", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))))\n/// }\n///\n/// #[derive(Default)]\n/// struct MyEguiApp {}\n///\n/// impl MyEguiApp {\n///     fn new(cc: &eframe::CreationContext<'_>) -> Self {\n///         // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_global_style.\n///         // Restore app state using cc.storage (requires the \"persistence\" feature).\n///         // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use\n///         // for e.g. egui::PaintCallback.\n///         Self::default()\n///     }\n/// }\n///\n/// impl eframe::App for MyEguiApp {\n///    fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {\n///        egui::CentralPanel::default().show_inside(ui, |ui| {\n///            ui.heading(\"Hello World!\");\n///        });\n///    }\n/// }\n/// ```\n///\n/// # Errors\n/// This function can fail if we fail to set up a graphics context.\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n#[allow(clippy::allow_attributes, clippy::needless_pass_by_value)]\npub fn run_native(\n    app_name: &str,\n    mut native_options: NativeOptions,\n    app_creator: AppCreator<'_>,\n) -> Result {\n    let renderer = init_native(app_name, &mut native_options);\n\n    match renderer {\n        #[cfg(feature = \"glow\")]\n        Renderer::Glow => {\n            log::debug!(\"Using the glow renderer\");\n            native::run::run_glow(app_name, native_options, app_creator)\n        }\n\n        #[cfg(feature = \"wgpu_no_default_features\")]\n        Renderer::Wgpu => {\n            log::debug!(\"Using the wgpu renderer\");\n            native::run::run_wgpu(app_name, native_options, app_creator)\n        }\n    }\n}\n\n/// Provides a proxy for your native eframe application to run on your own event loop.\n///\n/// See `run_native` for details about `app_name`.\n///\n/// Call from `fn main` like this:\n/// ``` no_run\n/// use eframe::{egui, UserEvent};\n/// use winit::event_loop::{ControlFlow, EventLoop};\n///\n/// fn main() -> eframe::Result {\n///     let native_options = eframe::NativeOptions::default();\n///     let eventloop = EventLoop::<UserEvent>::with_user_event().build()?;\n///     eventloop.set_control_flow(ControlFlow::Poll);\n///\n///     let mut winit_app = eframe::create_native(\n///         \"MyExtApp\",\n///         native_options,\n///         Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))),\n///         &eventloop,\n///     );\n///\n///     eventloop.run_app(&mut winit_app)?;\n///\n///     Ok(())\n/// }\n///\n/// #[derive(Default)]\n/// struct MyEguiApp {}\n///\n/// impl MyEguiApp {\n///     fn new(cc: &eframe::CreationContext<'_>) -> Self {\n///         Self::default()\n///     }\n/// }\n///\n/// impl eframe::App for MyEguiApp {\n///    fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {\n///        egui::CentralPanel::default().show_inside(ui, |ui| {\n///            ui.heading(\"Hello World!\");\n///        });\n///    }\n/// }\n/// ```\n///\n/// See the `external_eventloop` example for a more complete example.\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\npub fn create_native<'a>(\n    app_name: &str,\n    mut native_options: NativeOptions,\n    app_creator: AppCreator<'a>,\n    event_loop: &winit::event_loop::EventLoop<UserEvent>,\n) -> EframeWinitApplication<'a> {\n    let renderer = init_native(app_name, &mut native_options);\n\n    match renderer {\n        #[cfg(feature = \"glow\")]\n        Renderer::Glow => {\n            log::debug!(\"Using the glow renderer\");\n            EframeWinitApplication::new(native::run::create_glow(\n                app_name,\n                native_options,\n                app_creator,\n                event_loop,\n            ))\n        }\n\n        #[cfg(feature = \"wgpu_no_default_features\")]\n        Renderer::Wgpu => {\n            log::debug!(\"Using the wgpu renderer\");\n            EframeWinitApplication::new(native::run::create_wgpu(\n                app_name,\n                native_options,\n                app_creator,\n                event_loop,\n            ))\n        }\n    }\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\nfn init_native(app_name: &str, native_options: &mut NativeOptions) -> Renderer {\n    #[cfg(not(feature = \"__screenshot\"))]\n    assert!(\n        std::env::var(\"EFRAME_SCREENSHOT_TO\").is_err(),\n        \"EFRAME_SCREENSHOT_TO found without compiling with the '__screenshot' feature\"\n    );\n\n    if native_options.viewport.title.is_none() {\n        native_options.viewport.title = Some(app_name.to_owned());\n    }\n\n    let renderer = native_options.renderer;\n\n    #[cfg(all(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\n    {\n        match native_options.renderer {\n            Renderer::Glow => \"glow\",\n            Renderer::Wgpu => \"wgpu\",\n        };\n        log::info!(\"Both the glow and wgpu renderers are available. Using {renderer}.\");\n    }\n\n    renderer\n}\n\n// ----------------------------------------------------------------------------\n\n/// The simplest way to get started when writing a native app.\n///\n/// This does NOT support persistence of custom user data. For that you need to use [`run_native`].\n/// However, it DOES support persistence of egui data (window positions and sizes, how far the user has scrolled in a\n/// [`ScrollArea`](egui::ScrollArea), etc.) if the persistence feature is enabled.\n///\n/// # Example\n/// ``` no_run\n/// fn main() -> eframe::Result {\n///     // Our application state:\n///     let mut name = \"Arthur\".to_owned();\n///     let mut age = 42;\n///\n///     let options = eframe::NativeOptions::default();\n///     eframe::run_ui_native(\"My egui App\", options, move |ui, _frame| {\n///         // Wrap everything in a CentralPanel so we get some margins and a background color:\n///         egui::CentralPanel::default().show_inside(ui, |ui| {\n///             ui.heading(\"My egui Application\");\n///             ui.horizontal(|ui| {\n///                 let name_label = ui.label(\"Your name: \");\n///                 ui.text_edit_singleline(&mut name)\n///                     .labelled_by(name_label.id);\n///             });\n///             ui.add(egui::Slider::new(&mut age, 0..=120).text(\"age\"));\n///             if ui.button(\"Increment\").clicked() {\n///                 age += 1;\n///             }\n///             ui.label(format!(\"Hello '{name}', age {age}\"));\n///         });\n///     })\n/// }\n/// ```\n///\n/// # Errors\n/// This function can fail if we fail to set up a graphics context.\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\npub fn run_ui_native(\n    app_name: &str,\n    native_options: NativeOptions,\n    ui_fun: impl FnMut(&mut egui::Ui, &mut Frame) + 'static,\n) -> Result {\n    struct SimpleApp<U> {\n        ui_fun: U,\n    }\n\n    impl<U: FnMut(&mut egui::Ui, &mut Frame) + 'static> App for SimpleApp<U> {\n        fn ui(&mut self, ui: &mut egui::Ui, frame: &mut Frame) {\n            (self.ui_fun)(ui, frame);\n        }\n    }\n\n    run_native(\n        app_name,\n        native_options,\n        Box::new(|_cc| Ok(Box::new(SimpleApp { ui_fun }))),\n    )\n}\n\n/// The simplest way to get started when writing a native app.\n///\n/// This does NOT support persistence of custom user data. For that you need to use [`run_native`].\n/// However, it DOES support persistence of egui data (window positions and sizes, how far the user has scrolled in a\n/// [`ScrollArea`](egui::ScrollArea), etc.) if the persistence feature is enabled.\n///\n/// # Example\n/// ``` no_run\n/// fn main() -> eframe::Result {\n///     // Our application state:\n///     let mut name = \"Arthur\".to_owned();\n///     let mut age = 42;\n///\n///     let options = eframe::NativeOptions::default();\n///     eframe::run_simple_native(\"My egui App\", options, move |ctx, _frame| {\n///         egui::CentralPanel::default().show(ctx, |ui| {\n///             ui.heading(\"My egui Application\");\n///             ui.horizontal(|ui| {\n///                 let name_label = ui.label(\"Your name: \");\n///                 ui.text_edit_singleline(&mut name)\n///                     .labelled_by(name_label.id);\n///             });\n///             ui.add(egui::Slider::new(&mut age, 0..=120).text(\"age\"));\n///             if ui.button(\"Increment\").clicked() {\n///                 age += 1;\n///             }\n///             ui.label(format!(\"Hello '{name}', age {age}\"));\n///         });\n///     })\n/// }\n/// ```\n///\n/// # Errors\n/// This function can fail if we fail to set up a graphics context.\n#[deprecated = \"Use run_ui_native instead\"]\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(any(feature = \"glow\", feature = \"wgpu_no_default_features\"))]\npub fn run_simple_native(\n    app_name: &str,\n    native_options: NativeOptions,\n    update_fun: impl FnMut(&egui::Context, &mut Frame) + 'static,\n) -> Result {\n    struct SimpleApp<U> {\n        update_fun: U,\n    }\n\n    impl<U: FnMut(&egui::Context, &mut Frame) + 'static> App for SimpleApp<U> {\n        fn ui(&mut self, _ui: &mut egui::Ui, _frame: &mut Frame) {}\n\n        fn update(&mut self, ctx: &egui::Context, frame: &mut Frame) {\n            (self.update_fun)(ctx, frame);\n        }\n    }\n\n    run_native(\n        app_name,\n        native_options,\n        Box::new(|_cc| Ok(Box::new(SimpleApp { update_fun }))),\n    )\n}\n\n// ----------------------------------------------------------------------------\n\n/// The different problems that can occur when trying to run `eframe`.\n#[derive(Debug)]\npub enum Error {\n    /// Something went wrong in user code when creating the app.\n    AppCreation(Box<dyn std::error::Error + Send + Sync>),\n\n    /// An error from [`winit`].\n    #[cfg(not(target_arch = \"wasm32\"))]\n    Winit(winit::error::OsError),\n\n    /// An error from [`winit::event_loop::EventLoop`].\n    #[cfg(not(target_arch = \"wasm32\"))]\n    WinitEventLoop(winit::error::EventLoopError),\n\n    /// An error from [`glutin`] when using [`glow`].\n    #[cfg(all(feature = \"glow\", not(target_arch = \"wasm32\")))]\n    Glutin(glutin::error::Error),\n\n    /// An error from [`glutin`] when using [`glow`].\n    #[cfg(all(feature = \"glow\", not(target_arch = \"wasm32\")))]\n    NoGlutinConfigs(glutin::config::ConfigTemplate, Box<dyn std::error::Error>),\n\n    /// An error from [`glutin`] when using [`glow`].\n    #[cfg(feature = \"glow\")]\n    OpenGL(egui_glow::PainterError),\n\n    /// An error from [`wgpu`].\n    #[cfg(feature = \"wgpu_no_default_features\")]\n    Wgpu(egui_wgpu::WgpuError),\n}\n\nimpl std::error::Error for Error {}\n\n#[cfg(not(target_arch = \"wasm32\"))]\nimpl From<winit::error::OsError> for Error {\n    #[inline]\n    fn from(err: winit::error::OsError) -> Self {\n        Self::Winit(err)\n    }\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\nimpl From<winit::error::EventLoopError> for Error {\n    #[inline]\n    fn from(err: winit::error::EventLoopError) -> Self {\n        Self::WinitEventLoop(err)\n    }\n}\n\n#[cfg(all(feature = \"glow\", not(target_arch = \"wasm32\")))]\nimpl From<glutin::error::Error> for Error {\n    #[inline]\n    fn from(err: glutin::error::Error) -> Self {\n        Self::Glutin(err)\n    }\n}\n\n#[cfg(feature = \"glow\")]\nimpl From<egui_glow::PainterError> for Error {\n    #[inline]\n    fn from(err: egui_glow::PainterError) -> Self {\n        Self::OpenGL(err)\n    }\n}\n\n#[cfg(feature = \"wgpu_no_default_features\")]\nimpl From<egui_wgpu::WgpuError> for Error {\n    #[inline]\n    fn from(err: egui_wgpu::WgpuError) -> Self {\n        Self::Wgpu(err)\n    }\n}\n\nimpl std::fmt::Display for Error {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::AppCreation(err) => write!(f, \"app creation error: {err}\"),\n\n            #[cfg(not(target_arch = \"wasm32\"))]\n            Self::Winit(err) => {\n                write!(f, \"winit error: {err}\")\n            }\n\n            #[cfg(not(target_arch = \"wasm32\"))]\n            Self::WinitEventLoop(err) => {\n                write!(f, \"winit EventLoopError: {err}\")\n            }\n\n            #[cfg(all(feature = \"glow\", not(target_arch = \"wasm32\")))]\n            Self::Glutin(err) => {\n                write!(f, \"glutin error: {err}\")\n            }\n\n            #[cfg(all(feature = \"glow\", not(target_arch = \"wasm32\")))]\n            Self::NoGlutinConfigs(template, err) => {\n                write!(\n                    f,\n                    \"Found no glutin configs matching the template: {template:?}. Error: {err}\"\n                )\n            }\n\n            #[cfg(feature = \"glow\")]\n            Self::OpenGL(err) => {\n                write!(f, \"egui_glow: {err}\")\n            }\n\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            Self::Wgpu(err) => {\n                write!(f, \"WGPU error: {err}\")\n            }\n        }\n    }\n}\n\n/// Short for `Result<T, eframe::Error>`.\npub type Result<T = (), E = Error> = std::result::Result<T, E>;\n"
  },
  {
    "path": "crates/eframe/src/native/app_icon.rs",
    "content": "//! Set the native app icon at runtime.\n//!\n//! TODO(emilk): port this to [`winit`].\n\nuse std::sync::Arc;\n\nuse egui::IconData;\n\npub struct AppTitleIconSetter {\n    title: String,\n    icon_data: Option<Arc<IconData>>,\n    status: AppIconStatus,\n}\n\nimpl AppTitleIconSetter {\n    pub fn new(title: String, mut icon_data: Option<Arc<IconData>>) -> Self {\n        if let Some(icon) = &icon_data\n            && **icon == IconData::default()\n        {\n            icon_data = None;\n        }\n\n        Self {\n            title,\n            icon_data,\n            status: AppIconStatus::NotSetTryAgain,\n        }\n    }\n\n    /// Call once per frame; we will set the icon when we can.\n    pub fn update(&mut self) {\n        if self.status == AppIconStatus::NotSetTryAgain {\n            self.status = set_title_and_icon(&self.title, self.icon_data.as_deref());\n        }\n    }\n}\n\n/// In which state the app icon is (as far as we know).\n#[derive(PartialEq, Eq)]\nenum AppIconStatus {\n    /// We did not set it or failed to do it. In any case we won't try again.\n    NotSetIgnored,\n\n    /// We haven't set the icon yet, we should try again next frame.\n    ///\n    /// This can happen repeatedly due to lazy window creation on some platforms.\n    NotSetTryAgain,\n\n    /// We successfully set the icon and it should be visible now.\n    #[allow(clippy::allow_attributes, dead_code)] // Not used on Linux\n    Set,\n}\n\n/// Sets app icon at runtime.\n///\n/// By setting the icon at runtime and not via resource files etc. we ensure that we'll get the chance\n/// to set the same icon when the process/window is started from python (which sets its own icon ahead of us!).\n///\n/// Since window creation can be lazy, call this every frame until it's either successfully or gave up.\n/// (See [`AppIconStatus`])\nfn set_title_and_icon(_title: &str, _icon_data: Option<&IconData>) -> AppIconStatus {\n    profiling::function_scope!();\n\n    #[cfg(target_os = \"windows\")]\n    {\n        if let Some(icon_data) = _icon_data {\n            return set_app_icon_windows(icon_data);\n        }\n    }\n\n    #[cfg(target_os = \"macos\")]\n    return set_title_and_icon_mac(_title, _icon_data);\n\n    #[allow(clippy::allow_attributes, unreachable_code)]\n    AppIconStatus::NotSetIgnored\n}\n\n/// Set icon for Windows applications.\n#[cfg(target_os = \"windows\")]\n#[expect(unsafe_code)]\nfn set_app_icon_windows(icon_data: &IconData) -> AppIconStatus {\n    use crate::icon_data::IconDataExt as _;\n    use windows_sys::Win32::UI::Input::KeyboardAndMouse::GetActiveWindow;\n    use windows_sys::Win32::UI::WindowsAndMessaging::{\n        CreateIconFromResourceEx, GetSystemMetrics, HICON, ICON_BIG, ICON_SMALL, LR_DEFAULTCOLOR,\n        SM_CXICON, SM_CXSMICON, SendMessageW, WM_SETICON,\n    };\n\n    // We would get fairly far already with winit's `set_window_icon` (which is exposed to eframe) actually!\n    // However, it only sets ICON_SMALL, i.e. doesn't allow us to set a higher resolution icon for the task bar.\n    // Also, there is scaling issues, detailed below.\n\n    // TODO(andreas): This does not set the task bar icon for when our application is started from python.\n    //      Things tried so far:\n    //      * Querying for an owning window and setting icon there (there doesn't seem to be an owning window)\n    //      * using undocumented SetConsoleIcon method (successfully queried via GetProcAddress)\n\n    // SAFETY: WinApi function without side-effects.\n    let window_handle = unsafe { GetActiveWindow() };\n    if window_handle.is_null() {\n        // The Window isn't available yet. Try again later!\n        return AppIconStatus::NotSetTryAgain;\n    }\n\n    fn create_hicon_with_scale(unscaled_image: &image::RgbaImage, target_size: i32) -> HICON {\n        let image_scaled = image::imageops::resize(\n            unscaled_image,\n            target_size as _,\n            target_size as _,\n            image::imageops::Lanczos3,\n        );\n\n        // Creating transparent icons with WinApi is a huge mess.\n        // We'd need to go through CreateIconIndirect's ICONINFO struct which then\n        // takes a mask HBITMAP and a color HBITMAP and creating each of these is pain.\n        // Instead we workaround this by creating a png which CreateIconFromResourceEx magically understands.\n        // This is a pretty horrible hack as we spend a lot of time encoding, but at least the code is a lot shorter.\n        let mut image_scaled_bytes: Vec<u8> = Vec::new();\n        if image_scaled\n            .write_to(\n                &mut std::io::Cursor::new(&mut image_scaled_bytes),\n                image::ImageFormat::Png,\n            )\n            .is_err()\n        {\n            return std::ptr::null_mut();\n        }\n\n        // SAFETY: Creating an HICON which should be readonly on our data.\n        unsafe {\n            CreateIconFromResourceEx(\n                image_scaled_bytes.as_mut_ptr(),\n                image_scaled_bytes.len() as u32,\n                1,           // Means this is an icon, not a cursor.\n                0x00030000,  // Version number of the HICON\n                target_size, // Note that this method can scale, but it does so *very* poorly. So let's avoid that!\n                target_size,\n                LR_DEFAULTCOLOR,\n            )\n        }\n    }\n\n    let unscaled_image = match icon_data.to_image() {\n        Ok(unscaled_image) => unscaled_image,\n        Err(err) => {\n            log::warn!(\"Invalid icon: {err}\");\n            return AppIconStatus::NotSetIgnored;\n        }\n    };\n\n    // Only setting ICON_BIG with the icon size for big icons (SM_CXICON) works fine\n    // but the scaling it does then for the small icon is pretty bad.\n    // Instead we set the correct sizes manually and take over the scaling ourselves.\n    // For this to work we first need to set the big icon and then the small one.\n    //\n    // Note that ICON_SMALL may be used even if we don't render a title bar as it may be used in alt+tab!\n    {\n        // SAFETY: WinAPI getter function with no known side effects.\n        let icon_size_big = unsafe { GetSystemMetrics(SM_CXICON) };\n        let icon_big = create_hicon_with_scale(&unscaled_image, icon_size_big);\n        if icon_big.is_null() {\n            log::warn!(\"Failed to create HICON (for big icon) from embedded png data.\");\n            return AppIconStatus::NotSetIgnored; // We could try independently with the small icon but what's the point, it would look bad!\n        } else {\n            // SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior.\n            unsafe {\n                SendMessageW(\n                    window_handle,\n                    WM_SETICON,\n                    ICON_BIG as usize,\n                    icon_big as isize,\n                );\n            }\n        }\n    }\n    {\n        // SAFETY: WinAPI getter function with no known side effects.\n        let icon_size_small = unsafe { GetSystemMetrics(SM_CXSMICON) };\n        let icon_small = create_hicon_with_scale(&unscaled_image, icon_size_small);\n        if icon_small.is_null() {\n            log::warn!(\"Failed to create HICON (for small icon) from embedded png data.\");\n            return AppIconStatus::NotSetIgnored;\n        } else {\n            // SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior.\n            unsafe {\n                SendMessageW(\n                    window_handle,\n                    WM_SETICON,\n                    ICON_SMALL as usize,\n                    icon_small as isize,\n                );\n            }\n        }\n    }\n\n    // It _probably_ worked out.\n    AppIconStatus::Set\n}\n\n/// Set icon & app title for `MacOS` applications.\n#[cfg(target_os = \"macos\")]\n#[expect(unsafe_code)]\nfn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconStatus {\n    use crate::icon_data::IconDataExt as _;\n    profiling::function_scope!();\n\n    use objc2::ClassType as _;\n    use objc2_app_kit::{NSApplication, NSImage};\n    use objc2_foundation::NSString;\n\n    // Do NOT use png even though creating `NSImage` from it is much easier than from raw images data!\n    //\n    // Some MacOS versions have a bug where creating an `NSImage` from a png will cause it to load an arbitrary `libpng.dylib`.\n    // If this dylib isn't the right version, the application will crash with SIGBUS.\n    // For details see https://github.com/emilk/egui/issues/7155\n    let image = if let Some(icon_data) = icon_data {\n        match icon_data.to_image() {\n            Ok(image) => Some(image),\n            Err(err) => {\n                log::warn!(\"Failed to read icon data: {err}\");\n                return AppIconStatus::NotSetIgnored;\n            }\n        }\n    } else {\n        None\n    };\n\n    // TODO(madsmtm): Move this into `objc2-app-kit`\n    unsafe extern \"C\" {\n        static NSApp: Option<&'static NSApplication>;\n    }\n\n    // SAFETY: we don't do anything dangerous here\n    unsafe {\n        let Some(app) = NSApp else {\n            log::debug!(\"NSApp is null\");\n            return AppIconStatus::NotSetIgnored;\n        };\n\n        if let Some(image) = image {\n            use objc2_app_kit::{NSBitmapImageRep, NSDeviceRGBColorSpace};\n            use objc2_foundation::NSSize;\n\n            log::trace!(\n                \"NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel\"\n            );\n            let Some(image_rep) = NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel(\n                NSBitmapImageRep::alloc(),\n                [image.as_raw().as_ptr().cast_mut()].as_mut_ptr(),\n                image.width() as isize,\n                image.height() as isize,\n                8, // bits per sample\n                4, // samples per pixel\n                true, // has alpha\n                false, // is not planar\n                NSDeviceRGBColorSpace,\n                (image.width() * 4) as isize, // bytes per row\n                32 // bits per pixel\n            ) else {\n                log::warn!(\"Failed to create NSBitmapImageRep from app icon data.\");\n                return AppIconStatus::NotSetIgnored;\n            };\n\n            log::trace!(\"NSImage::initWithSize\");\n            let app_icon = NSImage::initWithSize(\n                NSImage::alloc(),\n                NSSize::new(image.width() as f64, image.height() as f64),\n            );\n            log::trace!(\"NSImage::addRepresentation\");\n            app_icon.addRepresentation(&image_rep);\n\n            profiling::scope!(\"setApplicationIconImage_\");\n            log::trace!(\"setApplicationIconImage…\");\n            app.setApplicationIconImage(Some(&app_icon));\n        }\n\n        // Change the title in the top bar - for python processes this would be again \"python\" otherwise.\n        if let Some(main_menu) = app.mainMenu()\n            && let Some(item) = main_menu.itemAtIndex(0)\n            && let Some(app_menu) = item.submenu()\n        {\n            profiling::scope!(\"setTitle_\");\n            app_menu.setTitle(&NSString::from_str(title));\n        }\n\n        // The title in the Dock apparently can't be changed.\n        // At least these people didn't figure it out either:\n        // https://stackoverflow.com/questions/69831167/qt-change-application-title-dynamically-on-macos\n        // https://stackoverflow.com/questions/28808226/changing-cocoa-app-icon-title-and-menu-labels-at-runtime\n    }\n\n    AppIconStatus::Set\n}\n"
  },
  {
    "path": "crates/eframe/src/native/epi_integration.rs",
    "content": "//! Common tools used by [`super::glow_integration`] and [`super::wgpu_integration`].\n\nuse web_time::Instant;\n\nuse std::path::PathBuf;\nuse winit::event_loop::ActiveEventLoop;\n\nuse raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _};\n\nuse egui::{DeferredViewportUiCallback, ViewportBuilder, ViewportId};\nuse egui_winit::{EventResponse, WindowSettings};\n\nuse crate::epi;\n\n#[cfg_attr(target_os = \"ios\", allow(dead_code, unused_variables, unused_mut))]\npub fn viewport_builder(\n    egui_zoom_factor: f32,\n    event_loop: &ActiveEventLoop,\n    native_options: &mut epi::NativeOptions,\n    window_settings: Option<WindowSettings>,\n) -> ViewportBuilder {\n    profiling::function_scope!();\n\n    let mut viewport_builder = native_options.viewport.clone();\n\n    // On some Linux systems, a window size larger than the monitor causes crashes,\n    // and on Windows the window does not appear at all.\n    let clamp_size_to_monitor_size = viewport_builder.clamp_size_to_monitor_size.unwrap_or(true);\n\n    // Always use the default window size / position on iOS. Trying to restore the previous position\n    // causes the window to be shown too small.\n    #[cfg(not(target_os = \"ios\"))]\n    let inner_size_points = if let Some(mut window_settings) = window_settings {\n        // Restore pos/size from previous session\n\n        if clamp_size_to_monitor_size {\n            window_settings.clamp_size_to_sane_values(largest_monitor_point_size(\n                egui_zoom_factor,\n                event_loop,\n            ));\n        }\n        window_settings.clamp_position_to_monitors(egui_zoom_factor, event_loop);\n\n        viewport_builder = window_settings.initialize_viewport_builder(\n            egui_zoom_factor,\n            event_loop,\n            viewport_builder,\n        );\n        window_settings.inner_size_points()\n    } else {\n        if let Some(pos) = viewport_builder.position {\n            viewport_builder = viewport_builder.with_position(pos);\n        }\n\n        if clamp_size_to_monitor_size && let Some(initial_window_size) = viewport_builder.inner_size\n        {\n            let initial_window_size = egui::NumExt::at_most(\n                initial_window_size,\n                largest_monitor_point_size(egui_zoom_factor, event_loop),\n            );\n            viewport_builder = viewport_builder.with_inner_size(initial_window_size);\n        }\n\n        viewport_builder.inner_size\n    };\n\n    #[cfg(not(target_os = \"ios\"))]\n    if native_options.centered {\n        profiling::scope!(\"center\");\n        if let Some(monitor) = event_loop\n            .primary_monitor()\n            .or_else(|| event_loop.available_monitors().next())\n        {\n            let monitor_size = monitor\n                .size()\n                .to_logical::<f32>(egui_zoom_factor as f64 * monitor.scale_factor());\n            let inner_size = inner_size_points.unwrap_or(egui::Vec2 { x: 800.0, y: 600.0 });\n            if 0.0 < monitor_size.width && 0.0 < monitor_size.height {\n                let x = (monitor_size.width - inner_size.x) / 2.0;\n                let y = (monitor_size.height - inner_size.y) / 2.0;\n                viewport_builder = viewport_builder.with_position([x, y]);\n            }\n        }\n    }\n\n    match std::mem::take(&mut native_options.window_builder) {\n        Some(hook) => hook(viewport_builder),\n        None => viewport_builder,\n    }\n}\n\npub fn apply_window_settings(\n    window: &winit::window::Window,\n    window_settings: Option<WindowSettings>,\n) {\n    profiling::function_scope!();\n    if let Some(window_settings) = window_settings {\n        window_settings.initialize_window(window);\n    }\n}\n\n#[cfg(not(target_os = \"ios\"))]\nfn largest_monitor_point_size(egui_zoom_factor: f32, event_loop: &ActiveEventLoop) -> egui::Vec2 {\n    profiling::function_scope!();\n    let mut max_size = egui::Vec2::ZERO;\n\n    let available_monitors = {\n        profiling::scope!(\"available_monitors\");\n        event_loop.available_monitors()\n    };\n\n    for monitor in available_monitors {\n        let size = monitor\n            .size()\n            .to_logical::<f32>(egui_zoom_factor as f64 * monitor.scale_factor());\n        let size = egui::vec2(size.width, size.height);\n        max_size = max_size.max(size);\n    }\n\n    if max_size == egui::Vec2::ZERO {\n        egui::Vec2::splat(16000.0)\n    } else {\n        max_size\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// For loading/saving app state and/or egui memory to disk.\npub fn create_storage(_app_name: &str) -> Option<Box<dyn epi::Storage>> {\n    #[cfg(feature = \"persistence\")]\n    if let Some(storage) = super::file_storage::FileStorage::from_app_id(_app_name) {\n        return Some(Box::new(storage));\n    }\n    None\n}\n\n#[allow(clippy::allow_attributes, clippy::unnecessary_wraps)]\npub fn create_storage_with_file(_file: impl Into<PathBuf>) -> Option<Box<dyn epi::Storage>> {\n    #[cfg(feature = \"persistence\")]\n    return Some(Box::new(\n        super::file_storage::FileStorage::from_ron_filepath(_file),\n    ));\n    #[cfg(not(feature = \"persistence\"))]\n    None\n}\n\n// ----------------------------------------------------------------------------\n\n/// Everything needed to make a winit-based integration for [`epi`].\n///\n/// Only one instance per app (not one per viewport).\npub struct EpiIntegration {\n    pub frame: epi::Frame,\n    last_auto_save: Instant,\n    pub beginning: Instant,\n    is_first_frame: bool,\n    pub egui_ctx: egui::Context,\n    pending_full_output: egui::FullOutput,\n\n    /// When set, it is time to close the native window.\n    close: bool,\n\n    can_drag_window: bool,\n    #[cfg(feature = \"persistence\")]\n    persist_window: bool,\n    app_icon_setter: super::app_icon::AppTitleIconSetter,\n}\n\nimpl EpiIntegration {\n    #[allow(clippy::allow_attributes, clippy::too_many_arguments)]\n    pub fn new(\n        egui_ctx: egui::Context,\n        window: &winit::window::Window,\n        app_name: &str,\n        native_options: &crate::NativeOptions,\n        storage: Option<Box<dyn epi::Storage>>,\n        #[cfg(feature = \"glow\")] gl: Option<std::sync::Arc<glow::Context>>,\n        #[cfg(feature = \"glow\")] glow_register_native_texture: Option<\n            Box<dyn FnMut(glow::Texture) -> egui::TextureId>,\n        >,\n        #[cfg(feature = \"wgpu_no_default_features\")] wgpu_render_state: Option<\n            egui_wgpu::RenderState,\n        >,\n    ) -> Self {\n        let frame = epi::Frame {\n            info: epi::IntegrationInfo { cpu_usage: None },\n            storage,\n            #[cfg(feature = \"glow\")]\n            gl,\n            #[cfg(feature = \"glow\")]\n            glow_register_native_texture,\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            wgpu_render_state,\n            raw_display_handle: window.display_handle().map(|h| h.as_raw()),\n            raw_window_handle: window.window_handle().map(|h| h.as_raw()),\n        };\n\n        let icon = native_options\n            .viewport\n            .icon\n            .clone()\n            .unwrap_or_else(|| std::sync::Arc::new(load_default_egui_icon()));\n\n        let app_icon_setter = super::app_icon::AppTitleIconSetter::new(\n            native_options\n                .viewport\n                .title\n                .clone()\n                .unwrap_or_else(|| app_name.to_owned()),\n            Some(icon),\n        );\n\n        Self {\n            frame,\n            last_auto_save: Instant::now(),\n            egui_ctx,\n            pending_full_output: Default::default(),\n            close: false,\n            can_drag_window: false,\n            #[cfg(feature = \"persistence\")]\n            persist_window: native_options.persist_window,\n            app_icon_setter,\n            beginning: Instant::now(),\n            is_first_frame: true,\n        }\n    }\n\n    /// If `true`, it is time to close the native window.\n    pub fn should_close(&self) -> bool {\n        self.close\n    }\n\n    pub fn on_window_event(\n        &mut self,\n        window: &winit::window::Window,\n        egui_winit: &mut egui_winit::State,\n        event: &winit::event::WindowEvent,\n    ) -> EventResponse {\n        profiling::function_scope!(egui_winit::short_window_event_description(event));\n\n        use winit::event::{ElementState, MouseButton, WindowEvent};\n\n        if let WindowEvent::MouseInput {\n            button: MouseButton::Left,\n            state: ElementState::Pressed,\n            ..\n        } = event\n        {\n            self.can_drag_window = true;\n        }\n\n        egui_winit.on_window_event(window, event)\n    }\n\n    pub fn pre_update(&mut self) {\n        self.app_icon_setter.update();\n    }\n\n    /// Run user code - this can create immediate viewports, so hold no locks over this!\n    ///\n    /// If `viewport_ui_cb` is None, we are in the root viewport and will call [`crate::App::update`].\n    pub fn update(\n        &mut self,\n        app: &mut dyn epi::App,\n        viewport_ui_cb: Option<&DeferredViewportUiCallback>,\n        mut raw_input: egui::RawInput,\n        is_visible: bool,\n    ) -> egui::FullOutput {\n        raw_input.time = Some(self.beginning.elapsed().as_secs_f64());\n\n        let close_requested = raw_input.viewport().close_requested();\n\n        app.raw_input_hook(&self.egui_ctx, &mut raw_input);\n\n        let full_output = self.egui_ctx.run_ui(raw_input, |ui| {\n            if let Some(viewport_ui_cb) = viewport_ui_cb {\n                // Child viewport\n                if is_visible {\n                    profiling::scope!(\"viewport_callback\");\n                    viewport_ui_cb(ui);\n                }\n            } else {\n                {\n                    profiling::scope!(\"App::logic\");\n                    app.logic(ui.ctx(), &mut self.frame);\n                }\n\n                if is_visible {\n                    {\n                        profiling::scope!(\"App::update\");\n                        #[expect(deprecated)]\n                        app.update(ui.ctx(), &mut self.frame);\n                    }\n\n                    {\n                        profiling::scope!(\"App::ui\");\n                        app.ui(ui, &mut self.frame);\n                    }\n                }\n            }\n        });\n\n        let is_root_viewport = viewport_ui_cb.is_none();\n        if is_root_viewport && close_requested {\n            let canceled = full_output.viewport_output[&ViewportId::ROOT]\n                .commands\n                .contains(&egui::ViewportCommand::CancelClose);\n            if canceled {\n                log::debug!(\"Closing of root viewport canceled with ViewportCommand::CancelClose\");\n            } else {\n                log::debug!(\"Closing root viewport (ViewportCommand::CancelClose was not sent)\");\n                self.close = true;\n            }\n        }\n\n        self.pending_full_output.append(full_output);\n        std::mem::take(&mut self.pending_full_output)\n    }\n\n    pub fn report_frame_time(&mut self, seconds: f32) {\n        self.frame.info.cpu_usage = Some(seconds);\n    }\n\n    pub fn post_rendering(&mut self, window: &winit::window::Window) {\n        profiling::function_scope!();\n        if std::mem::take(&mut self.is_first_frame) {\n            // We keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279\n            window.set_visible(true);\n        }\n    }\n\n    // ------------------------------------------------------------------------\n    // Persistence stuff:\n\n    pub fn maybe_autosave(\n        &mut self,\n        app: &mut dyn epi::App,\n        window: Option<&winit::window::Window>,\n    ) {\n        let now = Instant::now();\n        if now - self.last_auto_save > app.auto_save_interval() {\n            self.save(app, window);\n            self.last_auto_save = now;\n        }\n    }\n\n    pub fn save(&mut self, app: &mut dyn epi::App, window: Option<&winit::window::Window>) {\n        #[cfg(not(feature = \"persistence\"))]\n        let _ = (self, app, window);\n\n        #[cfg(feature = \"persistence\")]\n        if let Some(storage) = self.frame.storage_mut() {\n            profiling::function_scope!();\n\n            if let Some(window) = window\n                && self.persist_window\n            {\n                profiling::scope!(\"native_window\");\n                epi::set_value(\n                    storage,\n                    STORAGE_WINDOW_KEY,\n                    &WindowSettings::from_window(self.egui_ctx.zoom_factor(), window),\n                );\n            }\n            if app.persist_egui_memory() {\n                profiling::scope!(\"egui_memory\");\n                self.egui_ctx\n                    .memory(|mem| epi::set_value(storage, STORAGE_EGUI_MEMORY_KEY, mem));\n            }\n            {\n                profiling::scope!(\"App::save\");\n                app.save(storage);\n            }\n\n            profiling::scope!(\"Storage::flush\");\n            storage.flush();\n        }\n    }\n}\n\nfn load_default_egui_icon() -> egui::IconData {\n    profiling::function_scope!();\n    #[expect(clippy::unwrap_used)]\n    crate::icon_data::from_png_bytes(&include_bytes!(\"../../data/icon.png\")[..]).unwrap()\n}\n\n#[cfg(feature = \"persistence\")]\nconst STORAGE_EGUI_MEMORY_KEY: &str = \"egui\";\n\n#[cfg(feature = \"persistence\")]\nconst STORAGE_WINDOW_KEY: &str = \"window\";\n\npub fn load_window_settings(_storage: Option<&dyn epi::Storage>) -> Option<WindowSettings> {\n    profiling::function_scope!();\n    #[cfg(feature = \"persistence\")]\n    {\n        epi::get_value(_storage?, STORAGE_WINDOW_KEY)\n    }\n    #[cfg(not(feature = \"persistence\"))]\n    None\n}\n\npub fn load_egui_memory(_storage: Option<&dyn epi::Storage>) -> Option<egui::Memory> {\n    profiling::function_scope!();\n    #[cfg(feature = \"persistence\")]\n    {\n        epi::get_value(_storage?, STORAGE_EGUI_MEMORY_KEY)\n    }\n    #[cfg(not(feature = \"persistence\"))]\n    None\n}\n"
  },
  {
    "path": "crates/eframe/src/native/event_loop_context.rs",
    "content": "use std::cell::Cell;\nuse winit::event_loop::ActiveEventLoop;\n\nthread_local! {\n    static CURRENT_EVENT_LOOP: Cell<Option<*const ActiveEventLoop>> = const { Cell::new(None) };\n}\n\nstruct EventLoopGuard;\n\nimpl EventLoopGuard {\n    fn new(event_loop: &ActiveEventLoop) -> Self {\n        CURRENT_EVENT_LOOP.with(|cell| {\n            assert!(\n                cell.get().is_none(),\n                \"Attempted to set a new event loop while one is already set\"\n            );\n            cell.set(Some(std::ptr::from_ref::<ActiveEventLoop>(event_loop)));\n        });\n        Self\n    }\n}\n\nimpl Drop for EventLoopGuard {\n    fn drop(&mut self) {\n        CURRENT_EVENT_LOOP.with(|cell| cell.set(None));\n    }\n}\n\n// Helper function to safely use the current event loop\n#[expect(unsafe_code)]\npub fn with_current_event_loop<F, R>(f: F) -> Option<R>\nwhere\n    F: FnOnce(&ActiveEventLoop) -> R,\n{\n    CURRENT_EVENT_LOOP.with(|cell| {\n        cell.get().map(|ptr| {\n            // SAFETY:\n            // 1. The pointer is guaranteed to be valid when it's Some, as the EventLoopGuard that created it\n            //    lives at least as long as the reference, and clears it when it's dropped. Only run_with_event_loop creates\n            //    a new EventLoopGuard, and does not leak it.\n            // 2. Since the pointer was created from a borrow which lives at least as long as this pointer there are\n            //    no mutable references to the ActiveEventLoop.\n            let event_loop = unsafe { &*ptr };\n            f(event_loop)\n        })\n    })\n}\n\n// The only public interface to use the event loop\npub fn with_event_loop_context(event_loop: &ActiveEventLoop, f: impl FnOnce()) {\n    // NOTE: For safety, this guard must NOT be leaked.\n    let _guard = EventLoopGuard::new(event_loop);\n    f();\n}\n"
  },
  {
    "path": "crates/eframe/src/native/file_storage.rs",
    "content": "use std::{\n    collections::HashMap,\n    io::Write as _,\n    path::{Path, PathBuf},\n};\n\n/// The folder where `eframe` will store its state.\n///\n/// The given `app_id` is either the\n/// [`egui::ViewportBuilder::app_id`] of [`crate::NativeOptions::viewport`]\n/// or the title argument to [`crate::run_native`].\n///\n/// On native, the path is:\n/// * Linux:   `/home/UserName/.local/share/APP_ID`\n/// * macOS:   `/Users/UserName/Library/Application Support/APP_ID`\n/// * Windows: `C:\\Users\\UserName\\AppData\\Roaming\\APP_ID\\data`\npub fn storage_dir(app_id: &str) -> Option<PathBuf> {\n    use egui::os::OperatingSystem as OS;\n    use std::env::var_os;\n    match OS::from_target_os() {\n        OS::Nix => var_os(\"XDG_DATA_HOME\")\n            .map(PathBuf::from)\n            .filter(|p| p.is_absolute())\n            .or_else(|| home::home_dir().map(|p| p.join(\".local\").join(\"share\")))\n            .map(|p| {\n                p.join(\n                    app_id\n                        .to_lowercase()\n                        .replace(|c: char| c.is_ascii_whitespace(), \"\"),\n                )\n            }),\n        OS::Mac => home::home_dir().map(|p| {\n            p.join(\"Library\")\n                .join(\"Application Support\")\n                .join(app_id.replace(|c: char| c.is_ascii_whitespace(), \"-\"))\n        }),\n        OS::Windows => roaming_appdata().map(|p| p.join(app_id).join(\"data\")),\n        OS::Unknown | OS::Android | OS::IOS => None,\n    }\n}\n\n// Adapted from\n// https://github.com/rust-lang/cargo/blob/6e11c77384989726bb4f412a0e23b59c27222c34/crates/home/src/windows.rs#L19-L37\n#[cfg(all(windows, not(target_vendor = \"uwp\")))]\n#[expect(unsafe_code)]\nfn roaming_appdata() -> Option<PathBuf> {\n    use std::ffi::OsString;\n    use std::os::windows::ffi::OsStringExt as _;\n    use std::ptr;\n    use std::slice;\n\n    use windows_sys::Win32::Foundation::S_OK;\n    use windows_sys::Win32::System::Com::CoTaskMemFree;\n    use windows_sys::Win32::UI::Shell::{\n        FOLDERID_RoamingAppData, KF_FLAG_DONT_VERIFY, SHGetKnownFolderPath,\n    };\n\n    unsafe extern \"C\" {\n        fn wcslen(buf: *const u16) -> usize;\n    }\n    let mut path_raw = ptr::null_mut();\n\n    // SAFETY: SHGetKnownFolderPath allocates for us, we don't pass any pointers to it.\n    // See https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath\n    let result = unsafe {\n        SHGetKnownFolderPath(\n            &FOLDERID_RoamingAppData,\n            KF_FLAG_DONT_VERIFY as u32,\n            std::ptr::null_mut(),\n            &mut path_raw,\n        )\n    };\n\n    let path = if result == S_OK {\n        // SAFETY: SHGetKnownFolderPath indicated success and is supposed to allocate a null-terminated string for us.\n        let path_slice = unsafe { slice::from_raw_parts(path_raw, wcslen(path_raw)) };\n        Some(PathBuf::from(OsString::from_wide(path_slice)))\n    } else {\n        None\n    };\n\n    // SAFETY:\n    // This memory got allocated by SHGetKnownFolderPath, we didn't touch anything in the process.\n    // A null ptr is a no-op for `CoTaskMemFree`, so in case this failed we're still good.\n    // https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemfree\n    unsafe { CoTaskMemFree(path_raw.cast()) };\n\n    path\n}\n\n#[cfg(any(not(windows), target_vendor = \"uwp\"))]\nfn roaming_appdata() -> Option<PathBuf> {\n    None\n}\n\n// ----------------------------------------------------------------------------\n\n/// A key-value store backed by a [RON](https://github.com/ron-rs/ron) file on disk.\n/// Used to restore egui state, glow window position/size and app state.\npub struct FileStorage {\n    ron_filepath: PathBuf,\n    kv: HashMap<String, String>,\n    dirty: bool,\n    last_save_join_handle: Option<std::thread::JoinHandle<()>>,\n}\n\nimpl Drop for FileStorage {\n    fn drop(&mut self) {\n        if let Some(join_handle) = self.last_save_join_handle.take() {\n            profiling::scope!(\"wait_for_save\");\n            join_handle.join().ok();\n        }\n    }\n}\n\nimpl FileStorage {\n    /// Store the state in this .ron file.\n    pub(crate) fn from_ron_filepath(ron_filepath: impl Into<PathBuf>) -> Self {\n        profiling::function_scope!();\n        let ron_filepath: PathBuf = ron_filepath.into();\n        log::debug!(\"Loading app state from {}…\", ron_filepath.display());\n        Self {\n            kv: read_ron(&ron_filepath).unwrap_or_default(),\n            ron_filepath,\n            dirty: false,\n            last_save_join_handle: None,\n        }\n    }\n\n    /// Find a good place to put the files that the OS likes.\n    pub fn from_app_id(app_id: &str) -> Option<Self> {\n        profiling::function_scope!();\n        if let Some(data_dir) = storage_dir(app_id) {\n            if let Err(err) = std::fs::create_dir_all(&data_dir) {\n                log::warn!(\n                    \"Saving disabled: Failed to create app path at {}: {err}\",\n                    data_dir.display()\n                );\n                None\n            } else {\n                Some(Self::from_ron_filepath(data_dir.join(\"app.ron\")))\n            }\n        } else {\n            log::warn!(\"Saving disabled: Failed to find path to data_dir.\");\n            None\n        }\n    }\n}\n\nimpl crate::Storage for FileStorage {\n    fn get_string(&self, key: &str) -> Option<String> {\n        self.kv.get(key).cloned()\n    }\n\n    fn set_string(&mut self, key: &str, value: String) {\n        if self.kv.get(key) != Some(&value) {\n            self.kv.insert(key.to_owned(), value);\n            self.dirty = true;\n        }\n    }\n\n    fn flush(&mut self) {\n        if self.dirty {\n            profiling::scope!(\"FileStorage::flush\");\n            self.dirty = false;\n\n            let file_path = self.ron_filepath.clone();\n            let kv = self.kv.clone();\n\n            if let Some(join_handle) = self.last_save_join_handle.take() {\n                // wait for previous save to complete.\n                join_handle.join().ok();\n            }\n\n            let result = std::thread::Builder::new()\n                .name(\"eframe_persist\".to_owned())\n                .spawn(move || {\n                    save_to_disk(&file_path, &kv);\n                });\n            match result {\n                Ok(join_handle) => {\n                    self.last_save_join_handle = Some(join_handle);\n                }\n                Err(err) => {\n                    log::warn!(\"Failed to spawn thread to save app state: {err}\");\n                }\n            }\n        }\n    }\n}\n\nfn save_to_disk(file_path: &PathBuf, kv: &HashMap<String, String>) {\n    profiling::function_scope!();\n\n    if let Some(parent_dir) = file_path.parent()\n        && !parent_dir.exists()\n        && let Err(err) = std::fs::create_dir_all(parent_dir)\n    {\n        log::warn!(\"Failed to create directory {}: {err}\", parent_dir.display());\n    }\n\n    match std::fs::File::create(file_path) {\n        Ok(file) => {\n            let mut writer = std::io::BufWriter::new(file);\n            let config = Default::default();\n\n            profiling::scope!(\"ron::serialize\");\n            if let Err(err) = ron::Options::default()\n                .to_io_writer_pretty(&mut writer, &kv, config)\n                .and_then(|_| writer.flush().map_err(|err| err.into()))\n            {\n                log::warn!(\"Failed to serialize app state: {err}\");\n            } else {\n                log::trace!(\"Persisted to {}\", file_path.display());\n            }\n        }\n        Err(err) => {\n            log::warn!(\"Failed to create file {}: {err}\", file_path.display());\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nfn read_ron<T>(ron_path: impl AsRef<Path>) -> Option<T>\nwhere\n    T: serde::de::DeserializeOwned,\n{\n    profiling::function_scope!();\n    match std::fs::File::open(ron_path) {\n        Ok(file) => {\n            let reader = std::io::BufReader::new(file);\n            match ron::de::from_reader(reader) {\n                Ok(value) => Some(value),\n                Err(err) => {\n                    log::warn!(\"Failed to parse RON: {err}\");\n                    None\n                }\n            }\n        }\n        Err(_err) => {\n            // File probably doesn't exist. That's fine.\n            None\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn directories_storage_dir(app_id: &str) -> Option<PathBuf> {\n        directories::ProjectDirs::from(\"\", \"\", app_id)\n            .map(|proj_dirs| proj_dirs.data_dir().to_path_buf())\n    }\n\n    #[test]\n    fn storage_path_matches_directories() {\n        use super::storage_dir;\n        for app_id in [\n            \"MyApp\", \"My App\", \"my_app\", \"my-app\", \"My.App\", \"my/app\", \"my:app\", r\"my\\app\",\n        ] {\n            assert_eq!(directories_storage_dir(app_id), storage_dir(app_id));\n        }\n    }\n}\n"
  },
  {
    "path": "crates/eframe/src/native/glow_integration.rs",
    "content": "//! Note that this file contains code very similar to [`super::wgpu_integration`].\n//! When making changes to one you often also want to apply it to the other.\n//!\n//! This is also very complex code, and not very pretty.\n//! There is a bunch of improvements we could do,\n//! like removing a bunch of `unwraps`.\n\n#![expect(clippy::undocumented_unsafe_blocks)]\n#![expect(clippy::unwrap_used)]\n\nuse std::{cell::RefCell, num::NonZeroU32, rc::Rc, sync::Arc, time::Instant};\n\nuse egui_winit::ActionRequested;\nuse glutin::{\n    config::GlConfig as _,\n    context::NotCurrentGlContext as _,\n    display::GetGlDisplay as _,\n    prelude::{GlDisplay as _, PossiblyCurrentGlContext as _},\n    surface::GlSurface as _,\n};\nuse raw_window_handle::HasWindowHandle as _;\nuse winit::{\n    event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy},\n    window::{Window, WindowId},\n};\n\nuse ahash::HashMap;\nuse egui::{\n    DeferredViewportUiCallback, ImmediateViewport, OrderedViewportIdMap, ViewportBuilder,\n    ViewportClass, ViewportId, ViewportIdPair, ViewportInfo, ViewportOutput,\n};\n#[cfg(feature = \"accesskit\")]\nuse egui_winit::accesskit_winit;\n\nuse crate::{\n    App, AppCreator, CreationContext, NativeOptions, Result, Storage,\n    native::epi_integration::EpiIntegration,\n};\n\nuse super::{\n    epi_integration, event_loop_context,\n    winit_integration::{EventResult, UserEvent, WinitApp, create_egui_context},\n};\n\n// ----------------------------------------------------------------------------\n// Types:\n\npub struct GlowWinitApp<'app> {\n    repaint_proxy: Arc<egui::mutex::Mutex<EventLoopProxy<UserEvent>>>,\n    app_name: String,\n    native_options: NativeOptions,\n    running: Option<GlowWinitRunning<'app>>,\n\n    // Note that since this `AppCreator` is FnOnce we are currently unable to support\n    // re-initializing the `GlowWinitRunning` state on Android if the application\n    // suspends and resumes.\n    app_creator: Option<AppCreator<'app>>,\n}\n\n/// State that is initialized when the application is first starts running via\n/// a Resumed event. On Android this ensures that any graphics state is only\n/// initialized once the application has an associated `SurfaceView`.\nstruct GlowWinitRunning<'app> {\n    integration: EpiIntegration,\n    app: Box<dyn 'app + App>,\n\n    // These needs to be shared with the immediate viewport renderer, hence the Rc/Arc/RefCells:\n    glutin: Rc<RefCell<GlutinWindowContext>>,\n\n    // NOTE: one painter shared by all viewports.\n    painter: Rc<RefCell<egui_glow::Painter>>,\n}\n\n/// This struct will contain both persistent and temporary glutin state.\n///\n/// Platform Quirks:\n/// * Microsoft Windows: requires that we create a window before opengl context.\n/// * Android: window and surface should be destroyed when we receive a suspend event. recreate on resume event.\n///\n/// winit guarantees that we will get a Resumed event on startup on all platforms.\n/// * Before Resumed event: `gl_config`, `gl_context` can be created at any time. on windows, a window must be created to get `gl_context`.\n/// * Resumed: `gl_surface` will be created here. `window` will be re-created here for android.\n/// * Suspended: on android, we drop window + surface.  on other platforms, we don't get Suspended event.\n///\n/// The setup is divided between the `new` fn and `on_resume` fn. we can just assume that `on_resume` is a continuation of\n/// `new` fn on all platforms. only on android, do we get multiple resumed events because app can be suspended.\nstruct GlutinWindowContext {\n    egui_ctx: egui::Context,\n\n    swap_interval: glutin::surface::SwapInterval,\n    gl_config: glutin::config::Config,\n\n    max_texture_side: Option<usize>,\n\n    current_gl_context: Option<glutin::context::PossiblyCurrentContext>,\n    not_current_gl_context: Option<glutin::context::NotCurrentContext>,\n\n    viewports: OrderedViewportIdMap<Viewport>,\n    viewport_from_window: HashMap<WindowId, ViewportId>,\n    window_from_viewport: OrderedViewportIdMap<WindowId>,\n\n    focused_viewport: Option<ViewportId>,\n}\n\nstruct Viewport {\n    ids: ViewportIdPair,\n    class: ViewportClass,\n    builder: ViewportBuilder,\n    deferred_commands: Vec<egui::viewport::ViewportCommand>,\n    info: ViewportInfo,\n    actions_requested: Vec<egui_winit::ActionRequested>,\n\n    /// The user-callback that shows the ui.\n    /// None for immediate viewports.\n    viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,\n\n    // These three live and die together.\n    // TODO(emilk): clump them together into one struct!\n    gl_surface: Option<glutin::surface::Surface<glutin::surface::WindowSurface>>,\n    window: Option<Arc<Window>>,\n    egui_winit: Option<egui_winit::State>,\n}\n\n// ----------------------------------------------------------------------------\n\nimpl<'app> GlowWinitApp<'app> {\n    pub fn new(\n        event_loop: &EventLoop<UserEvent>,\n        app_name: &str,\n        native_options: NativeOptions,\n        app_creator: AppCreator<'app>,\n    ) -> Self {\n        profiling::function_scope!();\n        Self {\n            repaint_proxy: Arc::new(egui::mutex::Mutex::new(event_loop.create_proxy())),\n            app_name: app_name.to_owned(),\n            native_options,\n            running: None,\n            app_creator: Some(app_creator),\n        }\n    }\n\n    #[expect(unsafe_code)]\n    fn create_glutin_windowed_context(\n        egui_ctx: &egui::Context,\n        event_loop: &ActiveEventLoop,\n        storage: Option<&dyn Storage>,\n        native_options: &mut NativeOptions,\n    ) -> Result<(GlutinWindowContext, egui_glow::Painter)> {\n        profiling::function_scope!();\n        let window_settings = epi_integration::load_window_settings(storage);\n\n        let winit_window_builder = epi_integration::viewport_builder(\n            egui_ctx.zoom_factor(),\n            event_loop,\n            native_options,\n            window_settings,\n        )\n        .with_visible(false); // Start hidden until we render the first frame to fix white flash on startup (https://github.com/emilk/egui/pull/3631)\n\n        let mut glutin_window_context = unsafe {\n            GlutinWindowContext::new(egui_ctx, winit_window_builder, native_options, event_loop)?\n        };\n\n        // Creates the window - must come before we create our glow context\n        glutin_window_context.initialize_window(ViewportId::ROOT, event_loop)?;\n\n        {\n            let viewport = &glutin_window_context.viewports[&ViewportId::ROOT];\n            let window = viewport.window.as_ref().unwrap(); // Can't fail - we just called `initialize_all_viewports`\n            epi_integration::apply_window_settings(window, window_settings);\n        }\n\n        let gl = unsafe {\n            profiling::scope!(\"glow::Context::from_loader_function\");\n            Arc::new(glow::Context::from_loader_function(|s| {\n                let s = std::ffi::CString::new(s)\n                    .expect(\"failed to construct C string from string for gl proc address\");\n\n                glutin_window_context.get_proc_address(&s)\n            }))\n        };\n\n        let painter = egui_glow::Painter::new(\n            gl,\n            \"\",\n            native_options.shader_version,\n            native_options.dithering,\n        )?;\n\n        Ok((glutin_window_context, painter))\n    }\n\n    fn init_run_state(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n    ) -> Result<&mut GlowWinitRunning<'app>> {\n        profiling::function_scope!();\n\n        let storage = if let Some(file) = &self.native_options.persistence_path {\n            epi_integration::create_storage_with_file(file)\n        } else {\n            epi_integration::create_storage(\n                self.native_options\n                    .viewport\n                    .app_id\n                    .as_ref()\n                    .unwrap_or(&self.app_name),\n            )\n        };\n\n        let egui_ctx = create_egui_context(storage.as_deref());\n\n        let (mut glutin, painter) = Self::create_glutin_windowed_context(\n            &egui_ctx,\n            event_loop,\n            storage.as_deref(),\n            &mut self.native_options,\n        )?;\n        let gl = Arc::clone(painter.gl());\n\n        let max_texture_side = painter.max_texture_side();\n        glutin.max_texture_side = Some(max_texture_side);\n        for viewport in glutin.viewports.values_mut() {\n            if let Some(egui_winit) = viewport.egui_winit.as_mut() {\n                egui_winit.set_max_texture_side(max_texture_side);\n            }\n        }\n\n        let painter = Rc::new(RefCell::new(painter));\n\n        let integration = EpiIntegration::new(\n            egui_ctx,\n            &glutin.window(ViewportId::ROOT),\n            &self.app_name,\n            &self.native_options,\n            storage,\n            Some(Arc::clone(&gl)),\n            Some(Box::new({\n                let painter = Rc::clone(&painter);\n                move |native| painter.borrow_mut().register_native_texture(native)\n            })),\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            None,\n        );\n\n        {\n            let event_loop_proxy = Arc::clone(&self.repaint_proxy);\n            integration\n                .egui_ctx\n                .set_request_repaint_callback(move |info| {\n                    log::trace!(\"request_repaint_callback: {info:?}\");\n                    let when = Instant::now() + info.delay;\n                    let cumulative_pass_nr = info.current_cumulative_pass_nr;\n                    event_loop_proxy\n                        .lock()\n                        .send_event(UserEvent::RequestRepaint {\n                            viewport_id: info.viewport_id,\n                            when,\n                            cumulative_pass_nr,\n                        })\n                        .ok();\n                });\n        }\n\n        #[cfg(feature = \"accesskit\")]\n        {\n            let event_loop_proxy = self.repaint_proxy.lock().clone();\n            let viewport = glutin.viewports.get_mut(&ViewportId::ROOT).unwrap(); // we always have a root\n            if let Viewport {\n                window: Some(window),\n                egui_winit: Some(egui_winit),\n                ..\n            } = viewport\n            {\n                egui_winit.init_accesskit(event_loop, window, event_loop_proxy);\n            }\n        }\n\n        if self\n            .native_options\n            .viewport\n            .mouse_passthrough\n            .unwrap_or(false)\n            && let Err(err) = glutin.window(ViewportId::ROOT).set_cursor_hittest(false)\n        {\n            log::warn!(\"set_cursor_hittest(false) failed: {err}\");\n        }\n\n        let app_creator = std::mem::take(&mut self.app_creator)\n            .expect(\"Single-use AppCreator has unexpectedly already been taken\");\n\n        let app: Box<dyn 'app + App> = {\n            // Use latest raw_window_handle for eframe compatibility\n            use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _};\n\n            let gl_config = glutin.gl_config.clone();\n            let get_proc_address = move |addr: &_| gl_config.display().get_proc_address(addr);\n            let window = glutin.window(ViewportId::ROOT);\n            let cc = CreationContext {\n                egui_ctx: integration.egui_ctx.clone(),\n                integration_info: integration.frame.info().clone(),\n                storage: integration.frame.storage(),\n                gl: Some(gl),\n                get_proc_address: Some(Arc::new(get_proc_address)),\n                #[cfg(feature = \"wgpu_no_default_features\")]\n                wgpu_render_state: None,\n                raw_display_handle: window.display_handle().map(|h| h.as_raw()),\n                raw_window_handle: window.window_handle().map(|h| h.as_raw()),\n            };\n            profiling::scope!(\"app_creator\");\n            app_creator(&cc).map_err(crate::Error::AppCreation)?\n        };\n\n        let glutin = Rc::new(RefCell::new(glutin));\n\n        {\n            // Create weak pointers so that we don't keep\n            // state alive for too long.\n            let glutin = Rc::downgrade(&glutin);\n            let painter = Rc::downgrade(&painter);\n            let beginning = integration.beginning;\n\n            egui::Context::set_immediate_viewport_renderer(move |egui_ctx, immediate_viewport| {\n                if let (Some(glutin), Some(painter)) = (glutin.upgrade(), painter.upgrade()) {\n                    render_immediate_viewport(\n                        egui_ctx,\n                        &glutin,\n                        &painter,\n                        beginning,\n                        immediate_viewport,\n                    );\n                } else {\n                    log::warn!(\"render_sync_callback called after window closed\");\n                }\n            });\n        }\n\n        Ok(self.running.insert(GlowWinitRunning {\n            integration,\n            app,\n            glutin,\n            painter,\n        }))\n    }\n}\n\nimpl WinitApp for GlowWinitApp<'_> {\n    fn egui_ctx(&self) -> Option<&egui::Context> {\n        self.running.as_ref().map(|r| &r.integration.egui_ctx)\n    }\n\n    fn window(&self, window_id: WindowId) -> Option<Arc<Window>> {\n        let running = self.running.as_ref()?;\n        let glutin = running.glutin.borrow();\n        let viewport_id = *glutin.viewport_from_window.get(&window_id)?;\n        if let Some(viewport) = glutin.viewports.get(&viewport_id) {\n            viewport.window.clone()\n        } else {\n            None\n        }\n    }\n\n    fn window_id_from_viewport_id(&self, id: ViewportId) -> Option<WindowId> {\n        self.running\n            .as_ref()?\n            .glutin\n            .borrow()\n            .window_from_viewport\n            .get(&id)\n            .copied()\n    }\n\n    fn save(&mut self) {\n        log::debug!(\"WinitApp::save called\");\n        if let Some(running) = self.running.as_mut() {\n            profiling::function_scope!();\n\n            // This is used because of the \"save on suspend\" logic on Android. Once the application is suspended, there is no window associated to it, which was causing panics when `.window().expect()` was used.\n            let window_opt = running.glutin.borrow().window_opt(ViewportId::ROOT);\n\n            running\n                .integration\n                .save(running.app.as_mut(), window_opt.as_deref());\n        }\n    }\n\n    fn save_and_destroy(&mut self) {\n        if let Some(mut running) = self.running.take() {\n            profiling::function_scope!();\n\n            running.integration.save(\n                running.app.as_mut(),\n                Some(&running.glutin.borrow().window(ViewportId::ROOT)),\n            );\n            running.app.on_exit(Some(running.painter.borrow().gl()));\n            running.painter.borrow_mut().destroy();\n        }\n    }\n\n    fn run_ui_and_paint(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        window_id: WindowId,\n    ) -> Result<EventResult> {\n        if let Some(running) = &mut self.running {\n            running.run_ui_and_paint(event_loop, window_id)\n        } else {\n            Ok(EventResult::Wait)\n        }\n    }\n\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) -> crate::Result<EventResult> {\n        log::debug!(\"Event::Resumed\");\n\n        let running = if let Some(running) = &mut self.running {\n            // Not the first resume event. Create all outstanding windows.\n            running\n                .glutin\n                .borrow_mut()\n                .initialize_all_windows(event_loop);\n            running\n        } else {\n            // First resume event. Create our root window etc.\n            self.init_run_state(event_loop)?\n        };\n        let window_id = running.glutin.borrow().window_from_viewport[&ViewportId::ROOT];\n        Ok(EventResult::RepaintNow(window_id))\n    }\n\n    fn suspended(&mut self, _: &ActiveEventLoop) -> crate::Result<EventResult> {\n        if let Some(running) = &mut self.running {\n            running.glutin.borrow_mut().on_suspend()?;\n        }\n        Ok(EventResult::Save)\n    }\n\n    fn device_event(\n        &mut self,\n        _: &ActiveEventLoop,\n        _: winit::event::DeviceId,\n        event: winit::event::DeviceEvent,\n    ) -> crate::Result<EventResult> {\n        if let winit::event::DeviceEvent::MouseMotion { delta } = event\n            && let Some(running) = &mut self.running\n        {\n            let mut glutin = running.glutin.borrow_mut();\n            if let Some(viewport) = glutin\n                .focused_viewport\n                .and_then(|viewport| glutin.viewports.get_mut(&viewport))\n                && let Some(window) = viewport.window.as_ref()\n            {\n                if !window.has_focus()\n                    && !viewport\n                        .egui_winit\n                        .as_ref()\n                        .map(|state| state.is_any_pointer_button_down())\n                        .unwrap_or(false)\n                {\n                    return Ok(EventResult::Wait);\n                }\n\n                if let Some(egui_winit) = viewport.egui_winit.as_mut()\n                    && egui_winit.on_mouse_motion(delta)\n                {\n                    return Ok(EventResult::RepaintNext(window.id()));\n                }\n            }\n        }\n\n        Ok(EventResult::Wait)\n    }\n\n    fn window_event(\n        &mut self,\n        _: &ActiveEventLoop,\n        window_id: WindowId,\n        event: winit::event::WindowEvent,\n    ) -> Result<EventResult> {\n        if let Some(running) = &mut self.running {\n            Ok(running.on_window_event(window_id, &event))\n        } else {\n            Ok(EventResult::Exit)\n        }\n    }\n\n    #[cfg(feature = \"accesskit\")]\n    fn on_accesskit_event(&mut self, event: accesskit_winit::Event) -> crate::Result<EventResult> {\n        use super::winit_integration;\n\n        if let Some(running) = &self.running {\n            let mut glutin = running.glutin.borrow_mut();\n            if let Some(viewport_id) = glutin.viewport_from_window.get(&event.window_id).copied()\n                && let Some(viewport) = glutin.viewports.get_mut(&viewport_id)\n                && let Some(egui_winit) = &mut viewport.egui_winit\n            {\n                return Ok(winit_integration::on_accesskit_window_event(\n                    egui_winit,\n                    event.window_id,\n                    &event.window_event,\n                ));\n            }\n        }\n\n        Ok(EventResult::Wait)\n    }\n}\n\nimpl GlowWinitRunning<'_> {\n    fn run_ui_and_paint(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        window_id: WindowId,\n    ) -> Result<EventResult> {\n        profiling::function_scope!();\n\n        let Some(viewport_id) = self\n            .glutin\n            .borrow()\n            .viewport_from_window\n            .get(&window_id)\n            .copied()\n        else {\n            return Ok(EventResult::Wait);\n        };\n\n        profiling::finish_frame!();\n\n        let mut frame_timer = crate::stopwatch::Stopwatch::new();\n        frame_timer.start();\n\n        {\n            let glutin = self.glutin.borrow();\n            let viewport = &glutin.viewports[&viewport_id];\n            let is_immediate = viewport.viewport_ui_cb.is_none();\n            if is_immediate && viewport_id != ViewportId::ROOT {\n                // This will only happen if this is an immediate viewport.\n                // That means that the viewport cannot be rendered by itself and needs his parent to be rendered.\n                if let Some(parent_viewport) = glutin.viewports.get(&viewport.ids.parent)\n                    && let Some(window) = parent_viewport.window.as_ref()\n                {\n                    return Ok(EventResult::RepaintNext(window.id()));\n                }\n                return Ok(EventResult::Wait);\n            }\n        }\n\n        let (raw_input, viewport_ui_cb, is_visible) = {\n            let mut glutin = self.glutin.borrow_mut();\n            let egui_ctx = glutin.egui_ctx.clone();\n            let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else {\n                return Ok(EventResult::Wait);\n            };\n            let Some(window) = viewport.window.as_ref() else {\n                return Ok(EventResult::Wait);\n            };\n            egui_winit::update_viewport_info(&mut viewport.info, &egui_ctx, window, false);\n\n            let is_visible = viewport.info.visible().unwrap_or(true);\n\n            let Some(egui_winit) = viewport.egui_winit.as_mut() else {\n                return Ok(EventResult::Wait);\n            };\n            let mut raw_input = egui_winit.take_egui_input(window);\n            let viewport_ui_cb = viewport.viewport_ui_cb.clone();\n\n            self.integration.pre_update();\n\n            raw_input.time = Some(self.integration.beginning.elapsed().as_secs_f64());\n            raw_input.viewports = glutin\n                .viewports\n                .iter()\n                .map(|(id, viewport)| (*id, viewport.info.clone()))\n                .collect();\n\n            (raw_input, viewport_ui_cb, is_visible)\n        };\n\n        // HACK: In order to get the right clear_color, the system theme needs to be set, which\n        // usually only happens in the `update` call. So we call Options::begin_pass early\n        // to set the right theme. Without this there would be a black flash on the first frame.\n        self.integration\n            .egui_ctx\n            .options_mut(|opt| opt.begin_pass(&raw_input));\n        let clear_color = self\n            .app\n            .clear_color(&self.integration.egui_ctx.global_style().visuals);\n\n        let has_many_viewports = self.glutin.borrow().viewports.len() > 1;\n        let clear_before_update = !has_many_viewports; // HACK: for some reason, an early clear doesn't \"take\" on Mac with multiple viewports.\n\n        if is_visible && clear_before_update {\n            // clear before we call update, so users can paint between clear-color and egui windows:\n\n            let mut glutin = self.glutin.borrow_mut();\n            let GlutinWindowContext {\n                viewports,\n                current_gl_context,\n                not_current_gl_context,\n                ..\n            } = &mut *glutin;\n            let viewport = &viewports[&viewport_id];\n            let Some(window) = viewport.window.as_ref() else {\n                return Ok(EventResult::Wait);\n            };\n            let Some(gl_surface) = viewport.gl_surface.as_ref() else {\n                return Ok(EventResult::Wait);\n            };\n\n            let screen_size_in_pixels: [u32; 2] = window.inner_size().into();\n\n            {\n                frame_timer.pause();\n                change_gl_context(current_gl_context, not_current_gl_context, gl_surface);\n                frame_timer.resume();\n            }\n\n            self.painter\n                .borrow()\n                .clear(screen_size_in_pixels, clear_color);\n        }\n\n        // ------------------------------------------------------------\n        // The update function, which could call immediate viewports,\n        // so make sure we don't hold any locks here required by the immediate viewports rendeer.\n\n        let full_output = self.integration.update(\n            self.app.as_mut(),\n            viewport_ui_cb.as_deref(),\n            raw_input,\n            is_visible,\n        );\n\n        // ------------------------------------------------------------\n\n        let Self {\n            integration,\n            app,\n            glutin,\n            painter,\n            ..\n        } = self;\n\n        let mut glutin = glutin.borrow_mut();\n        let mut painter = painter.borrow_mut();\n\n        let egui::FullOutput {\n            platform_output,\n            textures_delta,\n            shapes,\n            pixels_per_point,\n            viewport_output,\n        } = full_output;\n\n        glutin.remove_viewports_not_in(&viewport_output);\n\n        let GlutinWindowContext {\n            viewports,\n            current_gl_context,\n            not_current_gl_context,\n            ..\n        } = &mut *glutin;\n\n        let Some(viewport) = viewports.get_mut(&viewport_id) else {\n            return Ok(EventResult::Wait);\n        };\n\n        viewport.info.events.clear(); // they should have been processed\n        let window = viewport.window.clone().unwrap();\n        let gl_surface = viewport.gl_surface.as_ref().unwrap();\n        let egui_winit = viewport.egui_winit.as_mut().unwrap();\n\n        egui_winit.handle_platform_output(&window, platform_output);\n\n        if is_visible {\n            let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);\n\n            {\n                // We may need to switch contexts again, because of immediate viewports:\n                frame_timer.pause();\n                change_gl_context(current_gl_context, not_current_gl_context, gl_surface);\n                frame_timer.resume();\n            }\n\n            let screen_size_in_pixels: [u32; 2] = window.inner_size().into();\n\n            if !clear_before_update {\n                painter.clear(screen_size_in_pixels, clear_color);\n            }\n\n            painter.paint_and_update_textures(\n                screen_size_in_pixels,\n                pixels_per_point,\n                &clipped_primitives,\n                &textures_delta,\n            );\n\n            {\n                for action in viewport.actions_requested.drain(..) {\n                    match action {\n                        ActionRequested::Screenshot(user_data) => {\n                            let screenshot = painter.read_screen_rgba(screen_size_in_pixels);\n                            egui_winit\n                                .egui_input_mut()\n                                .events\n                                .push(egui::Event::Screenshot {\n                                    viewport_id,\n                                    user_data,\n                                    image: screenshot.into(),\n                                });\n                        }\n                        ActionRequested::Cut => {\n                            egui_winit.egui_input_mut().events.push(egui::Event::Cut);\n                        }\n                        ActionRequested::Copy => {\n                            egui_winit.egui_input_mut().events.push(egui::Event::Copy);\n                        }\n                        ActionRequested::Paste => {\n                            if let Some(contents) = egui_winit.clipboard_text() {\n                                let contents = contents.replace(\"\\r\\n\", \"\\n\");\n                                if !contents.is_empty() {\n                                    egui_winit\n                                        .egui_input_mut()\n                                        .events\n                                        .push(egui::Event::Paste(contents));\n                                }\n                            }\n                        }\n                    }\n                }\n\n                integration.post_rendering(&window);\n            }\n\n            {\n                // vsync - don't count as frame-time:\n                frame_timer.pause();\n                profiling::scope!(\"swap_buffers\");\n                let context = current_gl_context.as_ref().ok_or_else(|| {\n                    egui_glow::PainterError::from(\n                        \"failed to get current context to swap buffers\".to_owned(),\n                    )\n                })?;\n\n                gl_surface.swap_buffers(context)?;\n                frame_timer.resume();\n            }\n\n            // give it time to settle:\n            #[cfg(feature = \"__screenshot\")]\n            if integration.egui_ctx.cumulative_pass_nr() == 2\n                && let Ok(path) = std::env::var(\"EFRAME_SCREENSHOT_TO\")\n            {\n                save_screenshot_and_exit(&path, &painter, screen_size_in_pixels);\n            }\n        }\n\n        glutin.handle_viewport_output(event_loop, &integration.egui_ctx, &viewport_output);\n\n        integration.report_frame_time(frame_timer.total_time_sec()); // don't count auto-save time as part of regular frame time\n\n        integration.maybe_autosave(app.as_mut(), Some(&window));\n\n        if window.is_minimized() == Some(true) {\n            // On Mac, a minimized Window uses up all CPU:\n            // https://github.com/emilk/egui/issues/325\n            profiling::scope!(\"minimized_sleep\");\n            std::thread::sleep(std::time::Duration::from_millis(10));\n        }\n\n        if integration.should_close() {\n            Ok(EventResult::CloseRequested)\n        } else {\n            Ok(EventResult::Wait)\n        }\n    }\n\n    fn on_window_event(\n        &mut self,\n        window_id: WindowId,\n        event: &winit::event::WindowEvent,\n    ) -> EventResult {\n        let mut glutin = self.glutin.borrow_mut();\n        let viewport_id = glutin.viewport_from_window.get(&window_id).copied();\n\n        // On Windows, if a window is resized by the user, it should repaint synchronously, inside the\n        // event handler.\n        //\n        // If this is not done, the compositor will assume that the window does not want to redraw,\n        // and continue ahead.\n        //\n        // In eframe's case, that causes the window to rapidly flicker, as it struggles to deliver\n        // new frames to the compositor in time.\n        //\n        // The flickering is technically glutin or glow's fault, but we should be responding properly\n        // to resizes anyway, as doing so avoids dropping frames.\n        //\n        // See: https://github.com/emilk/egui/issues/903\n        let mut repaint_asap = false;\n\n        match event {\n            winit::event::WindowEvent::Focused(focused) => {\n                let focused = if cfg!(target_os = \"macos\")\n                    && let Some(viewport_id) = viewport_id\n                    && let Some(viewport) = glutin.viewports.get(&viewport_id)\n                    && let Some(window) = &viewport.window\n                {\n                    // TODO(emilk): remove this work-around once we update winit\n                    // https://github.com/rust-windowing/winit/issues/4371\n                    // https://github.com/emilk/egui/issues/7588\n                    window.has_focus()\n                } else {\n                    *focused\n                };\n\n                glutin.focused_viewport = focused.then_some(viewport_id).flatten();\n            }\n\n            winit::event::WindowEvent::Resized(physical_size) => {\n                // Resize with 0 width and height is used by winit to signal a minimize event on Windows.\n                // See: https://github.com/rust-windowing/winit/issues/208\n                // This solves an issue where the app would panic when minimizing on Windows.\n                if 0 < physical_size.width\n                    && 0 < physical_size.height\n                    && let Some(viewport_id) = viewport_id\n                {\n                    repaint_asap = true;\n                    glutin.resize(viewport_id, *physical_size);\n                }\n            }\n\n            winit::event::WindowEvent::Occluded(is_occluded) => {\n                if let Some(viewport_id) = viewport_id\n                    && let Some(viewport) = glutin.viewports.get_mut(&viewport_id)\n                {\n                    viewport.info.occluded = Some(*is_occluded);\n                }\n            }\n\n            winit::event::WindowEvent::CloseRequested => {\n                if viewport_id == Some(ViewportId::ROOT) && self.integration.should_close() {\n                    log::debug!(\n                        \"Received WindowEvent::CloseRequested for main viewport - shutting down.\"\n                    );\n                    return EventResult::CloseRequested;\n                }\n\n                log::debug!(\"Received WindowEvent::CloseRequested for viewport {viewport_id:?}\");\n\n                if let Some(viewport_id) = viewport_id\n                    && let Some(viewport) = glutin.viewports.get_mut(&viewport_id)\n                {\n                    // Tell viewport it should close:\n                    viewport.info.events.push(egui::ViewportEvent::Close);\n\n                    // We may need to repaint both us and our parent to close the window,\n                    // and perhaps twice (once to notice the close-event, once again to enforce it).\n                    // `request_repaint_of` does a double-repaint though:\n                    self.integration.egui_ctx.request_repaint_of(viewport_id);\n                    self.integration\n                        .egui_ctx\n                        .request_repaint_of(viewport.ids.parent);\n                }\n            }\n            _ => {}\n        }\n\n        if self.integration.should_close() {\n            return EventResult::CloseRequested;\n        }\n\n        let mut event_response = egui_winit::EventResponse {\n            consumed: false,\n            repaint: false,\n        };\n        if let Some(viewport_id) = viewport_id {\n            if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) {\n                if let (Some(window), Some(egui_winit)) =\n                    (&viewport.window, &mut viewport.egui_winit)\n                {\n                    event_response = self.integration.on_window_event(window, egui_winit, event);\n                }\n            } else {\n                log::trace!(\"Ignoring event: no viewport for {viewport_id:?}\");\n            }\n        } else {\n            log::trace!(\"Ignoring event: no viewport_id\");\n        }\n\n        if event_response.repaint {\n            if repaint_asap {\n                EventResult::RepaintNow(window_id)\n            } else {\n                EventResult::RepaintNext(window_id)\n            }\n        } else {\n            EventResult::Wait\n        }\n    }\n}\n\nfn change_gl_context(\n    current_gl_context: &mut Option<glutin::context::PossiblyCurrentContext>,\n    not_current_gl_context: &mut Option<glutin::context::NotCurrentContext>,\n    gl_surface: &glutin::surface::Surface<glutin::surface::WindowSurface>,\n) {\n    profiling::function_scope!();\n\n    if !cfg!(target_os = \"windows\") {\n        // According to https://github.com/emilk/egui/issues/4289\n        // we cannot do this early-out on Windows.\n        // TODO(emilk): optimize context switching on Windows too.\n        // See https://github.com/emilk/egui/issues/4173\n\n        if let Some(current_gl_context) = current_gl_context {\n            profiling::scope!(\"is_current\");\n            if gl_surface.is_current(current_gl_context) {\n                return; // Early-out to save a lot of time.\n            }\n        }\n    }\n\n    let not_current = if let Some(not_current_context) = not_current_gl_context.take() {\n        not_current_context\n    } else {\n        profiling::scope!(\"make_not_current\");\n        current_gl_context\n            .take()\n            .unwrap()\n            .make_not_current()\n            .unwrap()\n    };\n\n    profiling::scope!(\"make_current\");\n    *current_gl_context = Some(not_current.make_current(gl_surface).unwrap());\n}\n\nimpl GlutinWindowContext {\n    #[expect(unsafe_code)]\n    unsafe fn new(\n        egui_ctx: &egui::Context,\n        viewport_builder: ViewportBuilder,\n        native_options: &NativeOptions,\n        event_loop: &ActiveEventLoop,\n    ) -> Result<Self> {\n        profiling::function_scope!();\n\n        // There is a lot of complexity with opengl creation,\n        // so prefer extensive logging to get all the help we can to debug issues.\n\n        use glutin::prelude::*;\n        // convert native options to glutin options\n        let hardware_acceleration = match native_options.hardware_acceleration {\n            crate::HardwareAcceleration::Required => Some(true),\n            crate::HardwareAcceleration::Preferred => None,\n            crate::HardwareAcceleration::Off => Some(false),\n        };\n        let swap_interval = if native_options.vsync {\n            glutin::surface::SwapInterval::Wait(NonZeroU32::MIN)\n        } else {\n            glutin::surface::SwapInterval::DontWait\n        };\n        /*  opengl setup flow goes like this:\n            1. we create a configuration for opengl \"Display\" / \"Config\" creation\n            2. choose between special extensions like glx or egl or wgl and use them to create config/display\n            3. opengl context configuration\n            4. opengl context creation\n        */\n        // start building config for gl display\n        let config_template_builder = glutin::config::ConfigTemplateBuilder::new()\n            .prefer_hardware_accelerated(hardware_acceleration)\n            .with_depth_size(native_options.depth_buffer)\n            .with_stencil_size(native_options.stencil_buffer)\n            .with_transparency(native_options.viewport.transparent.unwrap_or(false));\n        // we don't know if multi sampling option is set. so, check if its more than 0.\n        let config_template_builder = if native_options.multisampling > 0 {\n            config_template_builder.with_multisampling(\n                native_options\n                    .multisampling\n                    .try_into()\n                    .expect(\"failed to fit multisamples option of native_options into u8\"),\n            )\n        } else {\n            config_template_builder\n        };\n\n        log::debug!(\"trying to create glutin Display with config: {config_template_builder:?}\");\n\n        // Create GL display. This may probably create a window too on most platforms. Definitely on `MS windows`. Never on Android.\n        let display_builder = glutin_winit::DisplayBuilder::new()\n            // we might want to expose this option to users in the future. maybe using an env var or using native_options.\n            //\n            // The justification for FallbackEgl over PreferEgl is at https://github.com/emilk/egui/pull/2526#issuecomment-1400229576 .\n            .with_preference(glutin_winit::ApiPreference::FallbackEgl)\n            .with_window_attributes(Some(egui_winit::create_winit_window_attributes(\n                egui_ctx,\n                viewport_builder.clone(),\n            )));\n\n        let (window, gl_config) = {\n            profiling::scope!(\"DisplayBuilder::build\");\n\n            display_builder\n                .build(\n                    event_loop,\n                    config_template_builder.clone(),\n                    |mut config_iterator| {\n                        let config = config_iterator.next().expect(\n                            \"failed to find a matching configuration for creating glutin config\",\n                        );\n                        log::debug!(\n                            \"using the first config from config picker closure. config: {config:?}\"\n                        );\n                        config\n                    },\n                )\n                .map_err(|e| crate::Error::NoGlutinConfigs(config_template_builder.build(), e))?\n        };\n        if let Some(window) = &window {\n            egui_winit::apply_viewport_builder_to_window(egui_ctx, window, &viewport_builder);\n        }\n\n        let gl_display = gl_config.display();\n        log::debug!(\n            \"successfully created GL Display with version: {} and supported features: {:?}\",\n            gl_display.version_string(),\n            gl_display.supported_features()\n        );\n        let glutin_raw_window_handle = window.as_ref().map(|w| {\n            w.window_handle()\n                .expect(\"Failed to get window handle\")\n                .as_raw()\n        });\n        log::debug!(\"creating gl context using raw window handle: {glutin_raw_window_handle:?}\");\n\n        // create gl context. if core context cannot be created, try gl es context as fallback.\n        let context_attributes =\n            glutin::context::ContextAttributesBuilder::new().build(glutin_raw_window_handle);\n        let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new()\n            .with_context_api(glutin::context::ContextApi::Gles(None))\n            .build(glutin_raw_window_handle);\n\n        let gl_context_result = unsafe {\n            profiling::scope!(\"create_context\");\n            gl_config\n                .display()\n                .create_context(&gl_config, &context_attributes)\n        };\n\n        let gl_context = match gl_context_result {\n            Ok(it) => it,\n            Err(err) => {\n                log::warn!(\n                    \"Failed to create context using default context attributes {context_attributes:?} due to error: {err}\"\n                );\n                log::debug!(\n                    \"Retrying with fallback context attributes: {fallback_context_attributes:?}\"\n                );\n                unsafe {\n                    gl_config\n                        .display()\n                        .create_context(&gl_config, &fallback_context_attributes)?\n                }\n            }\n        };\n        let not_current_gl_context = Some(gl_context);\n\n        let mut viewport_from_window = HashMap::default();\n        let mut window_from_viewport = OrderedViewportIdMap::default();\n        let mut viewport_info = ViewportInfo::default();\n        if let Some(window) = &window {\n            viewport_from_window.insert(window.id(), ViewportId::ROOT);\n            window_from_viewport.insert(ViewportId::ROOT, window.id());\n            egui_winit::update_viewport_info(&mut viewport_info, egui_ctx, window, true);\n\n            // Tell egui right away about native_pixels_per_point etc,\n            // so that the app knows about it during app creation:\n            let pixels_per_point = egui_winit::pixels_per_point(egui_ctx, window);\n\n            egui_ctx.input_mut(|i| {\n                i.raw\n                    .viewports\n                    .insert(ViewportId::ROOT, viewport_info.clone());\n\n                i.pixels_per_point = pixels_per_point;\n            });\n        }\n\n        let mut viewports = OrderedViewportIdMap::default();\n        viewports.insert(\n            ViewportId::ROOT,\n            Viewport {\n                ids: ViewportIdPair::ROOT,\n                class: ViewportClass::Root,\n                builder: viewport_builder,\n                deferred_commands: vec![],\n                info: viewport_info,\n                actions_requested: Default::default(),\n                viewport_ui_cb: None,\n                gl_surface: None,\n                window: window.map(Arc::new),\n                egui_winit: None,\n            },\n        );\n\n        // the fun part with opengl gl is that we never know whether there is an error. the context creation might have failed, but\n        // it could keep working until we try to make surface current or swap buffers or something else. future glutin improvements might\n        // help us start from scratch again if we fail context creation and go back to preferEgl or try with different config etc..\n        // https://github.com/emilk/egui/pull/2541#issuecomment-1370767582\n\n        let mut slf = Self {\n            egui_ctx: egui_ctx.clone(),\n            swap_interval,\n            gl_config,\n            current_gl_context: None,\n            not_current_gl_context,\n            viewports,\n            viewport_from_window,\n            max_texture_side: None,\n            window_from_viewport,\n            focused_viewport: Some(ViewportId::ROOT),\n        };\n\n        slf.initialize_window(ViewportId::ROOT, event_loop)?;\n\n        Ok(slf)\n    }\n\n    /// Create a surface, window, and winit integration for all viewports lacking any of that.\n    ///\n    /// Errors will be logged.\n    fn initialize_all_windows(&mut self, event_loop: &ActiveEventLoop) {\n        profiling::function_scope!();\n\n        let viewports: Vec<ViewportId> = self.viewports.keys().copied().collect();\n\n        for viewport_id in viewports {\n            if let Err(err) = self.initialize_window(viewport_id, event_loop) {\n                log::error!(\"Failed to initialize a window for viewport {viewport_id:?}: {err}\");\n            }\n        }\n    }\n\n    /// Create a surface, window, and winit integration for the viewport, if missing.\n    #[expect(unsafe_code)]\n    pub(crate) fn initialize_window(\n        &mut self,\n        viewport_id: ViewportId,\n        event_loop: &ActiveEventLoop,\n    ) -> Result {\n        profiling::function_scope!();\n\n        let viewport = self\n            .viewports\n            .get_mut(&viewport_id)\n            .expect(\"viewport doesn't exist\");\n\n        let window = if let Some(window) = &mut viewport.window {\n            window\n        } else {\n            log::debug!(\"Creating a window for viewport {viewport_id:?}\");\n            let window_attributes = egui_winit::create_winit_window_attributes(\n                &self.egui_ctx,\n                viewport.builder.clone(),\n            );\n            if window_attributes.transparent()\n                && self.gl_config.supports_transparency() == Some(false)\n            {\n                log::error!(\"Cannot create transparent window: the GL config does not support it\");\n            }\n            let window =\n                glutin_winit::finalize_window(event_loop, window_attributes, &self.gl_config)?;\n            egui_winit::apply_viewport_builder_to_window(\n                &self.egui_ctx,\n                &window,\n                &viewport.builder,\n            );\n\n            egui_winit::update_viewport_info(&mut viewport.info, &self.egui_ctx, &window, true);\n            viewport.window.insert(Arc::new(window))\n        };\n\n        viewport.egui_winit.get_or_insert_with(|| {\n            log::debug!(\"Initializing egui_winit for viewport {viewport_id:?}\");\n            egui_winit::State::new(\n                self.egui_ctx.clone(),\n                viewport_id,\n                event_loop,\n                Some(window.scale_factor() as f32),\n                event_loop.system_theme(),\n                self.max_texture_side,\n            )\n        });\n\n        if viewport.gl_surface.is_none() {\n            log::debug!(\"Creating a gl_surface for viewport {viewport_id:?}\");\n\n            // surface attributes\n            let (width_px, height_px): (u32, u32) = window.inner_size().into();\n            let width_px = NonZeroU32::new(width_px).unwrap_or(NonZeroU32::MIN);\n            let height_px = NonZeroU32::new(height_px).unwrap_or(NonZeroU32::MIN);\n            let surface_attributes = {\n                glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new()\n                    .build(\n                        window\n                            .window_handle()\n                            .expect(\"Failed to get display handle\")\n                            .as_raw(),\n                        width_px,\n                        height_px,\n                    )\n            };\n\n            log::trace!(\"creating surface with attributes: {surface_attributes:?}\");\n            let gl_surface = unsafe {\n                self.gl_config\n                    .display()\n                    .create_window_surface(&self.gl_config, &surface_attributes)?\n            };\n\n            log::trace!(\"surface created successfully: {gl_surface:?}. making context current\");\n\n            let not_current_gl_context =\n                if let Some(not_current_context) = self.not_current_gl_context.take() {\n                    not_current_context\n                } else {\n                    self.current_gl_context\n                        .take()\n                        .unwrap()\n                        .make_not_current()\n                        .unwrap()\n                };\n            let current_gl_context = not_current_gl_context.make_current(&gl_surface)?;\n\n            // try setting swap interval. but its not absolutely necessary, so don't panic on failure.\n            log::trace!(\"made context current. setting swap interval for surface\");\n            if let Err(err) = gl_surface.set_swap_interval(&current_gl_context, self.swap_interval)\n            {\n                log::warn!(\"Failed to set swap interval due to error: {err}\");\n            }\n\n            // we will reach this point only once in most platforms except android.\n            // create window/surface/make context current once and just use them forever.\n\n            viewport.gl_surface = Some(gl_surface);\n\n            self.current_gl_context = Some(current_gl_context);\n        }\n\n        self.viewport_from_window.insert(window.id(), viewport_id);\n        self.window_from_viewport.insert(viewport_id, window.id());\n\n        Ok(())\n    }\n\n    /// only applies for android. but we basically drop surface + window and make context not current\n    fn on_suspend(&mut self) -> Result {\n        log::debug!(\"received suspend event. dropping window and surface\");\n        for viewport in self.viewports.values_mut() {\n            viewport.gl_surface = None;\n            viewport.window = None;\n        }\n        if let Some(current) = self.current_gl_context.take() {\n            log::debug!(\"context is current, so making it non-current\");\n            self.not_current_gl_context = Some(current.make_not_current()?);\n        } else {\n            log::debug!(\"context is already not current??? could be duplicate suspend event\");\n        }\n        Ok(())\n    }\n\n    fn viewport(&self, viewport_id: ViewportId) -> &Viewport {\n        self.viewports\n            .get(&viewport_id)\n            .expect(\"viewport doesn't exist\")\n    }\n\n    fn window_opt(&self, viewport_id: ViewportId) -> Option<Arc<Window>> {\n        self.viewport(viewport_id).window.clone()\n    }\n\n    fn window(&self, viewport_id: ViewportId) -> Arc<Window> {\n        self.window_opt(viewport_id)\n            .expect(\"winit window doesn't exist\")\n    }\n\n    fn resize(&mut self, viewport_id: ViewportId, physical_size: winit::dpi::PhysicalSize<u32>) {\n        let width_px = NonZeroU32::new(physical_size.width).unwrap_or(NonZeroU32::MIN);\n        let height_px = NonZeroU32::new(physical_size.height).unwrap_or(NonZeroU32::MIN);\n\n        if let Some(viewport) = self.viewports.get(&viewport_id)\n            && let Some(gl_surface) = &viewport.gl_surface\n        {\n            change_gl_context(\n                &mut self.current_gl_context,\n                &mut self.not_current_gl_context,\n                gl_surface,\n            );\n            gl_surface.resize(\n                self.current_gl_context\n                    .as_ref()\n                    .expect(\"failed to get current context to resize surface\"),\n                width_px,\n                height_px,\n            );\n        }\n    }\n\n    fn get_proc_address(&self, addr: &std::ffi::CStr) -> *const std::ffi::c_void {\n        self.gl_config.display().get_proc_address(addr)\n    }\n\n    pub(crate) fn remove_viewports_not_in(\n        &mut self,\n        viewport_output: &OrderedViewportIdMap<ViewportOutput>,\n    ) {\n        // GC old viewports\n        self.viewports\n            .retain(|id, _| viewport_output.contains_key(id));\n        self.viewport_from_window\n            .retain(|_, id| viewport_output.contains_key(id));\n        self.window_from_viewport\n            .retain(|id, _| viewport_output.contains_key(id));\n    }\n\n    fn handle_viewport_output(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        egui_ctx: &egui::Context,\n        viewport_output: &OrderedViewportIdMap<ViewportOutput>,\n    ) {\n        profiling::function_scope!();\n\n        for (\n            viewport_id,\n            ViewportOutput {\n                parent,\n                class,\n                builder,\n                viewport_ui_cb,\n                mut commands,\n                repaint_delay: _, // ignored - we listened to the repaint callback instead\n            },\n        ) in viewport_output.clone()\n        {\n            let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent);\n\n            let viewport = initialize_or_update_viewport(\n                &mut self.viewports,\n                ids,\n                class,\n                builder,\n                viewport_ui_cb,\n            );\n\n            if let Some(window) = &viewport.window {\n                let old_inner_size = window.inner_size();\n\n                viewport.deferred_commands.append(&mut commands);\n\n                egui_winit::process_viewport_commands(\n                    egui_ctx,\n                    &mut viewport.info,\n                    std::mem::take(&mut viewport.deferred_commands),\n                    window,\n                    &mut viewport.actions_requested,\n                );\n\n                // For Wayland : https://github.com/emilk/egui/issues/4196\n                if cfg!(target_os = \"linux\") {\n                    let new_inner_size = window.inner_size();\n                    if new_inner_size != old_inner_size {\n                        self.resize(viewport_id, new_inner_size);\n                    }\n                }\n            }\n        }\n\n        // Create windows for any new viewports:\n        self.initialize_all_windows(event_loop);\n\n        self.remove_viewports_not_in(viewport_output);\n    }\n}\n\nfn initialize_or_update_viewport(\n    viewports: &mut OrderedViewportIdMap<Viewport>,\n    ids: ViewportIdPair,\n    class: ViewportClass,\n    mut builder: ViewportBuilder,\n    viewport_ui_cb: Option<Arc<dyn Fn(&mut egui::Ui) + Send + Sync>>,\n) -> &mut Viewport {\n    profiling::function_scope!();\n\n    use std::collections::btree_map::Entry;\n\n    if builder.icon.is_none() {\n        // Inherit icon from parent\n        builder.icon = viewports\n            .get_mut(&ids.parent)\n            .and_then(|vp| vp.builder.icon.clone());\n    }\n\n    match viewports.entry(ids.this) {\n        Entry::Vacant(entry) => {\n            // New viewport:\n            log::debug!(\"Creating new viewport {:?} ({:?})\", ids.this, builder.title);\n            entry.insert(Viewport {\n                ids,\n                class,\n                builder,\n                deferred_commands: vec![],\n                info: Default::default(),\n                actions_requested: Default::default(),\n                viewport_ui_cb,\n                window: None,\n                egui_winit: None,\n                gl_surface: None,\n            })\n        }\n\n        Entry::Occupied(mut entry) => {\n            // Patch an existing viewport:\n            let viewport = entry.get_mut();\n\n            viewport.ids.parent = ids.parent;\n            viewport.class = class;\n            viewport.viewport_ui_cb = viewport_ui_cb;\n\n            let (mut delta_commands, recreate) = viewport.builder.patch(builder);\n\n            if recreate {\n                log::debug!(\n                    \"Recreating window for viewport {:?} ({:?})\",\n                    ids.this,\n                    viewport.builder.title\n                );\n                viewport.window = None;\n                viewport.egui_winit = None;\n                viewport.gl_surface = None;\n            }\n\n            viewport.deferred_commands.append(&mut delta_commands);\n\n            entry.into_mut()\n        }\n    }\n}\n\n/// This is called (via a callback) by user code to render immediate viewports,\n/// i.e. viewport that are directly nested inside a parent viewport.\nfn render_immediate_viewport(\n    egui_ctx: &egui::Context,\n    glutin: &RefCell<GlutinWindowContext>,\n    painter: &RefCell<egui_glow::Painter>,\n    beginning: Instant,\n    immediate_viewport: ImmediateViewport<'_>,\n) {\n    profiling::function_scope!();\n\n    let ImmediateViewport {\n        ids,\n        builder,\n        mut viewport_ui_cb,\n    } = immediate_viewport;\n\n    let viewport_id = ids.this;\n\n    {\n        let mut glutin = glutin.borrow_mut();\n\n        initialize_or_update_viewport(\n            &mut glutin.viewports,\n            ids,\n            ViewportClass::Immediate,\n            builder,\n            None,\n        );\n\n        let ret = event_loop_context::with_current_event_loop(|event_loop| {\n            glutin.initialize_window(viewport_id, event_loop)\n        });\n\n        if let Some(Err(err)) = ret {\n            log::error!(\n                \"Failed to initialize a window for immediate viewport {viewport_id:?}: {err}\"\n            );\n            return;\n        }\n    }\n\n    let input = {\n        let mut glutin = glutin.borrow_mut();\n\n        let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else {\n            return;\n        };\n        let (Some(egui_winit), Some(window)) = (&mut viewport.egui_winit, &viewport.window) else {\n            return;\n        };\n        egui_winit::update_viewport_info(&mut viewport.info, egui_ctx, window, false);\n\n        let mut raw_input = egui_winit.take_egui_input(window);\n        raw_input.viewports = glutin\n            .viewports\n            .iter()\n            .map(|(id, viewport)| (*id, viewport.info.clone()))\n            .collect();\n        raw_input.time = Some(beginning.elapsed().as_secs_f64());\n        raw_input\n    };\n\n    // ---------------------------------------------------\n    // Call the user ui-code, which could re-entrantly call this function again!\n    // No locks may be hold while calling this function.\n\n    let egui::FullOutput {\n        platform_output,\n        textures_delta,\n        shapes,\n        pixels_per_point,\n        viewport_output,\n    } = egui_ctx.run_ui(input, |ui| {\n        viewport_ui_cb(ui);\n    });\n\n    // ---------------------------------------------------\n\n    let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point);\n\n    let mut glutin = glutin.borrow_mut();\n\n    let GlutinWindowContext {\n        current_gl_context,\n        not_current_gl_context,\n        viewports,\n        ..\n    } = &mut *glutin;\n\n    let Some(viewport) = viewports.get_mut(&viewport_id) else {\n        return;\n    };\n\n    viewport.info.events.clear(); // they should have been processed\n\n    let (Some(egui_winit), Some(window), Some(gl_surface)) = (\n        &mut viewport.egui_winit,\n        &viewport.window,\n        &viewport.gl_surface,\n    ) else {\n        return;\n    };\n\n    let screen_size_in_pixels: [u32; 2] = window.inner_size().into();\n\n    change_gl_context(current_gl_context, not_current_gl_context, gl_surface);\n\n    let current_gl_context = current_gl_context.as_ref().unwrap();\n\n    if !gl_surface.is_current(current_gl_context) {\n        log::error!(\n            \"egui::show_viewport_immediate: viewport {:?} ({:?}) was not created on main thread.\",\n            viewport.ids.this,\n            viewport.builder.title\n        );\n    }\n\n    egui_glow::painter::clear(\n        painter.borrow().gl(),\n        screen_size_in_pixels,\n        [0.0, 0.0, 0.0, 0.0],\n    );\n\n    painter.borrow_mut().paint_and_update_textures(\n        screen_size_in_pixels,\n        pixels_per_point,\n        &clipped_primitives,\n        &textures_delta,\n    );\n\n    {\n        profiling::scope!(\"swap_buffers\");\n        if let Err(err) = gl_surface.swap_buffers(current_gl_context) {\n            log::error!(\"swap_buffers failed: {err}\");\n        }\n    }\n\n    egui_winit.handle_platform_output(window, platform_output);\n\n    event_loop_context::with_current_event_loop(|event_loop| {\n        glutin.handle_viewport_output(event_loop, egui_ctx, &viewport_output);\n    });\n}\n\n#[cfg(feature = \"__screenshot\")]\nfn save_screenshot_and_exit(\n    path: &str,\n    painter: &egui_glow::Painter,\n    screen_size_in_pixels: [u32; 2],\n) {\n    assert!(\n        path.ends_with(\".png\"),\n        \"Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}\"\n    );\n    let screenshot = painter.read_screen_rgba(screen_size_in_pixels);\n    image::save_buffer(\n        path,\n        screenshot.as_raw(),\n        screenshot.width() as u32,\n        screenshot.height() as u32,\n        image::ColorType::Rgba8,\n    )\n    .unwrap_or_else(|err| {\n        panic!(\"Failed to save screenshot to {path:?}: {err}\");\n    });\n    log::info!(\"Screenshot saved to {path:?}.\");\n\n    #[expect(clippy::exit)]\n    std::process::exit(0);\n}\n"
  },
  {
    "path": "crates/eframe/src/native/mod.rs",
    "content": "mod app_icon;\nmod epi_integration;\nmod event_loop_context;\npub mod run;\n\n/// File storage which can be used by native backends.\n#[cfg(feature = \"persistence\")]\npub mod file_storage;\n\npub(crate) mod winit_integration;\n\n#[cfg(feature = \"glow\")]\nmod glow_integration;\n\n#[cfg(feature = \"wgpu_no_default_features\")]\nmod wgpu_integration;\n"
  },
  {
    "path": "crates/eframe/src/native/run.rs",
    "content": "use std::time::Instant;\n\nuse winit::{\n    application::ApplicationHandler,\n    event_loop::{ActiveEventLoop, ControlFlow, EventLoop},\n    window::WindowId,\n};\n\nuse ahash::HashMap;\n\nuse super::winit_integration::{UserEvent, WinitApp};\nuse crate::{\n    Result, epi,\n    native::{event_loop_context, winit_integration::EventResult},\n};\n\n// ----------------------------------------------------------------------------\nfn create_event_loop(native_options: &mut epi::NativeOptions) -> Result<EventLoop<UserEvent>> {\n    #[cfg(target_os = \"android\")]\n    use winit::platform::android::EventLoopBuilderExtAndroid as _;\n\n    profiling::function_scope!();\n    let mut builder = winit::event_loop::EventLoop::with_user_event();\n\n    #[cfg(target_os = \"android\")]\n    let mut builder =\n        builder.with_android_app(native_options.android_app.take().ok_or_else(|| {\n            crate::Error::AppCreation(Box::from(\n                \"`NativeOptions` is missing required `android_app`\",\n            ))\n        })?);\n\n    if let Some(hook) = std::mem::take(&mut native_options.event_loop_builder) {\n        hook(&mut builder);\n    }\n\n    profiling::scope!(\"EventLoopBuilder::build\");\n    Ok(builder.build()?)\n}\n\n/// Access a thread-local event loop.\n///\n/// We reuse the event-loop so we can support closing and opening an eframe window\n/// multiple times. This is just a limitation of winit.\n#[cfg(not(target_os = \"ios\"))]\nfn with_event_loop<R>(\n    mut native_options: epi::NativeOptions,\n    f: impl FnOnce(&mut EventLoop<UserEvent>, epi::NativeOptions) -> R,\n) -> Result<R> {\n    thread_local!(static EVENT_LOOP: std::cell::RefCell<Option<EventLoop<UserEvent>>> = const { std::cell::RefCell::new(None) });\n\n    EVENT_LOOP.with(|event_loop| {\n        // Since we want to reference NativeOptions when creating the EventLoop we can't\n        // do that as part of the lazy thread local storage initialization and so we instead\n        // create the event loop lazily here\n        let mut event_loop_lock = event_loop.borrow_mut();\n        let event_loop = if let Some(event_loop) = &mut *event_loop_lock {\n            event_loop\n        } else {\n            event_loop_lock.insert(create_event_loop(&mut native_options)?)\n        };\n        Ok(f(event_loop, native_options))\n    })\n}\n\n/// Wraps a [`WinitApp`] to implement [`ApplicationHandler`]. This handles redrawing, exit states, and\n/// some events, but otherwise forwards events to the [`WinitApp`].\nstruct WinitAppWrapper<T: WinitApp> {\n    windows_next_repaint_times: HashMap<WindowId, Instant>,\n    winit_app: T,\n    return_result: Result<(), crate::Error>,\n    run_and_return: bool,\n}\n\nimpl<T: WinitApp> WinitAppWrapper<T> {\n    fn new(winit_app: T, run_and_return: bool) -> Self {\n        Self {\n            windows_next_repaint_times: HashMap::default(),\n            winit_app,\n            return_result: Ok(()),\n            run_and_return,\n        }\n    }\n\n    fn handle_event_result(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        event_result: Result<EventResult>,\n    ) {\n        let mut exit = false;\n        let mut save = false;\n\n        log::trace!(\"event_result: {event_result:?}\");\n\n        let mut event_result = event_result;\n\n        if cfg!(target_os = \"windows\")\n            && let Ok(EventResult::RepaintNow(window_id)) = event_result\n        {\n            log::trace!(\"RepaintNow of {window_id:?}\");\n            self.windows_next_repaint_times\n                .insert(window_id, Instant::now());\n\n            // Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280\n            event_result = self.winit_app.run_ui_and_paint(event_loop, window_id);\n        }\n\n        let combined_result = event_result.map(|event_result| match event_result {\n            EventResult::Wait => {\n                event_loop.set_control_flow(ControlFlow::Wait);\n                event_result\n            }\n            EventResult::RepaintNow(window_id) => {\n                log::trace!(\"RepaintNow of {window_id:?}\",);\n                self.windows_next_repaint_times\n                    .insert(window_id, Instant::now());\n                event_result\n            }\n            EventResult::RepaintNext(window_id) => {\n                log::trace!(\"RepaintNext of {window_id:?}\",);\n                self.windows_next_repaint_times\n                    .insert(window_id, Instant::now());\n                event_result\n            }\n            EventResult::RepaintAt(window_id, repaint_time) => {\n                self.windows_next_repaint_times.insert(\n                    window_id,\n                    self.windows_next_repaint_times\n                        .get(&window_id)\n                        .map_or(repaint_time, |last| (*last).min(repaint_time)),\n                );\n                event_result\n            }\n            EventResult::Save => {\n                save = true;\n                event_result\n            }\n            EventResult::Exit => {\n                exit = true;\n                event_result\n            }\n            EventResult::CloseRequested => {\n                // The windows need to be dropped whilst the event loop is running to allow for proper cleanup.\n                self.winit_app.save_and_destroy();\n                event_result\n            }\n        });\n\n        if let Err(err) = combined_result {\n            log::error!(\"Exiting because of error: {err}\");\n            exit = true;\n            self.return_result = Err(err);\n        }\n\n        if save {\n            log::debug!(\"Received an EventResult::Save - saving app state\");\n            self.winit_app.save();\n        }\n\n        if exit {\n            if self.run_and_return {\n                log::debug!(\"Asking to exit event loop…\");\n                event_loop.exit();\n            } else {\n                log::debug!(\"Quitting - saving app state…\");\n                self.winit_app.save_and_destroy();\n\n                log::debug!(\"Exiting with return code 0\");\n\n                std::process::exit(0);\n            }\n        }\n\n        self.check_redraw_requests(event_loop);\n    }\n\n    fn check_redraw_requests(&mut self, event_loop: &ActiveEventLoop) {\n        let now = Instant::now();\n\n        self.windows_next_repaint_times\n            .retain(|window_id, repaint_time| {\n                if now < *repaint_time {\n                    return true; // not yet ready\n                }\n\n                event_loop.set_control_flow(ControlFlow::Poll);\n\n                if let Some(window) = self.winit_app.window(*window_id) {\n                    log::trace!(\"request_redraw for {window_id:?}\");\n                    window.request_redraw();\n                } else {\n                    log::trace!(\"No window found for {window_id:?}\");\n                }\n                false\n            });\n\n        let next_repaint_time = self.windows_next_repaint_times.values().min().copied();\n        if let Some(next_repaint_time) = next_repaint_time {\n            event_loop.set_control_flow(ControlFlow::WaitUntil(next_repaint_time));\n        }\n    }\n}\n\nimpl<T: WinitApp> ApplicationHandler<UserEvent> for WinitAppWrapper<T> {\n    fn suspended(&mut self, event_loop: &ActiveEventLoop) {\n        profiling::scope!(\"Event::Suspended\");\n\n        event_loop_context::with_event_loop_context(event_loop, move || {\n            let event_result = self.winit_app.suspended(event_loop);\n            self.handle_event_result(event_loop, event_result);\n        });\n    }\n\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) {\n        profiling::scope!(\"Event::Resumed\");\n\n        // Nb: Make sure this guard is dropped after this function returns.\n        event_loop_context::with_event_loop_context(event_loop, move || {\n            let event_result = self.winit_app.resumed(event_loop);\n            self.handle_event_result(event_loop, event_result);\n        });\n    }\n\n    fn exiting(&mut self, event_loop: &ActiveEventLoop) {\n        // On Mac, Cmd-Q we get here and then `run_app_on_demand` doesn't return (despite its name),\n        // so we need to save state now:\n        log::debug!(\"Received Event::LoopExiting - saving app state…\");\n        event_loop_context::with_event_loop_context(event_loop, move || {\n            self.winit_app.save_and_destroy();\n        });\n    }\n\n    fn device_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        device_id: winit::event::DeviceId,\n        event: winit::event::DeviceEvent,\n    ) {\n        profiling::function_scope!(egui_winit::short_device_event_description(&event));\n\n        // Nb: Make sure this guard is dropped after this function returns.\n        event_loop_context::with_event_loop_context(event_loop, move || {\n            let event_result = self.winit_app.device_event(event_loop, device_id, event);\n            self.handle_event_result(event_loop, event_result);\n        });\n    }\n\n    fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {\n        profiling::function_scope!(match &event {\n            UserEvent::RequestRepaint { .. } => \"UserEvent::RequestRepaint\",\n            #[cfg(feature = \"accesskit\")]\n            UserEvent::AccessKitActionRequest(_) => \"UserEvent::AccessKitActionRequest\",\n        });\n\n        event_loop_context::with_event_loop_context(event_loop, move || {\n            let event_result = match event {\n                UserEvent::RequestRepaint {\n                    when,\n                    cumulative_pass_nr,\n                    viewport_id,\n                } => {\n                    let current_pass_nr = self\n                        .winit_app\n                        .egui_ctx()\n                        .map_or(0, |ctx| ctx.cumulative_pass_nr_for(viewport_id));\n                    if current_pass_nr == cumulative_pass_nr\n                        || current_pass_nr == cumulative_pass_nr + 1\n                    {\n                        log::trace!(\"UserEvent::RequestRepaint scheduling repaint at {when:?}\");\n                        if let Some(window_id) =\n                            self.winit_app.window_id_from_viewport_id(viewport_id)\n                        {\n                            Ok(EventResult::RepaintAt(window_id, when))\n                        } else {\n                            Ok(EventResult::Wait)\n                        }\n                    } else {\n                        log::trace!(\"Got outdated UserEvent::RequestRepaint\");\n                        Ok(EventResult::Wait) // old request - we've already repainted\n                    }\n                }\n                #[cfg(feature = \"accesskit\")]\n                UserEvent::AccessKitActionRequest(request) => {\n                    self.winit_app.on_accesskit_event(request)\n                }\n            };\n            self.handle_event_result(event_loop, event_result);\n        });\n    }\n\n    fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: winit::event::StartCause) {\n        if let winit::event::StartCause::ResumeTimeReached { .. } = cause {\n            log::trace!(\"Woke up to check next_repaint_time\");\n        }\n\n        self.check_redraw_requests(event_loop);\n    }\n\n    fn window_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        window_id: WindowId,\n        event: winit::event::WindowEvent,\n    ) {\n        profiling::function_scope!(egui_winit::short_window_event_description(&event));\n\n        // Nb: Make sure this guard is dropped after this function returns.\n        event_loop_context::with_event_loop_context(event_loop, move || {\n            let event_result = match event {\n                winit::event::WindowEvent::RedrawRequested => {\n                    self.winit_app.run_ui_and_paint(event_loop, window_id)\n                }\n                _ => self.winit_app.window_event(event_loop, window_id, event),\n            };\n\n            self.handle_event_result(event_loop, event_result);\n        });\n    }\n}\n\n#[cfg(not(target_os = \"ios\"))]\nfn run_and_return(event_loop: &mut EventLoop<UserEvent>, winit_app: impl WinitApp) -> Result {\n    use winit::platform::run_on_demand::EventLoopExtRunOnDemand as _;\n\n    log::trace!(\"Entering the winit event loop (run_app_on_demand)…\");\n\n    let mut app = WinitAppWrapper::new(winit_app, true);\n    event_loop.run_app_on_demand(&mut app)?;\n    log::debug!(\"eframe window closed\");\n    app.return_result\n}\n\nfn run_and_exit(event_loop: EventLoop<UserEvent>, winit_app: impl WinitApp) -> Result {\n    log::trace!(\"Entering the winit event loop (run_app)…\");\n\n    // When to repaint what window\n    let mut app = WinitAppWrapper::new(winit_app, false);\n    event_loop.run_app(&mut app)?;\n\n    log::debug!(\"winit event loop unexpectedly returned\");\n    Ok(())\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg(feature = \"glow\")]\npub fn run_glow(\n    app_name: &str,\n    mut native_options: epi::NativeOptions,\n    app_creator: epi::AppCreator<'_>,\n) -> Result {\n    use super::glow_integration::GlowWinitApp;\n\n    #[cfg(not(target_os = \"ios\"))]\n    if native_options.run_and_return {\n        return with_event_loop(native_options, |event_loop, native_options| {\n            let glow_eframe = GlowWinitApp::new(event_loop, app_name, native_options, app_creator);\n            run_and_return(event_loop, glow_eframe)\n        })?;\n    }\n\n    let event_loop = create_event_loop(&mut native_options)?;\n    let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator);\n    run_and_exit(event_loop, glow_eframe)\n}\n\n#[cfg(feature = \"glow\")]\npub fn create_glow<'a>(\n    app_name: &str,\n    native_options: epi::NativeOptions,\n    app_creator: epi::AppCreator<'a>,\n    event_loop: &EventLoop<UserEvent>,\n) -> impl ApplicationHandler<UserEvent> + 'a {\n    use super::glow_integration::GlowWinitApp;\n\n    let glow_eframe = GlowWinitApp::new(event_loop, app_name, native_options, app_creator);\n    WinitAppWrapper::new(glow_eframe, true)\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg(feature = \"wgpu_no_default_features\")]\npub fn run_wgpu(\n    app_name: &str,\n    mut native_options: epi::NativeOptions,\n    app_creator: epi::AppCreator<'_>,\n) -> Result {\n    use super::wgpu_integration::WgpuWinitApp;\n\n    #[cfg(not(target_os = \"ios\"))]\n    if native_options.run_and_return {\n        return with_event_loop(native_options, |event_loop, native_options| {\n            let wgpu_eframe = WgpuWinitApp::new(event_loop, app_name, native_options, app_creator);\n            run_and_return(event_loop, wgpu_eframe)\n        })?;\n    }\n\n    let event_loop = create_event_loop(&mut native_options)?;\n    let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator);\n    run_and_exit(event_loop, wgpu_eframe)\n}\n\n#[cfg(feature = \"wgpu_no_default_features\")]\npub fn create_wgpu<'a>(\n    app_name: &str,\n    native_options: epi::NativeOptions,\n    app_creator: epi::AppCreator<'a>,\n    event_loop: &EventLoop<UserEvent>,\n) -> impl ApplicationHandler<UserEvent> + 'a {\n    use super::wgpu_integration::WgpuWinitApp;\n\n    let wgpu_eframe = WgpuWinitApp::new(event_loop, app_name, native_options, app_creator);\n    WinitAppWrapper::new(wgpu_eframe, true)\n}\n\n// ----------------------------------------------------------------------------\n\n/// A proxy to the eframe application that implements [`ApplicationHandler`].\n///\n/// This can be run directly on your own [`EventLoop`] by itself or with other\n/// windows you manage outside of eframe.\npub struct EframeWinitApplication<'a> {\n    wrapper: Box<dyn ApplicationHandler<UserEvent> + 'a>,\n    control_flow: ControlFlow,\n}\n\nimpl ApplicationHandler<UserEvent> for EframeWinitApplication<'_> {\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) {\n        self.wrapper.resumed(event_loop);\n    }\n\n    fn window_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        window_id: winit::window::WindowId,\n        event: winit::event::WindowEvent,\n    ) {\n        self.wrapper.window_event(event_loop, window_id, event);\n    }\n\n    fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: winit::event::StartCause) {\n        self.wrapper.new_events(event_loop, cause);\n    }\n\n    fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {\n        self.wrapper.user_event(event_loop, event);\n    }\n\n    fn device_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        device_id: winit::event::DeviceId,\n        event: winit::event::DeviceEvent,\n    ) {\n        self.wrapper.device_event(event_loop, device_id, event);\n    }\n\n    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {\n        self.wrapper.about_to_wait(event_loop);\n        self.control_flow = event_loop.control_flow();\n    }\n\n    fn suspended(&mut self, event_loop: &ActiveEventLoop) {\n        self.wrapper.suspended(event_loop);\n    }\n\n    fn exiting(&mut self, event_loop: &ActiveEventLoop) {\n        self.wrapper.exiting(event_loop);\n    }\n\n    fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {\n        self.wrapper.memory_warning(event_loop);\n    }\n}\n\nimpl<'a> EframeWinitApplication<'a> {\n    pub(crate) fn new<T: ApplicationHandler<UserEvent> + 'a>(app: T) -> Self {\n        Self {\n            wrapper: Box::new(app),\n            control_flow: ControlFlow::default(),\n        }\n    }\n\n    /// Pump the `EventLoop` to check for and dispatch pending events to this application.\n    ///\n    /// Returns either the exit code for the application or the final state of the [`ControlFlow`]\n    /// after all events have been dispatched in this iteration.\n    ///\n    /// This is useful when your [`EventLoop`] is not the main event loop for your application.\n    /// See the `external_eventloop_async` example.\n    #[cfg(not(target_os = \"ios\"))]\n    pub fn pump_eframe_app(\n        &mut self,\n        event_loop: &mut EventLoop<UserEvent>,\n        timeout: Option<std::time::Duration>,\n    ) -> EframePumpStatus {\n        use winit::platform::pump_events::{EventLoopExtPumpEvents as _, PumpStatus};\n\n        match event_loop.pump_app_events(timeout, self) {\n            PumpStatus::Continue => EframePumpStatus::Continue(self.control_flow),\n            PumpStatus::Exit(code) => EframePumpStatus::Exit(code),\n        }\n    }\n}\n\n/// Either an exit code or a [`ControlFlow`] from the [`ActiveEventLoop`].\n///\n/// The result of [`EframeWinitApplication::pump_eframe_app`].\n#[cfg(not(target_os = \"ios\"))]\npub enum EframePumpStatus {\n    /// The final state of the [`ControlFlow`] after all events have been dispatched\n    ///\n    /// Callers should perform the action that is appropriate for the [`ControlFlow`] value.\n    Continue(ControlFlow),\n\n    /// The exit code for the application\n    Exit(i32),\n}\n"
  },
  {
    "path": "crates/eframe/src/native/wgpu_integration.rs",
    "content": "//! Note that this file contains code very similar to [`super::glow_integration`].\n//! When making changes to one you often also want to apply it to the other.\n//!\n//! This is also very complex code, and not very pretty.\n//! There is a bunch of improvements we could do,\n//! like removing a bunch of `unwraps`.\n\nuse std::{cell::RefCell, num::NonZeroU32, rc::Rc, sync::Arc, time::Instant};\n\nuse egui_winit::ActionRequested;\nuse parking_lot::Mutex;\nuse raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _};\nuse winit::{\n    event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy},\n    window::{Window, WindowId},\n};\n\nuse ahash::HashMap;\nuse egui::{\n    DeferredViewportUiCallback, FullOutput, ImmediateViewport, OrderedViewportIdMap,\n    ViewportBuilder, ViewportClass, ViewportId, ViewportIdPair, ViewportIdSet, ViewportInfo,\n    ViewportOutput,\n};\n#[cfg(feature = \"accesskit\")]\nuse egui_winit::accesskit_winit;\nuse winit_integration::UserEvent;\n\nuse crate::{\n    App, AppCreator, CreationContext, NativeOptions, Result, Storage,\n    native::{epi_integration::EpiIntegration, winit_integration::EventResult},\n};\n\nuse super::{epi_integration, event_loop_context, winit_integration, winit_integration::WinitApp};\n\n// ----------------------------------------------------------------------------\n// Types:\n\npub struct WgpuWinitApp<'app> {\n    repaint_proxy: Arc<Mutex<EventLoopProxy<UserEvent>>>,\n    app_name: String,\n    native_options: NativeOptions,\n\n    /// Set at initialization, then taken and set to `None` in `init_run_state`.\n    app_creator: Option<AppCreator<'app>>,\n\n    /// Set when we are actually up and running.\n    running: Option<WgpuWinitRunning<'app>>,\n}\n\n/// State that is initialized when the application is first starts running via\n/// a Resumed event. On Android this ensures that any graphics state is only\n/// initialized once the application has an associated `SurfaceView`.\nstruct WgpuWinitRunning<'app> {\n    integration: EpiIntegration,\n\n    /// The users application.\n    app: Box<dyn 'app + App>,\n\n    /// Wrapped in an `Rc<RefCell<…>>` so it can be re-entrantly shared via a weak-pointer.\n    shared: Rc<RefCell<SharedState>>,\n}\n\n/// Everything needed by the immediate viewport renderer.\\\n///\n/// This is shared by all viewports.\n///\n/// Wrapped in an `Rc<RefCell<…>>` so it can be re-entrantly shared via a weak-pointer.\npub struct SharedState {\n    egui_ctx: egui::Context,\n    viewports: Viewports,\n    painter: egui_wgpu::winit::Painter,\n    viewport_from_window: HashMap<WindowId, ViewportId>,\n    focused_viewport: Option<ViewportId>,\n    resized_viewport: Option<ViewportId>,\n}\n\npub type Viewports = egui::OrderedViewportIdMap<Viewport>;\n\npub struct Viewport {\n    ids: ViewportIdPair,\n    class: ViewportClass,\n    builder: ViewportBuilder,\n    deferred_commands: Vec<egui::viewport::ViewportCommand>,\n    info: ViewportInfo,\n    actions_requested: Vec<ActionRequested>,\n\n    /// `None` for sync viewports.\n    viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,\n\n    /// Window surface state that's initialized when the app starts running via a Resumed event\n    /// and on Android will also be destroyed if the application is paused.\n    window: Option<Arc<Window>>,\n\n    /// `window` and `egui_winit` are initialized together.\n    egui_winit: Option<egui_winit::State>,\n}\n\n// ----------------------------------------------------------------------------\n\nimpl<'app> WgpuWinitApp<'app> {\n    pub fn new(\n        event_loop: &EventLoop<UserEvent>,\n        app_name: &str,\n        native_options: NativeOptions,\n        app_creator: AppCreator<'app>,\n    ) -> Self {\n        profiling::function_scope!();\n\n        #[cfg(feature = \"__screenshot\")]\n        assert!(\n            std::env::var(\"EFRAME_SCREENSHOT_TO\").is_err(),\n            \"EFRAME_SCREENSHOT_TO not yet implemented for wgpu backend\"\n        );\n\n        Self {\n            repaint_proxy: Arc::new(Mutex::new(event_loop.create_proxy())),\n            app_name: app_name.to_owned(),\n            native_options,\n            running: None,\n            app_creator: Some(app_creator),\n        }\n    }\n\n    /// Create a window for all viewports lacking one.\n    fn initialized_all_windows(&mut self, event_loop: &ActiveEventLoop) {\n        let Some(running) = &mut self.running else {\n            return;\n        };\n        let mut shared = running.shared.borrow_mut();\n        let SharedState {\n            viewports,\n            painter,\n            viewport_from_window,\n            ..\n        } = &mut *shared;\n\n        for viewport in viewports.values_mut() {\n            viewport.initialize_window(\n                event_loop,\n                &running.integration.egui_ctx,\n                viewport_from_window,\n                painter,\n            );\n        }\n    }\n\n    #[cfg(target_os = \"android\")]\n    fn recreate_window(&self, event_loop: &ActiveEventLoop, running: &WgpuWinitRunning<'app>) {\n        let SharedState {\n            egui_ctx,\n            viewports,\n            viewport_from_window,\n            painter,\n            ..\n        } = &mut *running.shared.borrow_mut();\n\n        initialize_or_update_viewport(\n            viewports,\n            ViewportIdPair::ROOT,\n            ViewportClass::Root,\n            self.native_options.viewport.clone(),\n            None,\n            painter,\n        )\n        .initialize_window(event_loop, egui_ctx, viewport_from_window, painter);\n    }\n\n    #[cfg(target_os = \"android\")]\n    fn drop_window(&mut self) -> Result<(), egui_wgpu::WgpuError> {\n        if let Some(running) = &mut self.running {\n            let mut shared = running.shared.borrow_mut();\n            shared.viewports.remove(&ViewportId::ROOT);\n            pollster::block_on(shared.painter.set_window(ViewportId::ROOT, None))?;\n        }\n        Ok(())\n    }\n\n    fn init_run_state(\n        &mut self,\n        egui_ctx: egui::Context,\n        event_loop: &ActiveEventLoop,\n        storage: Option<Box<dyn Storage>>,\n        window: Window,\n        builder: ViewportBuilder,\n    ) -> crate::Result<&mut WgpuWinitRunning<'app>> {\n        profiling::function_scope!();\n        let mut painter = pollster::block_on(egui_wgpu::winit::Painter::new(\n            egui_ctx.clone(),\n            self.native_options.wgpu_options.clone(),\n            self.native_options.viewport.transparent.unwrap_or(false),\n            egui_wgpu::RendererOptions {\n                msaa_samples: self.native_options.multisampling as _,\n                depth_stencil_format: egui_wgpu::depth_format_from_bits(\n                    self.native_options.depth_buffer,\n                    self.native_options.stencil_buffer,\n                ),\n                dithering: self.native_options.dithering,\n                ..Default::default()\n            },\n        ));\n\n        let mut viewport_info = ViewportInfo::default();\n        egui_winit::update_viewport_info(&mut viewport_info, &egui_ctx, &window, true);\n\n        {\n            // Tell egui right away about native_pixels_per_point etc,\n            // so that the app knows about it during app creation:\n            let pixels_per_point = egui_winit::pixels_per_point(&egui_ctx, &window);\n\n            egui_ctx.input_mut(|i| {\n                i.raw\n                    .viewports\n                    .insert(ViewportId::ROOT, viewport_info.clone());\n                i.pixels_per_point = pixels_per_point;\n            });\n        }\n\n        let window = Arc::new(window);\n\n        {\n            profiling::scope!(\"set_window\");\n            pollster::block_on(painter.set_window(ViewportId::ROOT, Some(Arc::clone(&window))))?;\n        }\n\n        let wgpu_render_state = painter.render_state();\n\n        let integration = EpiIntegration::new(\n            egui_ctx.clone(),\n            &window,\n            &self.app_name,\n            &self.native_options,\n            storage,\n            #[cfg(feature = \"glow\")]\n            None,\n            #[cfg(feature = \"glow\")]\n            None,\n            wgpu_render_state.clone(),\n        );\n\n        {\n            let event_loop_proxy = Arc::clone(&self.repaint_proxy);\n\n            egui_ctx.set_request_repaint_callback(move |info| {\n                log::trace!(\"request_repaint_callback: {info:?}\");\n                let when = Instant::now() + info.delay;\n                let cumulative_pass_nr = info.current_cumulative_pass_nr;\n\n                event_loop_proxy\n                    .lock()\n                    .send_event(UserEvent::RequestRepaint {\n                        when,\n                        cumulative_pass_nr,\n                        viewport_id: info.viewport_id,\n                    })\n                    .ok();\n            });\n        }\n\n        #[allow(clippy::allow_attributes, unused_mut)] // used for accesskit\n        let mut egui_winit = egui_winit::State::new(\n            egui_ctx.clone(),\n            ViewportId::ROOT,\n            event_loop,\n            Some(window.scale_factor() as f32),\n            event_loop.system_theme(),\n            painter.max_texture_side(),\n        );\n\n        #[cfg(feature = \"accesskit\")]\n        {\n            let event_loop_proxy = self.repaint_proxy.lock().clone();\n            egui_winit.init_accesskit(event_loop, &window, event_loop_proxy);\n        }\n\n        let app_creator = std::mem::take(&mut self.app_creator)\n            .expect(\"Single-use AppCreator has unexpectedly already been taken\");\n        let cc = CreationContext {\n            egui_ctx: egui_ctx.clone(),\n            integration_info: integration.frame.info().clone(),\n            storage: integration.frame.storage(),\n            #[cfg(feature = \"glow\")]\n            gl: None,\n            #[cfg(feature = \"glow\")]\n            get_proc_address: None,\n            wgpu_render_state,\n            raw_display_handle: window.display_handle().map(|h| h.as_raw()),\n            raw_window_handle: window.window_handle().map(|h| h.as_raw()),\n        };\n        let app = {\n            profiling::scope!(\"user_app_creator\");\n            app_creator(&cc).map_err(crate::Error::AppCreation)?\n        };\n\n        let mut viewport_from_window = HashMap::default();\n        viewport_from_window.insert(window.id(), ViewportId::ROOT);\n\n        let mut viewports = Viewports::default();\n        viewports.insert(\n            ViewportId::ROOT,\n            Viewport {\n                ids: ViewportIdPair::ROOT,\n                class: ViewportClass::Root,\n                builder,\n                deferred_commands: vec![],\n                info: viewport_info,\n                actions_requested: Default::default(),\n                viewport_ui_cb: None,\n                window: Some(window),\n                egui_winit: Some(egui_winit),\n            },\n        );\n\n        let shared = Rc::new(RefCell::new(SharedState {\n            egui_ctx,\n            viewport_from_window,\n            viewports,\n            painter,\n            focused_viewport: Some(ViewportId::ROOT),\n            resized_viewport: None,\n        }));\n\n        {\n            // Create a weak pointer so that we don't keep state alive for too long.\n            let shared = Rc::downgrade(&shared);\n            let beginning = integration.beginning;\n\n            egui::Context::set_immediate_viewport_renderer(move |_egui_ctx, immediate_viewport| {\n                if let Some(shared) = shared.upgrade() {\n                    render_immediate_viewport(beginning, &shared, immediate_viewport);\n                } else {\n                    log::warn!(\"render_sync_callback called after window closed\");\n                }\n            });\n        }\n\n        Ok(self.running.insert(WgpuWinitRunning {\n            integration,\n            app,\n            shared,\n        }))\n    }\n}\n\nimpl WinitApp for WgpuWinitApp<'_> {\n    fn egui_ctx(&self) -> Option<&egui::Context> {\n        self.running.as_ref().map(|r| &r.integration.egui_ctx)\n    }\n\n    fn window(&self, window_id: WindowId) -> Option<Arc<Window>> {\n        self.running\n            .as_ref()\n            .and_then(|r| {\n                let shared = r.shared.borrow();\n                let id = shared.viewport_from_window.get(&window_id)?;\n                shared.viewports.get(id).map(|v| v.window.clone())\n            })\n            .flatten()\n    }\n\n    fn window_id_from_viewport_id(&self, id: ViewportId) -> Option<WindowId> {\n        Some(\n            self.running\n                .as_ref()?\n                .shared\n                .borrow()\n                .viewports\n                .get(&id)?\n                .window\n                .as_ref()?\n                .id(),\n        )\n    }\n\n    fn save(&mut self) {\n        log::debug!(\"WinitApp::save called\");\n        if let Some(running) = self.running.as_mut() {\n            running.save();\n        }\n    }\n\n    fn save_and_destroy(&mut self) {\n        if let Some(mut running) = self.running.take() {\n            running.save_and_destroy();\n        }\n    }\n\n    fn run_ui_and_paint(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        window_id: WindowId,\n    ) -> Result<EventResult> {\n        self.initialized_all_windows(event_loop);\n\n        if let Some(running) = &mut self.running {\n            running.run_ui_and_paint(window_id)\n        } else {\n            Ok(EventResult::Wait)\n        }\n    }\n\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) -> crate::Result<EventResult> {\n        log::debug!(\"Event::Resumed\");\n\n        let running = if let Some(running) = &self.running {\n            #[cfg(target_os = \"android\")]\n            self.recreate_window(event_loop, running);\n            running\n        } else {\n            let storage = if let Some(file) = &self.native_options.persistence_path {\n                epi_integration::create_storage_with_file(file)\n            } else {\n                epi_integration::create_storage(\n                    self.native_options\n                        .viewport\n                        .app_id\n                        .as_ref()\n                        .unwrap_or(&self.app_name),\n                )\n            };\n            let egui_ctx = winit_integration::create_egui_context(storage.as_deref());\n            let (window, builder) = create_window(\n                &egui_ctx,\n                event_loop,\n                storage.as_deref(),\n                &mut self.native_options,\n            )?;\n            self.init_run_state(egui_ctx, event_loop, storage, window, builder)?\n        };\n\n        let viewport = &running.shared.borrow().viewports[&ViewportId::ROOT];\n        if let Some(window) = &viewport.window {\n            Ok(EventResult::RepaintNow(window.id()))\n        } else {\n            Ok(EventResult::Wait)\n        }\n    }\n\n    fn suspended(&mut self, _: &ActiveEventLoop) -> crate::Result<EventResult> {\n        #[cfg(target_os = \"android\")]\n        self.drop_window()?;\n        Ok(EventResult::Save)\n    }\n\n    fn device_event(\n        &mut self,\n        _: &ActiveEventLoop,\n        _: winit::event::DeviceId,\n        event: winit::event::DeviceEvent,\n    ) -> crate::Result<EventResult> {\n        if let winit::event::DeviceEvent::MouseMotion { delta } = event\n            && let Some(running) = &mut self.running\n        {\n            let mut shared = running.shared.borrow_mut();\n            if let Some(viewport) = shared\n                .focused_viewport\n                .and_then(|viewport| shared.viewports.get_mut(&viewport))\n                && let Some(window) = viewport.window.as_ref()\n            {\n                if !window.has_focus()\n                    && !viewport\n                        .egui_winit\n                        .as_ref()\n                        .map(|state| state.is_any_pointer_button_down())\n                        .unwrap_or(false)\n                {\n                    return Ok(EventResult::Wait);\n                }\n\n                if let Some(egui_winit) = viewport.egui_winit.as_mut()\n                    && egui_winit.on_mouse_motion(delta)\n                {\n                    return Ok(EventResult::RepaintNext(window.id()));\n                }\n            }\n        }\n\n        Ok(EventResult::Wait)\n    }\n\n    fn window_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        window_id: WindowId,\n        event: winit::event::WindowEvent,\n    ) -> crate::Result<EventResult> {\n        self.initialized_all_windows(event_loop);\n\n        if let Some(running) = &mut self.running {\n            Ok(running.on_window_event(window_id, &event))\n        } else {\n            // running is removed to get ready for exiting\n            Ok(EventResult::Exit)\n        }\n    }\n\n    #[cfg(feature = \"accesskit\")]\n    fn on_accesskit_event(&mut self, event: accesskit_winit::Event) -> crate::Result<EventResult> {\n        if let Some(running) = &mut self.running {\n            let mut shared_lock = running.shared.borrow_mut();\n            let SharedState {\n                viewport_from_window,\n                viewports,\n                ..\n            } = &mut *shared_lock;\n            if let Some(viewport) = viewport_from_window\n                .get(&event.window_id)\n                .and_then(|id| viewports.get_mut(id))\n                && let Some(egui_winit) = &mut viewport.egui_winit\n            {\n                return Ok(winit_integration::on_accesskit_window_event(\n                    egui_winit,\n                    event.window_id,\n                    &event.window_event,\n                ));\n            }\n        }\n\n        Ok(EventResult::Wait)\n    }\n}\n\nimpl WgpuWinitRunning<'_> {\n    /// Saves the application state\n    fn save(&mut self) {\n        let shared = self.shared.borrow();\n        // This is done because of the \"save on suspend\" logic on Android. Once the application is suspended, there is no window associated to it.\n        let window = if let Some(Viewport { window, .. }) = shared.viewports.get(&ViewportId::ROOT)\n        {\n            window.as_deref()\n        } else {\n            None\n        };\n        self.integration.save(self.app.as_mut(), window);\n    }\n\n    fn save_and_destroy(&mut self) {\n        profiling::function_scope!();\n\n        self.save();\n\n        #[cfg(feature = \"glow\")]\n        self.app.on_exit(None);\n\n        #[cfg(not(feature = \"glow\"))]\n        self.app.on_exit();\n\n        let mut shared = self.shared.borrow_mut();\n        shared.painter.destroy();\n    }\n\n    /// This is called both for the root viewport, and all deferred viewports\n    fn run_ui_and_paint(&mut self, window_id: WindowId) -> Result<EventResult> {\n        profiling::function_scope!();\n\n        let Some(viewport_id) = self\n            .shared\n            .borrow()\n            .viewport_from_window\n            .get(&window_id)\n            .copied()\n        else {\n            return Ok(EventResult::Wait);\n        };\n\n        profiling::finish_frame!();\n\n        let Self {\n            app,\n            integration,\n            shared,\n        } = self;\n\n        let mut frame_timer = crate::stopwatch::Stopwatch::new();\n        frame_timer.start();\n\n        let (viewport_ui_cb, raw_input, is_visible) = {\n            profiling::scope!(\"Prepare\");\n            let mut shared_lock = shared.borrow_mut();\n\n            let SharedState {\n                viewports, painter, ..\n            } = &mut *shared_lock;\n\n            if viewport_id != ViewportId::ROOT {\n                let Some(viewport) = viewports.get(&viewport_id) else {\n                    return Ok(EventResult::Wait);\n                };\n\n                if viewport.viewport_ui_cb.is_none() {\n                    // This will only happen if this is an immediate viewport.\n                    // That means that the viewport cannot be rendered by itself and needs his parent to be rendered.\n                    if let Some(viewport) = viewports.get(&viewport.ids.parent)\n                        && let Some(window) = viewport.window.as_ref()\n                    {\n                        return Ok(EventResult::RepaintNext(window.id()));\n                    }\n                    return Ok(EventResult::Wait);\n                }\n            }\n\n            let Some(viewport) = viewports.get_mut(&viewport_id) else {\n                return Ok(EventResult::Wait);\n            };\n\n            let Viewport {\n                viewport_ui_cb,\n                window,\n                egui_winit,\n                info,\n                ..\n            } = viewport;\n\n            let viewport_ui_cb = viewport_ui_cb.clone();\n\n            let Some(window) = window else {\n                return Ok(EventResult::Wait);\n            };\n            egui_winit::update_viewport_info(info, &integration.egui_ctx, window, false);\n\n            let is_visible = viewport.info.visible().unwrap_or(true);\n\n            {\n                profiling::scope!(\"set_window\");\n                pollster::block_on(painter.set_window(viewport_id, Some(Arc::clone(window))))?;\n            }\n\n            let Some(egui_winit) = egui_winit.as_mut() else {\n                return Ok(EventResult::Wait);\n            };\n            let mut raw_input = egui_winit.take_egui_input(window);\n\n            integration.pre_update();\n\n            raw_input.time = Some(integration.beginning.elapsed().as_secs_f64());\n            raw_input.viewports = viewports\n                .iter()\n                .map(|(id, viewport)| (*id, viewport.info.clone()))\n                .collect();\n\n            painter.handle_screenshots(&mut raw_input.events);\n\n            (viewport_ui_cb, raw_input, is_visible)\n        };\n\n        // ------------------------------------------------------------\n\n        // Runs the update, which could call immediate viewports,\n        // so make sure we hold no locks here!\n        let full_output = integration.update(\n            app.as_mut(),\n            viewport_ui_cb.as_deref(),\n            raw_input,\n            is_visible,\n        );\n\n        // ------------------------------------------------------------\n\n        let mut shared_mut = shared.borrow_mut();\n\n        let SharedState {\n            egui_ctx,\n            viewports,\n            painter,\n            viewport_from_window,\n            ..\n        } = &mut *shared_mut;\n\n        let FullOutput {\n            platform_output,\n            textures_delta,\n            shapes,\n            pixels_per_point,\n            viewport_output,\n        } = full_output;\n\n        remove_viewports_not_in(viewports, painter, viewport_from_window, &viewport_output);\n\n        let Some(viewport) = viewports.get_mut(&viewport_id) else {\n            return Ok(EventResult::Wait);\n        };\n\n        viewport.info.events.clear(); // they should have been processed\n\n        let Viewport {\n            window: Some(window),\n            egui_winit: Some(egui_winit),\n            ..\n        } = viewport\n        else {\n            return Ok(EventResult::Wait);\n        };\n\n        egui_winit.handle_platform_output(window, platform_output);\n\n        let vsync_secs = if is_visible {\n            let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point);\n\n            let mut screenshot_commands = vec![];\n            viewport.actions_requested.retain(|cmd| {\n                if let ActionRequested::Screenshot(info) = cmd {\n                    screenshot_commands.push(info.clone());\n                    false\n                } else {\n                    true\n                }\n            });\n            let vsync_secs = painter.paint_and_update_textures(\n                viewport_id,\n                pixels_per_point,\n                app.clear_color(&egui_ctx.global_style().visuals),\n                &clipped_primitives,\n                &textures_delta,\n                screenshot_commands,\n            );\n\n            for action in viewport.actions_requested.drain(..) {\n                match action {\n                    ActionRequested::Screenshot { .. } => {\n                        // already handled above\n                    }\n                    ActionRequested::Cut => {\n                        egui_winit.egui_input_mut().events.push(egui::Event::Cut);\n                    }\n                    ActionRequested::Copy => {\n                        egui_winit.egui_input_mut().events.push(egui::Event::Copy);\n                    }\n                    ActionRequested::Paste => {\n                        if let Some(contents) = egui_winit.clipboard_text() {\n                            let contents = contents.replace(\"\\r\\n\", \"\\n\");\n                            if !contents.is_empty() {\n                                egui_winit\n                                    .egui_input_mut()\n                                    .events\n                                    .push(egui::Event::Paste(contents));\n                            }\n                        }\n                    }\n                }\n            }\n\n            integration.post_rendering(window);\n\n            vsync_secs\n        } else {\n            0.0\n        };\n\n        let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();\n\n        handle_viewport_output(\n            &integration.egui_ctx,\n            &viewport_output,\n            viewports,\n            painter,\n            viewport_from_window,\n        );\n\n        // Prune dead viewports:\n        viewports.retain(|id, _| active_viewports_ids.contains(id));\n        viewport_from_window.retain(|_, id| active_viewports_ids.contains(id));\n        painter.gc_viewports(&active_viewports_ids);\n\n        let window = viewport_from_window\n            .get(&window_id)\n            .and_then(|id| viewports.get(id))\n            .and_then(|vp| vp.window.as_ref());\n\n        integration.report_frame_time(frame_timer.total_time_sec() - vsync_secs); // don't count auto-save time as part of regular frame time\n\n        integration.maybe_autosave(app.as_mut(), window.map(|w| w.as_ref()));\n\n        if let Some(window) = window\n            && window.is_minimized() == Some(true)\n        {\n            // On Mac, a minimized Window uses up all CPU:\n            // https://github.com/emilk/egui/issues/325\n            profiling::scope!(\"minimized_sleep\");\n            std::thread::sleep(std::time::Duration::from_millis(10));\n        }\n\n        if integration.should_close() {\n            Ok(EventResult::CloseRequested)\n        } else {\n            Ok(EventResult::Wait)\n        }\n    }\n\n    fn on_window_event(\n        &mut self,\n        window_id: WindowId,\n        event: &winit::event::WindowEvent,\n    ) -> EventResult {\n        let Self {\n            integration,\n            shared,\n            ..\n        } = self;\n        let mut shared = shared.borrow_mut();\n\n        let viewport_id = shared.viewport_from_window.get(&window_id).copied();\n\n        // On Windows, if a window is resized by the user, it should repaint synchronously, inside the\n        // event handler. If this is not done, the compositor will assume that the window does not want\n        // to redraw and continue ahead.\n        //\n        // In eframe's case, that causes the window to rapidly flicker, as it struggles to deliver\n        // new frames to the compositor in time. The flickering is technically glutin or glow's fault, but we should be responding properly\n        // to resizes anyway, as doing so avoids dropping frames.\n        //\n        // See: https://github.com/emilk/egui/issues/903\n        let mut repaint_asap = false;\n\n        // On MacOS the asap repaint is not enough. The drawn frames must be synchronized with\n        // the CoreAnimation transactions driving the window resize process.\n        //\n        // Thus, Painter, responsible for wgpu surfaces and their resize, has to be notified of the\n        // resize lifecycle, yet winit does not provide any events for that. To work around,\n        // the last resized viewport is tracked until any next non-resize event is received.\n        //\n        // Accidental state change during the resize process due to an unexpected event fire\n        // is ok, state will switch back upon next resize event.\n        //\n        // See: https://github.com/emilk/egui/issues/903\n        if let Some(id) = viewport_id\n            && shared.resized_viewport == viewport_id\n        {\n            shared.painter.on_window_resize_state_change(id, false);\n            shared.resized_viewport = None;\n        }\n\n        match event {\n            winit::event::WindowEvent::Focused(focused) => {\n                let focused = if cfg!(target_os = \"macos\")\n                    && let Some(viewport_id) = viewport_id\n                    && let Some(viewport) = shared.viewports.get(&viewport_id)\n                    && let Some(window) = &viewport.window\n                {\n                    // TODO(emilk): remove this work-around once we update winit\n                    // https://github.com/rust-windowing/winit/issues/4371\n                    // https://github.com/emilk/egui/issues/7588\n                    window.has_focus()\n                } else {\n                    *focused\n                };\n\n                shared.focused_viewport = focused.then_some(viewport_id).flatten();\n            }\n\n            winit::event::WindowEvent::Resized(physical_size) => {\n                // Resize with 0 width and height is used by winit to signal a minimize event on Windows.\n                // See: https://github.com/rust-windowing/winit/issues/208\n                // This solves an issue where the app would panic when minimizing on Windows.\n                if let Some(id) = viewport_id\n                    && let (Some(width), Some(height)) = (\n                        NonZeroU32::new(physical_size.width),\n                        NonZeroU32::new(physical_size.height),\n                    )\n                {\n                    if shared.resized_viewport != viewport_id {\n                        shared.resized_viewport = viewport_id;\n                        shared.painter.on_window_resize_state_change(id, true);\n                    }\n                    shared.painter.on_window_resized(id, width, height);\n                    repaint_asap = true;\n                }\n            }\n\n            winit::event::WindowEvent::Occluded(is_occluded) => {\n                if let Some(viewport_id) = viewport_id\n                    && let Some(viewport) = shared.viewports.get_mut(&viewport_id)\n                {\n                    viewport.info.occluded = Some(*is_occluded);\n                }\n            }\n\n            winit::event::WindowEvent::CloseRequested => {\n                if viewport_id == Some(ViewportId::ROOT) && integration.should_close() {\n                    log::debug!(\n                        \"Received WindowEvent::CloseRequested for main viewport - shutting down.\"\n                    );\n                    return EventResult::CloseRequested;\n                }\n\n                log::debug!(\"Received WindowEvent::CloseRequested for viewport {viewport_id:?}\");\n\n                if let Some(viewport_id) = viewport_id\n                    && let Some(viewport) = shared.viewports.get_mut(&viewport_id)\n                {\n                    // Tell viewport it should close:\n                    viewport.info.events.push(egui::ViewportEvent::Close);\n\n                    // We may need to repaint both us and our parent to close the window,\n                    // and perhaps twice (once to notice the close-event, once again to enforce it).\n                    // `request_repaint_of` does a double-repaint though:\n                    integration.egui_ctx.request_repaint_of(viewport_id);\n                    integration.egui_ctx.request_repaint_of(viewport.ids.parent);\n                }\n            }\n\n            _ => {}\n        }\n\n        let event_response = viewport_id\n            .and_then(|viewport_id| {\n                let viewport = shared.viewports.get_mut(&viewport_id)?;\n                Some(integration.on_window_event(\n                    viewport.window.as_deref()?,\n                    viewport.egui_winit.as_mut()?,\n                    event,\n                ))\n            })\n            .unwrap_or_default();\n\n        if integration.should_close() {\n            EventResult::CloseRequested\n        } else if event_response.repaint {\n            if repaint_asap {\n                EventResult::RepaintNow(window_id)\n            } else {\n                EventResult::RepaintNext(window_id)\n            }\n        } else {\n            EventResult::Wait\n        }\n    }\n}\n\nimpl Viewport {\n    /// Create winit window, if needed.\n    fn initialize_window(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        egui_ctx: &egui::Context,\n        windows_id: &mut HashMap<WindowId, ViewportId>,\n        painter: &mut egui_wgpu::winit::Painter,\n    ) {\n        if self.window.is_some() {\n            return; // we already have one\n        }\n\n        profiling::function_scope!();\n\n        let viewport_id = self.ids.this;\n\n        match egui_winit::create_window(egui_ctx, event_loop, &self.builder) {\n            Ok(window) => {\n                windows_id.insert(window.id(), viewport_id);\n\n                let window = Arc::new(window);\n\n                if let Err(err) =\n                    pollster::block_on(painter.set_window(viewport_id, Some(Arc::clone(&window))))\n                {\n                    log::error!(\"on set_window: viewport_id {viewport_id:?} {err}\");\n                }\n\n                self.egui_winit = Some(egui_winit::State::new(\n                    egui_ctx.clone(),\n                    viewport_id,\n                    event_loop,\n                    Some(window.scale_factor() as f32),\n                    event_loop.system_theme(),\n                    painter.max_texture_side(),\n                ));\n\n                egui_winit::update_viewport_info(&mut self.info, egui_ctx, &window, true);\n                self.window = Some(window);\n            }\n            Err(err) => {\n                log::error!(\"Failed to create window: {err}\");\n            }\n        }\n    }\n}\n\nfn create_window(\n    egui_ctx: &egui::Context,\n    event_loop: &ActiveEventLoop,\n    storage: Option<&dyn Storage>,\n    native_options: &mut NativeOptions,\n) -> Result<(Window, ViewportBuilder), winit::error::OsError> {\n    profiling::function_scope!();\n\n    let window_settings = epi_integration::load_window_settings(storage);\n    let viewport_builder = epi_integration::viewport_builder(\n        egui_ctx.zoom_factor(),\n        event_loop,\n        native_options,\n        window_settings,\n    )\n    .with_visible(false); // Start hidden until we render the first frame to fix white flash on startup (https://github.com/emilk/egui/pull/3631)\n\n    let window = egui_winit::create_window(egui_ctx, event_loop, &viewport_builder)?;\n    epi_integration::apply_window_settings(&window, window_settings);\n    Ok((window, viewport_builder))\n}\n\nfn render_immediate_viewport(\n    beginning: Instant,\n    shared: &RefCell<SharedState>,\n    immediate_viewport: ImmediateViewport<'_>,\n) {\n    profiling::function_scope!();\n\n    let ImmediateViewport {\n        ids,\n        builder,\n        mut viewport_ui_cb,\n    } = immediate_viewport;\n\n    let input = {\n        let SharedState {\n            egui_ctx,\n            viewports,\n            painter,\n            viewport_from_window,\n            ..\n        } = &mut *shared.borrow_mut();\n\n        let viewport = initialize_or_update_viewport(\n            viewports,\n            ids,\n            ViewportClass::Immediate,\n            builder,\n            None,\n            painter,\n        );\n        if viewport.window.is_none() {\n            event_loop_context::with_current_event_loop(|event_loop| {\n                viewport.initialize_window(event_loop, egui_ctx, viewport_from_window, painter);\n            });\n        }\n\n        let (Some(window), Some(egui_winit)) = (&viewport.window, &mut viewport.egui_winit) else {\n            return;\n        };\n        egui_winit::update_viewport_info(&mut viewport.info, egui_ctx, window, false);\n\n        let mut input = egui_winit.take_egui_input(window);\n        input.viewports = viewports\n            .iter()\n            .map(|(id, viewport)| (*id, viewport.info.clone()))\n            .collect();\n        input.time = Some(beginning.elapsed().as_secs_f64());\n        input\n    };\n\n    let egui_ctx = shared.borrow().egui_ctx.clone();\n\n    // ------------------------------------------\n\n    // Run the user code, which could re-entrantly call this function again (!).\n    // Make sure no locks are held during this call.\n    let egui::FullOutput {\n        platform_output,\n        textures_delta,\n        shapes,\n        pixels_per_point,\n        viewport_output,\n    } = egui_ctx.run_ui(input, |ui| {\n        viewport_ui_cb(ui);\n    });\n\n    // ------------------------------------------\n\n    let mut shared_mut = shared.borrow_mut();\n    let SharedState {\n        viewports,\n        painter,\n        viewport_from_window,\n        ..\n    } = &mut *shared_mut;\n\n    let Some(viewport) = viewports.get_mut(&ids.this) else {\n        return;\n    };\n    viewport.info.events.clear(); // they should have been processed\n    let (Some(egui_winit), Some(window)) = (&mut viewport.egui_winit, &viewport.window) else {\n        return;\n    };\n\n    {\n        profiling::scope!(\"set_window\");\n        if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(Arc::clone(window))))\n        {\n            log::error!(\n                \"when rendering viewport_id={:?}, set_window Error {err}\",\n                ids.this\n            );\n        }\n    }\n\n    let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point);\n    painter.paint_and_update_textures(\n        ids.this,\n        pixels_per_point,\n        [0.0, 0.0, 0.0, 0.0],\n        &clipped_primitives,\n        &textures_delta,\n        vec![],\n    );\n\n    egui_winit.handle_platform_output(window, platform_output);\n\n    handle_viewport_output(\n        &egui_ctx,\n        &viewport_output,\n        viewports,\n        painter,\n        viewport_from_window,\n    );\n}\n\npub(crate) fn remove_viewports_not_in(\n    viewports: &mut Viewports,\n    painter: &mut egui_wgpu::winit::Painter,\n    viewport_from_window: &mut HashMap<WindowId, ViewportId>,\n    viewport_output: &OrderedViewportIdMap<ViewportOutput>,\n) {\n    let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();\n\n    // Prune dead viewports:\n    viewports.retain(|id, _| active_viewports_ids.contains(id));\n    viewport_from_window.retain(|_, id| active_viewports_ids.contains(id));\n    painter.gc_viewports(&active_viewports_ids);\n}\n\n/// Add new viewports, and update existing ones:\nfn handle_viewport_output(\n    egui_ctx: &egui::Context,\n    viewport_output: &OrderedViewportIdMap<ViewportOutput>,\n    viewports: &mut Viewports,\n    painter: &mut egui_wgpu::winit::Painter,\n    viewport_from_window: &mut HashMap<WindowId, ViewportId>,\n) {\n    for (\n        viewport_id,\n        ViewportOutput {\n            parent,\n            class,\n            builder,\n            viewport_ui_cb,\n            mut commands,\n            repaint_delay: _, // ignored - we listened to the repaint callback instead\n        },\n    ) in viewport_output.clone()\n    {\n        let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent);\n\n        let viewport =\n            initialize_or_update_viewport(viewports, ids, class, builder, viewport_ui_cb, painter);\n\n        if let Some(window) = viewport.window.as_ref() {\n            let old_inner_size = window.inner_size();\n\n            viewport.deferred_commands.append(&mut commands);\n\n            egui_winit::process_viewport_commands(\n                egui_ctx,\n                &mut viewport.info,\n                std::mem::take(&mut viewport.deferred_commands),\n                window,\n                &mut viewport.actions_requested,\n            );\n\n            // For Wayland : https://github.com/emilk/egui/issues/4196\n            if cfg!(target_os = \"linux\") {\n                let new_inner_size = window.inner_size();\n                if new_inner_size != old_inner_size\n                    && let (Some(width), Some(height)) = (\n                        NonZeroU32::new(new_inner_size.width),\n                        NonZeroU32::new(new_inner_size.height),\n                    )\n                {\n                    painter.on_window_resized(viewport_id, width, height);\n                }\n            }\n        }\n    }\n\n    remove_viewports_not_in(viewports, painter, viewport_from_window, viewport_output);\n}\n\nfn initialize_or_update_viewport<'a>(\n    viewports: &'a mut Viewports,\n    ids: ViewportIdPair,\n    class: ViewportClass,\n    mut builder: ViewportBuilder,\n    viewport_ui_cb: Option<Arc<dyn Fn(&mut egui::Ui) + Send + Sync>>,\n    painter: &mut egui_wgpu::winit::Painter,\n) -> &'a mut Viewport {\n    use std::collections::btree_map::Entry;\n\n    profiling::function_scope!();\n\n    if builder.icon.is_none() {\n        // Inherit icon from parent\n        builder.icon = viewports\n            .get_mut(&ids.parent)\n            .and_then(|vp| vp.builder.icon.clone());\n    }\n\n    match viewports.entry(ids.this) {\n        Entry::Vacant(entry) => {\n            // New viewport:\n            log::debug!(\"Creating new viewport {:?} ({:?})\", ids.this, builder.title);\n            entry.insert(Viewport {\n                ids,\n                class,\n                builder,\n                deferred_commands: vec![],\n                info: Default::default(),\n                actions_requested: Vec::new(),\n                viewport_ui_cb,\n                window: None,\n                egui_winit: None,\n            })\n        }\n\n        Entry::Occupied(mut entry) => {\n            // Patch an existing viewport:\n            let viewport = entry.get_mut();\n\n            viewport.class = class;\n            viewport.ids.parent = ids.parent;\n            viewport.viewport_ui_cb = viewport_ui_cb;\n\n            let (mut delta_commands, recreate) = viewport.builder.patch(builder);\n\n            if recreate {\n                log::debug!(\n                    \"Recreating window for viewport {:?} ({:?})\",\n                    ids.this,\n                    viewport.builder.title\n                );\n                viewport.window = None;\n                viewport.egui_winit = None;\n                if let Err(err) = pollster::block_on(painter.set_window(viewport.ids.this, None)) {\n                    log::error!(\n                        \"when rendering viewport_id={:?}, set_window Error {err}\",\n                        viewport.ids.this\n                    );\n                }\n            }\n\n            viewport.deferred_commands.append(&mut delta_commands);\n\n            entry.into_mut()\n        }\n    }\n}\n"
  },
  {
    "path": "crates/eframe/src/native/winit_integration.rs",
    "content": "use std::{sync::Arc, time::Instant};\n\nuse winit::{\n    event_loop::ActiveEventLoop,\n    window::{Window, WindowId},\n};\n\nuse egui::ViewportId;\n#[cfg(feature = \"accesskit\")]\nuse egui_winit::accesskit_winit;\n\n/// Create an egui context, restoring it from storage if possible.\npub fn create_egui_context(storage: Option<&dyn crate::Storage>) -> egui::Context {\n    profiling::function_scope!();\n\n    pub const IS_DESKTOP: bool = cfg!(any(\n        target_os = \"freebsd\",\n        target_os = \"linux\",\n        target_os = \"macos\",\n        target_os = \"openbsd\",\n        target_os = \"windows\",\n    ));\n\n    let egui_ctx = egui::Context::default();\n\n    egui_ctx.set_embed_viewports(!IS_DESKTOP);\n\n    egui_ctx.options_mut(|o| {\n        // eframe supports multi-pass (Context::request_discard).\n        #[expect(clippy::unwrap_used)]\n        {\n            o.max_passes = 2.try_into().unwrap();\n        }\n    });\n\n    let memory = crate::native::epi_integration::load_egui_memory(storage).unwrap_or_default();\n    egui_ctx.memory_mut(|mem| *mem = memory);\n\n    egui_ctx\n}\n\n/// The custom even `eframe` uses with the [`winit`] event loop.\n#[derive(Debug)]\npub enum UserEvent {\n    /// A repaint is requested.\n    RequestRepaint {\n        /// What to repaint.\n        viewport_id: ViewportId,\n\n        /// When to repaint.\n        when: Instant,\n\n        /// What the cumulative pass number was when the repaint was _requested_.\n        cumulative_pass_nr: u64,\n    },\n\n    /// A request related to [`accesskit`](https://accesskit.dev/).\n    #[cfg(feature = \"accesskit\")]\n    AccessKitActionRequest(accesskit_winit::Event),\n}\n\n#[cfg(feature = \"accesskit\")]\nimpl From<accesskit_winit::Event> for UserEvent {\n    fn from(inner: accesskit_winit::Event) -> Self {\n        Self::AccessKitActionRequest(inner)\n    }\n}\n\npub trait WinitApp {\n    fn egui_ctx(&self) -> Option<&egui::Context>;\n\n    fn window(&self, window_id: WindowId) -> Option<Arc<Window>>;\n\n    fn window_id_from_viewport_id(&self, id: ViewportId) -> Option<WindowId>;\n\n    fn save(&mut self);\n\n    fn save_and_destroy(&mut self);\n\n    fn run_ui_and_paint(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        window_id: WindowId,\n    ) -> crate::Result<EventResult>;\n\n    fn suspended(&mut self, event_loop: &ActiveEventLoop) -> crate::Result<EventResult>;\n\n    fn resumed(&mut self, event_loop: &ActiveEventLoop) -> crate::Result<EventResult>;\n\n    fn device_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        device_id: winit::event::DeviceId,\n        event: winit::event::DeviceEvent,\n    ) -> crate::Result<EventResult>;\n\n    fn window_event(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        window_id: WindowId,\n        event: winit::event::WindowEvent,\n    ) -> crate::Result<EventResult>;\n\n    #[cfg(feature = \"accesskit\")]\n    fn on_accesskit_event(&mut self, event: accesskit_winit::Event) -> crate::Result<EventResult>;\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum EventResult {\n    Wait,\n\n    /// Causes a synchronous repaint inside the event handler. This should only\n    /// be used in special situations if the window must be repainted while\n    /// handling a specific event. This occurs on Windows when handling resizes.\n    ///\n    /// `RepaintNow` creates a new frame synchronously, and should therefore\n    /// only be used for extremely urgent repaints.\n    RepaintNow(WindowId),\n\n    /// Queues a repaint for once the event loop handles its next redraw. Exists\n    /// so that multiple input events can be handled in one frame. Does not\n    /// cause any delay like `RepaintNow`.\n    RepaintNext(WindowId),\n\n    RepaintAt(WindowId, Instant),\n\n    /// Causes a save of the client state when the persistence feature is enabled.\n    Save,\n\n    /// Starts the process of ending eframe execution whilst allowing for proper\n    /// clean up of resources.\n    ///\n    /// # Warning\n    /// This event **must** occur before [`Exit`] to correctly exit eframe code.\n    /// If in doubt, return this event.\n    ///\n    /// [`Exit`]: [EventResult::Exit]\n    CloseRequested,\n\n    /// The event loop will exit, now.\n    /// The correct circumstance to return this event is in response to a winit \"Destroyed\" event.\n    ///\n    /// # Warning\n    /// The [`CloseRequested`] **must** occur before this event to ensure that winit\n    /// is able to remove any open windows. Otherwise the window(s) will remain open\n    /// until the program terminates.\n    ///\n    /// [`CloseRequested`]: EventResult::CloseRequested\n    Exit,\n}\n\n#[cfg(feature = \"accesskit\")]\npub(crate) fn on_accesskit_window_event(\n    egui_winit: &mut egui_winit::State,\n    window_id: WindowId,\n    event: &accesskit_winit::WindowEvent,\n) -> EventResult {\n    match event {\n        accesskit_winit::WindowEvent::InitialTreeRequested => {\n            egui_winit.egui_ctx().enable_accesskit();\n            // Because we can't provide the initial tree synchronously\n            // (because that would require the activation handler to access\n            // the same mutable state as the winit event handler), some\n            // AccessKit platform adapters will use a placeholder tree\n            // until we send the first tree update. To minimize the possible\n            // bad effects of that workaround, repaint and send the tree\n            // immediately.\n            EventResult::RepaintNow(window_id)\n        }\n        accesskit_winit::WindowEvent::ActionRequested(request) => {\n            egui_winit.on_accesskit_action_request(request.clone());\n            // As a form of user input, accessibility actions should cause\n            // a repaint, but not until the next regular frame.\n            EventResult::RepaintNext(window_id)\n        }\n        accesskit_winit::WindowEvent::AccessibilityDeactivated => {\n            egui_winit.egui_ctx().disable_accesskit();\n            // Disabling AccessKit support should have no visible effect,\n            // so there's no need to repaint.\n            EventResult::Wait\n        }\n    }\n}\n"
  },
  {
    "path": "crates/eframe/src/stopwatch.rs",
    "content": "#![allow(clippy::allow_attributes, dead_code)] // not used on all platforms\n\nuse web_time::Instant;\n\npub struct Stopwatch {\n    total_time_ns: u128,\n\n    /// None = not running\n    start: Option<Instant>,\n}\n\nimpl Stopwatch {\n    pub fn new() -> Self {\n        Self {\n            total_time_ns: 0,\n            start: None,\n        }\n    }\n\n    pub fn start(&mut self) {\n        assert!(self.start.is_none(), \"Stopwatch already running\");\n        self.start = Some(Instant::now());\n    }\n\n    pub fn pause(&mut self) {\n        let start = self.start.take().expect(\"Stopwatch is not running\");\n        let duration = start.elapsed();\n        self.total_time_ns += duration.as_nanos();\n    }\n\n    pub fn resume(&mut self) {\n        assert!(self.start.is_none(), \"Stopwatch still running\");\n        self.start = Some(Instant::now());\n    }\n\n    pub fn total_time_ns(&self) -> u128 {\n        if let Some(start) = self.start {\n            // Running\n            let duration = start.elapsed();\n            self.total_time_ns + duration.as_nanos()\n        } else {\n            // Paused\n            self.total_time_ns\n        }\n    }\n\n    pub fn total_time_sec(&self) -> f32 {\n        self.total_time_ns() as f32 * 1e-9\n    }\n}\n"
  },
  {
    "path": "crates/eframe/src/web/app_runner.rs",
    "content": "use std::sync::Arc;\n\nuse egui::{TexturesDelta, UserData, ViewportCommand};\n\nuse crate::{App, epi, web::web_painter::WebPainter};\n\nuse super::{NeedRepaint, now_sec, text_agent::TextAgent};\n\npub struct AppRunner {\n    #[allow(clippy::allow_attributes, dead_code)]\n    pub(crate) web_options: crate::WebOptions,\n    pub(crate) frame: epi::Frame,\n    egui_ctx: egui::Context,\n    painter: Box<dyn WebPainter>,\n    pub(crate) input: super::WebInput,\n    app: Box<dyn epi::App>,\n    pub(crate) needs_repaint: Arc<NeedRepaint>,\n    last_save_time: f64,\n    pub(crate) text_agent: TextAgent,\n\n    // If not empty, the painter should capture n frames from now.\n    // zero means capture the exact next frame.\n    screenshot_commands_with_frame_delay: Vec<(UserData, usize)>,\n\n    // Output for the last run:\n    textures_delta: TexturesDelta,\n    clipped_primitives: Option<Vec<egui::ClippedPrimitive>>,\n}\n\nimpl Drop for AppRunner {\n    fn drop(&mut self) {\n        log::debug!(\"AppRunner has fully dropped\");\n    }\n}\n\nimpl AppRunner {\n    /// # Errors\n    /// Failure to initialize WebGL renderer, or failure to create app.\n    #[cfg_attr(\n        not(feature = \"wgpu_no_default_features\"),\n        expect(clippy::unused_async)\n    )]\n    pub async fn new(\n        canvas: web_sys::HtmlCanvasElement,\n        web_options: crate::WebOptions,\n        app_creator: epi::AppCreator<'static>,\n        text_agent: TextAgent,\n    ) -> Result<Self, String> {\n        let egui_ctx = egui::Context::default();\n\n        #[allow(clippy::allow_attributes, unused_assignments)]\n        #[cfg(feature = \"glow\")]\n        let mut gl = None;\n\n        #[allow(clippy::allow_attributes, unused_assignments)]\n        #[cfg(feature = \"wgpu_no_default_features\")]\n        let mut wgpu_render_state = None;\n\n        let painter = match web_options.renderer {\n            #[cfg(feature = \"glow\")]\n            epi::Renderer::Glow => {\n                log::debug!(\"Using the glow renderer\");\n                let painter = super::web_painter_glow::WebPainterGlow::new(\n                    egui_ctx.clone(),\n                    canvas,\n                    &web_options,\n                )?;\n                gl = Some(Arc::clone(painter.gl()));\n                Box::new(painter) as Box<dyn WebPainter>\n            }\n\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            epi::Renderer::Wgpu => {\n                log::debug!(\"Using the wgpu renderer\");\n                let painter = super::web_painter_wgpu::WebPainterWgpu::new(\n                    egui_ctx.clone(),\n                    canvas,\n                    &web_options,\n                )\n                .await?;\n                wgpu_render_state = painter.render_state();\n                Box::new(painter) as Box<dyn WebPainter>\n            }\n        };\n\n        let info = epi::IntegrationInfo {\n            web_info: epi::WebInfo {\n                user_agent: super::user_agent().unwrap_or_default(),\n                location: super::web_location(),\n            },\n            cpu_usage: None,\n        };\n        let storage = LocalStorage::default();\n\n        egui_ctx.set_os(egui::os::OperatingSystem::from_user_agent(\n            &super::user_agent().unwrap_or_default(),\n        ));\n        super::storage::load_memory(&egui_ctx);\n\n        egui_ctx.options_mut(|o| {\n            // On web by default egui follows the zoom factor of the browser,\n            // and lets the browser handle the zoom shortcuts.\n            // A user can still zoom egui separately by calling [`egui::Context::set_zoom_factor`].\n            o.zoom_with_keyboard = false;\n            o.zoom_factor = 1.0;\n        });\n\n        // Tell egui right away about native_pixels_per_point\n        // so that the app knows about it during app creation:\n        egui_ctx.input_mut(|i| {\n            let viewport_info = i.raw.viewports.entry(egui::ViewportId::ROOT).or_default();\n            viewport_info.native_pixels_per_point = Some(super::native_pixels_per_point());\n            i.pixels_per_point = super::native_pixels_per_point();\n        });\n\n        let cc = epi::CreationContext {\n            egui_ctx: egui_ctx.clone(),\n            integration_info: info.clone(),\n            storage: Some(&storage),\n\n            #[cfg(feature = \"glow\")]\n            gl: gl.clone(),\n\n            #[cfg(feature = \"glow\")]\n            get_proc_address: None,\n\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            wgpu_render_state: wgpu_render_state.clone(),\n        };\n        let app = app_creator(&cc).map_err(|err| err.to_string())?;\n\n        let frame = epi::Frame {\n            info,\n            storage: Some(Box::new(storage)),\n\n            #[cfg(feature = \"glow\")]\n            gl,\n\n            #[cfg(feature = \"wgpu_no_default_features\")]\n            wgpu_render_state,\n        };\n\n        let needs_repaint: Arc<NeedRepaint> = Arc::new(NeedRepaint::new(web_options.max_fps));\n        {\n            let needs_repaint = Arc::clone(&needs_repaint);\n            egui_ctx.set_request_repaint_callback(move |info| {\n                needs_repaint.repaint_after(info.delay.as_secs_f64());\n            });\n        }\n\n        let mut runner = Self {\n            web_options,\n            frame,\n            egui_ctx,\n            painter,\n            input: Default::default(),\n            app,\n            needs_repaint,\n            last_save_time: now_sec(),\n            text_agent,\n            screenshot_commands_with_frame_delay: vec![],\n            textures_delta: Default::default(),\n            clipped_primitives: None,\n        };\n\n        runner.input.raw.max_texture_side = Some(runner.painter.max_texture_side());\n        runner\n            .input\n            .raw\n            .viewports\n            .entry(egui::ViewportId::ROOT)\n            .or_default()\n            .native_pixels_per_point = Some(super::native_pixels_per_point());\n        runner.input.raw.system_theme = super::system_theme();\n\n        Ok(runner)\n    }\n\n    pub fn egui_ctx(&self) -> &egui::Context {\n        &self.egui_ctx\n    }\n\n    /// Get mutable access to the concrete [`App`] we enclose.\n    ///\n    /// This will panic if your app does not implement [`App::as_any_mut`].\n    pub fn app_mut<ConcreteApp: 'static + App>(&mut self) -> &mut ConcreteApp {\n        self.app\n            .as_any_mut()\n            .expect(\"Your app must implement `as_any_mut`, but it doesn't\")\n            .downcast_mut::<ConcreteApp>()\n            .expect(\"app_mut got the wrong type of App\")\n    }\n\n    pub fn auto_save_if_needed(&mut self) {\n        let time_since_last_save = now_sec() - self.last_save_time;\n        if time_since_last_save > self.app.auto_save_interval().as_secs_f64() {\n            self.save();\n        }\n    }\n\n    pub fn save(&mut self) {\n        if self.app.persist_egui_memory() {\n            super::storage::save_memory(&self.egui_ctx);\n        }\n        if let Some(storage) = self.frame.storage_mut() {\n            self.app.save(storage);\n        }\n        self.last_save_time = now_sec();\n    }\n\n    pub fn canvas(&self) -> &web_sys::HtmlCanvasElement {\n        self.painter.canvas()\n    }\n\n    pub fn destroy(mut self) {\n        log::debug!(\"Destroying AppRunner\");\n        self.painter.destroy();\n    }\n\n    pub fn has_outstanding_paint_data(&self) -> bool {\n        self.clipped_primitives.is_some()\n    }\n\n    /// Does the eframe app have focus?\n    ///\n    /// Technically: does either the canvas or the [`TextAgent`] have focus?\n    pub fn has_focus(&self) -> bool {\n        let window = web_sys::window().unwrap();\n        let document = window.document().unwrap();\n        if document.hidden() {\n            return false;\n        }\n\n        super::has_focus(self.canvas()) || self.text_agent.has_focus()\n    }\n\n    pub fn update_focus(&mut self) {\n        let has_focus = self.has_focus();\n        if self.input.raw.focused != has_focus {\n            log::trace!(\"{} Focus changed to {has_focus}\", self.canvas().id());\n            self.input.set_focus(has_focus);\n\n            if !has_focus {\n                // We lost focus - good idea to save\n                self.save();\n            }\n            self.egui_ctx().request_repaint();\n        }\n    }\n\n    /// Runs the logic, but doesn't paint the result.\n    ///\n    /// The result can be painted later with a call to [`Self::run_and_paint`] or [`Self::paint`].\n    pub fn logic(&mut self) {\n        // We sometimes miss blur/focus events due to the text agent, so let's just poll each frame:\n        self.update_focus();\n        // We might have received a screenshot\n        self.painter.handle_screenshots(&mut self.input.raw.events);\n\n        let canvas_size = super::canvas_size_in_points(self.canvas(), self.egui_ctx());\n        let mut raw_input = self.input.new_frame(canvas_size);\n\n        if super::DEBUG_RESIZE {\n            log::info!(\n                \"egui running at canvas size: {}x{}, DPR: {}, zoom_factor: {}. egui size: {}x{} points\",\n                self.canvas().width(),\n                self.canvas().height(),\n                super::native_pixels_per_point(),\n                self.egui_ctx.zoom_factor(),\n                canvas_size.x,\n                canvas_size.y,\n            );\n        }\n\n        self.app.raw_input_hook(&self.egui_ctx, &mut raw_input);\n\n        let is_visible = raw_input\n            .viewports\n            .get(&egui::ViewportId::ROOT)\n            .and_then(|v| v.visible())\n            .unwrap_or(true);\n\n        let full_output = self.egui_ctx.run_ui(raw_input, |ui| {\n            self.app.logic(ui.ctx(), &mut self.frame);\n\n            if is_visible {\n                #[expect(deprecated)]\n                self.app.update(ui.ctx(), &mut self.frame);\n\n                self.app.ui(ui, &mut self.frame);\n            }\n        });\n        let egui::FullOutput {\n            platform_output,\n            textures_delta,\n            shapes,\n            pixels_per_point,\n            viewport_output,\n        } = full_output;\n\n        if viewport_output.len() > 1 {\n            log::warn!(\"Multiple viewports not yet supported on the web\");\n        }\n        for (_viewport_id, viewport_output) in viewport_output {\n            for command in viewport_output.commands {\n                match command {\n                    ViewportCommand::Screenshot(user_data) => {\n                        self.screenshot_commands_with_frame_delay\n                            .push((user_data, 1));\n                    }\n                    _ => {\n                        // TODO(emilk): handle some of the commands\n                        log::warn!(\n                            \"Unhandled egui viewport command: {command:?} - not implemented in web backend\"\n                        );\n                    }\n                }\n            }\n        }\n\n        self.handle_platform_output(platform_output);\n        if is_visible {\n            self.textures_delta.append(textures_delta);\n            self.clipped_primitives = Some(self.egui_ctx.tessellate(shapes, pixels_per_point));\n        }\n    }\n\n    /// Paint the results of the last call to [`Self::logic`].\n    pub fn paint(&mut self) {\n        let textures_delta = std::mem::take(&mut self.textures_delta);\n        let clipped_primitives = std::mem::take(&mut self.clipped_primitives);\n\n        if let Some(clipped_primitives) = clipped_primitives {\n            let mut screenshot_commands = vec![];\n            self.screenshot_commands_with_frame_delay\n                .retain_mut(|(user_data, frame_delay)| {\n                    if *frame_delay == 0 {\n                        screenshot_commands.push(user_data.clone());\n                        false\n                    } else {\n                        *frame_delay -= 1;\n                        true\n                    }\n                });\n            if !self.screenshot_commands_with_frame_delay.is_empty() {\n                self.egui_ctx().request_repaint();\n            }\n\n            if let Err(err) = self.painter.paint_and_update_textures(\n                self.app.clear_color(&self.egui_ctx.global_style().visuals),\n                &clipped_primitives,\n                self.egui_ctx.pixels_per_point(),\n                &textures_delta,\n                screenshot_commands,\n            ) {\n                log::error!(\"Failed to paint: {}\", super::string_from_js_value(&err));\n            }\n        }\n    }\n\n    pub fn report_frame_time(&mut self, cpu_usage_seconds: f32) {\n        self.frame.info.cpu_usage = Some(cpu_usage_seconds);\n    }\n\n    fn handle_platform_output(&self, platform_output: egui::PlatformOutput) {\n        #[cfg(feature = \"web_screen_reader\")]\n        if self.egui_ctx.options(|o| o.screen_reader) {\n            super::screen_reader::speak(&platform_output.events_description());\n        }\n\n        let egui::PlatformOutput {\n            commands,\n            cursor_icon,\n            events: _,                    // already handled\n            mutable_text_under_cursor: _, // TODO(#4569): https://github.com/emilk/egui/issues/4569\n            ime,\n            accesskit_update: _,        // not currently implemented\n            num_completed_passes: _,    // handled by `Context::run`\n            request_discard_reasons: _, // handled by `Context::run`\n        } = platform_output;\n\n        for command in commands {\n            match command {\n                egui::OutputCommand::CopyText(text) => {\n                    super::set_clipboard_text(&text);\n                }\n                egui::OutputCommand::CopyImage(image) => {\n                    super::set_clipboard_image(&image);\n                }\n                egui::OutputCommand::OpenUrl(open_url) => {\n                    super::open_url(&open_url.url, open_url.new_tab);\n                }\n            }\n        }\n\n        super::set_cursor_icon(cursor_icon);\n\n        if self.has_focus() {\n            // The eframe app has focus.\n            if ime.is_some() {\n                // We are editing text: give the focus to the text agent.\n                self.text_agent.focus();\n            } else {\n                // We are not editing text - give the focus to the canvas.\n                self.text_agent.blur();\n                self.canvas().focus().ok();\n            }\n        }\n\n        if let Err(err) = self\n            .text_agent\n            .move_to(ime, self.canvas(), self.egui_ctx.zoom_factor())\n        {\n            log::error!(\n                \"failed to update text agent position: {}\",\n                super::string_from_js_value(&err)\n            );\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Default)]\nstruct LocalStorage {}\n\nimpl epi::Storage for LocalStorage {\n    fn get_string(&self, key: &str) -> Option<String> {\n        super::storage::local_storage_get(key)\n    }\n\n    fn set_string(&mut self, key: &str, value: String) {\n        super::storage::local_storage_set(key, &value);\n    }\n\n    fn flush(&mut self) {}\n}\n"
  },
  {
    "path": "crates/eframe/src/web/backend.rs",
    "content": "use std::collections::BTreeMap;\n\nuse egui::mutex::Mutex;\n\nuse crate::epi;\n\nuse super::percent_decode;\n\n// ----------------------------------------------------------------------------\n\n/// Data gathered between frames.\n#[derive(Default)]\npub(crate) struct WebInput {\n    /// Required because we don't get a position on touchend\n    pub primary_touch: Option<egui::TouchId>,\n\n    /// Helps to track the delta scale from gesture events\n    pub accumulated_scale: f32,\n\n    /// Helps to track the delta rotation from gesture events\n    pub accumulated_rotation: f32,\n\n    /// The raw input to `egui`.\n    pub raw: egui::RawInput,\n}\n\nimpl WebInput {\n    pub fn new_frame(&mut self, canvas_size: egui::Vec2) -> egui::RawInput {\n        let mut raw_input = egui::RawInput {\n            screen_rect: Some(egui::Rect::from_min_size(Default::default(), canvas_size)),\n            time: Some(super::now_sec()),\n            ..self.raw.take()\n        };\n        let viewport = raw_input\n            .viewports\n            .entry(egui::ViewportId::ROOT)\n            .or_default();\n        viewport.native_pixels_per_point = Some(super::native_pixels_per_point());\n\n        // A hidden browser tab is effectively occluded.\n        let hidden = web_sys::window()\n            .and_then(|w| w.document())\n            .is_some_and(|doc| doc.hidden());\n        viewport.occluded = Some(hidden);\n\n        raw_input\n    }\n\n    /// On alt-tab, or user clicking another HTML element.\n    pub fn set_focus(&mut self, focused: bool) {\n        if self.raw.focused == focused {\n            return;\n        }\n\n        // log::debug!(\"on_web_page_focus_change: {focused}\");\n        self.raw.modifiers = egui::Modifiers::default(); // Avoid sticky modifier keys on alt-tab:\n        self.raw.focused = focused;\n        self.raw.events.push(egui::Event::WindowFocused(focused));\n        self.primary_touch = None;\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Stores when to do the next repaint.\npub(crate) struct NeedRepaint {\n    /// Time in seconds when the next repaint should happen.\n    next_repaint: Mutex<f64>,\n\n    /// Rate limit for repaint. 0 means \"unlimited\". The rate may still be limited by vsync.\n    max_fps: u32,\n}\n\nimpl NeedRepaint {\n    pub fn new(max_fps: Option<u32>) -> Self {\n        Self {\n            next_repaint: Mutex::new(f64::NEG_INFINITY), // start with a repaint\n            max_fps: max_fps.unwrap_or(0),\n        }\n    }\n}\n\nimpl NeedRepaint {\n    /// Returns the time (in [`now_sec`] scale) when\n    /// we should next repaint.\n    pub fn when_to_repaint(&self) -> f64 {\n        *self.next_repaint.lock()\n    }\n\n    /// Unschedule repainting.\n    pub fn clear(&self) {\n        *self.next_repaint.lock() = f64::INFINITY;\n    }\n\n    pub fn repaint_after(&self, num_seconds: f64) {\n        let mut time = super::now_sec() + num_seconds;\n        time = self.round_repaint_time_to_rate(time);\n        let mut repaint_time = self.next_repaint.lock();\n        *repaint_time = repaint_time.min(time);\n    }\n\n    /// Request a repaint. Depending on the presence of rate limiting, this may not be instant.\n    pub fn repaint(&self) {\n        let time = self.round_repaint_time_to_rate(super::now_sec());\n        let mut repaint_time = self.next_repaint.lock();\n        *repaint_time = repaint_time.min(time);\n    }\n\n    pub fn repaint_asap(&self) {\n        *self.next_repaint.lock() = f64::NEG_INFINITY;\n    }\n\n    pub fn needs_repaint(&self) -> bool {\n        self.when_to_repaint() <= super::now_sec()\n    }\n\n    fn round_repaint_time_to_rate(&self, time: f64) -> f64 {\n        if self.max_fps == 0 {\n            time\n        } else {\n            let interval = 1.0 / self.max_fps as f64;\n            (time / interval).ceil() * interval\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// The User-Agent of the user's browser.\npub fn user_agent() -> Option<String> {\n    web_sys::window()?.navigator().user_agent().ok()\n}\n\n/// Get the [`epi::Location`] from the browser.\npub fn web_location() -> epi::Location {\n    let location = web_sys::window().unwrap().location();\n\n    let hash = percent_decode(&location.hash().unwrap_or_default());\n\n    let query = location\n        .search()\n        .unwrap_or_default()\n        .strip_prefix('?')\n        .unwrap_or_default()\n        .to_owned();\n\n    epi::Location {\n        // TODO(emilk): should we really percent-decode the url? 🤷‍♂️\n        url: percent_decode(&location.href().unwrap_or_default()),\n        protocol: percent_decode(&location.protocol().unwrap_or_default()),\n        host: percent_decode(&location.host().unwrap_or_default()),\n        hostname: percent_decode(&location.hostname().unwrap_or_default()),\n        port: percent_decode(&location.port().unwrap_or_default()),\n        hash,\n        query_map: parse_query_map(&query),\n        query,\n        origin: percent_decode(&location.origin().unwrap_or_default()),\n    }\n}\n\n/// query is percent-encoded\nfn parse_query_map(query: &str) -> BTreeMap<String, Vec<String>> {\n    let mut map: BTreeMap<String, Vec<String>> = Default::default();\n\n    for pair in query.split('&') {\n        if !pair.is_empty() {\n            if let Some((key, value)) = pair.split_once('=') {\n                map.entry(percent_decode(key))\n                    .or_default()\n                    .push(percent_decode(value));\n            } else {\n                map.entry(percent_decode(pair))\n                    .or_default()\n                    .push(String::new());\n            }\n        }\n    }\n\n    map\n}\n\n// TODO(emilk): this test is never acgtually run, because this whole module is wasm32 only 🤦‍♂️\n#[test]\nfn test_parse_query() {\n    assert_eq!(parse_query_map(\"\"), BTreeMap::default());\n    assert_eq!(parse_query_map(\"foo\"), BTreeMap::from_iter([(\"foo\", \"\")]));\n    assert_eq!(\n        parse_query_map(\"foo=bar\"),\n        BTreeMap::from_iter([(\"foo\", \"bar\")])\n    );\n    assert_eq!(\n        parse_query_map(\"foo=bar&baz=42\"),\n        BTreeMap::from_iter([(\"foo\", \"bar\"), (\"baz\", \"42\")])\n    );\n    assert_eq!(\n        parse_query_map(\"foo&baz=42\"),\n        BTreeMap::from_iter([(\"foo\", \"\"), (\"baz\", \"42\")])\n    );\n    assert_eq!(\n        parse_query_map(\"foo&baz&&\"),\n        BTreeMap::from_iter([(\"foo\", \"\"), (\"baz\", \"\")])\n    );\n    assert_eq!(\n        parse_query_map(\"badger=data.rrd%3Fparam1%3Dfoo%26param2%3Dbar&mushroom=snake\"),\n        BTreeMap::from_iter([\n            (\"badger\", \"data.rrd?param1=foo&param2=bar\"),\n            (\"mushroom\", \"snake\")\n        ])\n    );\n}\n"
  },
  {
    "path": "crates/eframe/src/web/events.rs",
    "content": "use crate::web::string_from_js_value;\n\nuse super::{\n    AppRunner, Closure, DEBUG_RESIZE, JsCast as _, JsValue, WebRunner, button_from_mouse_event,\n    location_hash, modifiers_from_kb_event, modifiers_from_mouse_event, modifiers_from_wheel_event,\n    native_pixels_per_point, pos_from_mouse_event, prefers_color_scheme, primary_touch_pos,\n    push_touches, text_from_keyboard_event, translate_key,\n};\n\nuse js_sys::Reflect;\nuse web_sys::{Document, EventTarget, ShadowRoot};\n\n// TODO(emilk): there are more calls to `prevent_default` and `stop_propagation`\n// than what is probably needed.\n\n// ------------------------------------------------------------------------\n\n/// Calls `request_animation_frame` to schedule repaint.\n///\n/// It will only paint if needed, but will always call `request_animation_frame` immediately.\npub(crate) fn paint_and_schedule(runner_ref: &WebRunner) -> Result<(), JsValue> {\n    // Only paint and schedule if there has been no panic\n    if let Some(mut runner_lock) = runner_ref.try_lock() {\n        paint_if_needed(&mut runner_lock);\n        drop(runner_lock);\n        runner_ref.request_animation_frame()?;\n    }\n    Ok(())\n}\n\nfn paint_if_needed(runner: &mut AppRunner) {\n    if runner.needs_repaint.needs_repaint() {\n        if runner.has_outstanding_paint_data() {\n            // We have already run the logic, e.g. in an on-click event,\n            // so let's only present the results:\n            runner.paint();\n\n            // We schedule another repaint asap, so that we can run the actual logic\n            // again, which may schedule a new repaint (if there's animations):\n            runner.needs_repaint.repaint_asap();\n        } else {\n            // Clear the `needs_repaint` flags _before_\n            // running the logic, as the logic could cause it to be set again.\n            runner.needs_repaint.clear();\n\n            let mut stopwatch = crate::stopwatch::Stopwatch::new();\n            stopwatch.start();\n\n            // Run user code…\n            runner.logic();\n\n            // …and paint the result.\n            runner.paint();\n\n            runner.report_frame_time(stopwatch.total_time_sec());\n        }\n    }\n    runner.auto_save_if_needed();\n}\n\n// ------------------------------------------------------------------------\n\npub(crate) fn install_event_handlers(runner_ref: &WebRunner) -> Result<(), JsValue> {\n    let window = web_sys::window().unwrap();\n    let document = window.document().unwrap();\n    let canvas = runner_ref.try_lock().unwrap().canvas().clone();\n\n    install_blur_focus(runner_ref, &document)?;\n    install_blur_focus(runner_ref, &canvas)?;\n\n    prevent_default_and_stop_propagation(\n        runner_ref,\n        &canvas,\n        &[\n            // Allow users to use ctrl-p for e.g. a command palette:\n            \"afterprint\",\n            // By default, right-clicks open a browser context menu.\n            // We don't want to do that (right clicks are handled by egui):\n            \"contextmenu\",\n        ],\n    )?;\n\n    install_keydown(runner_ref, &canvas)?;\n    install_keyup(runner_ref, &canvas)?;\n\n    // It seems copy/cut/paste events only work on the document,\n    // so we check if we have focus inside of the handler.\n    install_copy_cut_paste(runner_ref, &document)?;\n\n    // Use `document` here to notice if the user releases a drag outside of the canvas:\n    // See https://github.com/emilk/egui/issues/3157\n    install_mousemove(runner_ref, &document)?;\n    install_pointerup(runner_ref, &document)?;\n    install_pointerdown(runner_ref, &canvas)?;\n    install_mouseleave(runner_ref, &canvas)?;\n\n    install_touchstart(runner_ref, &canvas)?;\n    // Use `document` here to notice if the user drag outside of the canvas:\n    // See https://github.com/emilk/egui/issues/3157\n    install_touchmove(runner_ref, &document)?;\n    install_touchend(runner_ref, &document)?;\n    install_touchcancel(runner_ref, &canvas)?;\n\n    install_wheel(runner_ref, &canvas)?;\n    install_gesture(runner_ref, &canvas)?;\n    install_drag_and_drop(runner_ref, &canvas)?;\n    install_window_events(runner_ref, &window)?;\n    install_color_scheme_change_event(runner_ref, &window)?;\n    Ok(())\n}\n\nfn install_blur_focus(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    // NOTE: because of the text agent we sometime miss 'blur' events,\n    // so we also poll the focus state each frame in `AppRunner::logic`.\n    for event_name in [\"blur\", \"focus\", \"visibilitychange\"] {\n        let closure = move |_event: web_sys::MouseEvent, runner: &mut AppRunner| {\n            log::trace!(\"{} {event_name:?}\", runner.canvas().id());\n            runner.update_focus();\n        };\n\n        runner_ref.add_event_listener(target, event_name, closure)?;\n    }\n    Ok(())\n}\n\nfn install_keydown(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(\n        target,\n        \"keydown\",\n        |event: web_sys::KeyboardEvent, runner| {\n            if !runner.input.raw.focused {\n                return;\n            }\n\n            let modifiers = modifiers_from_kb_event(&event);\n            if !modifiers.ctrl\n                && !modifiers.command\n                // When text agent is focused, it is responsible for handling input events\n                && !runner.text_agent.has_focus()\n                && let Some(text) = text_from_keyboard_event(&event)\n            {\n                let egui_event = egui::Event::Text(text);\n                let should_stop_propagation =\n                    (runner.web_options.should_stop_propagation)(&egui_event);\n                let should_prevent_default =\n                    (runner.web_options.should_prevent_default)(&egui_event);\n                runner.input.raw.events.push(egui_event);\n                runner.needs_repaint.repaint_asap();\n\n                // If this is indeed text, then prevent any other action.\n                if should_prevent_default {\n                    event.prevent_default();\n                }\n\n                // Use web options to tell if the event should be propagated to parent elements.\n                if should_stop_propagation {\n                    event.stop_propagation();\n                }\n            }\n\n            on_keydown(event, runner);\n        },\n    )\n}\n\n#[expect(clippy::needless_pass_by_value)] // So that we can pass it directly to `add_event_listener`\npub(crate) fn on_keydown(event: web_sys::KeyboardEvent, runner: &mut AppRunner) {\n    let has_focus = runner.input.raw.focused;\n    if !has_focus {\n        return;\n    }\n\n    if event.is_composing() || event.key_code() == 229 {\n        // https://web.archive.org/web/20200526195704/https://www.fxsitecompat.dev/en-CA/docs/2018/keydown-and-keyup-events-are-now-fired-during-ime-composition/\n        return;\n    }\n\n    let modifiers = modifiers_from_kb_event(&event);\n    runner.input.raw.modifiers = modifiers;\n\n    let key = event.key();\n    let egui_key = translate_key(&key);\n\n    if let Some(egui_key) = egui_key {\n        let egui_event = egui::Event::Key {\n            key: egui_key,\n            physical_key: None, // TODO(fornwall)\n            pressed: true,\n            repeat: false, // egui will fill this in for us!\n            modifiers,\n        };\n        let should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);\n        runner.input.raw.events.push(egui_event);\n        runner.needs_repaint.repaint_asap();\n\n        let prevent_default = should_prevent_default_for_key(runner, &modifiers, egui_key);\n\n        if false {\n            log::debug!(\n                \"On keydown {:?} {egui_key:?}, has_focus: {has_focus}, egui_wants_keyboard: {}, prevent_default: {prevent_default}\",\n                event.key().as_str(),\n                runner.egui_ctx().egui_wants_keyboard_input()\n            );\n        }\n\n        if prevent_default {\n            event.prevent_default();\n        }\n\n        // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n        if should_stop_propagation {\n            event.stop_propagation();\n        }\n    }\n}\n\n/// If the canvas (or text agent) has focus:\n/// should we prevent the default browser event action when the user presses this key?\nfn should_prevent_default_for_key(\n    runner: &AppRunner,\n    modifiers: &egui::Modifiers,\n    egui_key: egui::Key,\n) -> bool {\n    // NOTE: We never want to prevent:\n    // * F5 / cmd-R (refresh)\n    // * cmd-shift-C (debug tools)\n    // * cmd/ctrl-c/v/x (lest we prevent copy/paste/cut events)\n\n    // Prevent cmd/ctrl plus these keys from triggering the default browser action:\n    let keys = [\n        egui::Key::Comma, // cmd-, opens options on macOS, which egui apps may wanna \"steal\"\n        egui::Key::O,     // open\n        egui::Key::P,     // print (cmd-P is common for command palette)\n        egui::Key::S,     // save\n    ];\n    for key in keys {\n        if egui_key == key && (modifiers.ctrl || modifiers.command || modifiers.mac_cmd) {\n            return true;\n        }\n    }\n\n    if egui_key == egui::Key::Space && !runner.text_agent.has_focus() {\n        // Space scrolls the web page, but we don't want that while canvas has focus\n        // However, don't prevent it if text agent has focus, or we can't type space!\n        return true;\n    }\n\n    matches!(\n        egui_key,\n        // Prevent browser from focusing the next HTML element.\n        // egui uses Tab to move focus within the egui app.\n        egui::Key::Tab\n\n        // So we don't go back to previous page while canvas has focus\n        | egui::Key::Backspace\n\n        // Don't scroll web page while canvas has focus.\n        // Also, cmd-left is \"back\" on Mac (https://github.com/emilk/egui/issues/58)\n        | egui::Key::ArrowDown | egui::Key::ArrowLeft | egui::Key::ArrowRight |  egui::Key::ArrowUp\n    )\n}\n\nfn install_keyup(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(target, \"keyup\", on_keyup)\n}\n\n#[expect(clippy::needless_pass_by_value)] // So that we can pass it directly to `add_event_listener`\npub(crate) fn on_keyup(event: web_sys::KeyboardEvent, runner: &mut AppRunner) {\n    let modifiers = modifiers_from_kb_event(&event);\n    runner.input.raw.modifiers = modifiers;\n\n    let mut should_stop_propagation = true;\n\n    if let Some(key) = translate_key(&event.key()) {\n        let egui_event = egui::Event::Key {\n            key,\n            physical_key: None, // TODO(fornwall)\n            pressed: false,\n            repeat: false,\n            modifiers,\n        };\n        should_stop_propagation &= (runner.web_options.should_stop_propagation)(&egui_event);\n        runner.input.raw.events.push(egui_event);\n    }\n\n    if event.key() == \"Meta\" || event.key() == \"Control\" {\n        // When pressing Cmd+A (select all) or Ctrl+C (copy),\n        // chromium will not fire a `keyup` for the letter key.\n        // This leads to stuck keys, unless we do this hack.\n        // See https://github.com/emilk/egui/issues/4724\n\n        let keys_down = runner.egui_ctx().input(|i| i.keys_down.clone());\n\n        #[expect(clippy::iter_over_hash_type)]\n        for key in keys_down {\n            let egui_event = egui::Event::Key {\n                key,\n                physical_key: None,\n                pressed: false,\n                repeat: false,\n                modifiers,\n            };\n            should_stop_propagation &= (runner.web_options.should_stop_propagation)(&egui_event);\n            runner.input.raw.events.push(egui_event);\n        }\n    }\n\n    runner.needs_repaint.repaint_asap();\n\n    // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n    let has_focus = runner.input.raw.focused;\n    if has_focus && should_stop_propagation {\n        event.stop_propagation();\n    }\n}\n\nfn install_copy_cut_paste(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(target, \"paste\", |event: web_sys::ClipboardEvent, runner| {\n        if !runner.input.raw.focused {\n            return; // The eframe app is not interested\n        }\n\n        if let Some(data) = event.clipboard_data()\n            && let Ok(text) = data.get_data(\"text\")\n        {\n            let text = text.replace(\"\\r\\n\", \"\\n\");\n\n            let mut should_stop_propagation = true;\n            let mut should_prevent_default = true;\n            if !text.is_empty() {\n                let egui_event = egui::Event::Paste(text);\n                should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);\n                should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);\n                runner.input.raw.events.push(egui_event);\n                runner.needs_repaint.repaint_asap();\n            }\n\n            // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n            if should_stop_propagation {\n                event.stop_propagation();\n            }\n\n            if should_prevent_default {\n                event.prevent_default();\n            }\n        }\n    })?;\n\n    runner_ref.add_event_listener(target, \"cut\", |event: web_sys::ClipboardEvent, runner| {\n        if !runner.input.raw.focused {\n            return; // The eframe app is not interested\n        }\n\n        runner.input.raw.events.push(egui::Event::Cut);\n\n        // In Safari we are only allowed to write to the clipboard during the\n        // event callback, which is why we run the app logic here and now:\n        runner.logic();\n\n        // Make sure we paint the output of the above logic call asap:\n        runner.needs_repaint.repaint_asap();\n\n        // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n        if (runner.web_options.should_stop_propagation)(&egui::Event::Cut) {\n            event.stop_propagation();\n        }\n\n        if (runner.web_options.should_prevent_default)(&egui::Event::Cut) {\n            event.prevent_default();\n        }\n    })?;\n\n    runner_ref.add_event_listener(target, \"copy\", |event: web_sys::ClipboardEvent, runner| {\n        if !runner.input.raw.focused {\n            return; // The eframe app is not interested\n        }\n\n        runner.input.raw.events.push(egui::Event::Copy);\n\n        // In Safari we are only allowed to write to the clipboard during the\n        // event callback, which is why we run the app logic here and now:\n        runner.logic();\n\n        // Make sure we paint the output of the above logic call asap:\n        runner.needs_repaint.repaint_asap();\n\n        // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n        if (runner.web_options.should_stop_propagation)(&egui::Event::Copy) {\n            event.stop_propagation();\n        }\n\n        if (runner.web_options.should_prevent_default)(&egui::Event::Copy) {\n            event.prevent_default();\n        }\n    })?;\n\n    Ok(())\n}\n\nfn install_window_events(runner_ref: &WebRunner, window: &EventTarget) -> Result<(), JsValue> {\n    // Save-on-close\n    runner_ref.add_event_listener(window, \"onbeforeunload\", |_: web_sys::Event, runner| {\n        runner.save();\n    })?;\n\n    // We want to handle the case of dragging the browser from one monitor to another,\n    // which can cause the DPR to change without any resize event (e.g. Safari).\n    install_dpr_change_event(runner_ref)?;\n\n    // No need to subscribe to \"resize\": we already subscribe to the canvas\n    // size using a ResizeObserver, and we also subscribe to DPR changes of the monitor.\n    for event_name in &[\"load\", \"pagehide\", \"pageshow\", \"popstate\"] {\n        runner_ref.add_event_listener(window, event_name, move |_: web_sys::Event, runner| {\n            if DEBUG_RESIZE {\n                log::debug!(\"{event_name:?}\");\n            }\n            runner.needs_repaint.repaint_asap();\n        })?;\n    }\n\n    runner_ref.add_event_listener(window, \"hashchange\", |_: web_sys::Event, runner| {\n        // `epi::Frame::info(&self)` clones `epi::IntegrationInfo`, but we need to modify the original here\n        runner.frame.info.web_info.location.hash = location_hash();\n        runner.needs_repaint.repaint_asap(); // tell the user about the new hash\n    })?;\n\n    Ok(())\n}\n\nfn install_dpr_change_event(web_runner: &WebRunner) -> Result<(), JsValue> {\n    let original_dpr = native_pixels_per_point();\n\n    let window = web_sys::window().unwrap();\n    let Some(media_query_list) =\n        window.match_media(&format!(\"(resolution: {original_dpr}dppx)\"))?\n    else {\n        log::error!(\n            \"Failed to create MediaQueryList: eframe won't be able to detect changes in DPR\"\n        );\n        return Ok(());\n    };\n\n    let closure = move |_: web_sys::Event, app_runner: &mut AppRunner, web_runner: &WebRunner| {\n        let new_dpr = native_pixels_per_point();\n        log::debug!(\"Device Pixel Ratio changed from {original_dpr} to {new_dpr}\");\n\n        if true {\n            // Explicitly resize canvas to match the new DPR.\n            // This is a bit ugly, but I haven't found a better way to do it.\n            let canvas = app_runner.canvas();\n            canvas.set_width((canvas.width() as f32 * new_dpr / original_dpr).round() as _);\n            canvas.set_height((canvas.height() as f32 * new_dpr / original_dpr).round() as _);\n            log::debug!(\"Resized canvas to {}x{}\", canvas.width(), canvas.height());\n        }\n\n        // It may be tempting to call `resize_observer.observe(&canvas)` here,\n        // but unfortunately this has no effect.\n\n        if let Err(err) = install_dpr_change_event(web_runner) {\n            log::error!(\n                \"Failed to install DPR change event: {}\",\n                string_from_js_value(&err)\n            );\n        }\n    };\n\n    let options = web_sys::AddEventListenerOptions::default();\n    options.set_once(true);\n    web_runner.add_event_listener_ex(&media_query_list, \"change\", &options, closure)\n}\n\nfn install_color_scheme_change_event(\n    runner_ref: &WebRunner,\n    window: &web_sys::Window,\n) -> Result<(), JsValue> {\n    for theme in [egui::Theme::Dark, egui::Theme::Light] {\n        if let Some(media_query_list) = prefers_color_scheme(window, theme)? {\n            runner_ref.add_event_listener::<web_sys::MediaQueryListEvent>(\n                &media_query_list,\n                \"change\",\n                |_event, runner| {\n                    if let Some(theme) = super::system_theme() {\n                        runner.input.raw.system_theme = Some(theme);\n                        runner.needs_repaint.repaint_asap();\n                    }\n                },\n            )?;\n        }\n    }\n\n    Ok(())\n}\n\nfn prevent_default_and_stop_propagation(\n    runner_ref: &WebRunner,\n    target: &EventTarget,\n    event_names: &[&'static str],\n) -> Result<(), JsValue> {\n    for event_name in event_names {\n        let closure = move |event: web_sys::MouseEvent, _runner: &mut AppRunner| {\n            event.prevent_default();\n            event.stop_propagation();\n            // log::debug!(\"Preventing event {event_name:?}\");\n        };\n\n        runner_ref.add_event_listener(target, event_name, closure)?;\n    }\n\n    Ok(())\n}\n\nfn install_pointerdown(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(\n        target,\n        \"pointerdown\",\n        |event: web_sys::PointerEvent, runner: &mut AppRunner| {\n            let modifiers = modifiers_from_mouse_event(&event);\n            runner.input.raw.modifiers = modifiers;\n            let mut should_stop_propagation = true;\n            if let Some(button) = button_from_mouse_event(&event) {\n                let pos = pos_from_mouse_event(runner.canvas(), &event, runner.egui_ctx());\n                let modifiers = runner.input.raw.modifiers;\n                let egui_event = egui::Event::PointerButton {\n                    pos,\n                    button,\n                    pressed: true,\n                    modifiers,\n                };\n                should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);\n                runner.input.raw.events.push(egui_event);\n\n                // In Safari we are only allowed to write to the clipboard during the\n                // event callback, which is why we run the app logic here and now:\n                runner.logic();\n\n                // Make sure we paint the output of the above logic call asap:\n                runner.needs_repaint.repaint_asap();\n            }\n\n            // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n            if should_stop_propagation {\n                event.stop_propagation();\n            }\n            // Note: prevent_default breaks VSCode tab focusing, hence why we don't call it here.\n        },\n    )\n}\n\nfn install_pointerup(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(\n        target,\n        \"pointerup\",\n        |event: web_sys::PointerEvent, runner| {\n            let modifiers = modifiers_from_mouse_event(&event);\n            runner.input.raw.modifiers = modifiers;\n\n            let pos = pos_from_mouse_event(runner.canvas(), &event, runner.egui_ctx());\n\n            if is_interested_in_pointer_event(\n                runner,\n                egui::pos2(event.client_x() as f32, event.client_y() as f32),\n            ) && let Some(button) = button_from_mouse_event(&event)\n            {\n                let modifiers = runner.input.raw.modifiers;\n                let egui_event = egui::Event::PointerButton {\n                    pos,\n                    button,\n                    pressed: false,\n                    modifiers,\n                };\n                let should_stop_propagation =\n                    (runner.web_options.should_stop_propagation)(&egui_event);\n                let should_prevent_default =\n                    (runner.web_options.should_prevent_default)(&egui_event);\n                runner.input.raw.events.push(egui_event);\n\n                // Previously on iOS, the canvas would not receive focus on\n                // any touch event, which resulted in the on-screen keyboard\n                // not working when focusing on a text field in an egui app.\n                // This attempts to fix that by forcing the focus on any\n                // click on the canvas.\n                runner.canvas().focus().ok();\n\n                // In Safari we are only allowed to do certain things\n                // (like playing audio, start a download, etc)\n                // on user action, such as a click.\n                // So we need to run the app logic here and now:\n                runner.logic();\n\n                // Make sure we paint the output of the above logic call asap:\n                runner.needs_repaint.repaint_asap();\n\n                if should_prevent_default {\n                    event.prevent_default();\n                }\n\n                // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n                if should_stop_propagation {\n                    event.stop_propagation();\n                }\n            }\n        },\n    )\n}\n\n/// Returns true if the cursor is above the canvas, or if we're dragging something.\n/// Pass in the position in browser viewport coordinates (usually event.clientX/Y).\nfn is_interested_in_pointer_event(runner: &AppRunner, pos: egui::Pos2) -> bool {\n    let root_node = runner.canvas().get_root_node();\n\n    let element_at_point = if let Some(document) = root_node.dyn_ref::<Document>() {\n        document.element_from_point(pos.x, pos.y)\n    } else if let Some(shadow) = root_node.dyn_ref::<ShadowRoot>() {\n        shadow.element_from_point(pos.x, pos.y)\n    } else {\n        None\n    };\n\n    let is_hovering_canvas = element_at_point.is_some_and(|element| element.eq(runner.canvas()));\n    let is_pointer_down = runner\n        .egui_ctx()\n        .input(|i| i.pointer.any_down() || i.any_touches());\n\n    is_hovering_canvas || is_pointer_down\n}\n\nfn install_mousemove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(target, \"mousemove\", |event: web_sys::MouseEvent, runner| {\n        let modifiers = modifiers_from_mouse_event(&event);\n        runner.input.raw.modifiers = modifiers;\n\n        let pos = pos_from_mouse_event(runner.canvas(), &event, runner.egui_ctx());\n\n        if is_interested_in_pointer_event(\n            runner,\n            egui::pos2(event.client_x() as f32, event.client_y() as f32),\n        ) {\n            let egui_event = egui::Event::PointerMoved(pos);\n            let should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);\n            let should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);\n            runner.input.raw.events.push(egui_event);\n            runner.needs_repaint.repaint();\n\n            // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n            if should_stop_propagation {\n                event.stop_propagation();\n            }\n\n            if should_prevent_default {\n                event.prevent_default();\n            }\n        }\n    })\n}\n\nfn install_mouseleave(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(\n        target,\n        \"mouseleave\",\n        |event: web_sys::MouseEvent, runner| {\n            runner.input.raw.events.push(egui::Event::PointerGone);\n            runner.needs_repaint.repaint_asap();\n\n            // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n            if (runner.web_options.should_stop_propagation)(&egui::Event::PointerGone) {\n                event.stop_propagation();\n            }\n\n            if (runner.web_options.should_prevent_default)(&egui::Event::PointerGone) {\n                event.prevent_default();\n            }\n        },\n    )\n}\n\nfn install_touchstart(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(\n        target,\n        \"touchstart\",\n        |event: web_sys::TouchEvent, runner| {\n            let mut should_stop_propagation = true;\n            let mut should_prevent_default = true;\n            if let Some((pos, _)) = primary_touch_pos(runner, &event) {\n                let egui_event = egui::Event::PointerButton {\n                    pos,\n                    button: egui::PointerButton::Primary,\n                    pressed: true,\n                    modifiers: runner.input.raw.modifiers,\n                };\n                should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);\n                should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);\n                runner.input.raw.events.push(egui_event);\n            }\n\n            push_touches(runner, egui::TouchPhase::Start, &event);\n            runner.needs_repaint.repaint_asap();\n\n            // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n            if should_stop_propagation {\n                event.stop_propagation();\n            }\n\n            if should_prevent_default {\n                event.prevent_default();\n            }\n        },\n    )\n}\n\nfn install_touchmove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(target, \"touchmove\", |event: web_sys::TouchEvent, runner| {\n        if let Some((pos, touch)) = primary_touch_pos(runner, &event)\n            && is_interested_in_pointer_event(\n                runner,\n                egui::pos2(touch.client_x() as f32, touch.client_y() as f32),\n            )\n        {\n            let egui_event = egui::Event::PointerMoved(pos);\n            let should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);\n            let should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);\n            runner.input.raw.events.push(egui_event);\n\n            push_touches(runner, egui::TouchPhase::Move, &event);\n            runner.needs_repaint.repaint();\n\n            // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n            if should_stop_propagation {\n                event.stop_propagation();\n            }\n\n            if should_prevent_default {\n                event.prevent_default();\n            }\n        }\n    })\n}\n\nfn install_touchend(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(target, \"touchend\", |event: web_sys::TouchEvent, runner| {\n        if let Some((pos, touch)) = primary_touch_pos(runner, &event)\n            && is_interested_in_pointer_event(\n                runner,\n                egui::pos2(touch.client_x() as f32, touch.client_y() as f32),\n            )\n        {\n            // First release mouse to click:\n            let mut should_stop_propagation = true;\n            let mut should_prevent_default = true;\n            let egui_event = egui::Event::PointerButton {\n                pos,\n                button: egui::PointerButton::Primary,\n                pressed: false,\n                modifiers: runner.input.raw.modifiers,\n            };\n            should_stop_propagation &= (runner.web_options.should_stop_propagation)(&egui_event);\n            should_prevent_default &= (runner.web_options.should_prevent_default)(&egui_event);\n            runner.input.raw.events.push(egui_event);\n            // Then remove hover effect:\n            should_stop_propagation &=\n                (runner.web_options.should_stop_propagation)(&egui::Event::PointerGone);\n            should_prevent_default &=\n                (runner.web_options.should_prevent_default)(&egui::Event::PointerGone);\n            runner.input.raw.events.push(egui::Event::PointerGone);\n\n            push_touches(runner, egui::TouchPhase::End, &event);\n\n            runner.needs_repaint.repaint_asap();\n\n            // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n            if should_stop_propagation {\n                event.stop_propagation();\n            }\n\n            if should_prevent_default {\n                event.prevent_default();\n            }\n\n            // Fix virtual keyboard IOS\n            // Need call focus at the same time of event\n            if runner.text_agent.has_focus() {\n                runner.text_agent.set_focus(false);\n                runner.text_agent.set_focus(true);\n            }\n        }\n    })\n}\n\nfn install_touchcancel(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(\n        target,\n        \"touchcancel\",\n        |event: web_sys::TouchEvent, runner| {\n            push_touches(runner, egui::TouchPhase::Cancel, &event);\n            event.stop_propagation();\n            event.prevent_default();\n        },\n    )?;\n\n    Ok(())\n}\n\nfn install_wheel(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(target, \"wheel\", |event: web_sys::WheelEvent, runner| {\n        let unit = match event.delta_mode() {\n            web_sys::WheelEvent::DOM_DELTA_PIXEL => egui::MouseWheelUnit::Point,\n            web_sys::WheelEvent::DOM_DELTA_LINE => egui::MouseWheelUnit::Line,\n            web_sys::WheelEvent::DOM_DELTA_PAGE => egui::MouseWheelUnit::Page,\n            _ => return,\n        };\n\n        let delta = -egui::vec2(event.delta_x() as f32, event.delta_y() as f32);\n\n        let modifiers = modifiers_from_wheel_event(&event);\n\n        let egui_event = if modifiers.ctrl && !runner.input.raw.modifiers.ctrl {\n            // The browser is saying the ctrl key is down, but it isn't _really_.\n            // This happens on pinch-to-zoom on multitouch trackpads\n            // egui will treat ctrl+scroll as zoom, so it all works.\n            // However, we explicitly handle it here in order to better match the pinch-to-zoom\n            // speed of a native app, without being sensitive to egui's `scroll_zoom_speed` setting.\n            let pinch_to_zoom_sensitivity = 0.01; // Feels good on a Mac trackpad in 2024\n            let zoom_factor = (pinch_to_zoom_sensitivity * delta.y).exp();\n            egui::Event::Zoom(zoom_factor)\n        } else {\n            egui::Event::MouseWheel {\n                unit,\n                delta,\n                modifiers,\n                phase: egui::TouchPhase::Move,\n            }\n        };\n        let should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);\n        let should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);\n        runner.input.raw.events.push(egui_event);\n\n        runner.needs_repaint.repaint();\n\n        // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n        if should_stop_propagation {\n            event.stop_propagation();\n        }\n\n        if should_prevent_default {\n            event.prevent_default();\n        }\n    })\n}\n\nfn install_gesture(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(target, \"gesturestart\", |event: web_sys::Event, runner| {\n        runner.input.accumulated_scale = 1.0;\n        runner.input.accumulated_rotation = 0.0;\n        handle_gesture(event, runner);\n    })?;\n    runner_ref.add_event_listener(target, \"gesturechange\", handle_gesture)?;\n    runner_ref.add_event_listener(target, \"gestureend\", |event: web_sys::Event, runner| {\n        handle_gesture(event, runner);\n        runner.input.accumulated_scale = 1.0;\n        runner.input.accumulated_rotation = 0.0;\n    })?;\n\n    Ok(())\n}\n\n#[expect(clippy::needless_pass_by_value)] // So that we can pass it directly to `add_event_listener`\nfn handle_gesture(event: web_sys::Event, runner: &mut AppRunner) {\n    // GestureEvent is a non-standard API, so this attempts to get the relevant fields if they exist.\n    let new_scale = Reflect::get(&event, &JsValue::from_str(\"scale\"))\n        .ok()\n        .and_then(|scale| scale.as_f64())\n        .map_or(1.0, |scale| scale as f32);\n    let new_rotation = Reflect::get(&event, &JsValue::from_str(\"rotation\"))\n        .ok()\n        .and_then(|rotation| rotation.as_f64())\n        .map_or(0.0, |rotation| rotation.to_radians() as f32);\n\n    let scale_delta = new_scale / runner.input.accumulated_scale;\n    let rotation_delta = new_rotation - runner.input.accumulated_rotation;\n    runner.input.accumulated_scale *= scale_delta;\n    runner.input.accumulated_rotation += rotation_delta;\n\n    let mut should_stop_propagation = true;\n    let mut should_prevent_default = true;\n\n    if scale_delta != 1.0 {\n        let zoom_event = egui::Event::Zoom(scale_delta);\n\n        should_stop_propagation &= (runner.web_options.should_stop_propagation)(&zoom_event);\n        should_prevent_default &= (runner.web_options.should_prevent_default)(&zoom_event);\n        runner.input.raw.events.push(zoom_event);\n    }\n\n    if rotation_delta != 0.0 {\n        let rotate_event = egui::Event::Rotate(rotation_delta);\n\n        should_stop_propagation &= (runner.web_options.should_stop_propagation)(&rotate_event);\n        should_prevent_default &= (runner.web_options.should_prevent_default)(&rotate_event);\n        runner.input.raw.events.push(rotate_event);\n    }\n\n    if scale_delta != 1.0 || rotation_delta != 0.0 {\n        runner.needs_repaint.repaint_asap();\n\n        // Use web options to tell if the web event should be propagated to parent elements based on the egui event.\n        if should_stop_propagation {\n            event.stop_propagation();\n        }\n\n        if should_prevent_default {\n            // Prevents a simulated ctrl-scroll event for zoom\n            event.prevent_default();\n        }\n    }\n}\n\nfn install_drag_and_drop(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> {\n    runner_ref.add_event_listener(target, \"dragover\", |event: web_sys::DragEvent, runner| {\n        if let Some(data_transfer) = event.data_transfer() {\n            runner.input.raw.hovered_files.clear();\n\n            // NOTE: data_transfer.files() is always empty in dragover\n\n            let items = data_transfer.items();\n            for i in 0..items.length() {\n                if let Some(item) = items.get(i) {\n                    runner.input.raw.hovered_files.push(egui::HoveredFile {\n                        mime: item.type_(),\n                        ..Default::default()\n                    });\n                }\n            }\n\n            if runner.input.raw.hovered_files.is_empty() {\n                // Fallback: just preview anything. Needed on Desktop Safari.\n                runner\n                    .input\n                    .raw\n                    .hovered_files\n                    .push(egui::HoveredFile::default());\n            }\n\n            runner.needs_repaint.repaint_asap();\n            event.stop_propagation();\n            event.prevent_default();\n        }\n    })?;\n\n    runner_ref.add_event_listener(target, \"dragleave\", |event: web_sys::DragEvent, runner| {\n        runner.input.raw.hovered_files.clear();\n        runner.needs_repaint.repaint_asap();\n        event.stop_propagation();\n        event.prevent_default();\n    })?;\n\n    runner_ref.add_event_listener(target, \"drop\", {\n        let runner_ref = runner_ref.clone();\n\n        move |event: web_sys::DragEvent, runner| {\n            if let Some(data_transfer) = event.data_transfer() {\n                // TODO(https://github.com/emilk/egui/issues/3702): support dropping folders\n                runner.input.raw.hovered_files.clear();\n                runner.needs_repaint.repaint_asap();\n\n                if let Some(files) = data_transfer.files() {\n                    for i in 0..files.length() {\n                        if let Some(file) = files.get(i) {\n                            let name = file.name();\n                            let mime = file.type_();\n                            let last_modified = std::time::UNIX_EPOCH\n                                + std::time::Duration::from_millis(file.last_modified() as u64);\n\n                            log::debug!(\"Loading {:?} ({} bytes)…\", name, file.size());\n\n                            let future = wasm_bindgen_futures::JsFuture::from(file.array_buffer());\n\n                            let runner_ref = runner_ref.clone();\n                            let future = async move {\n                                match future.await {\n                                    Ok(array_buffer) => {\n                                        let bytes = js_sys::Uint8Array::new(&array_buffer).to_vec();\n                                        log::debug!(\"Loaded {:?} ({} bytes).\", name, bytes.len());\n\n                                        if let Some(mut runner_lock) = runner_ref.try_lock() {\n                                            runner_lock.input.raw.dropped_files.push(\n                                                egui::DroppedFile {\n                                                    name,\n                                                    mime,\n                                                    last_modified: Some(last_modified),\n                                                    bytes: Some(bytes.into()),\n                                                    ..Default::default()\n                                                },\n                                            );\n                                            runner_lock.needs_repaint.repaint_asap();\n                                        }\n                                    }\n                                    Err(err) => {\n                                        log::error!(\n                                            \"Failed to read file: {}\",\n                                            string_from_js_value(&err)\n                                        );\n                                    }\n                                }\n                            };\n                            wasm_bindgen_futures::spawn_local(future);\n                        }\n                    }\n                }\n                event.stop_propagation();\n                event.prevent_default();\n            }\n        }\n    })?;\n\n    Ok(())\n}\n\n/// A `ResizeObserver` is used to observe changes to the size of the canvas.\n///\n/// The resize observer is called the by the browser at `observe` time, instead of just on the first actual resize.\n/// We use that to trigger the first `request_animation_frame` _after_ updating the size of the canvas to the correct dimensions,\n/// to avoid [#4622](https://github.com/emilk/egui/issues/4622).\npub struct ResizeObserverContext {\n    observer: web_sys::ResizeObserver,\n\n    // Kept so it is not dropped until we are done with it.\n    _closure: Closure<dyn FnMut(js_sys::Array)>,\n}\n\nimpl Drop for ResizeObserverContext {\n    fn drop(&mut self) {\n        self.observer.disconnect();\n    }\n}\n\nimpl ResizeObserverContext {\n    pub fn new(runner_ref: &WebRunner) -> Result<Self, JsValue> {\n        let closure = Closure::wrap(Box::new({\n            let runner_ref = runner_ref.clone();\n            move |entries: js_sys::Array| {\n                if DEBUG_RESIZE {\n                    log::info!(\"ResizeObserverContext callback\");\n                }\n                // Only call the wrapped closure if the egui code has not panicked\n                if let Some(mut runner_lock) = runner_ref.try_lock() {\n                    let canvas = runner_lock.canvas();\n                    let (width, height) = match get_display_size(&entries) {\n                        Ok(v) => v,\n                        Err(err) => {\n                            log::error!(\"{}\", super::string_from_js_value(&err));\n                            return;\n                        }\n                    };\n                    if DEBUG_RESIZE {\n                        log::info!(\n                            \"ResizeObserver: new canvas size: {width}x{height}, DPR: {}\",\n                            web_sys::window().unwrap().device_pixel_ratio()\n                        );\n                    }\n                    canvas.set_width(width);\n                    canvas.set_height(height);\n\n                    // force an immediate repaint\n                    runner_lock.needs_repaint.repaint_asap();\n                    paint_if_needed(&mut runner_lock);\n                    drop(runner_lock);\n                    // we rely on the resize observer to trigger the first `request_animation_frame`:\n                    if let Err(err) = runner_ref.request_animation_frame() {\n                        log::error!(\"{}\", super::string_from_js_value(&err));\n                    }\n                } else {\n                    log::warn!(\"ResizeObserverContext callback: failed to lock runner\");\n                }\n            }\n        }) as Box<dyn FnMut(js_sys::Array)>);\n\n        let observer = web_sys::ResizeObserver::new(closure.as_ref().unchecked_ref())?;\n\n        Ok(Self {\n            observer,\n            _closure: closure,\n        })\n    }\n\n    pub fn observe(&self, canvas: &web_sys::HtmlCanvasElement) {\n        if DEBUG_RESIZE {\n            log::info!(\"Calling observe on canvas…\");\n        }\n        let options = web_sys::ResizeObserverOptions::new();\n        options.set_box(web_sys::ResizeObserverBoxOptions::ContentBox);\n        self.observer.observe_with_options(canvas, &options);\n    }\n}\n\n// Code ported to Rust from:\n// https://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html\nfn get_display_size(resize_observer_entries: &js_sys::Array) -> Result<(u32, u32), JsValue> {\n    let width;\n    let height;\n    let mut dpr = web_sys::window().unwrap().device_pixel_ratio();\n\n    let entry: web_sys::ResizeObserverEntry = resize_observer_entries.at(0).dyn_into()?;\n    if JsValue::from_str(\"devicePixelContentBoxSize\").js_in(entry.as_ref()) {\n        // NOTE: Only this path gives the correct answer for most browsers.\n        // Unfortunately this doesn't work perfectly everywhere.\n        let size: web_sys::ResizeObserverSize =\n            entry.device_pixel_content_box_size().at(0).dyn_into()?;\n        width = size.inline_size();\n        height = size.block_size();\n        dpr = 1.0; // no need to apply\n\n        if DEBUG_RESIZE {\n            // log::info!(\"devicePixelContentBoxSize {width}x{height}\");\n        }\n    } else if JsValue::from_str(\"contentBoxSize\").js_in(entry.as_ref()) {\n        let content_box_size = entry.content_box_size();\n        let idx0 = content_box_size.at(0);\n        if !idx0.is_undefined() {\n            let size: web_sys::ResizeObserverSize = idx0.dyn_into()?;\n            width = size.inline_size();\n            height = size.block_size();\n        } else {\n            // legacy\n            let size = JsValue::clone(content_box_size.as_ref());\n            let size: web_sys::ResizeObserverSize = size.dyn_into()?;\n            width = size.inline_size();\n            height = size.block_size();\n        }\n        if DEBUG_RESIZE {\n            log::info!(\"contentBoxSize {width}x{height}\");\n        }\n    } else {\n        // legacy\n        let content_rect = entry.content_rect();\n        width = content_rect.width();\n        height = content_rect.height();\n    }\n\n    Ok(((width.round() * dpr) as u32, (height.round() * dpr) as u32))\n}\n"
  },
  {
    "path": "crates/eframe/src/web/input.rs",
    "content": "use super::{AppRunner, canvas_content_rect};\n\npub fn pos_from_mouse_event(\n    canvas: &web_sys::HtmlCanvasElement,\n    event: &web_sys::MouseEvent,\n    ctx: &egui::Context,\n) -> egui::Pos2 {\n    let rect = canvas_content_rect(canvas);\n    let zoom_factor = ctx.zoom_factor();\n    egui::Pos2 {\n        x: (event.client_x() as f32 - rect.left()) / zoom_factor,\n        y: (event.client_y() as f32 - rect.top()) / zoom_factor,\n    }\n}\n\npub fn button_from_mouse_event(event: &web_sys::MouseEvent) -> Option<egui::PointerButton> {\n    match event.button() {\n        0 => Some(egui::PointerButton::Primary),\n        1 => Some(egui::PointerButton::Middle),\n        2 => Some(egui::PointerButton::Secondary),\n        3 => Some(egui::PointerButton::Extra1),\n        4 => Some(egui::PointerButton::Extra2),\n        _ => None,\n    }\n}\n\n/// A single touch is translated to a pointer movement. When a second touch is added, the pointer\n/// should not jump to a different position. Therefore, we do not calculate the average position\n/// of all touches, but we keep using the same touch as long as it is available.\npub fn primary_touch_pos(\n    runner: &mut AppRunner,\n    event: &web_sys::TouchEvent,\n) -> Option<(egui::Pos2, web_sys::Touch)> {\n    let all_touches: Vec<_> = (0..event.touches().length())\n        .filter_map(|i| event.touches().get(i))\n        // On touchend we don't get anything in `touches`, but we still get `changed_touches`, so include those:\n        .chain((0..event.changed_touches().length()).filter_map(|i| event.changed_touches().get(i)))\n        .collect();\n\n    if let Some(primary_touch) = runner.input.primary_touch {\n        // Is the primary touch is gone?\n        if !all_touches\n            .iter()\n            .any(|touch| primary_touch == egui::TouchId::from(touch.identifier()))\n        {\n            runner.input.primary_touch = None;\n        }\n    }\n\n    if runner.input.primary_touch.is_none() {\n        runner.input.primary_touch = all_touches\n            .first()\n            .map(|touch| egui::TouchId::from(touch.identifier()));\n    }\n\n    let primary_touch = runner.input.primary_touch;\n\n    if let Some(primary_touch) = primary_touch {\n        for touch in all_touches {\n            if primary_touch == egui::TouchId::from(touch.identifier()) {\n                let canvas_rect = canvas_content_rect(runner.canvas());\n                return Some((\n                    pos_from_touch(canvas_rect, &touch, runner.egui_ctx()),\n                    touch,\n                ));\n            }\n        }\n    }\n\n    None\n}\n\nfn pos_from_touch(\n    canvas_rect: egui::Rect,\n    touch: &web_sys::Touch,\n    egui_ctx: &egui::Context,\n) -> egui::Pos2 {\n    let zoom_factor = egui_ctx.zoom_factor();\n    egui::Pos2 {\n        x: (touch.client_x() as f32 - canvas_rect.left()) / zoom_factor,\n        y: (touch.client_y() as f32 - canvas_rect.top()) / zoom_factor,\n    }\n}\n\npub fn push_touches(runner: &mut AppRunner, phase: egui::TouchPhase, event: &web_sys::TouchEvent) {\n    let canvas_rect = canvas_content_rect(runner.canvas());\n    for touch_idx in 0..event.changed_touches().length() {\n        if let Some(touch) = event.changed_touches().item(touch_idx) {\n            runner.input.raw.events.push(egui::Event::Touch {\n                device_id: egui::TouchDeviceId(0),\n                id: egui::TouchId::from(touch.identifier()),\n                phase,\n                pos: pos_from_touch(canvas_rect, &touch, runner.egui_ctx()),\n                force: Some(touch.force()),\n            });\n        }\n    }\n}\n\n/// The text input from a keyboard event (e.g. `X` when pressing the `X` key).\npub fn text_from_keyboard_event(event: &web_sys::KeyboardEvent) -> Option<String> {\n    let key = event.key();\n\n    let is_function_key = key.starts_with('F') && key.len() > 1;\n    if is_function_key {\n        return None;\n    }\n\n    let is_control_key = matches!(\n        key.as_str(),\n        \"Alt\"\n      | \"ArrowDown\"\n      | \"ArrowLeft\"\n      | \"ArrowRight\"\n      | \"ArrowUp\"\n      | \"Backspace\"\n      | \"CapsLock\"\n      | \"ContextMenu\"\n      | \"Control\"\n      | \"Delete\"\n      | \"End\"\n      | \"Enter\"\n      | \"Esc\"\n      | \"Escape\"\n      | \"GroupNext\" // https://github.com/emilk/egui/issues/510\n      | \"Help\"\n      | \"Home\"\n      | \"Insert\"\n      | \"Meta\"\n      | \"NumLock\"\n      | \"PageDown\"\n      | \"PageUp\"\n      | \"Pause\"\n      | \"ScrollLock\"\n      | \"Shift\"\n      | \"Tab\"\n    );\n\n    if is_control_key {\n        return None;\n    }\n\n    Some(key)\n}\n\n/// Web sends all keys as strings, so it is up to us to figure out if it is\n/// a real text input or the name of a key.\npub fn translate_key(key: &str) -> Option<egui::Key> {\n    egui::Key::from_name(key)\n}\n\npub fn modifiers_from_kb_event(event: &web_sys::KeyboardEvent) -> egui::Modifiers {\n    egui::Modifiers {\n        alt: event.alt_key(),\n        ctrl: event.ctrl_key(),\n        shift: event.shift_key(),\n\n        // Ideally we should know if we are running or mac or not,\n        // but this works good enough for now.\n        mac_cmd: event.meta_key(),\n\n        // Ideally we should know if we are running or mac or not,\n        // but this works good enough for now.\n        command: event.ctrl_key() || event.meta_key(),\n    }\n}\n\npub fn modifiers_from_mouse_event(event: &web_sys::MouseEvent) -> egui::Modifiers {\n    egui::Modifiers {\n        alt: event.alt_key(),\n        ctrl: event.ctrl_key(),\n        shift: event.shift_key(),\n\n        // Ideally we should know if we are running or mac or not,\n        // but this works good enough for now.\n        mac_cmd: event.meta_key(),\n\n        // Ideally we should know if we are running or mac or not,\n        // but this works good enough for now.\n        command: event.ctrl_key() || event.meta_key(),\n    }\n}\n\npub fn modifiers_from_wheel_event(event: &web_sys::WheelEvent) -> egui::Modifiers {\n    egui::Modifiers {\n        alt: event.alt_key(),\n        ctrl: event.ctrl_key(),\n        shift: event.shift_key(),\n\n        // Ideally we should know if we are running or mac or not,\n        // but this works good enough for now.\n        mac_cmd: event.meta_key(),\n\n        // Ideally we should know if we are running or mac or not,\n        // but this works good enough for now.\n        command: event.ctrl_key() || event.meta_key(),\n    }\n}\n"
  },
  {
    "path": "crates/eframe/src/web/mod.rs",
    "content": "//! [`egui`] bindings for web apps (compiling to WASM).\n\n#![expect(clippy::missing_errors_doc)] // So many `-> Result<_, JsValue>`\n#![expect(clippy::unwrap_used)] // TODO(emilk): remove unwraps\n\nmod app_runner;\nmod backend;\nmod events;\nmod input;\nmod panic_handler;\nmod text_agent;\nmod web_logger;\nmod web_runner;\n\n/// Access to the browser screen reader.\n#[cfg(feature = \"web_screen_reader\")]\npub mod screen_reader;\n\n/// Access to local browser storage.\npub mod storage;\n\npub(crate) use app_runner::AppRunner;\npub use panic_handler::{PanicHandler, PanicSummary};\npub use web_logger::WebLogger;\npub use web_runner::WebRunner;\n\n#[cfg(not(any(feature = \"glow\", feature = \"wgpu_no_default_features\")))]\ncompile_error!(\"You must enable either the 'glow' or 'wgpu' feature\");\n\nmod web_painter;\n\n#[cfg(feature = \"glow\")]\nmod web_painter_glow;\n\n#[cfg(feature = \"wgpu_no_default_features\")]\nmod web_painter_wgpu;\n\npub use backend::*;\n\nuse egui::Theme;\nuse wasm_bindgen::prelude::*;\nuse web_sys::{Document, MediaQueryList, Node};\n\nuse input::{\n    button_from_mouse_event, modifiers_from_kb_event, modifiers_from_mouse_event,\n    modifiers_from_wheel_event, pos_from_mouse_event, primary_touch_pos, push_touches,\n    text_from_keyboard_event, translate_key,\n};\n\n// ----------------------------------------------------------------------------\n\n/// Debug browser resizing?\nconst DEBUG_RESIZE: bool = false;\n\npub(crate) fn string_from_js_value(value: &JsValue) -> String {\n    value.as_string().unwrap_or_else(|| format!(\"{value:#?}\"))\n}\n\n/// Returns the `Element` with active focus.\n///\n/// Elements can only be focused if they are:\n/// - `<a>`/`<area>` with an `href` attribute\n/// - `<input>`/`<select>`/`<textarea>`/`<button>` which aren't `disabled`\n/// - any other element with a `tabindex` attribute\npub(crate) fn focused_element(root: &Node) -> Option<web_sys::Element> {\n    if let Some(document) = root.dyn_ref::<Document>() {\n        document.active_element()\n    } else if let Some(shadow) = root.dyn_ref::<web_sys::ShadowRoot>() {\n        shadow.active_element()\n    } else {\n        None\n    }\n}\n\npub(crate) fn has_focus<T: JsCast>(element: &T) -> bool {\n    fn try_has_focus<T: JsCast>(element: &T) -> Option<bool> {\n        let element = element.dyn_ref::<web_sys::Element>()?;\n        let root = element.get_root_node();\n\n        let focused_element = focused_element(&root)?;\n        Some(element == &focused_element)\n    }\n    try_has_focus(element).unwrap_or(false)\n}\n\n/// Current time in seconds (since undefined point in time).\n///\n/// Monotonically increasing.\npub fn now_sec() -> f64 {\n    web_sys::window()\n        .expect(\"should have a Window\")\n        .performance()\n        .expect(\"should have a Performance\")\n        .now()\n        / 1000.0\n}\n\n/// The native GUI scale factor, taking into account the browser zoom.\n///\n/// Corresponds to [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) in JavaScript.\npub fn native_pixels_per_point() -> f32 {\n    let pixels_per_point = web_sys::window().unwrap().device_pixel_ratio() as f32;\n    if pixels_per_point > 0.0 && pixels_per_point.is_finite() {\n        pixels_per_point\n    } else {\n        1.0\n    }\n}\n\n/// Ask the browser about the preferred system theme.\n///\n/// `None` means unknown.\npub fn system_theme() -> Option<egui::Theme> {\n    let window = web_sys::window()?;\n    if does_prefer_color_scheme(&window, Theme::Dark) == Some(true) {\n        Some(Theme::Dark)\n    } else if does_prefer_color_scheme(&window, Theme::Light) == Some(true) {\n        Some(Theme::Light)\n    } else {\n        None\n    }\n}\n\nfn does_prefer_color_scheme(window: &web_sys::Window, theme: Theme) -> Option<bool> {\n    Some(prefers_color_scheme(window, theme).ok()??.matches())\n}\n\nfn prefers_color_scheme(\n    window: &web_sys::Window,\n    theme: Theme,\n) -> Result<Option<MediaQueryList>, JsValue> {\n    let theme = match theme {\n        Theme::Dark => \"dark\",\n        Theme::Light => \"light\",\n    };\n    window.match_media(format!(\"(prefers-color-scheme: {theme})\").as_str())\n}\n\n/// Returns the canvas in client coordinates.\nfn canvas_content_rect(canvas: &web_sys::HtmlCanvasElement) -> egui::Rect {\n    let bounding_rect = canvas.get_bounding_client_rect();\n\n    let mut rect = egui::Rect::from_min_max(\n        egui::pos2(bounding_rect.left() as f32, bounding_rect.top() as f32),\n        egui::pos2(bounding_rect.right() as f32, bounding_rect.bottom() as f32),\n    );\n\n    // We need to subtract padding and border:\n    if let Some(window) = web_sys::window()\n        && let Ok(Some(style)) = window.get_computed_style(canvas)\n    {\n        let get_property = |name: &str| -> Option<f32> {\n            let property = style.get_property_value(name).ok()?;\n            property.trim_end_matches(\"px\").parse::<f32>().ok()\n        };\n\n        rect.min.x += get_property(\"padding-left\").unwrap_or_default();\n        rect.min.y += get_property(\"padding-top\").unwrap_or_default();\n        rect.max.x -= get_property(\"padding-right\").unwrap_or_default();\n        rect.max.y -= get_property(\"padding-bottom\").unwrap_or_default();\n    }\n\n    rect\n}\n\nfn canvas_size_in_points(canvas: &web_sys::HtmlCanvasElement, ctx: &egui::Context) -> egui::Vec2 {\n    // ctx.pixels_per_point can be outdated\n\n    let pixels_per_point = ctx.zoom_factor() * native_pixels_per_point();\n\n    egui::vec2(\n        canvas.width() as f32 / pixels_per_point,\n        canvas.height() as f32 / pixels_per_point,\n    )\n}\n\n// ----------------------------------------------------------------------------\n\n/// Set the cursor icon.\nfn set_cursor_icon(cursor: egui::CursorIcon) -> Option<()> {\n    let document = web_sys::window()?.document()?;\n    document\n        .body()?\n        .style()\n        .set_property(\"cursor\", cursor_web_name(cursor))\n        .ok()\n}\n\n/// Set the clipboard text.\nfn set_clipboard_text(s: &str) {\n    if let Some(window) = web_sys::window() {\n        if !window.is_secure_context() {\n            log::error!(\n                \"Clipboard is not available because we are not in a secure context. \\\n                See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts\"\n            );\n            return;\n        }\n        let promise = window.navigator().clipboard().write_text(s);\n        let future = wasm_bindgen_futures::JsFuture::from(promise);\n        let future = async move {\n            if let Err(err) = future.await {\n                log::error!(\"Copy/cut action failed: {}\", string_from_js_value(&err));\n            }\n        };\n        wasm_bindgen_futures::spawn_local(future);\n    }\n}\n\n/// Set the clipboard image.\nfn set_clipboard_image(image: &egui::ColorImage) {\n    if let Some(window) = web_sys::window() {\n        if !window.is_secure_context() {\n            log::error!(\n                \"Clipboard is not available because we are not in a secure context. \\\n                See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts\"\n            );\n            return;\n        }\n\n        let png_bytes = to_image(image).and_then(|image| to_png_bytes(&image));\n        let png_bytes = match png_bytes {\n            Ok(png_bytes) => png_bytes,\n            Err(err) => {\n                log::error!(\"Failed to encode image to png: {err}\");\n                return;\n            }\n        };\n\n        let mime = \"image/png\";\n\n        let item = match create_clipboard_item(mime, &png_bytes) {\n            Ok(item) => item,\n            Err(err) => {\n                log::error!(\"Failed to copy image: {}\", string_from_js_value(&err));\n                return;\n            }\n        };\n        let items = js_sys::Array::of1(&item);\n        let promise = window.navigator().clipboard().write(&items);\n        let future = wasm_bindgen_futures::JsFuture::from(promise);\n        let future = async move {\n            if let Err(err) = future.await {\n                log::error!(\n                    \"Copy/cut image action failed: {}\",\n                    string_from_js_value(&err)\n                );\n            }\n        };\n        wasm_bindgen_futures::spawn_local(future);\n    }\n}\n\nfn to_image(image: &egui::ColorImage) -> Result<image::RgbaImage, String> {\n    profiling::function_scope!();\n    image::RgbaImage::from_raw(\n        image.width() as _,\n        image.height() as _,\n        bytemuck::cast_slice(&image.pixels).to_vec(),\n    )\n    .ok_or_else(|| \"Invalid IconData\".to_owned())\n}\n\nfn to_png_bytes(image: &image::RgbaImage) -> Result<Vec<u8>, String> {\n    profiling::function_scope!();\n    let mut png_bytes: Vec<u8> = Vec::new();\n    image\n        .write_to(\n            &mut std::io::Cursor::new(&mut png_bytes),\n            image::ImageFormat::Png,\n        )\n        .map_err(|err| err.to_string())?;\n    Ok(png_bytes)\n}\n\nfn create_clipboard_item(mime: &str, bytes: &[u8]) -> Result<web_sys::ClipboardItem, JsValue> {\n    let array = js_sys::Uint8Array::from(bytes);\n    let blob_parts = js_sys::Array::new();\n    blob_parts.push(&array);\n\n    let options = web_sys::BlobPropertyBag::new();\n    options.set_type(mime);\n\n    let blob = web_sys::Blob::new_with_u8_array_sequence_and_options(&blob_parts, &options)?;\n\n    let items = js_sys::Object::new();\n\n    #[expect(unsafe_code, unused_unsafe)] // Weird false positive\n    // SAFETY: I hope so\n    unsafe {\n        js_sys::Reflect::set(&items, &JsValue::from_str(mime), &blob)?\n    };\n\n    let clipboard_item = web_sys::ClipboardItem::new_with_record_from_str_to_blob_promise(&items)?;\n\n    Ok(clipboard_item)\n}\n\nfn cursor_web_name(cursor: egui::CursorIcon) -> &'static str {\n    match cursor {\n        egui::CursorIcon::Alias => \"alias\",\n        egui::CursorIcon::AllScroll => \"all-scroll\",\n        egui::CursorIcon::Cell => \"cell\",\n        egui::CursorIcon::ContextMenu => \"context-menu\",\n        egui::CursorIcon::Copy => \"copy\",\n        egui::CursorIcon::Crosshair => \"crosshair\",\n        egui::CursorIcon::Default => \"default\",\n        egui::CursorIcon::Grab => \"grab\",\n        egui::CursorIcon::Grabbing => \"grabbing\",\n        egui::CursorIcon::Help => \"help\",\n        egui::CursorIcon::Move => \"move\",\n        egui::CursorIcon::NoDrop => \"no-drop\",\n        egui::CursorIcon::None => \"none\",\n        egui::CursorIcon::NotAllowed => \"not-allowed\",\n        egui::CursorIcon::PointingHand => \"pointer\",\n        egui::CursorIcon::Progress => \"progress\",\n        egui::CursorIcon::ResizeHorizontal => \"ew-resize\",\n        egui::CursorIcon::ResizeNeSw => \"nesw-resize\",\n        egui::CursorIcon::ResizeNwSe => \"nwse-resize\",\n        egui::CursorIcon::ResizeVertical => \"ns-resize\",\n\n        egui::CursorIcon::ResizeEast => \"e-resize\",\n        egui::CursorIcon::ResizeSouthEast => \"se-resize\",\n        egui::CursorIcon::ResizeSouth => \"s-resize\",\n        egui::CursorIcon::ResizeSouthWest => \"sw-resize\",\n        egui::CursorIcon::ResizeWest => \"w-resize\",\n        egui::CursorIcon::ResizeNorthWest => \"nw-resize\",\n        egui::CursorIcon::ResizeNorth => \"n-resize\",\n        egui::CursorIcon::ResizeNorthEast => \"ne-resize\",\n        egui::CursorIcon::ResizeColumn => \"col-resize\",\n        egui::CursorIcon::ResizeRow => \"row-resize\",\n\n        egui::CursorIcon::Text => \"text\",\n        egui::CursorIcon::VerticalText => \"vertical-text\",\n        egui::CursorIcon::Wait => \"wait\",\n        egui::CursorIcon::ZoomIn => \"zoom-in\",\n        egui::CursorIcon::ZoomOut => \"zoom-out\",\n    }\n}\n\n/// Open the given url in the browser.\npub fn open_url(url: &str, new_tab: bool) -> Option<()> {\n    let name = if new_tab { \"_blank\" } else { \"_self\" };\n\n    web_sys::window()?\n        .open_with_url_and_target(url, name)\n        .ok()?;\n    Some(())\n}\n\n/// e.g. \"#fragment\" part of \"www.example.com/index.html#fragment\",\n///\n/// Percent decoded\npub fn location_hash() -> String {\n    percent_decode(\n        &web_sys::window()\n            .unwrap()\n            .location()\n            .hash()\n            .unwrap_or_default(),\n    )\n}\n\n/// Percent-decodes a string.\npub fn percent_decode(s: &str) -> String {\n    percent_encoding::percent_decode_str(s)\n        .decode_utf8_lossy()\n        .to_string()\n}\n\n/// Are we running inside the Safari browser?\npub fn is_safari_browser() -> bool {\n    web_sys::window().is_some_and(|window| window.has_own_property(&JsValue::from(\"safari\")))\n}\n"
  },
  {
    "path": "crates/eframe/src/web/panic_handler.rs",
    "content": "use std::sync::Arc;\n\nuse egui::mutex::Mutex;\nuse wasm_bindgen::prelude::*;\n\n/// Detects panics, logs them using `console.error`, and stores the panics message and callstack.\n///\n/// This lets you query `PanicHandler` for the panic message (if any) so you can show it in the HTML.\n///\n/// Chep to clone (ref-counted).\n#[derive(Clone)]\npub struct PanicHandler(Arc<Mutex<PanicHandlerInner>>);\n\nimpl PanicHandler {\n    /// Install a panic hook.\n    pub fn install() -> Self {\n        let handler = Self(Arc::new(Mutex::new(Default::default())));\n\n        let handler_clone = handler.clone();\n        let previous_hook = std::panic::take_hook();\n        std::panic::set_hook(Box::new(move |panic_info| {\n            let summary = PanicSummary::new(panic_info);\n\n            // Log it using console.error\n            error(format!(\n                \"{}\\n\\nStack:\\n\\n{}\",\n                summary.message(),\n                summary.callstack()\n            ));\n\n            // Remember the summary:\n            handler_clone.0.lock().summary = Some(summary);\n\n            // Propagate panic info to the previously registered panic hook\n            previous_hook(panic_info);\n        }));\n\n        handler\n    }\n\n    /// Has there been a panic?\n    pub fn has_panicked(&self) -> bool {\n        self.0.lock().summary.is_some()\n    }\n\n    /// What was the panic message and callstack?\n    pub fn panic_summary(&self) -> Option<PanicSummary> {\n        self.0.lock().summary.clone()\n    }\n}\n\n#[derive(Clone, Default)]\nstruct PanicHandlerInner {\n    summary: Option<PanicSummary>,\n}\n\n/// Contains a summary about a panics.\n///\n/// This is basically a human-readable version of [`std::panic::PanicHookInfo`]\n/// with an added callstack.\n#[derive(Clone, Debug)]\npub struct PanicSummary {\n    message: String,\n    callstack: String,\n}\n\nimpl PanicSummary {\n    /// Construct a summary from a panic.\n    pub fn new(info: &std::panic::PanicHookInfo<'_>) -> Self {\n        let message = info.to_string();\n        let callstack = Error::new().stack();\n        Self { message, callstack }\n    }\n\n    /// The panic message.\n    pub fn message(&self) -> String {\n        self.message.clone()\n    }\n\n    /// The backtrace.\n    pub fn callstack(&self) -> String {\n        self.callstack.clone()\n    }\n}\n\n#[wasm_bindgen]\nextern \"C\" {\n    #[wasm_bindgen(js_namespace = console)]\n    fn error(msg: String);\n\n    type Error;\n\n    #[wasm_bindgen(constructor)]\n    fn new() -> Error;\n\n    #[wasm_bindgen(structural, method, getter)]\n    fn stack(error: &Error) -> String;\n}\n"
  },
  {
    "path": "crates/eframe/src/web/screen_reader.rs",
    "content": "/// Speak the given text out loud.\npub fn speak(text: &str) {\n    if text.is_empty() {\n        return;\n    }\n\n    if let Some(window) = web_sys::window() {\n        log::debug!(\"Speaking {text:?}\");\n\n        if let Ok(speech_synthesis) = window.speech_synthesis() {\n            speech_synthesis.cancel(); // interrupt previous speech, if any\n\n            if let Ok(utterance) = web_sys::SpeechSynthesisUtterance::new_with_text(text) {\n                utterance.set_rate(1.0);\n                utterance.set_pitch(1.0);\n                utterance.set_volume(1.0);\n                speech_synthesis.speak(&utterance);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/eframe/src/web/storage.rs",
    "content": "fn local_storage() -> Option<web_sys::Storage> {\n    web_sys::window()?.local_storage().ok()?\n}\n\n/// Read data from local storage.\npub fn local_storage_get(key: &str) -> Option<String> {\n    local_storage().map(|storage| storage.get_item(key).ok())??\n}\n\n/// Write data to local storage.\npub fn local_storage_set(key: &str, value: &str) {\n    local_storage().map(|storage| storage.set_item(key, value));\n}\n\n#[cfg(feature = \"persistence\")]\npub(crate) fn load_memory(ctx: &egui::Context) {\n    if let Some(memory_string) = local_storage_get(\"egui_memory_ron\") {\n        match ron::from_str(&memory_string) {\n            Ok(memory) => {\n                ctx.memory_mut(|m| *m = memory);\n            }\n            Err(err) => {\n                log::warn!(\"Failed to parse memory RON: {err}\");\n            }\n        }\n    }\n}\n\n#[cfg(not(feature = \"persistence\"))]\npub(crate) fn load_memory(_: &egui::Context) {}\n\n#[cfg(feature = \"persistence\")]\npub(crate) fn save_memory(ctx: &egui::Context) {\n    match ctx.memory(ron::to_string) {\n        Ok(ron) => {\n            local_storage_set(\"egui_memory_ron\", &ron);\n        }\n        Err(err) => {\n            log::warn!(\"Failed to serialize memory as RON: {err}\");\n        }\n    }\n}\n\n#[cfg(not(feature = \"persistence\"))]\npub(crate) fn save_memory(_: &egui::Context) {}\n"
  },
  {
    "path": "crates/eframe/src/web/text_agent.rs",
    "content": "//! The text agent is a hidden `<input>` element used to capture\n//! IME and mobile keyboard input events.\n\nuse std::cell::Cell;\n\nuse wasm_bindgen::prelude::*;\nuse web_sys::{Document, Node};\n\nuse super::{AppRunner, WebRunner};\n\npub struct TextAgent {\n    input: web_sys::HtmlInputElement,\n    prev_ime_output: Cell<Option<egui::output::IMEOutput>>,\n}\n\nimpl TextAgent {\n    /// Attach the agent to the document.\n    pub fn attach(runner_ref: &WebRunner, root: Node) -> Result<Self, JsValue> {\n        let document = web_sys::window().unwrap().document().unwrap();\n\n        // create an `<input>` element\n        let input = document\n            .create_element(\"input\")?\n            .dyn_into::<web_sys::HtmlElement>()?;\n        input.set_autofocus(true)?;\n        let input = input.dyn_into::<web_sys::HtmlInputElement>()?;\n        input.set_type(\"text\");\n        input.set_attribute(\"autocapitalize\", \"off\")?;\n\n        // append it to `<body>` and hide it outside of the viewport\n        let style = input.style();\n        style.set_property(\"background-color\", \"transparent\")?;\n        style.set_property(\"border\", \"none\")?;\n        style.set_property(\"outline\", \"none\")?;\n        style.set_property(\"width\", \"1px\")?;\n        style.set_property(\"height\", \"1px\")?;\n        style.set_property(\"caret-color\", \"transparent\")?;\n        style.set_property(\"position\", \"absolute\")?;\n        style.set_property(\"top\", \"0\")?;\n        style.set_property(\"left\", \"0\")?;\n\n        if root.has_type::<Document>() {\n            // root object is a document, append to its body\n            root.dyn_into::<Document>()?\n                .body()\n                .unwrap()\n                .append_child(&input)?;\n        } else {\n            // append input into root directly\n            root.append_child(&input)?;\n        }\n\n        // attach event listeners\n\n        let on_input = {\n            let input = input.clone();\n            move |event: web_sys::InputEvent, runner: &mut AppRunner| {\n                let text = input.value();\n                // Fix android virtual keyboard Gboard\n                // This removes the virtual keyboard's suggestion.\n                if !event.is_composing() {\n                    input.blur().ok();\n                    input.focus().ok();\n                }\n                // if `is_composing` is true, then user is using IME, for example: emoji, pinyin, kanji, hangul, etc.\n                // In that case, the browser emits both `input` and `compositionupdate` events,\n                // and we need to ignore the `input` event.\n                if !text.is_empty() && !event.is_composing() {\n                    input.set_value(\"\");\n                    let event = egui::Event::Text(text);\n                    runner.input.raw.events.push(event);\n                    runner.needs_repaint.repaint_asap();\n                }\n            }\n        };\n\n        let on_composition_start = {\n            let input = input.clone();\n            move |_: web_sys::CompositionEvent, runner: &mut AppRunner| {\n                input.set_value(\"\");\n                let event = egui::Event::Ime(egui::ImeEvent::Enabled);\n                runner.input.raw.events.push(event);\n                // Repaint moves the text agent into place,\n                // see `move_to` in `AppRunner::handle_platform_output`.\n                runner.needs_repaint.repaint_asap();\n            }\n        };\n\n        let on_composition_update = {\n            move |event: web_sys::CompositionEvent, runner: &mut AppRunner| {\n                let Some(text) = event.data() else { return };\n                let event = egui::Event::Ime(egui::ImeEvent::Preedit(text));\n                runner.input.raw.events.push(event);\n                runner.needs_repaint.repaint_asap();\n            }\n        };\n\n        let on_composition_end = {\n            let input = input.clone();\n            move |event: web_sys::CompositionEvent, runner: &mut AppRunner| {\n                let Some(text) = event.data() else { return };\n                input.set_value(\"\");\n                let event = egui::Event::Ime(egui::ImeEvent::Commit(text));\n                runner.input.raw.events.push(event);\n                runner.needs_repaint.repaint_asap();\n            }\n        };\n\n        runner_ref.add_event_listener(&input, \"input\", on_input)?;\n        runner_ref.add_event_listener(&input, \"compositionstart\", on_composition_start)?;\n        runner_ref.add_event_listener(&input, \"compositionupdate\", on_composition_update)?;\n        runner_ref.add_event_listener(&input, \"compositionend\", on_composition_end)?;\n\n        // The canvas doesn't get keydown/keyup events when the text agent is focused,\n        // so we need to forward them to the runner:\n        runner_ref.add_event_listener(&input, \"keydown\", super::events::on_keydown)?;\n        runner_ref.add_event_listener(&input, \"keyup\", super::events::on_keyup)?;\n\n        Ok(Self {\n            input,\n            prev_ime_output: Default::default(),\n        })\n    }\n\n    pub fn move_to(\n        &self,\n        ime: Option<egui::output::IMEOutput>,\n        canvas: &web_sys::HtmlCanvasElement,\n        zoom_factor: f32,\n    ) -> Result<(), JsValue> {\n        // Don't move the text agent unless the position actually changed:\n        if self.prev_ime_output.get() == ime {\n            return Ok(());\n        }\n        self.prev_ime_output.set(ime);\n\n        let Some(ime) = ime else { return Ok(()) };\n\n        let mut canvas_rect = super::canvas_content_rect(canvas);\n        // Fix for safari with virtual keyboard flapping position\n        if is_mobile_safari() {\n            canvas_rect.min.y = canvas.offset_top() as f32;\n        }\n        let cursor_rect = ime.cursor_rect.translate(canvas_rect.min.to_vec2());\n\n        let style = self.input.style();\n\n        // This is where the IME input will point to:\n        style.set_property(\n            \"left\",\n            &format!(\"{}px\", cursor_rect.center().x * zoom_factor),\n        )?;\n        style.set_property(\n            \"top\",\n            &format!(\"{}px\", cursor_rect.center().y * zoom_factor),\n        )?;\n\n        Ok(())\n    }\n\n    pub fn set_focus(&self, on: bool) {\n        if on {\n            self.focus();\n        } else {\n            self.blur();\n        }\n    }\n\n    pub fn has_focus(&self) -> bool {\n        super::has_focus(&self.input)\n    }\n\n    pub fn focus(&self) {\n        if self.has_focus() {\n            return;\n        }\n\n        log::trace!(\"Focusing text agent\");\n\n        if let Err(err) = self.input.focus() {\n            log::error!(\"failed to set focus: {}\", super::string_from_js_value(&err));\n        }\n    }\n\n    pub fn blur(&self) {\n        if !self.has_focus() {\n            return;\n        }\n\n        log::trace!(\"Blurring text agent\");\n\n        if let Err(err) = self.input.blur() {\n            log::error!(\"failed to set focus: {}\", super::string_from_js_value(&err));\n        }\n    }\n}\n\nimpl Drop for TextAgent {\n    fn drop(&mut self) {\n        self.input.remove();\n    }\n}\n\n/// Returns `true` if the app is likely running on a mobile device on navigator Safari.\nfn is_mobile_safari() -> bool {\n    (|| {\n        let user_agent = web_sys::window()?.navigator().user_agent().ok()?;\n        let is_ios = user_agent.contains(\"iPhone\")\n            || user_agent.contains(\"iPad\")\n            || user_agent.contains(\"iPod\");\n        let is_safari = user_agent.contains(\"Safari\");\n        Some(is_ios && is_safari)\n    })()\n    .unwrap_or(false)\n}\n"
  },
  {
    "path": "crates/eframe/src/web/web_logger.rs",
    "content": "/// Implements [`log::Log`] to log messages to `console.log`, `console.warn`, etc.\npub struct WebLogger {\n    filter: log::LevelFilter,\n}\n\nimpl WebLogger {\n    /// Install a new `WebLogger`, piping all [`log`] events to the web console.\n    pub fn init(filter: log::LevelFilter) -> Result<(), log::SetLoggerError> {\n        log::set_max_level(filter);\n        log::set_boxed_logger(Box::new(Self::new(filter)))\n    }\n\n    /// Create a new [`WebLogger`] with the given filter,\n    /// but don't install it.\n    pub fn new(filter: log::LevelFilter) -> Self {\n        Self { filter }\n    }\n}\n\nimpl log::Log for WebLogger {\n    fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {\n        /// Never log anything less serious than a `INFO` from these crates.\n        const CRATES_AT_INFO_LEVEL: &[&str] = &[\n            // wgpu crates spam a lot on debug level, which is really annoying\n            \"naga\",\n            \"wgpu_core\",\n            \"wgpu_hal\",\n        ];\n\n        if CRATES_AT_INFO_LEVEL\n            .iter()\n            .any(|crate_name| metadata.target().starts_with(crate_name))\n        {\n            return metadata.level() <= log::LevelFilter::Info;\n        }\n\n        metadata.level() <= self.filter\n    }\n\n    fn log(&self, record: &log::Record<'_>) {\n        #![expect(clippy::match_same_arms)]\n\n        if !self.enabled(record.metadata()) {\n            return;\n        }\n\n        let msg = if let (Some(file), Some(line)) = (record.file(), record.line()) {\n            let file = shorten_file_path(file);\n            format!(\"[{}] {file}:{line}: {}\", record.target(), record.args())\n        } else {\n            format!(\"[{}] {}\", record.target(), record.args())\n        };\n\n        match record.level() {\n            // NOTE: the `console::trace` includes a stack trace, which is super-noisy.\n            log::Level::Trace => console::debug(&msg),\n\n            log::Level::Debug => console::debug(&msg),\n            log::Level::Info => console::info(&msg),\n            log::Level::Warn => console::warn(&msg),\n\n            // Using console.error causes crashes for unknown reason\n            // https://github.com/emilk/egui/pull/2961\n            // log::Level::Error => console::error(&msg),\n            log::Level::Error => console::warn(&format!(\"ERROR: {msg}\")),\n        }\n    }\n\n    fn flush(&self) {}\n}\n\n/// js-bindings for console.log, console.warn, etc\nmod console {\n    use wasm_bindgen::prelude::*;\n\n    #[wasm_bindgen]\n    extern \"C\" {\n        /// `console.trace`\n        #[wasm_bindgen(js_namespace = console)]\n        pub fn trace(s: &str);\n\n        /// `console.debug`\n        #[wasm_bindgen(js_namespace = console)]\n        pub fn debug(s: &str);\n\n        /// `console.info`\n        #[wasm_bindgen(js_namespace = console)]\n        pub fn info(s: &str);\n\n        /// `console.warn`\n        #[wasm_bindgen(js_namespace = console)]\n        pub fn warn(s: &str);\n\n        // Using console.error causes crashes for unknown reason\n        // https://github.com/emilk/egui/pull/2961\n        // /// `console.error`\n        // #[wasm_bindgen(js_namespace = console)]\n        // pub fn error(s: &str);\n    }\n}\n\n/// Shorten a path to a Rust source file.\n///\n/// Example input:\n/// * `/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs`\n/// * `crates/rerun/src/main.rs`\n/// * `/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs`\n///\n/// Example output:\n/// * `tokio-1.24.1/src/runtime/runtime.rs`\n/// * `rerun/src/main.rs`\n/// * `core/src/ops/function.rs`\n#[allow(clippy::allow_attributes, dead_code)] // only used on web and in tests\nfn shorten_file_path(file_path: &str) -> &str {\n    if let Some(i) = file_path.rfind(\"/src/\") {\n        if let Some(prev_slash) = file_path[..i].rfind('/') {\n            &file_path[prev_slash + 1..]\n        } else {\n            file_path\n        }\n    } else {\n        file_path\n    }\n}\n\n#[test]\nfn test_shorten_file_path() {\n    for (before, after) in [\n        (\n            \"/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs\",\n            \"tokio-1.24.1/src/runtime/runtime.rs\",\n        ),\n        (\"crates/rerun/src/main.rs\", \"rerun/src/main.rs\"),\n        (\n            \"/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs\",\n            \"core/src/ops/function.rs\",\n        ),\n        (\"/weird/path/file.rs\", \"/weird/path/file.rs\"),\n    ] {\n        assert_eq!(shorten_file_path(before), after);\n    }\n}\n"
  },
  {
    "path": "crates/eframe/src/web/web_painter.rs",
    "content": "use egui::{Event, UserData};\nuse wasm_bindgen::JsValue;\n\n/// Renderer for a browser canvas.\n/// As of writing we're not allowing to decide on the painter at runtime,\n/// therefore this trait is merely there for specifying and documenting the interface.\npub(crate) trait WebPainter {\n    // Create a new web painter targeting a given canvas.\n    // fn new(canvas: HtmlCanvasElement, options: &WebOptions) -> Result<Self, String>\n    // where\n    //     Self: Sized;\n\n    /// Reference to the canvas in use.\n    fn canvas(&self) -> &web_sys::HtmlCanvasElement;\n\n    /// Maximum size of a texture in one direction.\n    fn max_texture_side(&self) -> usize;\n\n    /// Update all internal textures and paint gui.\n    /// When `capture` isn't empty, the rendered screen should be captured.\n    /// Once the screenshot is ready, the screenshot should be returned via [`Self::handle_screenshots`].\n    fn paint_and_update_textures(\n        &mut self,\n        clear_color: [f32; 4],\n        clipped_primitives: &[egui::ClippedPrimitive],\n        pixels_per_point: f32,\n        textures_delta: &egui::TexturesDelta,\n        capture: Vec<UserData>,\n    ) -> Result<(), JsValue>;\n\n    fn handle_screenshots(&mut self, events: &mut Vec<Event>);\n\n    /// Destroy all resources.\n    fn destroy(&mut self);\n}\n"
  },
  {
    "path": "crates/eframe/src/web/web_painter_glow.rs",
    "content": "use egui::{Event, UserData, ViewportId};\nuse egui_glow::glow;\nuse std::sync::Arc;\nuse wasm_bindgen::JsCast as _;\nuse wasm_bindgen::JsValue;\nuse web_sys::HtmlCanvasElement;\n\nuse crate::{WebGlContextOption, WebOptions};\n\nuse super::web_painter::WebPainter;\n\npub(crate) struct WebPainterGlow {\n    canvas: HtmlCanvasElement,\n    painter: egui_glow::Painter,\n    screenshots: Vec<(egui::ColorImage, Vec<UserData>)>,\n}\n\nimpl WebPainterGlow {\n    pub fn gl(&self) -> &std::sync::Arc<glow::Context> {\n        self.painter.gl()\n    }\n\n    pub fn new(\n        _ctx: egui::Context,\n        canvas: HtmlCanvasElement,\n        options: &WebOptions,\n    ) -> Result<Self, String> {\n        let (gl, shader_prefix) =\n            init_glow_context_from_canvas(&canvas, options.webgl_context_option)?;\n\n        #[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm\n        let gl = std::sync::Arc::new(gl);\n\n        let painter = egui_glow::Painter::new(gl, shader_prefix, None, options.dithering)\n            .map_err(|err| format!(\"Error starting glow painter: {err}\"))?;\n\n        Ok(Self {\n            canvas,\n            painter,\n            screenshots: Vec::new(),\n        })\n    }\n}\n\nimpl WebPainter for WebPainterGlow {\n    fn max_texture_side(&self) -> usize {\n        self.painter.max_texture_side()\n    }\n\n    fn canvas(&self) -> &HtmlCanvasElement {\n        &self.canvas\n    }\n\n    fn paint_and_update_textures(\n        &mut self,\n        clear_color: [f32; 4],\n        clipped_primitives: &[egui::ClippedPrimitive],\n        pixels_per_point: f32,\n        textures_delta: &egui::TexturesDelta,\n        capture: Vec<UserData>,\n    ) -> Result<(), JsValue> {\n        let canvas_dimension = [self.canvas.width(), self.canvas.height()];\n\n        for (id, image_delta) in &textures_delta.set {\n            self.painter.set_texture(*id, image_delta);\n        }\n\n        egui_glow::painter::clear(self.painter.gl(), canvas_dimension, clear_color);\n        self.painter\n            .paint_primitives(canvas_dimension, pixels_per_point, clipped_primitives);\n\n        if !capture.is_empty() {\n            let image = self.painter.read_screen_rgba(canvas_dimension);\n            self.screenshots.push((image, capture));\n        }\n\n        for &id in &textures_delta.free {\n            self.painter.free_texture(id);\n        }\n\n        Ok(())\n    }\n\n    fn destroy(&mut self) {\n        self.painter.destroy();\n    }\n\n    fn handle_screenshots(&mut self, events: &mut Vec<Event>) {\n        for (image, data) in self.screenshots.drain(..) {\n            let image = Arc::new(image);\n            for data in data {\n                events.push(Event::Screenshot {\n                    viewport_id: ViewportId::default(),\n                    image: Arc::clone(&image),\n                    user_data: data,\n                });\n            }\n        }\n    }\n}\n\n/// Returns glow context and shader prefix.\nfn init_glow_context_from_canvas(\n    canvas: &HtmlCanvasElement,\n    options: WebGlContextOption,\n) -> Result<(glow::Context, &'static str), String> {\n    let result = match options {\n        // Force use WebGl1\n        WebGlContextOption::WebGl1 => init_webgl1(canvas),\n        // Force use WebGl2\n        WebGlContextOption::WebGl2 => init_webgl2(canvas),\n        // Trying WebGl2 first\n        WebGlContextOption::BestFirst => init_webgl2(canvas).or_else(|| init_webgl1(canvas)),\n        // Trying WebGl1 first (useful for testing).\n        WebGlContextOption::CompatibilityFirst => {\n            init_webgl1(canvas).or_else(|| init_webgl2(canvas))\n        }\n    };\n\n    if let Some(result) = result {\n        Ok(result)\n    } else {\n        Err(\"WebGL isn't supported\".into())\n    }\n}\n\nfn init_webgl1(canvas: &HtmlCanvasElement) -> Option<(glow::Context, &'static str)> {\n    let gl1_ctx = canvas\n        .get_context(\"webgl\")\n        .expect(\"Failed to query about WebGL2 context\");\n\n    let gl1_ctx = gl1_ctx?;\n    log::debug!(\"WebGL1 selected.\");\n\n    let gl1_ctx = gl1_ctx\n        .dyn_into::<web_sys::WebGlRenderingContext>()\n        .unwrap();\n\n    let shader_prefix = if webgl1_requires_brightening(&gl1_ctx) {\n        log::debug!(\"Enabling webkitGTK brightening workaround.\");\n        \"#define APPLY_BRIGHTENING_GAMMA\"\n    } else {\n        \"\"\n    };\n\n    let gl = glow::Context::from_webgl1_context(gl1_ctx);\n\n    Some((gl, shader_prefix))\n}\n\nfn init_webgl2(canvas: &HtmlCanvasElement) -> Option<(glow::Context, &'static str)> {\n    let gl2_ctx = canvas\n        .get_context(\"webgl2\")\n        .expect(\"Failed to query about WebGL2 context\");\n\n    let gl2_ctx = gl2_ctx?;\n    log::debug!(\"WebGL2 selected.\");\n\n    let gl2_ctx = gl2_ctx\n        .dyn_into::<web_sys::WebGl2RenderingContext>()\n        .unwrap();\n    let gl = glow::Context::from_webgl2_context(gl2_ctx);\n    let shader_prefix = \"\";\n\n    Some((gl, shader_prefix))\n}\n\nfn webgl1_requires_brightening(gl: &web_sys::WebGlRenderingContext) -> bool {\n    // See https://github.com/emilk/egui/issues/794\n\n    // detect WebKitGTK\n\n    // WebKitGTK use WebKit default unmasked vendor and renderer\n    // but safari use same vendor and renderer\n    // so exclude \"Mac OS X\" user-agent.\n    let user_agent = web_sys::window().unwrap().navigator().user_agent().unwrap();\n    !user_agent.contains(\"Mac OS X\") && is_safari_and_webkit_gtk(gl)\n}\n\n/// detecting Safari and `webkitGTK`.\n///\n/// Safari and `webkitGTK` use unmasked renderer :Apple GPU\n///\n/// If we detect safari or `webkitGTKs` returns true.\n///\n/// This function used to avoid displaying linear color with `sRGB` supported systems.\nfn is_safari_and_webkit_gtk(gl: &web_sys::WebGlRenderingContext) -> bool {\n    // This call produces a warning in Firefox (\"WEBGL_debug_renderer_info is deprecated in Firefox and will be removed.\")\n    // but unless we call it we get errors in Chrome when we call `get_parameter` below.\n    // TODO(emilk): do something smart based on user agent?\n    if gl\n        .get_extension(\"WEBGL_debug_renderer_info\")\n        .unwrap()\n        .is_some()\n        && let Ok(renderer) =\n            gl.get_parameter(web_sys::WebglDebugRendererInfo::UNMASKED_RENDERER_WEBGL)\n        && let Some(renderer) = renderer.as_string()\n        && renderer.contains(\"Apple\")\n    {\n        true\n    } else {\n        false\n    }\n}\n"
  },
  {
    "path": "crates/eframe/src/web/web_painter_wgpu.rs",
    "content": "use std::sync::Arc;\n\nuse egui::{Event, UserData, ViewportId};\nuse egui_wgpu::{\n    RenderState, SurfaceErrorAction,\n    capture::{CaptureReceiver, CaptureSender, CaptureState, capture_channel},\n};\nuse wasm_bindgen::JsValue;\nuse web_sys::HtmlCanvasElement;\n\nuse super::web_painter::WebPainter;\n\npub(crate) struct WebPainterWgpu {\n    canvas: HtmlCanvasElement,\n    surface: wgpu::Surface<'static>,\n    surface_configuration: wgpu::SurfaceConfiguration,\n    render_state: Option<RenderState>,\n    on_surface_error: Arc<dyn Fn(wgpu::SurfaceError) -> SurfaceErrorAction>,\n    depth_stencil_format: Option<wgpu::TextureFormat>,\n    depth_texture_view: Option<wgpu::TextureView>,\n    screen_capture_state: Option<CaptureState>,\n    capture_tx: CaptureSender,\n    capture_rx: CaptureReceiver,\n    ctx: egui::Context,\n}\n\nimpl WebPainterWgpu {\n    pub fn render_state(&self) -> Option<RenderState> {\n        self.render_state.clone()\n    }\n\n    pub fn generate_depth_texture_view(\n        &self,\n        render_state: &RenderState,\n        width_in_pixels: u32,\n        height_in_pixels: u32,\n    ) -> Option<wgpu::TextureView> {\n        let device = &render_state.device;\n        self.depth_stencil_format.map(|depth_stencil_format| {\n            device\n                .create_texture(&wgpu::TextureDescriptor {\n                    label: Some(\"egui_depth_texture\"),\n                    size: wgpu::Extent3d {\n                        width: width_in_pixels,\n                        height: height_in_pixels,\n                        depth_or_array_layers: 1,\n                    },\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    dimension: wgpu::TextureDimension::D2,\n                    format: depth_stencil_format,\n                    usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n                    view_formats: &[depth_stencil_format],\n                })\n                .create_view(&wgpu::TextureViewDescriptor::default())\n        })\n    }\n\n    pub async fn new(\n        ctx: egui::Context,\n        canvas: web_sys::HtmlCanvasElement,\n        options: &crate::WebOptions,\n    ) -> Result<Self, String> {\n        log::debug!(\"Creating wgpu painter\");\n\n        let instance = options.wgpu_options.wgpu_setup.new_instance().await;\n        let surface = instance\n            .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))\n            .map_err(|err| format!(\"failed to create wgpu surface: {err}\"))?;\n\n        let depth_stencil_format = egui_wgpu::depth_format_from_bits(options.depth_buffer, 0);\n\n        let render_state = RenderState::create(\n            &options.wgpu_options,\n            &instance,\n            Some(&surface),\n            egui_wgpu::RendererOptions {\n                dithering: options.dithering,\n                depth_stencil_format,\n                ..Default::default()\n            },\n        )\n        .await\n        .map_err(|err| err.to_string())?;\n\n        let default_configuration = surface\n            .get_default_config(&render_state.adapter, 0, 0) // Width/height is set later.\n            .ok_or(\"The surface isn't supported by this adapter\")?;\n\n        let surface_configuration = wgpu::SurfaceConfiguration {\n            format: render_state.target_format,\n            present_mode: options.wgpu_options.present_mode,\n            view_formats: vec![render_state.target_format],\n            ..default_configuration\n        };\n\n        log::debug!(\"wgpu painter initialized.\");\n\n        let (capture_tx, capture_rx) = capture_channel();\n\n        Ok(Self {\n            canvas,\n            render_state: Some(render_state),\n            surface,\n            surface_configuration,\n            depth_stencil_format,\n            depth_texture_view: None,\n            on_surface_error: Arc::clone(&options.wgpu_options.on_surface_error) as _,\n            screen_capture_state: None,\n            capture_tx,\n            capture_rx,\n            ctx,\n        })\n    }\n}\n\nimpl WebPainter for WebPainterWgpu {\n    fn canvas(&self) -> &HtmlCanvasElement {\n        &self.canvas\n    }\n\n    fn max_texture_side(&self) -> usize {\n        self.render_state.as_ref().map_or(0, |state| {\n            state.device.limits().max_texture_dimension_2d as _\n        })\n    }\n\n    fn paint_and_update_textures(\n        &mut self,\n        clear_color: [f32; 4],\n        clipped_primitives: &[egui::ClippedPrimitive],\n        pixels_per_point: f32,\n        textures_delta: &egui::TexturesDelta,\n        capture_data: Vec<UserData>,\n    ) -> Result<(), JsValue> {\n        let capture = !capture_data.is_empty();\n\n        let size_in_pixels = [self.canvas.width(), self.canvas.height()];\n\n        let Some(render_state) = &self.render_state else {\n            return Err(JsValue::from_str(\n                \"Can't paint, wgpu renderer was already disposed\",\n            ));\n        };\n\n        let mut encoder =\n            render_state\n                .device\n                .create_command_encoder(&wgpu::CommandEncoderDescriptor {\n                    label: Some(\"egui_webpainter_paint_and_update_textures\"),\n                });\n\n        // Upload all resources for the GPU.\n        let screen_descriptor = egui_wgpu::ScreenDescriptor {\n            size_in_pixels,\n            pixels_per_point,\n        };\n\n        let user_cmd_bufs = {\n            let mut renderer = render_state.renderer.write();\n            for (id, image_delta) in &textures_delta.set {\n                renderer.update_texture(\n                    &render_state.device,\n                    &render_state.queue,\n                    *id,\n                    image_delta,\n                );\n            }\n\n            renderer.update_buffers(\n                &render_state.device,\n                &render_state.queue,\n                &mut encoder,\n                clipped_primitives,\n                &screen_descriptor,\n            )\n        };\n\n        // Resize surface if needed\n        let is_zero_sized_surface = size_in_pixels[0] == 0 || size_in_pixels[1] == 0;\n        let frame_and_capture_buffer = if is_zero_sized_surface {\n            None\n        } else {\n            if size_in_pixels[0] != self.surface_configuration.width\n                || size_in_pixels[1] != self.surface_configuration.height\n            {\n                self.surface_configuration.width = size_in_pixels[0];\n                self.surface_configuration.height = size_in_pixels[1];\n                self.surface\n                    .configure(&render_state.device, &self.surface_configuration);\n                self.depth_texture_view = self.generate_depth_texture_view(\n                    render_state,\n                    size_in_pixels[0],\n                    size_in_pixels[1],\n                );\n            }\n\n            let output_frame = match self.surface.get_current_texture() {\n                Ok(frame) => frame,\n                Err(err) => match (*self.on_surface_error)(err) {\n                    SurfaceErrorAction::RecreateSurface => {\n                        self.surface\n                            .configure(&render_state.device, &self.surface_configuration);\n                        return Ok(());\n                    }\n                    SurfaceErrorAction::SkipFrame => {\n                        return Ok(());\n                    }\n                },\n            };\n\n            {\n                let renderer = render_state.renderer.read();\n\n                let target_texture = if capture {\n                    let capture_state = self.screen_capture_state.get_or_insert_with(|| {\n                        CaptureState::new(&render_state.device, &output_frame.texture)\n                    });\n                    capture_state.update(&render_state.device, &output_frame.texture);\n\n                    &capture_state.texture\n                } else {\n                    &output_frame.texture\n                };\n                let target_view =\n                    target_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n                let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                    color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                        view: &target_view,\n                        resolve_target: None,\n                        ops: wgpu::Operations {\n                            load: wgpu::LoadOp::Clear(wgpu::Color {\n                                r: clear_color[0] as f64,\n                                g: clear_color[1] as f64,\n                                b: clear_color[2] as f64,\n                                a: clear_color[3] as f64,\n                            }),\n                            store: wgpu::StoreOp::Store,\n                        },\n                        depth_slice: None,\n                    })],\n                    depth_stencil_attachment: self.depth_texture_view.as_ref().map(|view| {\n                        wgpu::RenderPassDepthStencilAttachment {\n                            view,\n                            depth_ops: self\n                                .depth_stencil_format\n                                .is_some_and(|depth_stencil_format| {\n                                    depth_stencil_format.has_depth_aspect()\n                                })\n                                .then_some(wgpu::Operations {\n                                    load: wgpu::LoadOp::Clear(1.0),\n                                    // It is very unlikely that the depth buffer is needed after egui finished rendering\n                                    // so no need to store it. (this can improve performance on tiling GPUs like mobile chips or Apple Silicon)\n                                    store: wgpu::StoreOp::Discard,\n                                }),\n                            stencil_ops: self\n                                .depth_stencil_format\n                                .is_some_and(|depth_stencil_format| {\n                                    depth_stencil_format.has_stencil_aspect()\n                                })\n                                .then_some(wgpu::Operations {\n                                    load: wgpu::LoadOp::Clear(0),\n                                    store: wgpu::StoreOp::Discard,\n                                }),\n                        }\n                    }),\n                    label: Some(\"egui_render\"),\n                    occlusion_query_set: None,\n                    timestamp_writes: None,\n                    multiview_mask: None,\n                });\n\n                // Forgetting the pass' lifetime means that we are no longer compile-time protected from\n                // runtime errors caused by accessing the parent encoder before the render pass is dropped.\n                // Since we don't pass it on to the renderer, we should be perfectly safe against this mistake here!\n                renderer.render(\n                    &mut render_pass.forget_lifetime(),\n                    clipped_primitives,\n                    &screen_descriptor,\n                );\n            }\n\n            let capture_buffer = if capture\n                && let Some(capture_state) = &mut self.screen_capture_state\n            {\n                Some(capture_state.copy_textures(&render_state.device, &output_frame, &mut encoder))\n            } else {\n                None\n            };\n\n            Some((output_frame, capture_buffer))\n        };\n\n        // Submit the commands: both the main buffer and user-defined ones.\n        render_state\n            .queue\n            .submit(user_cmd_bufs.into_iter().chain([encoder.finish()]));\n\n        if let Some((frame, capture_buffer)) = frame_and_capture_buffer {\n            if let Some(capture_buffer) = capture_buffer\n                && let Some(capture_state) = &self.screen_capture_state\n            {\n                capture_state.read_screen_rgba(\n                    self.ctx.clone(),\n                    capture_buffer,\n                    capture_data,\n                    self.capture_tx.clone(),\n                    ViewportId::ROOT,\n                );\n            }\n\n            frame.present();\n        }\n\n        // Free textures marked for destruction **after** queue submit since they might still be used in the current frame.\n        // Calling `wgpu::Texture::destroy` on a texture that is still in use would invalidate the command buffer(s) it is used in.\n        // However, once we called `wgpu::Queue::submit`, it is up for wgpu to determine how long the underlying gpu resource has to live.\n        {\n            let mut renderer = render_state.renderer.write();\n            for id in &textures_delta.free {\n                renderer.free_texture(id);\n            }\n        }\n\n        Ok(())\n    }\n\n    fn handle_screenshots(&mut self, events: &mut Vec<Event>) {\n        for (viewport_id, user_data, screenshot) in self.capture_rx.try_iter() {\n            let screenshot = Arc::new(screenshot);\n            for data in user_data {\n                events.push(Event::Screenshot {\n                    viewport_id,\n                    user_data: data,\n                    image: Arc::clone(&screenshot),\n                });\n            }\n        }\n    }\n\n    fn destroy(&mut self) {\n        self.render_state = None;\n    }\n}\n"
  },
  {
    "path": "crates/eframe/src/web/web_runner.rs",
    "content": "use std::{cell::RefCell, rc::Rc};\n\nuse wasm_bindgen::prelude::*;\n\nuse crate::{App, epi};\n\nuse super::{\n    AppRunner, PanicHandler,\n    events::{self, ResizeObserverContext},\n    text_agent::TextAgent,\n};\n\n/// This is how `eframe` runs your web application\n///\n/// This is cheap to clone.\n///\n/// See [the crate level docs](crate) for an example.\n#[derive(Clone)]\npub struct WebRunner {\n    /// Have we ever panicked?\n    panic_handler: PanicHandler,\n\n    /// If we ever panic during running, this `RefCell` is poisoned.\n    /// So before we use it, we need to check [`Self::panic_handler`].\n    app_runner: Rc<RefCell<Option<AppRunner>>>,\n\n    /// In case of a panic, unsubscribe these.\n    /// They have to be in a separate `Rc` so that we don't need to pass them to\n    /// the panic handler, since they aren't `Send`.\n    events_to_unsubscribe: Rc<RefCell<Vec<EventToUnsubscribe>>>,\n\n    /// Current animation frame in flight.\n    frame: Rc<RefCell<Option<AnimationFrameRequest>>>,\n\n    resize_observer: Rc<RefCell<Option<ResizeObserverContext>>>,\n}\n\nimpl WebRunner {\n    /// Will install a panic handler that will catch and log any panics\n    #[expect(clippy::new_without_default)]\n    pub fn new() -> Self {\n        let panic_handler = PanicHandler::install();\n\n        Self {\n            panic_handler,\n            app_runner: Rc::new(RefCell::new(None)),\n            events_to_unsubscribe: Rc::new(RefCell::new(Default::default())),\n            frame: Default::default(),\n            resize_observer: Default::default(),\n        }\n    }\n\n    /// Create the application, install callbacks, and start running the app.\n    ///\n    /// # Errors\n    /// Failing to initialize graphics, or failure to create app.\n    pub async fn start(\n        &self,\n        canvas: web_sys::HtmlCanvasElement,\n        web_options: crate::WebOptions,\n        app_creator: epi::AppCreator<'static>,\n    ) -> Result<(), JsValue> {\n        self.destroy();\n\n        {\n            // Make sure the canvas can be given focus.\n            // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex\n            canvas.set_tab_index(0);\n\n            // Don't outline the canvas when it has focus:\n            canvas.style().set_property(\"outline\", \"none\")?;\n        }\n\n        {\n            // First set up the app runner:\n            let text_agent = TextAgent::attach(self, canvas.get_root_node())?;\n            let app_runner =\n                AppRunner::new(canvas.clone(), web_options, app_creator, text_agent).await?;\n            self.app_runner.replace(Some(app_runner));\n        }\n\n        {\n            let resize_observer = events::ResizeObserverContext::new(self)?;\n\n            // Properly size the canvas. Will also call `self.request_animation_frame()` (eventually)\n            resize_observer.observe(&canvas);\n\n            self.resize_observer.replace(Some(resize_observer));\n        }\n\n        events::install_event_handlers(self)?;\n\n        log::info!(\"event handlers installed.\");\n\n        Ok(())\n    }\n\n    /// Has there been a panic?\n    pub fn has_panicked(&self) -> bool {\n        self.panic_handler.has_panicked()\n    }\n\n    /// What was the panic message and callstack?\n    pub fn panic_summary(&self) -> Option<super::PanicSummary> {\n        self.panic_handler.panic_summary()\n    }\n\n    fn unsubscribe_from_all_events(&self) {\n        let events_to_unsubscribe: Vec<_> =\n            std::mem::take(&mut *self.events_to_unsubscribe.borrow_mut());\n\n        if !events_to_unsubscribe.is_empty() {\n            log::debug!(\"Unsubscribing from {} events\", events_to_unsubscribe.len());\n            for x in events_to_unsubscribe {\n                if let Err(err) = x.unsubscribe() {\n                    log::warn!(\n                        \"Failed to unsubscribe from event: {}\",\n                        super::string_from_js_value(&err)\n                    );\n                }\n            }\n        }\n\n        self.resize_observer.replace(None);\n    }\n\n    /// Shut down eframe and clean up resources.\n    pub fn destroy(&self) {\n        self.unsubscribe_from_all_events();\n\n        if let Some(frame) = self.frame.take() {\n            let window = web_sys::window().unwrap();\n            window.cancel_animation_frame(frame.id).ok();\n        }\n\n        if let Some(runner) = self.app_runner.replace(None) {\n            runner.destroy();\n        }\n    }\n\n    /// Returns `None` if there has been a panic, or if we have been destroyed.\n    /// In that case, just return to JS.\n    pub(crate) fn try_lock(&self) -> Option<std::cell::RefMut<'_, AppRunner>> {\n        if self.panic_handler.has_panicked() {\n            // Unsubscribe from all events so that we don't get any more callbacks\n            // that will try to access the poisoned runner.\n            self.unsubscribe_from_all_events();\n            None\n        } else {\n            let lock = self.app_runner.try_borrow_mut().ok()?;\n            std::cell::RefMut::filter_map(lock, |lock| -> Option<&mut AppRunner> { lock.as_mut() })\n                .ok()\n        }\n    }\n\n    /// Get mutable access to the concrete [`App`] we enclose.\n    ///\n    /// This will panic if your app does not implement [`App::as_any_mut`],\n    /// and return `None` if this  runner has panicked.\n    pub fn app_mut<ConcreteApp: 'static + App>(\n        &self,\n    ) -> Option<std::cell::RefMut<'_, ConcreteApp>> {\n        self.try_lock()\n            .map(|lock| std::cell::RefMut::map(lock, |runner| runner.app_mut::<ConcreteApp>()))\n    }\n\n    /// Convenience function to reduce boilerplate and ensure that all event handlers\n    /// are dealt with in the same way.\n    ///\n    /// All events added with this method will automatically be unsubscribed on panic,\n    /// or when [`Self::destroy`] is called.\n    pub fn add_event_listener<E: wasm_bindgen::JsCast>(\n        &self,\n        target: &web_sys::EventTarget,\n        event_name: &'static str,\n        mut closure: impl FnMut(E, &mut AppRunner) + 'static,\n    ) -> Result<(), wasm_bindgen::JsValue> {\n        let options = web_sys::AddEventListenerOptions::default();\n        self.add_event_listener_ex(\n            target,\n            event_name,\n            &options,\n            move |event, app_runner, _web_runner| closure(event, app_runner),\n        )\n    }\n\n    /// Convenience function to reduce boilerplate and ensure that all event handlers\n    /// are dealt with in the same way.\n    ///\n    /// All events added with this method will automatically be unsubscribed on panic,\n    /// or when [`Self::destroy`] is called.\n    pub fn add_event_listener_ex<E: wasm_bindgen::JsCast>(\n        &self,\n        target: &web_sys::EventTarget,\n        event_name: &'static str,\n        options: &web_sys::AddEventListenerOptions,\n        mut closure: impl FnMut(E, &mut AppRunner, &Self) + 'static,\n    ) -> Result<(), wasm_bindgen::JsValue> {\n        let web_runner = self.clone();\n\n        // Create a JS closure based on the FnMut provided\n        let closure = Closure::wrap(Box::new(move |event: web_sys::Event| {\n            // Only call the wrapped closure if the egui code has not panicked\n            if let Some(mut runner_lock) = web_runner.try_lock() {\n                // Cast the event to the expected event type\n                let event = event.unchecked_into::<E>();\n                closure(event, &mut runner_lock, &web_runner);\n            }\n        }) as Box<dyn FnMut(web_sys::Event)>);\n\n        // Add the event listener to the target\n        target.add_event_listener_with_callback_and_add_event_listener_options(\n            event_name,\n            closure.as_ref().unchecked_ref(),\n            options,\n        )?;\n\n        let handle = TargetEvent {\n            target: target.clone(),\n            event_name: event_name.to_owned(),\n            closure,\n        };\n\n        // Remember it so we unsubscribe on panic.\n        // Otherwise we get calls into `self.runner` after it has been poisoned by a panic.\n        self.events_to_unsubscribe\n            .borrow_mut()\n            .push(EventToUnsubscribe::TargetEvent(handle));\n\n        Ok(())\n    }\n\n    /// Request an animation frame from the browser in which we can perform a paint.\n    ///\n    /// It is safe to call `request_animation_frame` multiple times in quick succession,\n    /// this function guarantees that only one animation frame is scheduled at a time.\n    pub(crate) fn request_animation_frame(&self) -> Result<(), wasm_bindgen::JsValue> {\n        if self.frame.borrow().is_some() {\n            // there is already an animation frame in flight\n            return Ok(());\n        }\n\n        let window = web_sys::window().unwrap();\n        let closure = Closure::once({\n            let web_runner = self.clone();\n            move || {\n                // We can paint now, so clear the animation frame.\n                // This drops the `closure` and allows another\n                // animation frame to be scheduled\n                let _ = web_runner.frame.take();\n                events::paint_and_schedule(&web_runner)\n            }\n        });\n\n        let id = window.request_animation_frame(closure.as_ref().unchecked_ref())?;\n        self.frame.borrow_mut().replace(AnimationFrameRequest {\n            id,\n            _closure: closure,\n        });\n\n        Ok(())\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n// https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html#using-fnonce-and-closureonce-with-requestanimationframe\nstruct AnimationFrameRequest {\n    /// Represents the ID of a frame in flight.\n    id: i32,\n\n    /// The callback given to `request_animation_frame`, stored here both to prevent it\n    /// from being canceled, and from having to `.forget()` it.\n    _closure: Closure<dyn FnMut() -> Result<(), JsValue>>,\n}\n\nstruct TargetEvent {\n    target: web_sys::EventTarget,\n    event_name: String,\n    closure: Closure<dyn FnMut(web_sys::Event)>,\n}\n\n#[expect(unused)]\nstruct IntervalHandle {\n    handle: i32,\n    closure: Closure<dyn FnMut()>,\n}\n\nenum EventToUnsubscribe {\n    TargetEvent(TargetEvent),\n\n    #[expect(unused)]\n    IntervalHandle(IntervalHandle),\n}\n\nimpl EventToUnsubscribe {\n    pub fn unsubscribe(self) -> Result<(), JsValue> {\n        match self {\n            Self::TargetEvent(handle) => {\n                handle.target.remove_event_listener_with_callback(\n                    handle.event_name.as_str(),\n                    handle.closure.as_ref().unchecked_ref(),\n                )?;\n                Ok(())\n            }\n            Self::IntervalHandle(handle) => {\n                let window = web_sys::window().unwrap();\n                window.clear_interval_with_handle(handle.handle);\n                Ok(())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/Cargo.toml",
    "content": "[package]\nname = \"egui\"\nversion.workspace = true\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\ndescription = \"An easy-to-use immediate mode GUI that runs on both web and native\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui\"\nlicense.workspace = true\nreadme = \"../../README.md\"\nrepository = \"https://github.com/emilk/egui\"\ncategories = [\"gui\", \"game-development\"]\nkeywords = [\"gui\", \"imgui\", \"immediate\", \"portable\", \"gamedev\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"Cargo.toml\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[lib]\n\n\n[features]\ndefault = [\"default_fonts\"]\n\n## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`epaint::Vertex`], [`emath::Vec2`] etc to `&[u8]`.\nbytemuck = [\"epaint/bytemuck\"]\n\n## Show a debug-ui on hover including the stacktrace to the hovered item.\n## This is very useful in finding the code that creates a part of the UI.\n## Does not work on web.\ncallstack = [\"dep:backtrace\"]\n\n## [`cint`](https://docs.rs/cint) enables interoperability with other color libraries.\ncint = [\"epaint/cint\"]\n\n## Enable the [`hex_color`] macro.\ncolor-hex = [\"epaint/color-hex\"]\n\n## If set, egui will use `include_bytes!` to bundle some fonts.\n## If you plan on specifying your own fonts you may disable this feature.\ndefault_fonts = [\"epaint/default_fonts\"]\n\n## [`mint`](https://docs.rs/mint) enables interoperability with other math libraries such as [`glam`](https://docs.rs/glam) and [`nalgebra`](https://docs.rs/nalgebra).\nmint = [\"epaint/mint\"]\n\n## Enable persistence of memory (window positions etc).\npersistence = [\"serde\", \"epaint/serde\", \"ron\"]\n\n\n## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon).\n##\n## This can help performance for graphics-intense applications.\nrayon = [\"epaint/rayon\"]\n\n## Allow serialization using [`serde`](https://docs.rs/serde).\nserde = [\"dep:serde\", \"epaint/serde\", \"accesskit/serde\"]\n\n## Change Vertex layout to be compatible with unity\nunity = [\"epaint/unity\"]\n\n## Override and disable the unity feature\n## This exists, so that when testing with --all-features, snapshots render correctly.\n_override_unity = [\"epaint/_override_unity\"]\n\n\n[dependencies]\nemath = { workspace = true, default-features = false }\nepaint = { workspace = true, default-features = false }\n\naccesskit.workspace = true\nahash.workspace = true\nbitflags.workspace = true\nlog.workspace = true\nnohash-hasher.workspace = true\nprofiling.workspace = true\nsmallvec.workspace = true\nunicode-segmentation.workspace = true\n\n#! ### Optional dependencies\n\nbacktrace = { workspace = true, optional = true }\n\n## Enable this when generating docs.\ndocument-features = { workspace = true, optional = true }\n\nron = { workspace = true, optional = true }\nserde = { workspace = true, optional = true, features = [\"derive\", \"rc\"] }\n"
  },
  {
    "path": "crates/egui/README.md",
    "content": "# GUI implementation\nThis is the core library crate egui. It is fully platform independent without any backend. You give the egui library input each frame (mouse pos etc), and it outputs a triangle mesh for you to paint.\n"
  },
  {
    "path": "crates/egui/examples/README.md",
    "content": "There are no stand-alone egui examples, because egui is not stand-alone!\n\nSee the top-level [examples](https://github.com/emilk/egui/tree/main/examples/) folder instead.\n\nThere are also plenty of examples in [the online demo](https://www.egui.rs/#demo). You can find the source code for it at <https://github.com/emilk/egui/tree/main/crates/egui_demo_lib>.\n\nTo learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!\n"
  },
  {
    "path": "crates/egui/src/animation_manager.rs",
    "content": "use crate::{\n    Id, IdMap, InputState,\n    emath::{NumExt as _, remap_clamp},\n};\n\n#[derive(Clone, Default)]\npub(crate) struct AnimationManager {\n    bools: IdMap<BoolAnim>,\n    values: IdMap<ValueAnim>,\n}\n\n#[derive(Clone, Debug)]\nstruct BoolAnim {\n    last_value: f32,\n    last_tick: f64,\n}\n\n#[derive(Clone, Debug)]\nstruct ValueAnim {\n    from_value: f32,\n\n    to_value: f32,\n\n    /// when did `value` last toggle?\n    toggle_time: f64,\n}\n\nimpl AnimationManager {\n    /// See [`crate::Context::animate_bool`] for documentation\n    pub fn animate_bool(\n        &mut self,\n        input: &InputState,\n        animation_time: f32,\n        id: Id,\n        value: bool,\n    ) -> f32 {\n        let (start, end) = if value { (0.0, 1.0) } else { (1.0, 0.0) };\n        match self.bools.get_mut(&id) {\n            None => {\n                self.bools.insert(\n                    id,\n                    BoolAnim {\n                        last_value: end,\n                        last_tick: input.time - input.stable_dt as f64,\n                    },\n                );\n                end\n            }\n            Some(anim) => {\n                let BoolAnim {\n                    last_value,\n                    last_tick,\n                } = anim;\n                let current_time = input.time;\n                let elapsed = ((current_time - *last_tick) as f32).at_most(input.stable_dt);\n                let new_value = *last_value + (end - start) * elapsed / animation_time;\n                *last_value = if new_value.is_finite() {\n                    new_value.clamp(0.0, 1.0)\n                } else {\n                    end\n                };\n                *last_tick = current_time;\n                *last_value\n            }\n        }\n    }\n\n    pub fn animate_value(\n        &mut self,\n        input: &InputState,\n        animation_time: f32,\n        id: Id,\n        value: f32,\n    ) -> f32 {\n        match self.values.get_mut(&id) {\n            None => {\n                self.values.insert(\n                    id,\n                    ValueAnim {\n                        from_value: value,\n                        to_value: value,\n                        toggle_time: -f64::INFINITY, // long time ago\n                    },\n                );\n                value\n            }\n            Some(anim) => {\n                let time_since_toggle = (input.time - anim.toggle_time) as f32;\n                // On the frame we toggle we don't want to return the old value,\n                // so we extrapolate forwards by half a frame:\n                let time_since_toggle = time_since_toggle + input.predicted_dt / 2.0;\n                let current_value = remap_clamp(\n                    time_since_toggle,\n                    0.0..=animation_time,\n                    anim.from_value..=anim.to_value,\n                );\n                if anim.to_value != value {\n                    anim.from_value = current_value; //start new animation from current position of playing animation\n                    anim.to_value = value;\n                    anim.toggle_time = input.time;\n                }\n                if animation_time == 0.0 {\n                    anim.from_value = value;\n                    anim.to_value = value;\n                }\n                current_value\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/atomics/atom.rs",
    "content": "use crate::{AtomKind, FontSelection, Id, IntoSizedArgs, IntoSizedResult, SizedAtom, Ui};\nuse emath::{Align2, NumExt as _, Vec2};\nuse epaint::text::TextWrapMode;\n\n/// A low-level ui building block.\n///\n/// Implements [`From`] for [`String`], [`str`], [`crate::Image`] and much more for convenience.\n/// You can directly call the `atom_*` methods on anything that implements `Into<Atom>`.\n/// ```\n/// # use egui::{Image, emath::Vec2};\n/// use egui::AtomExt as _;\n/// let string_atom = \"Hello\".atom_grow(true);\n/// let image_atom = Image::new(\"some_image_url\").atom_size(Vec2::splat(20.0));\n/// ```\n#[derive(Clone, Debug)]\npub struct Atom<'a> {\n    /// See [`crate::AtomExt::atom_id`]\n    pub id: Option<Id>,\n\n    /// See [`crate::AtomExt::atom_size`]\n    pub size: Option<Vec2>,\n\n    /// See [`crate::AtomExt::atom_max_size`]\n    pub max_size: Vec2,\n\n    /// See [`crate::AtomExt::atom_grow`]\n    pub grow: bool,\n\n    /// See [`crate::AtomExt::atom_shrink`]\n    pub shrink: bool,\n\n    /// See [`crate::AtomExt::atom_align`]\n    pub align: Align2,\n\n    /// The atom type / content\n    pub kind: AtomKind<'a>,\n}\n\nimpl Default for Atom<'_> {\n    fn default() -> Self {\n        Atom {\n            id: None,\n            size: None,\n            max_size: Vec2::INFINITY,\n            grow: false,\n            shrink: false,\n            align: Align2::CENTER_CENTER,\n            kind: AtomKind::Empty,\n        }\n    }\n}\n\nimpl<'a> Atom<'a> {\n    /// Create an empty [`Atom`] marked as `grow`.\n    ///\n    /// This will expand in size, allowing all preceding atoms to be left-aligned,\n    /// and all following atoms to be right-aligned\n    pub fn grow() -> Self {\n        Atom {\n            grow: true,\n            ..Default::default()\n        }\n    }\n\n    /// Create an [`AtomKind::Empty`] with a specific size.\n    ///\n    /// Example:\n    /// ```\n    /// # use egui::{AtomExt, AtomKind, Atom, Button, Id, __run_test_ui};\n    /// # use emath::Vec2;\n    /// # __run_test_ui(|ui| {\n    /// let id = Id::new(\"my_button\");\n    /// let response = Button::new((\"Hi!\", Atom::custom(id, Vec2::splat(18.0)))).atom_ui(ui);\n    ///\n    /// let rect = response.rect(id);\n    /// if let Some(rect) = rect {\n    ///     ui.place(rect, Button::new(\"⏵\"));\n    /// }\n    /// # });\n    /// ```\n    pub fn custom(id: Id, size: impl Into<Vec2>) -> Self {\n        Atom {\n            size: Some(size.into()),\n            kind: AtomKind::Empty,\n            id: Some(id),\n            ..Default::default()\n        }\n    }\n\n    /// Turn this into a [`SizedAtom`].\n    pub fn into_sized(\n        self,\n        ui: &Ui,\n        mut available_size: Vec2,\n        mut wrap_mode: Option<TextWrapMode>,\n        fallback_font: FontSelection,\n    ) -> SizedAtom<'a> {\n        if !self.shrink && self.max_size.x.is_infinite() {\n            wrap_mode = Some(TextWrapMode::Extend);\n        }\n        available_size = available_size.at_most(self.max_size);\n        if let Some(size) = self.size {\n            available_size = available_size.at_most(size);\n        }\n        if self.max_size.x.is_finite() {\n            wrap_mode = Some(TextWrapMode::Truncate);\n        }\n\n        let id = self.id;\n\n        let wrap_mode = wrap_mode.unwrap_or_else(|| ui.wrap_mode());\n        let IntoSizedResult {\n            intrinsic_size,\n            sized,\n        } = self.kind.into_sized(\n            ui,\n            IntoSizedArgs {\n                available_size,\n                wrap_mode,\n                fallback_font,\n            },\n        );\n\n        let size = self\n            .size\n            .map_or_else(|| sized.size(), |s| s.at_most(self.max_size));\n\n        SizedAtom {\n            id,\n            size,\n            intrinsic_size: intrinsic_size.at_least(self.size.unwrap_or_default()),\n            grow: self.grow,\n            align: self.align,\n            kind: sized,\n        }\n    }\n}\n\nimpl<'a, T> From<T> for Atom<'a>\nwhere\n    T: Into<AtomKind<'a>>,\n{\n    fn from(value: T) -> Self {\n        Atom {\n            kind: value.into(),\n            ..Default::default()\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/atomics/atom_ext.rs",
    "content": "use crate::{Atom, FontSelection, Id, Ui};\nuse emath::Vec2;\n\n/// A trait for conveniently building [`Atom`]s.\n///\n/// The functions are prefixed with `atom_` to avoid conflicts with e.g. [`crate::RichText::size`].\npub trait AtomExt<'a> {\n    /// Set the [`Id`] for custom rendering.\n    ///\n    /// You can get the [`crate::Rect`] with the [`Id`] from [`crate::AtomLayoutResponse`] and use a\n    /// [`crate::Painter`] or [`Ui::place`] to add/draw some custom content.\n    fn atom_id(self, id: Id) -> Atom<'a>;\n\n    /// Set the atom to a fixed size.\n    ///\n    /// If [`Atom::grow`] is `true`, this will be the minimum width.\n    /// If [`Atom::shrink`] is `true`, this will be the maximum width.\n    /// If both are true, the width will have no effect.\n    ///\n    /// [`Self::atom_max_size`] will limit size.\n    ///\n    /// See [`crate::AtomKind`] docs to see how the size affects the different types.\n    fn atom_size(self, size: Vec2) -> Atom<'a>;\n\n    /// Grow this atom to the available space.\n    ///\n    /// This will affect the size of the [`Atom`] in the main direction. Since\n    /// [`crate::AtomLayout`] today only supports horizontal layout, it will affect the width.\n    ///\n    /// You can also combine this with [`Self::atom_shrink`] to make it always take exactly the\n    /// remaining space.\n    fn atom_grow(self, grow: bool) -> Atom<'a>;\n\n    /// Shrink this atom if there isn't enough space.\n    ///\n    /// This will affect the size of the [`Atom`] in the main direction. Since\n    /// [`crate::AtomLayout`] today only supports horizontal layout, it will affect the width.\n    ///\n    /// NOTE: Only a single [`Atom`] may shrink for each widget.\n    ///\n    /// If no atom was set to shrink and `wrap_mode != TextWrapMode::Extend`, the first\n    /// `AtomKind::Text` is set to shrink.\n    fn atom_shrink(self, shrink: bool) -> Atom<'a>;\n\n    /// Set the maximum size of this atom.\n    ///\n    /// Will not affect the space taken by `grow` (All atoms marked as grow will always grow\n    /// equally to fill the available space).\n    fn atom_max_size(self, max_size: Vec2) -> Atom<'a>;\n\n    /// Set the maximum width of this atom.\n    ///\n    /// Will not affect the space taken by `grow` (All atoms marked as grow will always grow\n    /// equally to fill the available space).\n    fn atom_max_width(self, max_width: f32) -> Atom<'a>;\n\n    /// Set the maximum height of this atom.\n    fn atom_max_height(self, max_height: f32) -> Atom<'a>;\n\n    /// Set the max height of this atom to match the font size.\n    ///\n    /// This is useful for e.g. limiting the height of icons in buttons.\n    fn atom_max_height_font_size(self, ui: &Ui) -> Atom<'a>\n    where\n        Self: Sized,\n    {\n        let font_selection = FontSelection::default();\n        let font_id = font_selection.resolve(ui.style());\n        let height = ui.fonts_mut(|f| f.row_height(&font_id));\n        self.atom_max_height(height)\n    }\n\n    /// Sets the [`emath::Align2`] of a single atom within its available space.\n    ///\n    /// Defaults to center-center.\n    fn atom_align(self, align: emath::Align2) -> Atom<'a>;\n}\n\nimpl<'a, T> AtomExt<'a> for T\nwhere\n    T: Into<Atom<'a>> + Sized,\n{\n    fn atom_id(self, id: Id) -> Atom<'a> {\n        let mut atom = self.into();\n        atom.id = Some(id);\n        atom\n    }\n\n    fn atom_size(self, size: Vec2) -> Atom<'a> {\n        let mut atom = self.into();\n        atom.size = Some(size);\n        atom\n    }\n\n    fn atom_grow(self, grow: bool) -> Atom<'a> {\n        let mut atom = self.into();\n        atom.grow = grow;\n        atom\n    }\n\n    fn atom_shrink(self, shrink: bool) -> Atom<'a> {\n        let mut atom = self.into();\n        atom.shrink = shrink;\n        atom\n    }\n\n    fn atom_max_size(self, max_size: Vec2) -> Atom<'a> {\n        let mut atom = self.into();\n        atom.max_size = max_size;\n        atom\n    }\n\n    fn atom_max_width(self, max_width: f32) -> Atom<'a> {\n        let mut atom = self.into();\n        atom.max_size.x = max_width;\n        atom\n    }\n\n    fn atom_max_height(self, max_height: f32) -> Atom<'a> {\n        let mut atom = self.into();\n        atom.max_size.y = max_height;\n        atom\n    }\n\n    fn atom_align(self, align: emath::Align2) -> Atom<'a> {\n        let mut atom = self.into();\n        atom.align = align;\n        atom\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/atomics/atom_kind.rs",
    "content": "use crate::{FontSelection, Image, ImageSource, SizedAtomKind, Ui, WidgetText};\nuse emath::Vec2;\nuse epaint::text::TextWrapMode;\nuse std::fmt::Debug;\n\n/// Args passed when sizing an [`super::Atom`]\npub struct IntoSizedArgs {\n    pub available_size: Vec2,\n    pub wrap_mode: TextWrapMode,\n    pub fallback_font: FontSelection,\n}\n\n/// Result returned when sizing an [`super::Atom`]\npub struct IntoSizedResult<'a> {\n    pub intrinsic_size: Vec2,\n    pub sized: SizedAtomKind<'a>,\n}\n\n/// See [`AtomKind::Closure`]\n// We need 'static in the result (or need to introduce another lifetime on the enum).\n// Otherwise, a single 'static Atom would force the closure to be 'static.\npub type AtomClosure<'a> = Box<dyn FnOnce(&Ui, IntoSizedArgs) -> IntoSizedResult<'static> + 'a>;\n\n/// The different kinds of [`crate::Atom`]s.\n#[derive(Default)]\npub enum AtomKind<'a> {\n    /// Empty, that can be used with [`crate::AtomExt::atom_grow`] to reserve space.\n    #[default]\n    Empty,\n\n    /// Text atom.\n    ///\n    /// Truncation within [`crate::AtomLayout`] works like this:\n    /// -\n    /// - if `wrap_mode` is not Extend\n    ///   - if no atom is `shrink`\n    ///     - the first text atom is selected and will be marked as `shrink`\n    ///   - the atom marked as `shrink` will shrink / wrap based on the selected wrap mode\n    ///   - any other text atoms will have `wrap_mode` extend\n    /// - if `wrap_mode` is extend, Text will extend as expected.\n    ///\n    /// Unless [`crate::AtomExt::atom_max_width`] is set, `wrap_mode` should only be set via [`crate::Style`] or\n    /// [`crate::AtomLayout::wrap_mode`], as setting a wrap mode on a [`WidgetText`] atom\n    /// that is not `shrink` will have unexpected results.\n    ///\n    /// The size is determined by converting the [`WidgetText`] into a galley and using the galleys\n    /// size. You can use [`crate::AtomExt::atom_size`] to override this, and [`crate::AtomExt::atom_max_width`]\n    /// to limit the width (Causing the text to wrap or truncate, depending on the `wrap_mode`.\n    /// [`crate::AtomExt::atom_max_height`] has no effect on text.\n    Text(WidgetText),\n\n    /// Image atom.\n    ///\n    /// By default the size is determined via [`Image::calc_size`].\n    /// You can use [`crate::AtomExt::atom_max_size`] or [`crate::AtomExt::atom_size`] to customize the size.\n    /// There is also a helper [`crate::AtomExt::atom_max_height_font_size`] to set the max height to the\n    /// default font height, which is convenient for icons.\n    Image(Image<'a>),\n\n    /// A custom closure that produces a sized atom.\n    ///\n    /// The vec2 passed in is the available size to this atom. The returned vec2 should be the\n    /// preferred / intrinsic size.\n    ///\n    /// Note: This api is experimental, expect breaking changes here.\n    /// When cloning, this will be cloned as [`AtomKind::Empty`].\n    Closure(AtomClosure<'a>),\n}\n\nimpl Clone for AtomKind<'_> {\n    fn clone(&self) -> Self {\n        match self {\n            AtomKind::Empty => AtomKind::Empty,\n            AtomKind::Text(text) => AtomKind::Text(text.clone()),\n            AtomKind::Image(image) => AtomKind::Image(image.clone()),\n            AtomKind::Closure(_) => {\n                log::warn!(\"Cannot clone atom closures\");\n                AtomKind::Empty\n            }\n        }\n    }\n}\n\nimpl Debug for AtomKind<'_> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            AtomKind::Empty => write!(f, \"AtomKind::Empty\"),\n            AtomKind::Text(text) => write!(f, \"AtomKind::Text({text:?})\"),\n            AtomKind::Image(image) => write!(f, \"AtomKind::Image({image:?})\"),\n            AtomKind::Closure(_) => write!(f, \"AtomKind::Closure(<closure>)\"),\n        }\n    }\n}\n\nimpl<'a> AtomKind<'a> {\n    /// See [`Self::Text`]\n    pub fn text(text: impl Into<WidgetText>) -> Self {\n        AtomKind::Text(text.into())\n    }\n\n    /// See [`Self::Image`]\n    pub fn image(image: impl Into<Image<'a>>) -> Self {\n        AtomKind::Image(image.into())\n    }\n\n    /// See [`Self::Closure`]\n    pub fn closure(func: impl FnOnce(&Ui, IntoSizedArgs) -> IntoSizedResult<'static> + 'a) -> Self {\n        AtomKind::Closure(Box::new(func))\n    }\n\n    /// Turn this [`AtomKind`] into a [`SizedAtomKind`].\n    ///\n    /// This converts [`WidgetText`] into [`crate::Galley`] and tries to load and size [`Image`].\n    /// The first returned argument is the preferred size.\n    pub fn into_sized(\n        self,\n        ui: &Ui,\n        IntoSizedArgs {\n            available_size,\n            wrap_mode,\n            fallback_font,\n        }: IntoSizedArgs,\n    ) -> IntoSizedResult<'a> {\n        match self {\n            AtomKind::Text(text) => {\n                let galley = text.into_galley(ui, Some(wrap_mode), available_size.x, fallback_font);\n                IntoSizedResult {\n                    intrinsic_size: galley.intrinsic_size(),\n                    sized: SizedAtomKind::Text(galley),\n                }\n            }\n            AtomKind::Image(image) => {\n                let size = image.load_and_calc_size(ui, available_size);\n                let size = size.unwrap_or(Vec2::ZERO);\n                IntoSizedResult {\n                    intrinsic_size: size,\n                    sized: SizedAtomKind::Image { image, size },\n                }\n            }\n            AtomKind::Empty => IntoSizedResult {\n                intrinsic_size: Vec2::ZERO,\n                sized: SizedAtomKind::Empty { size: None },\n            },\n            AtomKind::Closure(func) => func(\n                ui,\n                IntoSizedArgs {\n                    available_size,\n                    wrap_mode,\n                    fallback_font,\n                },\n            ),\n        }\n    }\n}\n\nimpl<'a> From<ImageSource<'a>> for AtomKind<'a> {\n    fn from(value: ImageSource<'a>) -> Self {\n        AtomKind::Image(value.into())\n    }\n}\n\nimpl<'a> From<Image<'a>> for AtomKind<'a> {\n    fn from(value: Image<'a>) -> Self {\n        AtomKind::Image(value)\n    }\n}\n\nimpl<T> From<T> for AtomKind<'_>\nwhere\n    T: Into<WidgetText>,\n{\n    fn from(value: T) -> Self {\n        AtomKind::Text(value.into())\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/atomics/atom_layout.rs",
    "content": "use crate::atomics::ATOMS_SMALL_VEC_SIZE;\nuse crate::{\n    AtomKind, Atoms, FontSelection, Frame, Id, Image, IntoAtoms, Response, Sense, SizedAtom,\n    SizedAtomKind, Ui, Widget,\n};\nuse emath::{Align2, GuiRounding as _, NumExt as _, Rect, Vec2};\nuse epaint::text::TextWrapMode;\nuse epaint::{Color32, Galley};\nuse smallvec::SmallVec;\nuse std::ops::{Deref, DerefMut};\nuse std::sync::Arc;\n\n/// Intra-widget layout utility.\n///\n/// Used to lay out and paint [`crate::Atom`]s.\n/// This is used internally by widgets like [`crate::Button`] and [`crate::Checkbox`].\n/// You can use it to make your own widgets.\n///\n/// Painting the atoms can be split in two phases:\n/// - [`AtomLayout::allocate`]\n///   - calculates sizes\n///   - converts texts to [`Galley`]s\n///   - allocates a [`Response`]\n///   - returns a [`AllocatedAtomLayout`]\n/// - [`AllocatedAtomLayout::paint`]\n///   - paints the [`Frame`]\n///   - calculates individual [`crate::Atom`] positions\n///   - paints each single atom\n///\n/// You can use this to first allocate a response and then modify, e.g., the [`Frame`] on the\n/// [`AllocatedAtomLayout`] for interaction styling.\npub struct AtomLayout<'a> {\n    id: Option<Id>,\n    pub atoms: Atoms<'a>,\n    gap: Option<f32>,\n    pub(crate) frame: Frame,\n    pub(crate) sense: Sense,\n    fallback_text_color: Option<Color32>,\n    fallback_font: Option<FontSelection>,\n    min_size: Vec2,\n    max_size: Vec2,\n    wrap_mode: Option<TextWrapMode>,\n    align2: Option<Align2>,\n}\n\nimpl Default for AtomLayout<'_> {\n    fn default() -> Self {\n        Self::new(())\n    }\n}\n\nimpl<'a> AtomLayout<'a> {\n    pub fn new(atoms: impl IntoAtoms<'a>) -> Self {\n        Self {\n            id: None,\n            atoms: atoms.into_atoms(),\n            gap: None,\n            frame: Frame::default(),\n            sense: Sense::hover(),\n            fallback_text_color: None,\n            fallback_font: None,\n            min_size: Vec2::ZERO,\n            max_size: Vec2::INFINITY,\n            wrap_mode: None,\n            align2: None,\n        }\n    }\n\n    /// Set the gap between atoms.\n    ///\n    /// Default: `Spacing::icon_spacing`\n    #[inline]\n    pub fn gap(mut self, gap: f32) -> Self {\n        self.gap = Some(gap);\n        self\n    }\n\n    /// Set the [`Frame`].\n    #[inline]\n    pub fn frame(mut self, frame: Frame) -> Self {\n        self.frame = frame;\n        self\n    }\n\n    /// Set the [`Sense`] used when allocating the [`Response`].\n    #[inline]\n    pub fn sense(mut self, sense: Sense) -> Self {\n        self.sense = sense;\n        self\n    }\n\n    /// Set the fallback (default) text color.\n    ///\n    /// Default: [`crate::Visuals::text_color`]\n    #[inline]\n    pub fn fallback_text_color(mut self, color: Color32) -> Self {\n        self.fallback_text_color = Some(color);\n        self\n    }\n\n    /// Set the fallback (default) font.\n    #[inline]\n    pub fn fallback_font(mut self, font: impl Into<FontSelection>) -> Self {\n        self.fallback_font = Some(font.into());\n        self\n    }\n\n    /// Set the minimum size of the Widget.\n    ///\n    /// This will find and expand atoms with `grow: true`.\n    /// If there are no growable atoms then everything will be left-aligned.\n    #[inline]\n    pub fn min_size(mut self, size: Vec2) -> Self {\n        self.min_size = size;\n        self\n    }\n\n    /// Set the maximum size of the Widget.\n    ///\n    /// By default, the size is limited by the available size in the [`Ui`].\n    #[inline]\n    pub fn max_size(mut self, size: Vec2) -> Self {\n        self.max_size = size;\n        self\n    }\n\n    /// Set the maximum width of the Widget.\n    ///\n    /// By default, the width is limited by the available width in the [`Ui`].\n    #[inline]\n    pub fn max_width(mut self, width: f32) -> Self {\n        self.max_size.x = width;\n        self\n    }\n\n    /// Set the maximum height of the Widget.\n    ///\n    /// By default, the height is limited by the available height in the [`Ui`].\n    #[inline]\n    pub fn max_height(mut self, height: f32) -> Self {\n        self.max_size.y = height;\n        self\n    }\n\n    /// Set the [`Id`] used to allocate a [`Response`].\n    #[inline]\n    pub fn id(mut self, id: Id) -> Self {\n        self.id = Some(id);\n        self\n    }\n\n    /// Set the [`TextWrapMode`] for the [`crate::Atom`] marked as `shrink`.\n    ///\n    /// Only a single [`crate::Atom`] may shrink. If this (or `ui.wrap_mode()`) is not\n    /// [`TextWrapMode::Extend`] and no item is set to shrink, the first (left-most)\n    /// [`AtomKind::Text`] will be set to shrink.\n    #[inline]\n    pub fn wrap_mode(mut self, wrap_mode: TextWrapMode) -> Self {\n        self.wrap_mode = Some(wrap_mode);\n        self\n    }\n\n    /// Set the [`Align2`].\n    ///\n    /// This will align the [`crate::Atom`]s within the [`Rect`] returned by [`Ui::allocate_space`].\n    ///\n    /// The default is chosen based on the [`Ui`]s [`crate::Layout`]. See\n    /// [this snapshot](https://github.com/emilk/egui/blob/master/tests/egui_tests/tests/snapshots/layout/button.png)\n    /// for info on how the [`crate::Layout`] affects the alignment.\n    #[inline]\n    pub fn align2(mut self, align2: Align2) -> Self {\n        self.align2 = Some(align2);\n        self\n    }\n\n    /// [`AtomLayout::allocate`] and [`AllocatedAtomLayout::paint`] in one go.\n    pub fn show(self, ui: &mut Ui) -> AtomLayoutResponse {\n        self.allocate(ui).paint(ui)\n    }\n\n    /// Calculate sizes, create [`Galley`]s and allocate a [`Response`].\n    ///\n    /// Use the returned [`AllocatedAtomLayout`] for painting.\n    pub fn allocate(self, ui: &mut Ui) -> AllocatedAtomLayout<'a> {\n        let Self {\n            id,\n            mut atoms,\n            gap,\n            frame,\n            sense,\n            fallback_text_color,\n            min_size,\n            mut max_size,\n            wrap_mode,\n            align2,\n            fallback_font,\n        } = self;\n\n        let fallback_font = fallback_font.unwrap_or_default();\n\n        let wrap_mode = wrap_mode.unwrap_or_else(|| ui.wrap_mode());\n\n        // If the TextWrapMode is not Extend, ensure there is some item marked as `shrink`.\n        // If none is found, mark the first text item as `shrink`.\n        if wrap_mode != TextWrapMode::Extend {\n            let any_shrink = atoms.iter().any(|a| a.shrink);\n            if !any_shrink {\n                let first_text = atoms\n                    .iter_mut()\n                    .find(|a| matches!(a.kind, AtomKind::Text(..)));\n                if let Some(atom) = first_text {\n                    atom.shrink = true; // Will make the text truncate or shrink depending on wrap_mode\n                }\n            }\n        }\n\n        let id = id.unwrap_or_else(|| ui.next_auto_id());\n\n        let fallback_text_color =\n            fallback_text_color.unwrap_or_else(|| ui.style().visuals.text_color());\n        let gap = gap.unwrap_or_else(|| ui.spacing().icon_spacing);\n\n        // max_size has no effect in justified layouts. If we'd limit the available size here,\n        // the content would be sized differently than the frame which would look weird.\n        if ui.layout().horizontal_justify() {\n            max_size.x = f32::INFINITY;\n        }\n\n        let available_size = ui.available_size().at_most(max_size);\n\n        // The size available for the content\n        let available_inner_size = available_size - frame.total_margin().sum();\n\n        let mut desired_width = 0.0;\n\n        // intrinsic width / height is the ideal size of the widget, e.g. the size where the\n        // text is not wrapped. Used to set Response::intrinsic_size.\n        let mut intrinsic_width = 0.0;\n        let mut intrinsic_height = 0.0;\n\n        let mut height: f32 = 0.0;\n\n        let mut sized_items = SmallVec::new();\n\n        let mut grow_count = 0;\n\n        let mut shrink_item = None;\n\n        let align2 = align2.unwrap_or_else(|| {\n            Align2([ui.layout().horizontal_align(), ui.layout().vertical_align()])\n        });\n\n        if atoms.len() > 1 {\n            let gap_space = gap * (atoms.len() as f32 - 1.0);\n            desired_width += gap_space;\n            intrinsic_width += gap_space;\n        }\n\n        for (idx, item) in atoms.into_iter().enumerate() {\n            if item.grow {\n                grow_count += 1;\n            }\n            if item.shrink {\n                debug_assert!(\n                    shrink_item.is_none(),\n                    \"Only one atomic may be marked as shrink. {item:?}\"\n                );\n                if shrink_item.is_none() {\n                    shrink_item = Some((idx, item));\n                    continue;\n                }\n            }\n            let sized = item.into_sized(\n                ui,\n                available_inner_size,\n                Some(wrap_mode),\n                fallback_font.clone(),\n            );\n            let size = sized.size;\n\n            desired_width += size.x;\n            intrinsic_width += sized.intrinsic_size.x;\n\n            height = height.at_least(size.y);\n            intrinsic_height = intrinsic_height.at_least(sized.intrinsic_size.y);\n\n            sized_items.push(sized);\n        }\n\n        if let Some((index, item)) = shrink_item {\n            // The `shrink` item gets the remaining space\n            let available_size_for_shrink_item = Vec2::new(\n                available_inner_size.x - desired_width,\n                available_inner_size.y,\n            );\n\n            let sized = item.into_sized(\n                ui,\n                available_size_for_shrink_item,\n                Some(wrap_mode),\n                fallback_font,\n            );\n            let size = sized.size;\n\n            desired_width += size.x;\n            intrinsic_width += sized.intrinsic_size.x;\n\n            height = height.at_least(size.y);\n            intrinsic_height = intrinsic_height.at_least(sized.intrinsic_size.y);\n\n            sized_items.insert(index, sized);\n        }\n\n        let margin = frame.total_margin();\n        let desired_size = Vec2::new(desired_width, height);\n        let frame_size = (desired_size + margin.sum()).at_least(min_size);\n\n        let (_, rect) = ui.allocate_space(frame_size);\n        let mut response = ui.interact(rect, id, sense);\n\n        response.intrinsic_size =\n            Some((Vec2::new(intrinsic_width, intrinsic_height) + margin.sum()).at_least(min_size));\n\n        AllocatedAtomLayout {\n            sized_atoms: sized_items,\n            frame,\n            fallback_text_color,\n            response,\n            grow_count,\n            desired_size,\n            align2,\n            gap,\n        }\n    }\n}\n\n/// Instructions for painting an [`AtomLayout`].\n#[derive(Clone, Debug)]\npub struct AllocatedAtomLayout<'a> {\n    pub sized_atoms: SmallVec<[SizedAtom<'a>; ATOMS_SMALL_VEC_SIZE]>,\n    pub frame: Frame,\n    pub fallback_text_color: Color32,\n    pub response: Response,\n    grow_count: usize,\n    // The size of the inner content, before any growing.\n    desired_size: Vec2,\n    align2: Align2,\n    gap: f32,\n}\n\nimpl<'atom> AllocatedAtomLayout<'atom> {\n    pub fn iter_kinds(&self) -> impl Iterator<Item = &SizedAtomKind<'atom>> {\n        self.sized_atoms.iter().map(|atom| &atom.kind)\n    }\n\n    pub fn iter_kinds_mut(&mut self) -> impl Iterator<Item = &mut SizedAtomKind<'atom>> {\n        self.sized_atoms.iter_mut().map(|atom| &mut atom.kind)\n    }\n\n    pub fn iter_images(&self) -> impl Iterator<Item = &Image<'atom>> {\n        self.iter_kinds().filter_map(|kind| {\n            if let SizedAtomKind::Image { image, size: _ } = kind {\n                Some(image)\n            } else {\n                None\n            }\n        })\n    }\n\n    pub fn iter_images_mut(&mut self) -> impl Iterator<Item = &mut Image<'atom>> {\n        self.iter_kinds_mut().filter_map(|kind| {\n            if let SizedAtomKind::Image { image, size: _ } = kind {\n                Some(image)\n            } else {\n                None\n            }\n        })\n    }\n\n    pub fn iter_texts(&self) -> impl Iterator<Item = &Arc<Galley>> + use<'atom, '_> {\n        self.iter_kinds().filter_map(|kind| {\n            if let SizedAtomKind::Text(text) = kind {\n                Some(text)\n            } else {\n                None\n            }\n        })\n    }\n\n    pub fn iter_texts_mut(&mut self) -> impl Iterator<Item = &mut Arc<Galley>> + use<'atom, '_> {\n        self.iter_kinds_mut().filter_map(|kind| {\n            if let SizedAtomKind::Text(text) = kind {\n                Some(text)\n            } else {\n                None\n            }\n        })\n    }\n\n    pub fn map_kind<F>(&mut self, mut f: F)\n    where\n        F: FnMut(SizedAtomKind<'atom>) -> SizedAtomKind<'atom>,\n    {\n        for kind in self.iter_kinds_mut() {\n            *kind = f(std::mem::take(kind));\n        }\n    }\n\n    pub fn map_images<F>(&mut self, mut f: F)\n    where\n        F: FnMut(Image<'atom>) -> Image<'atom>,\n    {\n        self.map_kind(|kind| {\n            if let SizedAtomKind::Image { image, size } = kind {\n                SizedAtomKind::Image {\n                    image: f(image),\n                    size,\n                }\n            } else {\n                kind\n            }\n        });\n    }\n\n    /// Paint the [`Frame`] and individual [`crate::Atom`]s.\n    pub fn paint(self, ui: &Ui) -> AtomLayoutResponse {\n        let Self {\n            sized_atoms,\n            frame,\n            fallback_text_color,\n            response,\n            grow_count,\n            desired_size,\n            align2,\n            gap,\n        } = self;\n\n        let inner_rect = response.rect - self.frame.total_margin();\n\n        ui.painter().add(frame.paint(inner_rect));\n\n        let width_to_fill = inner_rect.width();\n        let extra_space = f32::max(width_to_fill - desired_size.x, 0.0);\n        let grow_width = f32::max(extra_space / grow_count as f32, 0.0).floor_ui();\n\n        let aligned_rect = if grow_count > 0 {\n            align2.align_size_within_rect(Vec2::new(width_to_fill, desired_size.y), inner_rect)\n        } else {\n            align2.align_size_within_rect(desired_size, inner_rect)\n        };\n\n        let mut cursor = aligned_rect.left();\n\n        let mut response = AtomLayoutResponse::empty(response);\n\n        for sized in sized_atoms {\n            let size = sized.size;\n            // TODO(lucasmerlin): This is not ideal, since this might lead to accumulated rounding errors\n            // https://github.com/emilk/egui/pull/5830#discussion_r2079627864\n            let growth = if sized.is_grow() { grow_width } else { 0.0 };\n\n            let frame = aligned_rect\n                .with_min_x(cursor)\n                .with_max_x(cursor + size.x + growth);\n            cursor = frame.right() + gap;\n            let rect = sized.align.align_size_within_rect(size, frame);\n\n            if let Some(id) = sized.id {\n                debug_assert!(\n                    !response.custom_rects.iter().any(|(i, _)| *i == id),\n                    \"Duplicate custom id\"\n                );\n                response.custom_rects.push((id, rect));\n            }\n\n            match sized.kind {\n                SizedAtomKind::Text(galley) => {\n                    ui.painter().galley(rect.min, galley, fallback_text_color);\n                }\n                SizedAtomKind::Image { image, size: _ } => {\n                    image.paint_at(ui, rect);\n                }\n                SizedAtomKind::Empty { .. } => {}\n            }\n        }\n\n        response\n    }\n}\n\n/// Response from a [`AtomLayout::show`] or [`AllocatedAtomLayout::paint`].\n///\n/// Use [`AtomLayoutResponse::rect`] to get the response rects from [`crate::Atom::custom`].\n#[derive(Clone, Debug)]\npub struct AtomLayoutResponse {\n    pub response: Response,\n    // There should rarely be more than one custom rect.\n    custom_rects: SmallVec<[(Id, Rect); 1]>,\n}\n\nimpl AtomLayoutResponse {\n    pub fn empty(response: Response) -> Self {\n        Self {\n            response,\n            custom_rects: Default::default(),\n        }\n    }\n\n    pub fn custom_rects(&self) -> impl Iterator<Item = (Id, Rect)> + '_ {\n        self.custom_rects.iter().copied()\n    }\n\n    /// Use this together with [`crate::Atom::custom`] to add custom painting / child widgets.\n    ///\n    /// NOTE: Don't `unwrap` rects, they might be empty when the widget is not visible.\n    pub fn rect(&self, id: Id) -> Option<Rect> {\n        self.custom_rects\n            .iter()\n            .find_map(|(i, r)| if *i == id { Some(*r) } else { None })\n    }\n}\n\nimpl Widget for AtomLayout<'_> {\n    fn ui(self, ui: &mut Ui) -> Response {\n        self.show(ui).response\n    }\n}\n\nimpl<'a> Deref for AtomLayout<'a> {\n    type Target = Atoms<'a>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.atoms\n    }\n}\n\nimpl DerefMut for AtomLayout<'_> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.atoms\n    }\n}\n\nimpl<'a> Deref for AllocatedAtomLayout<'a> {\n    type Target = [SizedAtom<'a>];\n\n    fn deref(&self) -> &Self::Target {\n        &self.sized_atoms\n    }\n}\n\nimpl DerefMut for AllocatedAtomLayout<'_> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.sized_atoms\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/atomics/atoms.rs",
    "content": "use crate::{Atom, AtomKind, Image, WidgetText};\nuse smallvec::SmallVec;\nuse std::borrow::Cow;\nuse std::ops::{Deref, DerefMut};\n\n// Rarely there should be more than 2 atoms in one Widget.\n// I guess it could happen in a menu button with Image and right text...\npub(crate) const ATOMS_SMALL_VEC_SIZE: usize = 2;\n\n/// A list of [`Atom`]s.\n#[derive(Clone, Debug, Default)]\npub struct Atoms<'a>(SmallVec<[Atom<'a>; ATOMS_SMALL_VEC_SIZE]>);\n\nimpl<'a> Atoms<'a> {\n    pub fn new(atoms: impl IntoAtoms<'a>) -> Self {\n        atoms.into_atoms()\n    }\n\n    /// Insert a new [`Atom`] at the end of the list (right side).\n    pub fn push_right(&mut self, atom: impl Into<Atom<'a>>) {\n        self.0.push(atom.into());\n    }\n\n    /// Extend the list of atoms by appending more atoms to the right side.\n    ///\n    /// If you have weird lifetime issues with this, use [`Self::push_right`] in a loop instead.\n    pub fn extend_right(&mut self, atoms: Self) {\n        self.0.extend(atoms.0);\n    }\n\n    /// Insert a new [`Atom`] at the beginning of the list (left side).\n    pub fn push_left(&mut self, atom: impl Into<Atom<'a>>) {\n        self.0.insert(0, atom.into());\n    }\n\n    /// Extend the list of atoms by prepending more atoms to the left side.\n    ///\n    /// If you have weird lifetime issues with this, use [`Self::push_left`] in a loop instead.\n    pub fn extend_left(&mut self, mut atoms: Self) {\n        std::mem::swap(&mut atoms.0, &mut self.0);\n        self.0.extend(atoms.0);\n    }\n\n    /// Concatenate and return the text contents.\n    // TODO(lucasmerlin): It might not always make sense to return the concatenated text, e.g.\n    // in a submenu button there is a right text '⏵' which is now passed to the screen reader.\n    pub fn text(&self) -> Option<Cow<'_, str>> {\n        let mut string: Option<Cow<'_, str>> = None;\n        for atom in &self.0 {\n            if let AtomKind::Text(text) = &atom.kind {\n                if let Some(string) = &mut string {\n                    let string = string.to_mut();\n                    string.push(' ');\n                    string.push_str(text.text());\n                } else {\n                    string = Some(Cow::Borrowed(text.text()));\n                }\n            }\n        }\n\n        // If there is no text, try to find an image with alt text.\n        if string.is_none() {\n            string = self.iter().find_map(|a| match &a.kind {\n                AtomKind::Image(image) => image.alt_text.as_deref().map(Cow::Borrowed),\n                _ => None,\n            });\n        }\n\n        string\n    }\n\n    pub fn iter_kinds(&self) -> impl Iterator<Item = &AtomKind<'a>> {\n        self.0.iter().map(|atom| &atom.kind)\n    }\n\n    pub fn iter_kinds_mut(&mut self) -> impl Iterator<Item = &mut AtomKind<'a>> {\n        self.0.iter_mut().map(|atom| &mut atom.kind)\n    }\n\n    pub fn iter_images(&self) -> impl Iterator<Item = &Image<'a>> {\n        self.iter_kinds().filter_map(|kind| {\n            if let AtomKind::Image(image) = kind {\n                Some(image)\n            } else {\n                None\n            }\n        })\n    }\n\n    pub fn iter_images_mut(&mut self) -> impl Iterator<Item = &mut Image<'a>> {\n        self.iter_kinds_mut().filter_map(|kind| {\n            if let AtomKind::Image(image) = kind {\n                Some(image)\n            } else {\n                None\n            }\n        })\n    }\n\n    pub fn iter_texts(&self) -> impl Iterator<Item = &WidgetText> + use<'_, 'a> {\n        self.iter_kinds().filter_map(|kind| {\n            if let AtomKind::Text(text) = kind {\n                Some(text)\n            } else {\n                None\n            }\n        })\n    }\n\n    pub fn iter_texts_mut(&mut self) -> impl Iterator<Item = &mut WidgetText> + use<'a, '_> {\n        self.iter_kinds_mut().filter_map(|kind| {\n            if let AtomKind::Text(text) = kind {\n                Some(text)\n            } else {\n                None\n            }\n        })\n    }\n\n    pub fn map_atoms(&mut self, mut f: impl FnMut(Atom<'a>) -> Atom<'a>) {\n        self.iter_mut()\n            .for_each(|atom| *atom = f(std::mem::take(atom)));\n    }\n\n    pub fn map_kind<F>(&mut self, mut f: F)\n    where\n        F: FnMut(AtomKind<'a>) -> AtomKind<'a>,\n    {\n        for kind in self.iter_kinds_mut() {\n            *kind = f(std::mem::take(kind));\n        }\n    }\n\n    pub fn map_images<F>(&mut self, mut f: F)\n    where\n        F: FnMut(Image<'a>) -> Image<'a>,\n    {\n        self.map_kind(|kind| {\n            if let AtomKind::Image(image) = kind {\n                AtomKind::Image(f(image))\n            } else {\n                kind\n            }\n        });\n    }\n\n    pub fn map_texts<F>(&mut self, mut f: F)\n    where\n        F: FnMut(WidgetText) -> WidgetText,\n    {\n        self.map_kind(|kind| {\n            if let AtomKind::Text(text) = kind {\n                AtomKind::Text(f(text))\n            } else {\n                kind\n            }\n        });\n    }\n}\n\nimpl<'a> IntoIterator for Atoms<'a> {\n    type Item = Atom<'a>;\n    type IntoIter = smallvec::IntoIter<[Atom<'a>; ATOMS_SMALL_VEC_SIZE]>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.into_iter()\n    }\n}\n\n/// Helper trait to convert a tuple of atoms into [`Atoms`].\n///\n/// ```\n/// use egui::{Atoms, Image, IntoAtoms, RichText};\n/// let atoms: Atoms = (\n///     \"Some text\",\n///     RichText::new(\"Some RichText\"),\n///     Image::new(\"some_image_url\"),\n/// ).into_atoms();\n/// ```\nimpl<'a, T> IntoAtoms<'a> for T\nwhere\n    T: Into<Atom<'a>>,\n{\n    fn collect(self, atoms: &mut Atoms<'a>) {\n        atoms.push_right(self);\n    }\n}\n\n/// Trait for turning a tuple of [`Atom`]s into [`Atoms`].\npub trait IntoAtoms<'a> {\n    fn collect(self, atoms: &mut Atoms<'a>);\n\n    fn into_atoms(self) -> Atoms<'a>\n    where\n        Self: Sized,\n    {\n        let mut atoms = Atoms::default();\n        self.collect(&mut atoms);\n        atoms\n    }\n}\n\nimpl<'a> IntoAtoms<'a> for Atoms<'a> {\n    fn collect(self, atoms: &mut Self) {\n        atoms.0.extend(self.0);\n    }\n}\n\nmacro_rules! all_the_atoms {\n    ($($T:ident),*) => {\n        impl<'a, $($T),*> IntoAtoms<'a> for ($($T),*)\n        where\n            $($T: IntoAtoms<'a>),*\n        {\n            fn collect(self, _atoms: &mut Atoms<'a>) {\n                #[allow(clippy::allow_attributes, non_snake_case)]\n                let ($($T),*) = self;\n                $($T.collect(_atoms);)*\n            }\n        }\n    };\n}\n\nall_the_atoms!();\nall_the_atoms!(T0, T1);\nall_the_atoms!(T0, T1, T2);\nall_the_atoms!(T0, T1, T2, T3);\nall_the_atoms!(T0, T1, T2, T3, T4);\nall_the_atoms!(T0, T1, T2, T3, T4, T5);\n\nimpl<'a> Deref for Atoms<'a> {\n    type Target = [Atom<'a>];\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl DerefMut for Atoms<'_> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\nimpl<'a, T: Into<Atom<'a>>> From<Vec<T>> for Atoms<'a> {\n    fn from(vec: Vec<T>) -> Self {\n        Atoms(vec.into_iter().map(Into::into).collect())\n    }\n}\n\nimpl<'a, T: Into<Atom<'a>> + Clone> From<&[T]> for Atoms<'a> {\n    fn from(slice: &[T]) -> Self {\n        Atoms(slice.iter().cloned().map(Into::into).collect())\n    }\n}\n\nimpl<'a, Item: Into<Atom<'a>>> FromIterator<Item> for Atoms<'a> {\n    fn from_iter<T: IntoIterator<Item = Item>>(iter: T) -> Self {\n        Atoms(iter.into_iter().map(Into::into).collect())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::Atoms;\n\n    #[test]\n    fn collect_atoms() {\n        let _: Atoms<'_> = [\"Hello\", \"World\"].into_iter().collect();\n        let _ = Atoms::from(vec![\"Hi\"]);\n        let _ = Atoms::from([\"Hi\"].as_slice());\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/atomics/mod.rs",
    "content": "mod atom;\nmod atom_ext;\nmod atom_kind;\nmod atom_layout;\nmod atoms;\nmod sized_atom;\nmod sized_atom_kind;\n\npub use atom::*;\npub use atom_ext::*;\npub use atom_kind::*;\npub use atom_layout::*;\npub use atoms::*;\npub use sized_atom::*;\npub use sized_atom_kind::*;\n"
  },
  {
    "path": "crates/egui/src/atomics/sized_atom.rs",
    "content": "use crate::SizedAtomKind;\nuse emath::Vec2;\n\n/// A [`crate::Atom`] which has been sized.\n#[derive(Clone, Debug)]\npub struct SizedAtom<'a> {\n    pub id: Option<crate::Id>,\n\n    pub(crate) grow: bool,\n\n    /// The size of the atom.\n    ///\n    /// Used for placing this atom in [`crate::AtomLayout`], the cursor will advance by\n    /// size.x + gap.\n    pub size: Vec2,\n\n    /// Intrinsic size of the atom. This is used to calculate `Response::intrinsic_size`.\n    pub intrinsic_size: Vec2,\n\n    /// How will the atom be aligned in its available space?\n    pub align: emath::Align2,\n\n    pub kind: SizedAtomKind<'a>,\n}\n\nimpl SizedAtom<'_> {\n    /// Was this [`crate::Atom`] marked as `grow`?\n    pub fn is_grow(&self) -> bool {\n        self.grow\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/atomics/sized_atom_kind.rs",
    "content": "use crate::Image;\nuse emath::Vec2;\nuse epaint::Galley;\nuse std::sync::Arc;\n\n/// A sized [`crate::AtomKind`].\n#[derive(Clone, Debug)]\npub enum SizedAtomKind<'a> {\n    Empty { size: Option<Vec2> },\n    Text(Arc<Galley>),\n    Image { image: Image<'a>, size: Vec2 },\n}\n\nimpl Default for SizedAtomKind<'_> {\n    fn default() -> Self {\n        Self::Empty { size: None }\n    }\n}\n\nimpl SizedAtomKind<'_> {\n    /// Get the calculated size.\n    pub fn size(&self) -> Vec2 {\n        match self {\n            SizedAtomKind::Text(galley) => galley.size(),\n            SizedAtomKind::Image { image: _, size } => *size,\n            SizedAtomKind::Empty { size } => size.unwrap_or_default(),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/cache/cache_storage.rs",
    "content": "use super::CacheTrait;\n\n/// A typemap of many caches, all implemented with [`CacheTrait`].\n///\n/// You can access egui's caches via [`crate::Memory::caches`],\n/// found with [`crate::Context::memory_mut`].\n///\n/// ```\n/// use egui::cache::{CacheStorage, ComputerMut, FrameCache};\n///\n/// #[derive(Default)]\n/// struct CharCounter {}\n/// impl ComputerMut<&str, usize> for CharCounter {\n///     fn compute(&mut self, s: &str) -> usize {\n///         s.chars().count()\n///     }\n/// }\n/// type CharCountCache<'a> = FrameCache<usize, CharCounter>;\n///\n/// # let mut cache_storage = CacheStorage::default();\n/// let mut cache = cache_storage.cache::<CharCountCache<'_>>();\n/// assert_eq!(*cache.get(\"hello\"), 5);\n/// ```\n#[derive(Default)]\npub struct CacheStorage {\n    caches: ahash::HashMap<std::any::TypeId, Box<dyn CacheTrait>>,\n}\n\nimpl CacheStorage {\n    pub fn cache<Cache: CacheTrait + Default>(&mut self) -> &mut Cache {\n        let cache = self\n            .caches\n            .entry(std::any::TypeId::of::<Cache>())\n            .or_insert_with(|| Box::<Cache>::default());\n\n        #[expect(clippy::unwrap_used)]\n        (cache.as_mut() as &mut dyn std::any::Any)\n            .downcast_mut::<Cache>()\n            .unwrap()\n    }\n\n    /// Total number of cached values\n    fn num_values(&self) -> usize {\n        self.caches.values().map(|cache| cache.len()).sum()\n    }\n\n    /// Call once per frame to evict cache.\n    pub fn update(&mut self) {\n        self.caches.retain(|_, cache| {\n            cache.update();\n            cache.len() > 0\n        });\n    }\n}\n\nimpl Clone for CacheStorage {\n    fn clone(&self) -> Self {\n        // We return an empty cache that can be filled in again.\n        Self::default()\n    }\n}\n\nimpl std::fmt::Debug for CacheStorage {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(\n            f,\n            \"FrameCacheStorage[{} caches with {} elements]\",\n            self.caches.len(),\n            self.num_values()\n        )\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/cache/cache_trait.rs",
    "content": "/// A cache, storing some value for some length of time.\n#[expect(clippy::len_without_is_empty)]\npub trait CacheTrait: 'static + Send + Sync + std::any::Any {\n    /// Call once per frame to evict cache.\n    fn update(&mut self);\n\n    /// Number of values currently in the cache.\n    fn len(&self) -> usize;\n}\n"
  },
  {
    "path": "crates/egui/src/cache/frame_cache.rs",
    "content": "use super::CacheTrait;\n\n/// Something that does an expensive computation that we want to cache\n/// to save us from recomputing it each frame.\npub trait ComputerMut<Key, Value>: 'static + Send + Sync {\n    fn compute(&mut self, key: Key) -> Value;\n}\n\n/// Caches the results of a computation for one frame.\n/// If it is still used next frame, it is not recomputed.\n/// If it is not used next frame, it is evicted from the cache to save memory.\npub struct FrameCache<Value, Computer> {\n    generation: u32,\n    computer: Computer,\n    cache: nohash_hasher::IntMap<u64, (u32, Value)>,\n}\n\nimpl<Value, Computer> Default for FrameCache<Value, Computer>\nwhere\n    Computer: Default,\n{\n    fn default() -> Self {\n        Self::new(Computer::default())\n    }\n}\n\nimpl<Value, Computer> FrameCache<Value, Computer> {\n    pub fn new(computer: Computer) -> Self {\n        Self {\n            generation: 0,\n            computer,\n            cache: Default::default(),\n        }\n    }\n\n    /// Must be called once per frame to clear the cache.\n    pub fn evict_cache(&mut self) {\n        let current_generation = self.generation;\n        self.cache.retain(|_key, cached| {\n            cached.0 == current_generation // only keep those that were used this frame\n        });\n        self.generation = self.generation.wrapping_add(1);\n    }\n}\n\nimpl<Value, Computer> FrameCache<Value, Computer> {\n    /// Get from cache (if the same key was used last frame)\n    /// or recompute and store in the cache.\n    pub fn get<Key>(&mut self, key: Key) -> &Value\n    where\n        Key: Copy + std::hash::Hash,\n        Computer: ComputerMut<Key, Value>,\n    {\n        let hash = crate::util::hash(key);\n\n        match self.cache.entry(hash) {\n            std::collections::hash_map::Entry::Occupied(entry) => {\n                let cached = entry.into_mut();\n                cached.0 = self.generation;\n                &cached.1\n            }\n            std::collections::hash_map::Entry::Vacant(entry) => {\n                let value = self.computer.compute(key);\n                let inserted = entry.insert((self.generation, value));\n                &inserted.1\n            }\n        }\n    }\n}\n\nimpl<Value: 'static + Send + Sync, Computer: 'static + Send + Sync> CacheTrait\n    for FrameCache<Value, Computer>\n{\n    fn update(&mut self) {\n        self.evict_cache();\n    }\n\n    fn len(&self) -> usize {\n        self.cache.len()\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/cache/frame_publisher.rs",
    "content": "use std::hash::Hash;\n\nuse super::CacheTrait;\n\n/// Stores a key:value pair for the duration of this frame and the next.\npub struct FramePublisher<Key: Eq + Hash, Value> {\n    generation: u32,\n    cache: ahash::HashMap<Key, (u32, Value)>,\n}\n\nimpl<Key: Eq + Hash, Value> Default for FramePublisher<Key, Value> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<Key: Eq + Hash, Value> FramePublisher<Key, Value> {\n    pub fn new() -> Self {\n        Self {\n            generation: 0,\n            cache: Default::default(),\n        }\n    }\n\n    /// Publish the value. It will be available for the duration of this and the next frame.\n    pub fn set(&mut self, key: Key, value: Value) {\n        self.cache.insert(key, (self.generation, value));\n    }\n\n    /// Retrieve a value if it was published this or the previous frame.\n    pub fn get(&self, key: &Key) -> Option<&Value> {\n        self.cache.get(key).map(|(_, value)| value)\n    }\n\n    /// Must be called once per frame to clear the cache.\n    pub fn evict_cache(&mut self) {\n        let current_generation = self.generation;\n        self.cache.retain(|_key, cached| {\n            cached.0 == current_generation // only keep those that were published this frame\n        });\n        self.generation = self.generation.wrapping_add(1);\n    }\n}\n\nimpl<Key, Value> CacheTrait for FramePublisher<Key, Value>\nwhere\n    Key: 'static + Eq + Hash + Send + Sync,\n    Value: 'static + Send + Sync,\n{\n    fn update(&mut self) {\n        self.evict_cache();\n    }\n\n    fn len(&self) -> usize {\n        self.cache.len()\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/cache/mod.rs",
    "content": "//! Caches for preventing the same value from being recomputed every frame.\n//!\n//! Computing the same thing each frame can be expensive,\n//! so often you want to save the result from the previous frame and reuse it.\n//!\n//! Enter [`FrameCache`]: it caches the results of a computation for one frame.\n//! If it is still used next frame, it is not recomputed.\n//! If it is not used next frame, it is evicted from the cache to save memory.\n//!\n//! You can access egui's caches via [`crate::Memory::caches`],\n//! found with [`crate::Context::memory_mut`].\n\nmod cache_storage;\nmod cache_trait;\nmod frame_cache;\nmod frame_publisher;\n\npub use cache_storage::CacheStorage;\npub use cache_trait::CacheTrait;\npub use frame_cache::{ComputerMut, FrameCache};\npub use frame_publisher::FramePublisher;\n"
  },
  {
    "path": "crates/egui/src/callstack.rs",
    "content": "#[derive(Clone)]\nstruct Frame {\n    /// `_main` is usually as the deepest depth.\n    depth: usize,\n    name: String,\n    file_and_line: String,\n}\n\n/// Capture a callstack, skipping the frames that are not interesting.\n///\n/// In particular: slips everything before `egui::Context::run`,\n/// and skipping all frames in the `egui::` namespace.\n#[inline(never)]\npub fn capture() -> String {\n    let mut frames = vec![];\n    let mut depth = 0;\n\n    backtrace::trace(|frame| {\n        // Resolve this instruction pointer to a symbol name\n        backtrace::resolve_frame(frame, |symbol| {\n            let mut file_and_line = symbol.filename().map(shorten_source_file_path);\n\n            if let Some(file_and_line) = &mut file_and_line\n                && let Some(line_nr) = symbol.lineno()\n            {\n                file_and_line.push_str(&format!(\":{line_nr}\"));\n            }\n            let file_and_line = file_and_line.unwrap_or_default();\n\n            let name = symbol\n                .name()\n                .map(|name| clean_symbol_name(name.to_string()))\n                .unwrap_or_default();\n\n            frames.push(Frame {\n                depth,\n                name,\n                file_and_line,\n            });\n        });\n\n        depth += 1; // note: we can resolve multiple symbols on the same frame.\n\n        true // keep going to the next frame\n    });\n\n    if frames.is_empty() {\n        return\n            \"Failed to capture a backtrace. A common cause of this is compiling with panic=\\\"abort\\\" (https://github.com/rust-lang/backtrace-rs/issues/397)\".to_owned();\n    }\n\n    // Inclusive:\n    let mut min_depth = 0;\n    let mut max_depth = usize::MAX;\n\n    for frame in &frames {\n        if frame.name.starts_with(\"egui::callstack::capture\") {\n            min_depth = frame.depth + 1;\n        }\n        if frame.name.starts_with(\"egui::context::Context::run\") {\n            max_depth = frame.depth;\n        }\n    }\n\n    /// Is this the name of some sort of useful entry point?\n    fn is_start_name(name: &str) -> bool {\n        name == \"main\"\n            || name == \"_main\"\n            || name.starts_with(\"eframe::run_native\")\n            || name.starts_with(\"egui::context::Context::run\")\n    }\n\n    let mut has_kept_any_start_names = false;\n\n    frames.reverse(); // main on top, i.e. chronological order. Same as Python.\n\n    // Remove frames that are uninteresting:\n    frames.retain(|frame| {\n        // Keep the first \"start\" frame we can detect (e.g. `main`) to give the user a sense of chronology:\n        if is_start_name(&frame.name) && !has_kept_any_start_names {\n            has_kept_any_start_names = true;\n            return true;\n        }\n\n        if frame.depth < min_depth || max_depth < frame.depth {\n            return false;\n        }\n\n        // Remove stuff that isn't user calls:\n        let skip_prefixes = [\n            // \"backtrace::\", // not needed, since we cut at egui::callstack::capture\n            \"egui::\",\n            \"<egui::\",\n            \"<F as egui::widgets::Widget>\",\n            \"egui_plot::\",\n            \"egui_extras::\",\n            \"core::ptr::drop_in_place<egui::ui::Ui>\",\n            \"eframe::\",\n            \"core::ops::function::FnOnce::call_once\",\n            \"<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once\",\n        ];\n        for prefix in skip_prefixes {\n            if frame.name.starts_with(prefix) {\n                return false;\n            }\n        }\n        true\n    });\n\n    let mut deepest_depth = 0;\n    let mut widest_file_line = 0;\n    for frame in &frames {\n        deepest_depth = frame.depth.max(deepest_depth);\n        widest_file_line = frame.file_and_line.len().max(widest_file_line);\n    }\n\n    let widest_depth = deepest_depth.to_string().len();\n\n    let mut formatted = String::new();\n\n    if !frames.is_empty() {\n        let mut last_depth = frames[0].depth;\n\n        for frame in &frames {\n            let Frame {\n                depth,\n                name,\n                file_and_line,\n            } = frame;\n\n            if frame.depth + 1 < last_depth || last_depth + 1 < frame.depth {\n                // Show that some frames were elided\n                formatted.push_str(&format!(\"{:widest_depth$}  …\\n\", \"\"));\n            }\n\n            formatted.push_str(&format!(\n                \"{depth:widest_depth$}: {file_and_line:widest_file_line$}  {name}\\n\"\n            ));\n\n            last_depth = frame.depth;\n        }\n    }\n\n    formatted\n}\n\nfn clean_symbol_name(mut s: String) -> String {\n    // We get a hex suffix (at least on macOS) which is quite unhelpful,\n    // e.g. `my_crate::my_function::h3bedd97b1e03baa5`.\n    // Let's strip that.\n    if let Some(h) = s.rfind(\"::h\") {\n        let hex = &s[h + 3..];\n        if hex.len() == 16 && hex.chars().all(|c| c.is_ascii_hexdigit()) {\n            s.truncate(h);\n        }\n    }\n\n    s\n}\n\n#[test]\nfn test_clean_symbol_name() {\n    assert_eq!(\n        clean_symbol_name(\"my_crate::my_function::h3bedd97b1e03baa5\".to_owned()),\n        \"my_crate::my_function\"\n    );\n}\n\n/// Shorten a path to a Rust source file from a callstack.\n///\n/// Example input:\n/// * `/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs`\n/// * `crates/rerun/src/main.rs`\n/// * `/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs`\nfn shorten_source_file_path(path: &std::path::Path) -> String {\n    // Look for `src` and strip everything up to it.\n\n    let components: Vec<_> = path.iter().map(|path| path.to_string_lossy()).collect();\n\n    let mut src_idx = None;\n    for (i, c) in components.iter().enumerate() {\n        if c == \"src\" {\n            src_idx = Some(i);\n        }\n    }\n\n    // Look for the last `src`:\n    if let Some(src_idx) = src_idx {\n        // Before `src` comes the name of the crate - let's include that:\n        let first_index = src_idx.saturating_sub(1);\n\n        let mut output = components[first_index].to_string();\n        for component in &components[first_index + 1..] {\n            output.push('/');\n            output.push_str(component);\n        }\n        output\n    } else {\n        // No `src` directory found - weird!\n        path.display().to_string()\n    }\n}\n\n#[test]\nfn test_shorten_path() {\n    for (before, after) in [\n        (\n            \"/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs\",\n            \"tokio-1.24.1/src/runtime/runtime.rs\",\n        ),\n        (\"crates/rerun/src/main.rs\", \"rerun/src/main.rs\"),\n        (\n            \"/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs\",\n            \"core/src/ops/function.rs\",\n        ),\n        (\"/weird/path/file.rs\", \"/weird/path/file.rs\"),\n    ] {\n        use std::str::FromStr as _;\n        let before = std::path::PathBuf::from_str(before).unwrap();\n        assert_eq!(shorten_source_file_path(&before), after);\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/area.rs",
    "content": "//! Area is a [`Ui`] that has no parent, it floats on the background.\n//! It has no frame or own size. It is potentially movable.\n//! It is the foundation for windows and popups.\n\nuse emath::GuiRounding as _;\n\nuse crate::{\n    Align2, Context, Id, InnerResponse, LayerId, Layout, NumExt as _, Order, Pos2, Rect, Response,\n    Sense, Ui, UiBuilder, UiKind, UiStackInfo, Vec2, WidgetRect, WidgetWithState, emath, pos2,\n};\n\n/// State of an [`Area`] that is persisted between frames.\n///\n/// Areas back [`crate::Window`]s and other floating containers,\n/// like tooltips and the popups of [`crate::ComboBox`].\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct AreaState {\n    /// Last known position of the pivot.\n    pub pivot_pos: Option<Pos2>,\n\n    /// The anchor point of the area, i.e. where on the area the [`Self::pivot_pos`] refers to.\n    pub pivot: Align2,\n\n    /// Last known size.\n    ///\n    /// Area size is intentionally NOT persisted between sessions,\n    /// so that a bad tooltip or menu size won't be remembered forever.\n    /// A resizable [`crate::Window`] remembers the size the user picked using\n    /// the state in the [`crate::Resize`] container.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub size: Option<Vec2>,\n\n    /// If false, clicks goes straight through to what is behind us. Useful for tooltips etc.\n    pub interactable: bool,\n\n    /// At what time was this area first shown?\n    ///\n    /// Used to fade in the area.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub last_became_visible_at: Option<f64>,\n}\n\nimpl Default for AreaState {\n    fn default() -> Self {\n        Self {\n            pivot_pos: None,\n            pivot: Align2::LEFT_TOP,\n            size: None,\n            interactable: true,\n            last_became_visible_at: None,\n        }\n    }\n}\n\nimpl AreaState {\n    /// Load the state of an [`Area`] from memory.\n    pub fn load(ctx: &Context, id: Id) -> Option<Self> {\n        // TODO(emilk): Area state is not currently stored in `Memory::data`, but maybe it should be?\n        ctx.memory(|mem| mem.areas().get(id).copied())\n    }\n\n    /// The left top positions of the area.\n    pub fn left_top_pos(&self) -> Pos2 {\n        let pivot_pos = self.pivot_pos.unwrap_or_default();\n        let size = self.size.unwrap_or_default();\n        pos2(\n            pivot_pos.x - self.pivot.x().to_factor() * size.x,\n            pivot_pos.y - self.pivot.y().to_factor() * size.y,\n        )\n        .round_ui()\n    }\n\n    /// Move the left top positions of the area.\n    pub fn set_left_top_pos(&mut self, pos: Pos2) {\n        let size = self.size.unwrap_or_default();\n        self.pivot_pos = Some(pos2(\n            pos.x + self.pivot.x().to_factor() * size.x,\n            pos.y + self.pivot.y().to_factor() * size.y,\n        ));\n    }\n\n    /// Where the area is on screen.\n    pub fn rect(&self) -> Rect {\n        let size = self.size.unwrap_or_default();\n        Rect::from_min_size(self.left_top_pos(), size).round_ui()\n    }\n}\n\n/// An area on the screen that can be moved by dragging.\n///\n/// This forms the base of the [`crate::Window`] container.\n///\n/// ```\n/// # egui::__run_test_ctx(|ctx| {\n/// egui::Area::new(egui::Id::new(\"my_area\"))\n///     .fixed_pos(egui::pos2(32.0, 32.0))\n///     .show(ctx, |ui| {\n///         ui.label(\"Floating text!\");\n///     });\n/// # });\n/// ```\n///\n/// The previous rectangle used by this area can be obtained through [`crate::Memory::area_rect()`].\n#[must_use = \"You should call .show()\"]\n#[derive(Clone, Debug)]\npub struct Area {\n    pub(crate) id: Id,\n    info: UiStackInfo,\n    sense: Option<Sense>,\n    movable: bool,\n    interactable: bool,\n    enabled: bool,\n    constrain: bool,\n    constrain_rect: Option<Rect>,\n    order: Order,\n    default_pos: Option<Pos2>,\n    default_size: Vec2,\n    pivot: Align2,\n    anchor: Option<(Align2, Vec2)>,\n    new_pos: Option<Pos2>,\n    fade_in: bool,\n    layout: Layout,\n    sizing_pass: bool,\n}\n\nimpl WidgetWithState for Area {\n    type State = AreaState;\n}\n\nimpl Area {\n    /// The `id` must be globally unique.\n    pub fn new(id: Id) -> Self {\n        Self {\n            id,\n            info: UiStackInfo::new(UiKind::GenericArea),\n            sense: None,\n            movable: true,\n            interactable: true,\n            constrain: true,\n            constrain_rect: None,\n            enabled: true,\n            order: Order::Middle,\n            default_pos: None,\n            default_size: Vec2::NAN,\n            new_pos: None,\n            pivot: Align2::LEFT_TOP,\n            anchor: None,\n            fade_in: true,\n            layout: Layout::default(),\n            sizing_pass: false,\n        }\n    }\n\n    /// Let's you change the `id` that you assigned in [`Self::new`].\n    ///\n    /// The `id` must be globally unique.\n    #[inline]\n    pub fn id(mut self, id: Id) -> Self {\n        self.id = id;\n        self\n    }\n\n    /// Change the [`UiKind`] of the arena.\n    ///\n    /// Default to [`UiKind::GenericArea`].\n    #[inline]\n    pub fn kind(mut self, kind: UiKind) -> Self {\n        self.info = UiStackInfo::new(kind);\n        self\n    }\n\n    /// Set the [`UiStackInfo`] of the area's [`Ui`].\n    ///\n    /// Default to [`UiStackInfo`] with kind [`UiKind::GenericArea`].\n    #[inline]\n    pub fn info(mut self, info: UiStackInfo) -> Self {\n        self.info = info;\n        self\n    }\n\n    pub fn layer(&self) -> LayerId {\n        LayerId::new(self.order, self.id)\n    }\n\n    /// If false, no content responds to click\n    /// and widgets will be shown grayed out.\n    /// You won't be able to move the window.\n    /// Default: `true`.\n    #[inline]\n    pub fn enabled(mut self, enabled: bool) -> Self {\n        self.enabled = enabled;\n        self\n    }\n\n    /// Moveable by dragging the area?\n    #[inline]\n    pub fn movable(mut self, movable: bool) -> Self {\n        self.movable = movable;\n        self.interactable |= movable;\n        self\n    }\n\n    pub fn is_enabled(&self) -> bool {\n        self.enabled\n    }\n\n    pub fn is_movable(&self) -> bool {\n        self.movable && self.enabled\n    }\n\n    /// If false, clicks goes straight through to what is behind us.\n    ///\n    /// Can be used for semi-invisible areas that the user should be able to click through.\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn interactable(mut self, interactable: bool) -> Self {\n        self.interactable = interactable;\n        self.movable &= interactable;\n        self\n    }\n\n    /// Explicitly set a sense.\n    ///\n    /// If not set, this will default to `Sense::drag()` if movable, `Sense::click()` if interactable, and `Sense::hover()` otherwise.\n    #[inline]\n    pub fn sense(mut self, sense: Sense) -> Self {\n        self.sense = Some(sense);\n        self\n    }\n\n    /// `order(Order::Foreground)` for an Area that should always be on top\n    #[inline]\n    pub fn order(mut self, order: Order) -> Self {\n        self.order = order;\n        self\n    }\n\n    #[inline]\n    pub fn default_pos(mut self, default_pos: impl Into<Pos2>) -> Self {\n        self.default_pos = Some(default_pos.into());\n        self\n    }\n\n    /// The size used for the [`Ui::max_rect`] the first frame.\n    ///\n    /// Text will wrap at this width, and images that expand to fill the available space\n    /// will expand to this size.\n    ///\n    /// If the contents are smaller than this size, the area will shrink to fit the contents.\n    /// If the contents overflow, the area will grow.\n    ///\n    /// If not set, [`crate::style::Spacing::default_area_size`] will be used.\n    #[inline]\n    pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {\n        self.default_size = default_size.into();\n        self\n    }\n\n    /// See [`Self::default_size`].\n    #[inline]\n    pub fn default_width(mut self, default_width: f32) -> Self {\n        self.default_size.x = default_width;\n        self\n    }\n\n    /// See [`Self::default_size`].\n    #[inline]\n    pub fn default_height(mut self, default_height: f32) -> Self {\n        self.default_size.y = default_height;\n        self\n    }\n\n    /// Positions the window and prevents it from being moved\n    #[inline]\n    pub fn fixed_pos(mut self, fixed_pos: impl Into<Pos2>) -> Self {\n        self.new_pos = Some(fixed_pos.into());\n        self.movable = false;\n        self\n    }\n\n    /// Constrains this area to [`Context::screen_rect`]?\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn constrain(mut self, constrain: bool) -> Self {\n        self.constrain = constrain;\n        self\n    }\n\n    /// Constrain the movement of the window to the given rectangle.\n    ///\n    /// For instance: `.constrain_to(ctx.screen_rect())`.\n    #[inline]\n    pub fn constrain_to(mut self, constrain_rect: Rect) -> Self {\n        self.constrain = true;\n        self.constrain_rect = Some(constrain_rect);\n        self\n    }\n\n    /// Where the \"root\" of the area is.\n    ///\n    /// For instance, if you set this to [`Align2::RIGHT_TOP`]\n    /// then [`Self::fixed_pos`] will set the position of the right-top\n    /// corner of the area.\n    ///\n    /// Default: [`Align2::LEFT_TOP`].\n    #[inline]\n    pub fn pivot(mut self, pivot: Align2) -> Self {\n        self.pivot = pivot;\n        self\n    }\n\n    /// Positions the window but you can still move it.\n    #[inline]\n    pub fn current_pos(mut self, current_pos: impl Into<Pos2>) -> Self {\n        self.new_pos = Some(current_pos.into());\n        self\n    }\n\n    /// Set anchor and distance.\n    ///\n    /// An anchor of `Align2::RIGHT_TOP` means \"put the right-top corner of the window\n    /// in the right-top corner of the screen\".\n    ///\n    /// The offset is added to the position, so e.g. an offset of `[-5.0, 5.0]`\n    /// would move the window left and down from the given anchor.\n    ///\n    /// Anchoring also makes the window immovable.\n    ///\n    /// It is an error to set both an anchor and a position.\n    #[inline]\n    pub fn anchor(mut self, align: Align2, offset: impl Into<Vec2>) -> Self {\n        self.anchor = Some((align, offset.into()));\n        self.movable(false)\n    }\n\n    pub(crate) fn get_pivot(&self) -> Align2 {\n        if let Some((pivot, _)) = self.anchor {\n            pivot\n        } else {\n            Align2::LEFT_TOP\n        }\n    }\n\n    /// If `true`, quickly fade in the area.\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn fade_in(mut self, fade_in: bool) -> Self {\n        self.fade_in = fade_in;\n        self\n    }\n\n    /// Set the layout for the child Ui.\n    #[inline]\n    pub fn layout(mut self, layout: Layout) -> Self {\n        self.layout = layout;\n        self\n    }\n\n    /// While true, a sizing pass will be done. This means the area will be invisible\n    /// and the contents will be laid out to estimate the proper containing size of the area.\n    /// If false, there will be no change to the default area behavior. This is useful if the\n    /// area contents area dynamic and you need to need to make sure the area adjusts its size\n    /// accordingly.\n    ///\n    /// This should only be set to true during the specific frames you want force a sizing pass.\n    /// Do NOT hard-code this as `.sizing_pass(true)`, as it will cause the area to never be\n    /// visible.\n    ///\n    /// # Arguments\n    /// - resize: If true, the area will be resized to fit its contents. False will keep the\n    ///   default area resizing behavior.\n    ///\n    /// Default: `false`.\n    #[inline]\n    pub fn sizing_pass(mut self, resize: bool) -> Self {\n        self.sizing_pass = resize;\n        self\n    }\n}\n\npub(crate) struct Prepared {\n    info: Option<UiStackInfo>,\n    layer_id: LayerId,\n    state: AreaState,\n    move_response: Response,\n    enabled: bool,\n    constrain: bool,\n    constrain_rect: Rect,\n\n    /// We always make windows invisible the first frame to hide \"first-frame-jitters\".\n    ///\n    /// This is so that we use the first frame to calculate the window size,\n    /// and then can correctly position the window and its contents the next frame,\n    /// without having one frame where the window is wrongly positioned or sized.\n    sizing_pass: bool,\n\n    fade_in: bool,\n    layout: Layout,\n}\n\nimpl Area {\n    pub fn show<R>(\n        self,\n        ctx: &Context,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        let mut prepared = self.begin(ctx);\n        let mut content_ui = prepared.content_ui(ctx);\n        let inner = add_contents(&mut content_ui);\n        let response = prepared.end(ctx, content_ui);\n        InnerResponse { inner, response }\n    }\n\n    pub(crate) fn begin(self, ctx: &Context) -> Prepared {\n        let Self {\n            id,\n            info,\n            sense,\n            movable,\n            order,\n            interactable,\n            enabled,\n            default_pos,\n            default_size,\n            new_pos,\n            pivot,\n            anchor,\n            constrain,\n            constrain_rect,\n            fade_in,\n            layout,\n            sizing_pass: force_sizing_pass,\n        } = self;\n\n        let constrain_rect = constrain_rect.unwrap_or_else(|| ctx.content_rect());\n\n        let layer_id = LayerId::new(order, id);\n\n        let state = AreaState::load(ctx, id);\n        let mut sizing_pass = state.is_none();\n        let mut state = state.unwrap_or(AreaState {\n            pivot_pos: None,\n            pivot,\n            size: None,\n            interactable,\n            last_became_visible_at: None,\n        });\n        if force_sizing_pass {\n            sizing_pass = true;\n            state.size = None;\n        }\n        state.pivot = pivot;\n        state.interactable = interactable;\n        if let Some(new_pos) = new_pos {\n            state.pivot_pos = Some(new_pos);\n        }\n        state.pivot_pos.get_or_insert_with(|| {\n            default_pos.unwrap_or_else(|| automatic_area_position(ctx, constrain_rect, layer_id))\n        });\n        state.interactable = interactable;\n\n        let size = *state.size.get_or_insert_with(|| {\n            sizing_pass = true;\n\n            // during the sizing pass we will use this as the max size\n            let mut size = default_size;\n\n            let default_area_size = ctx.global_style().spacing.default_area_size;\n            if size.x.is_nan() {\n                size.x = default_area_size.x;\n            }\n            if size.y.is_nan() {\n                size.y = default_area_size.y;\n            }\n\n            if constrain {\n                size = size.at_most(constrain_rect.size());\n            }\n\n            size\n        });\n\n        // TODO(emilk): if last frame was sizing pass, it should be considered invisible for smoother fade-in\n        let visible_last_frame = ctx.memory(|mem| mem.areas().visible_last_frame(&layer_id));\n\n        if !visible_last_frame || state.last_became_visible_at.is_none() {\n            state.last_became_visible_at = Some(ctx.input(|i| i.time));\n        }\n\n        if let Some((anchor, offset)) = anchor {\n            state.set_left_top_pos(\n                anchor\n                    .align_size_within_rect(size, constrain_rect)\n                    .left_top()\n                    + offset,\n            );\n        }\n\n        // interact right away to prevent frame-delay\n        let mut move_response = {\n            let interact_id = layer_id.id.with(\"move\");\n            let sense = sense.unwrap_or_else(|| {\n                if movable {\n                    Sense::DRAG\n                } else if interactable {\n                    Sense::CLICK // allow clicks to bring to front\n                } else {\n                    Sense::hover()\n                }\n            });\n\n            let move_response = ctx.create_widget(\n                WidgetRect {\n                    id: interact_id,\n                    layer_id,\n                    rect: state.rect(),\n                    interact_rect: state.rect().intersect(constrain_rect),\n                    sense,\n                    enabled,\n                },\n                true,\n                Default::default(),\n            );\n\n            // Used to prevent drift\n            let pivot_at_start_of_drag_id = id.with(\"pivot_at_drag_start\");\n\n            if movable\n                && move_response.dragged()\n                && let Some(pivot_pos) = &mut state.pivot_pos\n            {\n                let pivot_at_start_of_drag = ctx.data_mut(|data| {\n                    *data.get_temp_mut_or::<Pos2>(pivot_at_start_of_drag_id, *pivot_pos)\n                });\n\n                *pivot_pos =\n                    pivot_at_start_of_drag + move_response.total_drag_delta().unwrap_or_default();\n            } else {\n                ctx.data_mut(|data| data.remove::<Pos2>(pivot_at_start_of_drag_id));\n            }\n\n            if (move_response.dragged() || move_response.clicked())\n                || pointer_pressed_on_area(ctx, layer_id)\n                || !ctx.memory(|m| m.areas().visible_last_frame(&layer_id))\n            {\n                ctx.memory_mut(|m| m.areas_mut().move_to_top(layer_id));\n                ctx.request_repaint();\n            }\n\n            move_response\n        };\n\n        state.set_left_top_pos(round_area_position(\n            ctx,\n            if constrain {\n                Context::constrain_window_rect_to_area(state.rect(), constrain_rect).min\n            } else {\n                state.left_top_pos()\n            },\n        ));\n\n        // Update response with possibly moved/constrained rect:\n        move_response.rect = state.rect();\n        move_response.interact_rect = state.rect();\n\n        Prepared {\n            info: Some(info),\n            layer_id,\n            state,\n            move_response,\n            enabled,\n            constrain,\n            constrain_rect,\n            sizing_pass,\n            fade_in,\n            layout,\n        }\n    }\n}\n\nfn round_area_position(ctx: &Context, pos: Pos2) -> Pos2 {\n    // We round a lot of rendering to pixels, so we round the whole\n    // area positions to pixels too, so avoid widgets appearing to float\n    // around independently of each other when the area is dragged.\n    // But just in case pixels_per_point is irrational,\n    // we then also round to ui coordinates:\n\n    pos.round_to_pixels(ctx.pixels_per_point()).round_ui()\n}\n\nimpl Prepared {\n    pub(crate) fn state(&self) -> &AreaState {\n        &self.state\n    }\n\n    pub(crate) fn state_mut(&mut self) -> &mut AreaState {\n        &mut self.state\n    }\n\n    pub(crate) fn constrain(&self) -> bool {\n        self.constrain\n    }\n\n    pub(crate) fn constrain_rect(&self) -> Rect {\n        self.constrain_rect\n    }\n\n    pub(crate) fn content_ui(&mut self, ctx: &Context) -> Ui {\n        let max_rect = self.state.rect();\n\n        let mut ui_builder = UiBuilder::new()\n            .ui_stack_info(self.info.take().unwrap_or_default())\n            .layer_id(self.layer_id)\n            .max_rect(max_rect)\n            .layout(self.layout)\n            .accessibility_parent(self.move_response.id)\n            .closable();\n\n        if !self.enabled {\n            ui_builder = ui_builder.disabled();\n        }\n        if self.sizing_pass {\n            ui_builder = ui_builder.sizing_pass().invisible();\n        }\n\n        let mut ui = Ui::new(ctx.clone(), self.layer_id.id, ui_builder);\n        ui.set_clip_rect(self.constrain_rect); // Don't paint outside our bounds\n\n        if self.fade_in\n            && let Some(last_became_visible_at) = self.state.last_became_visible_at\n        {\n            let age =\n                ctx.input(|i| (i.time - last_became_visible_at) as f32 + i.predicted_dt / 2.0);\n            let opacity =\n                crate::remap_clamp(age, 0.0..=ctx.global_style().animation_time, 0.0..=1.0);\n            let opacity = emath::easing::quadratic_out(opacity); // slow fade-out = quick fade-in\n            ui.multiply_opacity(opacity);\n            if opacity < 1.0 {\n                ctx.request_repaint();\n            }\n        }\n\n        ui\n    }\n\n    pub(crate) fn with_widget_info(&self, make_info: impl Fn() -> crate::WidgetInfo) {\n        self.move_response.widget_info(make_info);\n    }\n\n    pub(crate) fn id(&self) -> Id {\n        self.move_response.id\n    }\n\n    #[expect(clippy::needless_pass_by_value)] // intentional to swallow up `content_ui`.\n    pub(crate) fn end(self, ctx: &Context, content_ui: Ui) -> Response {\n        let Self {\n            info: _,\n            layer_id,\n            mut state,\n            move_response: mut response,\n            sizing_pass,\n            ..\n        } = self;\n\n        state.size = Some(content_ui.min_size());\n\n        // Make sure we report back the correct size.\n        // Very important after the initial sizing pass, when the initial estimate of the size is way off.\n        let final_rect = state.rect();\n        response.rect = final_rect;\n        response.interact_rect = final_rect;\n\n        // TODO(lucasmerlin): Can the area response be based on Ui::response? Then this won't be needed\n        // Bubble up the close event\n        if content_ui.should_close() {\n            response.set_close();\n        }\n\n        ctx.memory_mut(|m| m.areas_mut().set_state(layer_id, state));\n\n        if sizing_pass {\n            // If we didn't know the size, we were likely drawing the area in the wrong place.\n            ctx.request_repaint();\n        }\n\n        response\n    }\n}\n\nfn pointer_pressed_on_area(ctx: &Context, layer_id: LayerId) -> bool {\n    if let Some(pointer_pos) = ctx.pointer_interact_pos() {\n        let any_pressed = ctx.input(|i| i.pointer.any_pressed());\n        any_pressed && ctx.layer_id_at(pointer_pos) == Some(layer_id)\n    } else {\n        false\n    }\n}\n\nfn automatic_area_position(ctx: &Context, constrain_rect: Rect, layer_id: LayerId) -> Pos2 {\n    let mut existing: Vec<Rect> = ctx.memory(|mem| {\n        mem.areas()\n            .visible_windows()\n            .filter(|(id, _)| id != &layer_id) // ignore ourselves\n            .filter(|(_, state)| state.pivot_pos.is_some() && state.size.is_some())\n            .map(|(_, state)| state.rect())\n            .collect()\n    });\n    existing.sort_by_key(|r| r.left().round() as i32);\n\n    let spacing = 16.0;\n    let left = constrain_rect.left() + spacing;\n    let top = constrain_rect.top() + spacing;\n\n    if existing.is_empty() {\n        return pos2(left, top);\n    }\n\n    // Separate existing rectangles into columns:\n    let mut column_bbs = vec![existing[0]];\n\n    for &rect in &existing {\n        #[expect(clippy::unwrap_used)]\n        let current_column_bb = column_bbs.last_mut().unwrap();\n        if rect.left() < current_column_bb.right() {\n            // same column\n            *current_column_bb |= rect;\n        } else {\n            // new column\n            column_bbs.push(rect);\n        }\n    }\n\n    {\n        // Look for large spaces between columns (empty columns):\n        let mut x = left;\n        for col_bb in &column_bbs {\n            let available = col_bb.left() - x;\n            if available >= 300.0 {\n                return pos2(x, top);\n            }\n            x = col_bb.right() + spacing;\n        }\n    }\n\n    // Find first column with some available space at the bottom of it:\n    for col_bb in &column_bbs {\n        if col_bb.bottom() < constrain_rect.center().y {\n            return pos2(col_bb.left(), col_bb.bottom() + spacing);\n        }\n    }\n\n    // Maybe we can fit a new column?\n    #[expect(clippy::unwrap_used)]\n    let rightmost = column_bbs.last().unwrap().right();\n    if rightmost + 200.0 < constrain_rect.right() {\n        return pos2(rightmost + spacing, top);\n    }\n\n    // Ok, just put us in the column with the most space at the bottom:\n    let mut best_pos = pos2(left, column_bbs[0].bottom() + spacing);\n    for col_bb in &column_bbs {\n        let col_pos = pos2(col_bb.left(), col_bb.bottom() + spacing);\n        if col_pos.y < best_pos.y {\n            best_pos = col_pos;\n        }\n    }\n    best_pos\n}\n"
  },
  {
    "path": "crates/egui/src/containers/close_tag.rs",
    "content": "#[expect(unused_imports)]\nuse crate::{Ui, UiBuilder};\nuse std::sync::atomic::AtomicBool;\n\n/// A tag to mark a container as closable.\n///\n/// Usually set via [`UiBuilder::closable`].\n///\n/// [`Ui::close`] will find the closest parent [`ClosableTag`] and set its `close` field to `true`.\n/// Use [`Ui::should_close`] to check if close has been called.\n#[derive(Debug, Default)]\npub struct ClosableTag {\n    pub close: AtomicBool,\n}\n\nimpl ClosableTag {\n    pub const NAME: &'static str = \"egui_close_tag\";\n\n    /// Set close to `true`\n    pub fn set_close(&self) {\n        self.close.store(true, std::sync::atomic::Ordering::Relaxed);\n    }\n\n    /// Returns `true` if [`ClosableTag::set_close`] has been called.\n    pub fn should_close(&self) -> bool {\n        self.close.load(std::sync::atomic::Ordering::Relaxed)\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/collapsing_header.rs",
    "content": "use std::hash::Hash;\n\nuse crate::{\n    Context, Id, InnerResponse, NumExt as _, Rect, Response, Sense, Stroke, TextStyle,\n    TextWrapMode, Ui, UiBuilder, UiKind, UiStackInfo, Vec2, WidgetInfo, WidgetText, WidgetType,\n    emath, epaint, pos2, remap, remap_clamp, vec2,\n};\nuse emath::GuiRounding as _;\nuse epaint::{Shape, StrokeKind};\n\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub(crate) struct InnerState {\n    open: bool,\n\n    /// Height of the region when open. Used for animations\n    #[cfg_attr(feature = \"serde\", serde(default))]\n    open_height: Option<f32>,\n}\n\n/// This is a a building block for building collapsing regions.\n///\n/// It is used by [`CollapsingHeader`] and [`crate::Window`], but can also be used on its own.\n///\n/// See [`CollapsingState::show_header`] for how to show a collapsing header with a custom header.\n#[derive(Clone, Debug)]\npub struct CollapsingState {\n    id: Id,\n    state: InnerState,\n}\n\nimpl CollapsingState {\n    pub fn load(ctx: &Context, id: Id) -> Option<Self> {\n        ctx.data_mut(|d| {\n            d.get_persisted::<InnerState>(id)\n                .map(|state| Self { id, state })\n        })\n    }\n\n    pub fn store(&self, ctx: &Context) {\n        ctx.data_mut(|d| d.insert_persisted(self.id, self.state));\n    }\n\n    pub fn remove(&self, ctx: &Context) {\n        ctx.data_mut(|d| d.remove::<InnerState>(self.id));\n    }\n\n    pub fn id(&self) -> Id {\n        self.id\n    }\n\n    pub fn load_with_default_open(ctx: &Context, id: Id, default_open: bool) -> Self {\n        Self::load(ctx, id).unwrap_or(Self {\n            id,\n            state: InnerState {\n                open: default_open,\n                open_height: None,\n            },\n        })\n    }\n\n    pub fn is_open(&self) -> bool {\n        self.state.open\n    }\n\n    pub fn set_open(&mut self, open: bool) {\n        self.state.open = open;\n    }\n\n    pub fn toggle(&mut self, ui: &Ui) {\n        self.state.open = !self.state.open;\n        ui.request_repaint();\n    }\n\n    /// 0 for closed, 1 for open, with tweening\n    pub fn openness(&self, ctx: &Context) -> f32 {\n        if ctx.memory(|mem| mem.everything_is_visible()) {\n            1.0\n        } else {\n            ctx.animate_bool_responsive(self.id, self.state.open)\n        }\n    }\n\n    /// Will toggle when clicked, etc.\n    pub(crate) fn show_default_button_with_size(\n        &mut self,\n        ui: &mut Ui,\n        button_size: Vec2,\n    ) -> Response {\n        let (_id, rect) = ui.allocate_space(button_size);\n        let response = ui.interact(rect, self.id, Sense::click());\n        response.widget_info(|| {\n            WidgetInfo::labeled(\n                WidgetType::Button,\n                ui.is_enabled(),\n                if self.is_open() { \"Hide\" } else { \"Show\" },\n            )\n        });\n\n        if response.clicked() {\n            self.toggle(ui);\n        }\n        let openness = self.openness(ui.ctx());\n        paint_default_icon(ui, openness, &response);\n        response\n    }\n\n    /// Will toggle when clicked, etc.\n    fn show_default_button_indented(&mut self, ui: &mut Ui) -> Response {\n        self.show_button_indented(ui, paint_default_icon)\n    }\n\n    /// Will toggle when clicked, etc.\n    fn show_button_indented(\n        &mut self,\n        ui: &mut Ui,\n        icon_fn: impl FnOnce(&mut Ui, f32, &Response) + 'static,\n    ) -> Response {\n        let size = vec2(ui.spacing().indent, ui.spacing().icon_width);\n        let (_id, rect) = ui.allocate_space(size);\n        let response = ui.interact(rect, self.id, Sense::click());\n        if response.clicked() {\n            self.toggle(ui);\n        }\n\n        let (mut icon_rect, _) = ui.spacing().icon_rectangles(response.rect);\n        icon_rect.set_center(pos2(\n            response.rect.left() + ui.spacing().indent / 2.0,\n            response.rect.center().y,\n        ));\n        let openness = self.openness(ui.ctx());\n        let small_icon_response = response.clone().with_new_rect(icon_rect);\n        icon_fn(ui, openness, &small_icon_response);\n        response\n    }\n\n    /// Shows header and body (if expanded).\n    ///\n    /// The header will start with the default button in a horizontal layout, followed by whatever you add.\n    ///\n    /// Will also store the state.\n    ///\n    /// Returns the response of the collapsing button, the custom header, and the custom body.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let id = ui.make_persistent_id(\"my_collapsing_header\");\n    /// egui::collapsing_header::CollapsingState::load_with_default_open(ui.ctx(), id, false)\n    ///     .show_header(ui, |ui| {\n    ///         ui.label(\"Header\"); // you can put checkboxes or whatever here\n    ///     })\n    ///     .body(|ui| ui.label(\"Body\"));\n    /// # });\n    /// ```\n    pub fn show_header<HeaderRet>(\n        mut self,\n        ui: &mut Ui,\n        add_header: impl FnOnce(&mut Ui) -> HeaderRet,\n    ) -> HeaderResponse<'_, HeaderRet> {\n        let header_response = ui.horizontal(|ui| {\n            let prev_item_spacing = ui.spacing_mut().item_spacing;\n            ui.spacing_mut().item_spacing.x = 0.0; // the toggler button uses the full indent width\n            let collapser = self.show_default_button_indented(ui);\n            ui.spacing_mut().item_spacing = prev_item_spacing;\n            (collapser, add_header(ui))\n        });\n        HeaderResponse {\n            state: self,\n            ui,\n            toggle_button_response: header_response.inner.0,\n            header_response: InnerResponse {\n                response: header_response.response,\n                inner: header_response.inner.1,\n            },\n        }\n    }\n\n    /// Show body if we are open, with a nice animation between closed and open.\n    /// Indent the body to show it belongs to the header.\n    ///\n    /// Will also store the state.\n    pub fn show_body_indented<R>(\n        &mut self,\n        header_response: &Response,\n        ui: &mut Ui,\n        add_body: impl FnOnce(&mut Ui) -> R,\n    ) -> Option<InnerResponse<R>> {\n        let id = self.id;\n        self.show_body_unindented(ui, |ui| {\n            ui.indent(id, |ui| {\n                // make as wide as the header:\n                ui.expand_to_include_x(header_response.rect.right());\n                add_body(ui)\n            })\n            .inner\n        })\n    }\n\n    /// Show body if we are open, with a nice animation between closed and open.\n    /// Will also store the state.\n    pub fn show_body_unindented<R>(\n        &mut self,\n        ui: &mut Ui,\n        add_body: impl FnOnce(&mut Ui) -> R,\n    ) -> Option<InnerResponse<R>> {\n        let openness = self.openness(ui.ctx());\n\n        let builder = UiBuilder::new()\n            .ui_stack_info(UiStackInfo::new(UiKind::Collapsible))\n            .closable();\n\n        if openness <= 0.0 {\n            self.store(ui.ctx()); // we store any earlier toggling as promised in the docstring\n            None\n        } else if openness < 1.0 {\n            Some(ui.scope_builder(builder, |child_ui| {\n                let max_height = if self.state.open && self.state.open_height.is_none() {\n                    // First frame of expansion.\n                    // We don't know full height yet, but we will next frame.\n                    // Just use a placeholder value that shows some movement:\n                    10.0\n                } else {\n                    let full_height = self.state.open_height.unwrap_or_default();\n                    remap_clamp(openness, 0.0..=1.0, 0.0..=full_height).round_ui()\n                };\n\n                let mut clip_rect = child_ui.clip_rect();\n                clip_rect.max.y = clip_rect.max.y.min(child_ui.max_rect().top() + max_height);\n                child_ui.set_clip_rect(clip_rect);\n\n                let ret = add_body(child_ui);\n\n                let mut min_rect = child_ui.min_rect();\n                self.state.open_height = Some(min_rect.height());\n                if child_ui.should_close() {\n                    self.state.open = false;\n                }\n                self.store(child_ui.ctx()); // remember the height\n\n                // Pretend children took up at most `max_height` space:\n                min_rect.max.y = min_rect.max.y.at_most(min_rect.top() + max_height);\n                child_ui.force_set_min_rect(min_rect);\n                ret\n            }))\n        } else {\n            let ret_response = ui.scope_builder(builder, add_body);\n            if ret_response.response.should_close() {\n                self.state.open = false;\n            }\n            let full_size = ret_response.response.rect.size();\n            self.state.open_height = Some(full_size.y);\n            self.store(ui.ctx()); // remember the height\n            Some(ret_response)\n        }\n    }\n\n    /// Paint this [`CollapsingState`]'s toggle button. Takes an [`IconPainter`] as the icon.\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// fn circle_icon(ui: &mut egui::Ui, openness: f32, response: &egui::Response) {\n    ///     let stroke = ui.style().interact(&response).fg_stroke;\n    ///     let radius = egui::lerp(2.0..=3.0, openness);\n    ///     ui.painter().circle_filled(response.rect.center(), radius, stroke.color);\n    /// }\n    ///\n    /// let mut state = egui::collapsing_header::CollapsingState::load_with_default_open(\n    ///     ui.ctx(),\n    ///     ui.make_persistent_id(\"my_collapsing_state\"),\n    ///     false,\n    /// );\n    ///\n    /// let header_res = ui.horizontal(|ui| {\n    ///     ui.label(\"Header\");\n    ///     state.show_toggle_button(ui, circle_icon);\n    /// });\n    ///\n    /// state.show_body_indented(&header_res.response, ui, |ui| ui.label(\"Body\"));\n    /// # });\n    /// ```\n    pub fn show_toggle_button(\n        &mut self,\n        ui: &mut Ui,\n        icon_fn: impl FnOnce(&mut Ui, f32, &Response) + 'static,\n    ) -> Response {\n        self.show_button_indented(ui, icon_fn)\n    }\n}\n\n/// From [`CollapsingState::show_header`].\n#[must_use = \"Remember to show the body\"]\npub struct HeaderResponse<'ui, HeaderRet> {\n    state: CollapsingState,\n    ui: &'ui mut Ui,\n    toggle_button_response: Response,\n    header_response: InnerResponse<HeaderRet>,\n}\n\nimpl<HeaderRet> HeaderResponse<'_, HeaderRet> {\n    pub fn is_open(&self) -> bool {\n        self.state.is_open()\n    }\n\n    pub fn set_open(&mut self, open: bool) {\n        self.state.set_open(open);\n    }\n\n    pub fn toggle(&mut self) {\n        self.state.toggle(self.ui);\n    }\n\n    /// Returns the response of the collapsing button, the custom header, and the custom body.\n    pub fn body<BodyRet>(\n        mut self,\n        add_body: impl FnOnce(&mut Ui) -> BodyRet,\n    ) -> (\n        Response,\n        InnerResponse<HeaderRet>,\n        Option<InnerResponse<BodyRet>>,\n    ) {\n        let body_response =\n            self.state\n                .show_body_indented(&self.header_response.response, self.ui, add_body);\n        (\n            self.toggle_button_response,\n            self.header_response,\n            body_response,\n        )\n    }\n\n    /// Returns the response of the collapsing button, the custom header, and the custom body, without indentation.\n    pub fn body_unindented<BodyRet>(\n        mut self,\n        add_body: impl FnOnce(&mut Ui) -> BodyRet,\n    ) -> (\n        Response,\n        InnerResponse<HeaderRet>,\n        Option<InnerResponse<BodyRet>>,\n    ) {\n        let body_response = self.state.show_body_unindented(self.ui, add_body);\n        (\n            self.toggle_button_response,\n            self.header_response,\n            body_response,\n        )\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Paint the arrow icon that indicated if the region is open or not\npub fn paint_default_icon(ui: &mut Ui, openness: f32, response: &Response) {\n    let visuals = ui.style().interact(response);\n\n    let rect = response.rect;\n\n    // Draw a pointy triangle arrow:\n    let rect = Rect::from_center_size(rect.center(), vec2(rect.width(), rect.height()) * 0.75);\n    let rect = rect.expand(visuals.expansion);\n    let mut points = vec![rect.left_top(), rect.right_top(), rect.center_bottom()];\n    use std::f32::consts::TAU;\n    let rotation = emath::Rot2::from_angle(remap(openness, 0.0..=1.0, -TAU / 4.0..=0.0));\n    for p in &mut points {\n        *p = rect.center() + rotation * (*p - rect.center());\n    }\n\n    ui.painter().add(Shape::convex_polygon(\n        points,\n        visuals.fg_stroke.color,\n        Stroke::NONE,\n    ));\n}\n\n/// A function that paints an icon indicating if the region is open or not\npub type IconPainter = Box<dyn FnOnce(&mut Ui, f32, &Response)>;\n\n/// A header which can be collapsed/expanded, revealing a contained [`Ui`] region.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// egui::CollapsingHeader::new(\"Heading\")\n///     .show(ui, |ui| {\n///         ui.label(\"Body\");\n///     });\n///\n/// // Short version:\n/// ui.collapsing(\"Heading\", |ui| { ui.label(\"Body\"); });\n/// # });\n/// ```\n///\n/// If you want to customize the header contents, see [`CollapsingState::show_header`].\n#[must_use = \"You should call .show()\"]\npub struct CollapsingHeader {\n    text: WidgetText,\n    default_open: bool,\n    open: Option<bool>,\n    id_salt: Id,\n    enabled: bool,\n    selectable: bool,\n    selected: bool,\n    show_background: bool,\n    icon: Option<IconPainter>,\n}\n\nimpl CollapsingHeader {\n    /// The [`CollapsingHeader`] starts out collapsed unless you call `default_open`.\n    ///\n    /// The label is used as an [`Id`] source.\n    /// If the label is unique and static this is fine,\n    /// but if it changes or there are several [`CollapsingHeader`] with the same title\n    /// you need to provide a unique id source with [`Self::id_salt`].\n    pub fn new(text: impl Into<WidgetText>) -> Self {\n        let text = text.into();\n        let id_salt = Id::new(text.text());\n        Self {\n            text,\n            default_open: false,\n            open: None,\n            id_salt,\n            enabled: true,\n            selectable: false,\n            selected: false,\n            show_background: false,\n            icon: None,\n        }\n    }\n\n    /// By default, the [`CollapsingHeader`] is collapsed.\n    /// Call `.default_open(true)` to change this.\n    #[inline]\n    pub fn default_open(mut self, open: bool) -> Self {\n        self.default_open = open;\n        self\n    }\n\n    /// Calling `.open(Some(true))` will make the collapsing header open this frame (or stay open).\n    ///\n    /// Calling `.open(Some(false))` will make the collapsing header close this frame (or stay closed).\n    ///\n    /// Calling `.open(None)` has no effect (default).\n    #[inline]\n    pub fn open(mut self, open: Option<bool>) -> Self {\n        self.open = open;\n        self\n    }\n\n    /// Explicitly set the source of the [`Id`] of this widget, instead of using title label.\n    /// This is useful if the title label is dynamic or not unique.\n    #[inline]\n    pub fn id_salt(mut self, id_salt: impl Hash) -> Self {\n        self.id_salt = Id::new(id_salt);\n        self\n    }\n\n    /// Explicitly set the source of the [`Id`] of this widget, instead of using title label.\n    /// This is useful if the title label is dynamic or not unique.\n    #[deprecated = \"Renamed id_salt\"]\n    #[inline]\n    pub fn id_source(mut self, id_salt: impl Hash) -> Self {\n        self.id_salt = Id::new(id_salt);\n        self\n    }\n\n    /// If you set this to `false`, the [`CollapsingHeader`] will be grayed out and un-clickable.\n    ///\n    /// This is a convenience for [`Ui::disable`].\n    #[inline]\n    pub fn enabled(mut self, enabled: bool) -> Self {\n        self.enabled = enabled;\n        self\n    }\n\n    /// Should the [`CollapsingHeader`] show a background behind it? Default: `false`.\n    ///\n    /// To show it behind all [`CollapsingHeader`] you can just use:\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.visuals_mut().collapsing_header_frame = true;\n    /// # });\n    /// ```\n    #[inline]\n    pub fn show_background(mut self, show_background: bool) -> Self {\n        self.show_background = show_background;\n        self\n    }\n\n    /// Use the provided function to render a different [`CollapsingHeader`] icon.\n    /// Defaults to a triangle that animates as the [`CollapsingHeader`] opens and closes.\n    ///\n    /// For example:\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// fn circle_icon(ui: &mut egui::Ui, openness: f32, response: &egui::Response) {\n    ///     let stroke = ui.style().interact(&response).fg_stroke;\n    ///     let radius = egui::lerp(2.0..=3.0, openness);\n    ///     ui.painter().circle_filled(response.rect.center(), radius, stroke.color);\n    /// }\n    ///\n    /// egui::CollapsingHeader::new(\"Circles\")\n    ///   .icon(circle_icon)\n    ///   .show(ui, |ui| { ui.label(\"Hi!\"); });\n    /// # });\n    /// ```\n    #[inline]\n    pub fn icon(mut self, icon_fn: impl FnOnce(&mut Ui, f32, &Response) + 'static) -> Self {\n        self.icon = Some(Box::new(icon_fn));\n        self\n    }\n}\n\nstruct Prepared {\n    header_response: Response,\n    state: CollapsingState,\n    openness: f32,\n}\n\nimpl CollapsingHeader {\n    fn begin(self, ui: &mut Ui) -> Prepared {\n        assert!(\n            ui.layout().main_dir().is_vertical(),\n            \"Horizontal collapsing is unimplemented\"\n        );\n        let Self {\n            icon,\n            text,\n            default_open,\n            open,\n            id_salt,\n            enabled: _,\n            selectable,\n            selected,\n            show_background,\n        } = self;\n\n        // TODO(emilk): horizontal layout, with icon and text as labels. Insert background behind using Frame.\n\n        let id = ui.make_persistent_id(id_salt);\n        let button_padding = ui.spacing().button_padding;\n\n        let available = ui.available_rect_before_wrap();\n        let text_pos = available.min + vec2(ui.spacing().indent, 0.0);\n        let wrap_width = available.right() - text_pos.x;\n        let galley = text.into_galley(\n            ui,\n            Some(TextWrapMode::Extend),\n            wrap_width,\n            TextStyle::Button,\n        );\n        let text_max_x = text_pos.x + galley.size().x;\n\n        let mut desired_width = text_max_x + button_padding.x - available.left();\n        if ui.visuals().collapsing_header_frame {\n            desired_width = desired_width.max(available.width()); // fill full width\n        }\n\n        let mut desired_size = vec2(desired_width, galley.size().y + 2.0 * button_padding.y);\n        desired_size = desired_size.at_least(ui.spacing().interact_size);\n        let (_, rect) = ui.allocate_space(desired_size);\n\n        let mut header_response = ui.interact(rect, id, Sense::click());\n        let text_pos = pos2(\n            text_pos.x,\n            header_response.rect.center().y - galley.size().y / 2.0,\n        );\n\n        let mut state = CollapsingState::load_with_default_open(ui.ctx(), id, default_open);\n        if let Some(open) = open {\n            if open != state.is_open() {\n                state.toggle(ui);\n                header_response.mark_changed();\n            }\n        } else if header_response.clicked() {\n            state.toggle(ui);\n            header_response.mark_changed();\n        }\n\n        header_response.widget_info(|| {\n            WidgetInfo::labeled(WidgetType::CollapsingHeader, ui.is_enabled(), galley.text())\n        });\n\n        let openness = state.openness(ui.ctx());\n\n        if ui.is_rect_visible(rect) {\n            let visuals = ui.style().interact_selectable(&header_response, selected);\n\n            if ui.visuals().collapsing_header_frame || show_background {\n                ui.painter().add(epaint::RectShape::new(\n                    header_response.rect.expand(visuals.expansion),\n                    visuals.corner_radius,\n                    visuals.weak_bg_fill,\n                    visuals.bg_stroke,\n                    StrokeKind::Inside,\n                ));\n            }\n\n            if selected || selectable && (header_response.hovered() || header_response.has_focus())\n            {\n                let rect = rect.expand(visuals.expansion);\n\n                ui.painter().rect(\n                    rect,\n                    visuals.corner_radius,\n                    visuals.bg_fill,\n                    visuals.bg_stroke,\n                    StrokeKind::Inside,\n                );\n            }\n\n            {\n                let (mut icon_rect, _) = ui.spacing().icon_rectangles(header_response.rect);\n                icon_rect.set_center(pos2(\n                    header_response.rect.left() + ui.spacing().indent / 2.0,\n                    header_response.rect.center().y,\n                ));\n                let icon_response = header_response.clone().with_new_rect(icon_rect);\n                if let Some(icon) = icon {\n                    icon(ui, openness, &icon_response);\n                } else {\n                    paint_default_icon(ui, openness, &icon_response);\n                }\n            }\n\n            ui.painter().galley(text_pos, galley, visuals.text_color());\n        }\n\n        Prepared {\n            header_response,\n            state,\n            openness,\n        }\n    }\n\n    #[inline]\n    pub fn show<R>(\n        self,\n        ui: &mut Ui,\n        add_body: impl FnOnce(&mut Ui) -> R,\n    ) -> CollapsingResponse<R> {\n        self.show_dyn(ui, Box::new(add_body), true)\n    }\n\n    #[inline]\n    pub fn show_unindented<R>(\n        self,\n        ui: &mut Ui,\n        add_body: impl FnOnce(&mut Ui) -> R,\n    ) -> CollapsingResponse<R> {\n        self.show_dyn(ui, Box::new(add_body), false)\n    }\n\n    fn show_dyn<'c, R>(\n        self,\n        ui: &mut Ui,\n        add_body: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n        indented: bool,\n    ) -> CollapsingResponse<R> {\n        // Make sure body is bellow header,\n        // and make sure it is one unit (necessary for putting a [`CollapsingHeader`] in a grid).\n        ui.vertical(|ui| {\n            if !self.enabled {\n                ui.disable();\n            }\n\n            let Prepared {\n                header_response,\n                mut state,\n                openness,\n            } = self.begin(ui); // show the header\n\n            let ret_response = if indented {\n                state.show_body_indented(&header_response, ui, add_body)\n            } else {\n                state.show_body_unindented(ui, add_body)\n            };\n\n            if let Some(ret_response) = ret_response {\n                CollapsingResponse {\n                    header_response,\n                    body_response: Some(ret_response.response),\n                    body_returned: Some(ret_response.inner),\n                    openness,\n                }\n            } else {\n                CollapsingResponse {\n                    header_response,\n                    body_response: None,\n                    body_returned: None,\n                    openness,\n                }\n            }\n        })\n        .inner\n    }\n}\n\n/// The response from showing a [`CollapsingHeader`].\npub struct CollapsingResponse<R> {\n    /// Response of the actual clickable header.\n    pub header_response: Response,\n\n    /// None iff collapsed.\n    pub body_response: Option<Response>,\n\n    /// None iff collapsed.\n    pub body_returned: Option<R>,\n\n    /// 0.0 if fully closed, 1.0 if fully open, and something in-between while animating.\n    pub openness: f32,\n}\n\nimpl<R> CollapsingResponse<R> {\n    /// Was the [`CollapsingHeader`] fully closed (and not being animated)?\n    pub fn fully_closed(&self) -> bool {\n        self.openness <= 0.0\n    }\n\n    /// Was the [`CollapsingHeader`] fully open (and not being animated)?\n    pub fn fully_open(&self) -> bool {\n        self.openness >= 1.0\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/combo_box.rs",
    "content": "use epaint::Shape;\n\nuse crate::{\n    Align2, Context, Id, InnerResponse, NumExt as _, Painter, Popup, PopupCloseBehavior, Rect,\n    Response, ScrollArea, Sense, Stroke, TextStyle, TextWrapMode, Ui, UiBuilder, Vec2, WidgetInfo,\n    WidgetText, WidgetType, epaint, style::StyleModifier, style::WidgetVisuals, vec2,\n};\n\n#[expect(unused_imports)] // Documentation\nuse crate::style::Spacing;\n\n/// A function that paints the [`ComboBox`] icon\npub type IconPainter = Box<dyn FnOnce(&Ui, Rect, &WidgetVisuals, bool)>;\n\n/// A drop-down selection menu with a descriptive label.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # #[derive(Debug, PartialEq, Copy, Clone)]\n/// # enum Enum { First, Second, Third }\n/// # let mut selected = Enum::First;\n/// let before = selected;\n/// egui::ComboBox::from_label(\"Select one!\")\n///     .selected_text(format!(\"{:?}\", selected))\n///     .show_ui(ui, |ui| {\n///         ui.selectable_value(&mut selected, Enum::First, \"First\");\n///         ui.selectable_value(&mut selected, Enum::Second, \"Second\");\n///         ui.selectable_value(&mut selected, Enum::Third, \"Third\");\n///     }\n/// );\n///\n/// if selected != before {\n///     // Handle selection change\n/// }\n/// # });\n/// ```\n#[must_use = \"You should call .show*\"]\npub struct ComboBox {\n    id_salt: Id,\n    label: Option<WidgetText>,\n    selected_text: WidgetText,\n    width: Option<f32>,\n    height: Option<f32>,\n    icon: Option<IconPainter>,\n    wrap_mode: Option<TextWrapMode>,\n    close_behavior: Option<PopupCloseBehavior>,\n    popup_style: StyleModifier,\n}\n\nimpl ComboBox {\n    /// Create new [`ComboBox`] with id and label\n    pub fn new(id_salt: impl std::hash::Hash, label: impl Into<WidgetText>) -> Self {\n        Self {\n            id_salt: Id::new(id_salt),\n            label: Some(label.into()),\n            selected_text: Default::default(),\n            width: None,\n            height: None,\n            icon: None,\n            wrap_mode: None,\n            close_behavior: None,\n            popup_style: StyleModifier::default(),\n        }\n    }\n\n    /// Label shown next to the combo box\n    pub fn from_label(label: impl Into<WidgetText>) -> Self {\n        let label = label.into();\n        Self {\n            id_salt: Id::new(label.text()),\n            label: Some(label),\n            selected_text: Default::default(),\n            width: None,\n            height: None,\n            icon: None,\n            wrap_mode: None,\n            close_behavior: None,\n            popup_style: StyleModifier::default(),\n        }\n    }\n\n    /// Without label.\n    pub fn from_id_salt(id_salt: impl std::hash::Hash) -> Self {\n        Self {\n            id_salt: Id::new(id_salt),\n            label: Default::default(),\n            selected_text: Default::default(),\n            width: None,\n            height: None,\n            icon: None,\n            wrap_mode: None,\n            close_behavior: None,\n            popup_style: StyleModifier::default(),\n        }\n    }\n\n    /// Without label.\n    #[deprecated = \"Renamed from_id_salt\"]\n    pub fn from_id_source(id_salt: impl std::hash::Hash) -> Self {\n        Self::from_id_salt(id_salt)\n    }\n\n    /// Set the outer width of the button and menu.\n    ///\n    /// Default is [`Spacing::combo_width`].\n    #[inline]\n    pub fn width(mut self, width: f32) -> Self {\n        self.width = Some(width);\n        self\n    }\n\n    /// Set the maximum outer height of the menu.\n    ///\n    /// Default is [`Spacing::combo_height`].\n    #[inline]\n    pub fn height(mut self, height: f32) -> Self {\n        self.height = Some(height);\n        self\n    }\n\n    /// What we show as the currently selected value\n    #[inline]\n    pub fn selected_text(mut self, selected_text: impl Into<WidgetText>) -> Self {\n        self.selected_text = selected_text.into();\n        self\n    }\n\n    /// Use the provided function to render a different [`ComboBox`] icon.\n    /// Defaults to a triangle that expands when the cursor is hovering over the [`ComboBox`].\n    ///\n    /// For example:\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let text = \"Selected text\";\n    /// pub fn filled_triangle(\n    ///     ui: &egui::Ui,\n    ///     rect: egui::Rect,\n    ///     visuals: &egui::style::WidgetVisuals,\n    ///     _is_open: bool,\n    /// ) {\n    ///     let rect = egui::Rect::from_center_size(\n    ///         rect.center(),\n    ///         egui::vec2(rect.width() * 0.6, rect.height() * 0.4),\n    ///     );\n    ///     ui.painter().add(egui::Shape::convex_polygon(\n    ///         vec![rect.left_top(), rect.right_top(), rect.center_bottom()],\n    ///         visuals.fg_stroke.color,\n    ///         visuals.fg_stroke,\n    ///     ));\n    /// }\n    ///\n    /// egui::ComboBox::from_id_salt(\"my-combobox\")\n    ///     .selected_text(text)\n    ///     .icon(filled_triangle)\n    ///     .show_ui(ui, |_ui| {});\n    /// # });\n    /// ```\n    #[inline]\n    pub fn icon(mut self, icon_fn: impl FnOnce(&Ui, Rect, &WidgetVisuals, bool) + 'static) -> Self {\n        self.icon = Some(Box::new(icon_fn));\n        self\n    }\n\n    /// Controls the wrap mode used for the selected text.\n    ///\n    /// By default, [`Ui::wrap_mode`] will be used, which can be overridden with [`crate::Style::wrap_mode`].\n    ///\n    /// Note that any `\\n` in the text will always produce a new line.\n    #[inline]\n    pub fn wrap_mode(mut self, wrap_mode: TextWrapMode) -> Self {\n        self.wrap_mode = Some(wrap_mode);\n        self\n    }\n\n    /// Set [`Self::wrap_mode`] to [`TextWrapMode::Wrap`].\n    #[inline]\n    pub fn wrap(mut self) -> Self {\n        self.wrap_mode = Some(TextWrapMode::Wrap);\n        self\n    }\n\n    /// Set [`Self::wrap_mode`] to [`TextWrapMode::Truncate`].\n    #[inline]\n    pub fn truncate(mut self) -> Self {\n        self.wrap_mode = Some(TextWrapMode::Truncate);\n        self\n    }\n\n    /// Controls the close behavior for the popup.\n    ///\n    /// By default, `PopupCloseBehavior::CloseOnClick` will be used.\n    #[inline]\n    pub fn close_behavior(mut self, close_behavior: PopupCloseBehavior) -> Self {\n        self.close_behavior = Some(close_behavior);\n        self\n    }\n\n    /// Set the style of the popup menu.\n    ///\n    /// Could for example be used with [`crate::containers::menu::menu_style`] to get the frame-less\n    /// menu button style.\n    #[inline]\n    pub fn popup_style(mut self, popup_style: StyleModifier) -> Self {\n        self.popup_style = popup_style;\n        self\n    }\n\n    /// Show the combo box, with the given ui code for the menu contents.\n    ///\n    /// Returns `InnerResponse { inner: None }` if the combo box is closed.\n    pub fn show_ui<R>(\n        self,\n        ui: &mut Ui,\n        menu_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<Option<R>> {\n        self.show_ui_dyn(ui, Box::new(menu_contents))\n    }\n\n    fn show_ui_dyn<'c, R>(\n        self,\n        ui: &mut Ui,\n        menu_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> InnerResponse<Option<R>> {\n        let Self {\n            id_salt,\n            label,\n            selected_text,\n            width,\n            height,\n            icon,\n            wrap_mode,\n            close_behavior,\n            popup_style,\n        } = self;\n\n        let button_id = ui.make_persistent_id(id_salt);\n\n        ui.horizontal(|ui| {\n            let mut ir = combo_box_dyn(\n                ui,\n                button_id,\n                selected_text.clone(),\n                menu_contents,\n                icon,\n                wrap_mode,\n                close_behavior,\n                popup_style,\n                (width, height),\n            );\n            ir.response.widget_info(|| {\n                let mut info = WidgetInfo::new(WidgetType::ComboBox);\n                info.enabled = ui.is_enabled();\n                info.current_text_value = Some(selected_text.text().to_owned());\n                info\n            });\n            if let Some(label) = label {\n                let label_response = ui.label(label);\n                ir.response = ir.response.labelled_by(label_response.id);\n                ir.response |= label_response;\n            }\n            ir\n        })\n        .inner\n    }\n\n    /// Show a list of items with the given selected index.\n    ///\n    ///\n    /// ```\n    /// # #[derive(Debug, PartialEq)]\n    /// # enum Enum { First, Second, Third }\n    /// # let mut selected = Enum::First;\n    /// # egui::__run_test_ui(|ui| {\n    /// let alternatives = [\"a\", \"b\", \"c\", \"d\"];\n    /// let mut selected = 2;\n    /// egui::ComboBox::from_label(\"Select one!\").show_index(\n    ///     ui,\n    ///     &mut selected,\n    ///     alternatives.len(),\n    ///     |i| alternatives[i]\n    /// );\n    /// # });\n    /// ```\n    pub fn show_index<Text: Into<WidgetText>>(\n        self,\n        ui: &mut Ui,\n        selected: &mut usize,\n        len: usize,\n        get: impl Fn(usize) -> Text,\n    ) -> Response {\n        let slf = self.selected_text(get(*selected));\n\n        let mut changed = false;\n\n        let mut response = slf\n            .show_ui(ui, |ui| {\n                for i in 0..len {\n                    if ui.selectable_label(i == *selected, get(i)).clicked() {\n                        *selected = i;\n                        changed = true;\n                    }\n                }\n            })\n            .response;\n\n        if changed {\n            response.mark_changed();\n        }\n        response\n    }\n\n    /// Check if the [`ComboBox`] with the given id has its popup menu currently opened.\n    pub fn is_open(ctx: &Context, id: Id) -> bool {\n        Popup::is_id_open(ctx, Self::widget_to_popup_id(id))\n    }\n\n    /// Convert a [`ComboBox`] id to the id used to store it's popup state.\n    fn widget_to_popup_id(widget_id: Id) -> Id {\n        widget_id.with(\"popup\")\n    }\n}\n\n#[expect(clippy::too_many_arguments)]\nfn combo_box_dyn<'c, R>(\n    ui: &mut Ui,\n    button_id: Id,\n    selected_text: WidgetText,\n    menu_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    icon: Option<IconPainter>,\n    wrap_mode: Option<TextWrapMode>,\n    close_behavior: Option<PopupCloseBehavior>,\n    popup_style: StyleModifier,\n    (width, height): (Option<f32>, Option<f32>),\n) -> InnerResponse<Option<R>> {\n    let popup_id = ComboBox::widget_to_popup_id(button_id);\n\n    let is_popup_open = Popup::is_id_open(ui.ctx(), popup_id);\n\n    let wrap_mode = wrap_mode.unwrap_or_else(|| ui.wrap_mode());\n\n    let close_behavior = close_behavior.unwrap_or(PopupCloseBehavior::CloseOnClick);\n\n    let margin = ui.spacing().button_padding;\n    let button_response = button_frame(ui, button_id, is_popup_open, Sense::click(), |ui| {\n        let icon_spacing = ui.spacing().icon_spacing;\n        let icon_size = Vec2::splat(ui.spacing().icon_width);\n\n        // The combo box selected text will always have this minimum width.\n        // Note: the `ComboBox::width()` if set or `Spacing::combo_width` are considered as the\n        // minimum overall width, regardless of the wrap mode.\n        let minimum_width = width.unwrap_or_else(|| ui.spacing().combo_width) - 2.0 * margin.x;\n\n        // width against which to lay out the selected text\n        let wrap_width = if wrap_mode == TextWrapMode::Extend {\n            // Use all the width necessary to display the currently selected value's text.\n            f32::INFINITY\n        } else {\n            // Use the available width, currently selected value's text will be wrapped if exceeds this value.\n            ui.available_width() - icon_spacing - icon_size.x\n        };\n\n        let galley = selected_text.into_galley(ui, Some(wrap_mode), wrap_width, TextStyle::Button);\n\n        let actual_width = (galley.size().x + icon_spacing + icon_size.x).at_least(minimum_width);\n        let actual_height = galley.size().y.max(icon_size.y);\n\n        let (_, rect) = ui.allocate_space(Vec2::new(actual_width, actual_height));\n        let button_rect = ui.min_rect().expand2(ui.spacing().button_padding);\n        let response = ui.interact(button_rect, button_id, Sense::click());\n        // response.active |= is_popup_open;\n\n        if ui.is_rect_visible(rect) {\n            let icon_rect = Align2::RIGHT_CENTER.align_size_within_rect(icon_size, rect);\n            let visuals = if is_popup_open {\n                &ui.visuals().widgets.open\n            } else {\n                ui.style().interact(&response)\n            };\n\n            if let Some(icon) = icon {\n                icon(\n                    ui,\n                    icon_rect.expand(visuals.expansion),\n                    visuals,\n                    is_popup_open,\n                );\n            } else {\n                paint_default_icon(ui.painter(), icon_rect.expand(visuals.expansion), visuals);\n            }\n\n            let text_rect = Align2::LEFT_CENTER.align_size_within_rect(galley.size(), rect);\n            ui.painter()\n                .galley(text_rect.min, galley, visuals.text_color());\n        }\n    });\n\n    let height = height.unwrap_or_else(|| ui.spacing().combo_height);\n\n    let inner = Popup::menu(&button_response)\n        .id(popup_id)\n        .width(button_response.rect.width())\n        .close_behavior(close_behavior)\n        .style(popup_style)\n        .show(|ui| {\n            ui.set_min_width(ui.available_width());\n\n            ScrollArea::vertical()\n                .max_height(height)\n                .show(ui, |ui| {\n                    // Often the button is very narrow, which means this popup\n                    // is also very narrow. Having wrapping on would therefore\n                    // result in labels that wrap very early.\n                    // Instead, we turn it off by default so that the labels\n                    // expand the width of the menu.\n                    ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);\n                    menu_contents(ui)\n                })\n                .inner\n        })\n        .map(|r| r.inner);\n\n    InnerResponse {\n        inner,\n        response: button_response,\n    }\n}\n\nfn button_frame(\n    ui: &mut Ui,\n    id: Id,\n    is_popup_open: bool,\n    sense: Sense,\n    add_contents: impl FnOnce(&mut Ui),\n) -> Response {\n    let where_to_put_background = ui.painter().add(Shape::Noop);\n\n    let margin = ui.spacing().button_padding;\n    let interact_size = ui.spacing().interact_size;\n\n    let mut outer_rect = ui.available_rect_before_wrap();\n    outer_rect.set_height(outer_rect.height().at_least(interact_size.y));\n\n    let inner_rect = outer_rect.shrink2(margin);\n    let mut content_ui = ui.new_child(UiBuilder::new().max_rect(inner_rect));\n    add_contents(&mut content_ui);\n\n    let mut outer_rect = content_ui.min_rect().expand2(margin);\n    outer_rect.set_height(outer_rect.height().at_least(interact_size.y));\n\n    let response = ui.interact(outer_rect, id, sense);\n\n    if ui.is_rect_visible(outer_rect) {\n        let visuals = if is_popup_open {\n            &ui.visuals().widgets.open\n        } else {\n            ui.style().interact(&response)\n        };\n\n        ui.painter().set(\n            where_to_put_background,\n            epaint::RectShape::new(\n                outer_rect.expand(visuals.expansion),\n                visuals.corner_radius,\n                visuals.weak_bg_fill,\n                visuals.bg_stroke,\n                epaint::StrokeKind::Inside,\n            ),\n        );\n    }\n\n    ui.advance_cursor_after_rect(outer_rect);\n\n    response\n}\n\nfn paint_default_icon(painter: &Painter, rect: Rect, visuals: &WidgetVisuals) {\n    let rect = Rect::from_center_size(\n        rect.center(),\n        vec2(rect.width() * 0.7, rect.height() * 0.45),\n    );\n\n    // Downward pointing triangle\n    // Previously, we would show an up arrow when we expected the popup to open upwards\n    // (due to lack of space below the button), but this could look weird in edge cases, so this\n    // feature was removed. (See https://github.com/emilk/egui/pull/5713#issuecomment-2654420245)\n    painter.add(Shape::convex_polygon(\n        vec![rect.left_top(), rect.right_top(), rect.center_bottom()],\n        visuals.fg_stroke.color,\n        Stroke::NONE,\n    ));\n}\n"
  },
  {
    "path": "crates/egui/src/containers/frame.rs",
    "content": "//! Frame container\n\nuse crate::{\n    InnerResponse, Response, Sense, Style, Ui, UiBuilder, UiKind, UiStackInfo, epaint,\n    layers::ShapeIdx,\n};\nuse epaint::{Color32, CornerRadius, Margin, MarginF32, Rect, Shadow, Shape, Stroke};\n\n/// A frame around some content, including margin, colors, etc.\n///\n/// ## Definitions\n/// The total (outer) size of a frame is\n/// `content_size + inner_margin + 2 * stroke.width + outer_margin`.\n///\n/// Everything within the stroke is filled with the fill color (if any).\n///\n/// ```text\n/// +-----------------^-------------------------------------- -+\n/// |                 | outer_margin                           |\n/// |    +------------v----^------------------------------+    |\n/// |    |                 | stroke width                 |    |\n/// |    |    +------------v---^---------------------+    |    |\n/// |    |    |                | inner_margin        |    |    |\n/// |    |    |    +-----------v----------------+    |    |    |\n/// |    |    |    |             ^              |    |    |    |\n/// |    |    |    |             |              |    |    |    |\n/// |    |    |    |<------ content_size ------>|    |    |    |\n/// |    |    |    |             |              |    |    |    |\n/// |    |    |    |             v              |    |    |    |\n/// |    |    |    +------- content_rect -------+    |    |    |\n/// |    |    |                                      |    |    |\n/// |    |    +-------------fill_rect ---------------+    |    |\n/// |    |                                                |    |\n/// |    +----------------- widget_rect ------------------+    |\n/// |                                                          |\n/// +---------------------- outer_rect ------------------------+\n/// ```\n///\n/// The four rectangles, from inside to outside, are:\n/// * `content_rect`: the rectangle that is made available to the inner [`Ui`] or widget.\n/// * `fill_rect`: the rectangle that is filled with the fill color (inside the stroke, if any).\n/// * `widget_rect`: is the interactive part of the widget (what sense clicks etc).\n/// * `outer_rect`: what is allocated in the outer [`Ui`], and is what is returned by [`Response::rect`].\n///\n/// ## Usage\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// egui::Frame::NONE\n///     .fill(egui::Color32::RED)\n///     .show(ui, |ui| {\n///         ui.label(\"Label with red background\");\n///     });\n/// # });\n/// ```\n///\n/// ## Dynamic color\n/// If you want to change the color of the frame based on the response of\n/// the widget, you need to break it up into multiple steps:\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// let mut frame = egui::Frame::default().inner_margin(4.0).begin(ui);\n/// {\n///     let response = frame.content_ui.label(\"Inside the frame\");\n///     if response.hovered() {\n///         frame.frame.fill = egui::Color32::RED;\n///     }\n/// }\n/// frame.end(ui); // Will \"close\" the frame.\n/// # });\n/// ```\n///\n/// You can also respond to the hovering of the frame itself:\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// let mut frame = egui::Frame::default().inner_margin(4.0).begin(ui);\n/// {\n///     frame.content_ui.label(\"Inside the frame\");\n///     frame.content_ui.label(\"This too\");\n/// }\n/// let response = frame.allocate_space(ui);\n/// if response.hovered() {\n///     frame.frame.fill = egui::Color32::RED;\n/// }\n/// frame.paint(ui);\n/// # });\n/// ```\n///\n/// Note that you cannot change the margins after calling `begin`.\n#[doc(alias = \"border\")]\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[must_use = \"You should call .show()\"]\npub struct Frame {\n    // Fields are ordered inside-out.\n    // TODO(emilk): add `min_content_size: Vec2`\n    //\n    /// Margin within the painted frame.\n    ///\n    /// Known as `padding` in CSS.\n    #[doc(alias = \"padding\")]\n    pub inner_margin: Margin,\n\n    /// The background fill color of the frame, within the [`Self::stroke`].\n    ///\n    /// Known as `background` in CSS.\n    #[doc(alias = \"background\")]\n    pub fill: Color32,\n\n    /// The width and color of the outline around the frame.\n    ///\n    /// The width of the stroke is part of the total margin/padding of the frame.\n    #[doc(alias = \"border\")]\n    pub stroke: Stroke,\n\n    /// The rounding of the _outer_ corner of the [`Self::stroke`]\n    /// (or, if there is no stroke, the outer corner of [`Self::fill`]).\n    ///\n    /// In other words, this is the corner radius of the _widget rect_.\n    pub corner_radius: CornerRadius,\n\n    /// Margin outside the painted frame.\n    ///\n    /// Similar to what is called `margin` in CSS.\n    /// However, egui does NOT do \"Margin Collapse\" like in CSS,\n    /// i.e. when placing two frames next to each other,\n    /// the distance between their borders is the SUM\n    /// of their other margins.\n    /// In CSS the distance would be the MAX of their outer margins.\n    /// Supporting margin collapse is difficult, and would\n    /// requires complicating the already complicated egui layout code.\n    ///\n    /// Consider using [`crate::Spacing::item_spacing`]\n    /// for adding space between widgets.\n    pub outer_margin: Margin,\n\n    /// Optional drop-shadow behind the frame.\n    pub shadow: Shadow,\n}\n\n#[test]\nfn frame_size() {\n    assert_eq!(\n        std::mem::size_of::<Frame>(),\n        32,\n        \"Frame changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it.\"\n    );\n    assert!(\n        std::mem::size_of::<Frame>() <= 64,\n        \"Frame is getting way too big!\"\n    );\n}\n\n/// ## Constructors\nimpl Frame {\n    /// No colors, no margins, no border.\n    ///\n    /// This is also the default.\n    pub const NONE: Self = Self {\n        inner_margin: Margin::ZERO,\n        stroke: Stroke::NONE,\n        fill: Color32::TRANSPARENT,\n        corner_radius: CornerRadius::ZERO,\n        outer_margin: Margin::ZERO,\n        shadow: Shadow::NONE,\n    };\n\n    /// No colors, no margins, no border.\n    ///\n    /// Same as [`Frame::NONE`].\n    pub const fn new() -> Self {\n        Self::NONE\n    }\n\n    #[deprecated = \"Use `Frame::NONE` or `Frame::new()` instead.\"]\n    pub const fn none() -> Self {\n        Self::NONE\n    }\n\n    /// For when you want to group a few widgets together within a frame.\n    pub fn group(style: &Style) -> Self {\n        Self::new()\n            .inner_margin(6)\n            .corner_radius(style.visuals.widgets.noninteractive.corner_radius)\n            .stroke(style.visuals.widgets.noninteractive.bg_stroke)\n    }\n\n    pub fn side_top_panel(style: &Style) -> Self {\n        Self::new()\n            .inner_margin(Margin::symmetric(8, 2))\n            .fill(style.visuals.panel_fill)\n    }\n\n    pub fn central_panel(style: &Style) -> Self {\n        Self::new().inner_margin(8).fill(style.visuals.panel_fill)\n    }\n\n    pub fn window(style: &Style) -> Self {\n        Self::new()\n            .inner_margin(style.spacing.window_margin)\n            .corner_radius(style.visuals.window_corner_radius)\n            .shadow(style.visuals.window_shadow)\n            .fill(style.visuals.window_fill())\n            .stroke(style.visuals.window_stroke())\n    }\n\n    pub fn menu(style: &Style) -> Self {\n        Self::new()\n            .inner_margin(style.spacing.menu_margin)\n            .corner_radius(style.visuals.menu_corner_radius)\n            .shadow(style.visuals.popup_shadow)\n            .fill(style.visuals.window_fill())\n            .stroke(style.visuals.window_stroke())\n    }\n\n    pub fn popup(style: &Style) -> Self {\n        Self::new()\n            .inner_margin(style.spacing.menu_margin)\n            .corner_radius(style.visuals.menu_corner_radius)\n            .shadow(style.visuals.popup_shadow)\n            .fill(style.visuals.window_fill())\n            .stroke(style.visuals.window_stroke())\n    }\n\n    /// A canvas to draw on.\n    ///\n    /// In bright mode this will be very bright,\n    /// and in dark mode this will be very dark.\n    pub fn canvas(style: &Style) -> Self {\n        Self::new()\n            .inner_margin(2)\n            .corner_radius(style.visuals.widgets.noninteractive.corner_radius)\n            .fill(style.visuals.extreme_bg_color)\n            .stroke(style.visuals.window_stroke())\n    }\n\n    /// A dark canvas to draw on.\n    pub fn dark_canvas(style: &Style) -> Self {\n        Self::canvas(style).fill(Color32::from_black_alpha(250))\n    }\n}\n\n/// ## Builders\nimpl Frame {\n    /// Margin within the painted frame.\n    ///\n    /// Known as `padding` in CSS.\n    #[doc(alias = \"padding\")]\n    #[inline]\n    pub fn inner_margin(mut self, inner_margin: impl Into<Margin>) -> Self {\n        self.inner_margin = inner_margin.into();\n        self\n    }\n\n    /// The background fill color of the frame, within the [`Self::stroke`].\n    ///\n    /// Known as `background` in CSS.\n    #[doc(alias = \"background\")]\n    #[inline]\n    pub fn fill(mut self, fill: Color32) -> Self {\n        self.fill = fill;\n        self\n    }\n\n    /// The width and color of the outline around the frame.\n    ///\n    /// The width of the stroke is part of the total margin/padding of the frame.\n    #[inline]\n    pub fn stroke(mut self, stroke: impl Into<Stroke>) -> Self {\n        self.stroke = stroke.into();\n        self\n    }\n\n    /// The rounding of the _outer_ corner of the [`Self::stroke`]\n    /// (or, if there is no stroke, the outer corner of [`Self::fill`]).\n    ///\n    /// In other words, this is the corner radius of the _widget rect_.\n    #[inline]\n    pub fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {\n        self.corner_radius = corner_radius.into();\n        self\n    }\n\n    /// The rounding of the _outer_ corner of the [`Self::stroke`]\n    /// (or, if there is no stroke, the outer corner of [`Self::fill`]).\n    ///\n    /// In other words, this is the corner radius of the _widget rect_.\n    #[inline]\n    #[deprecated = \"Renamed to `corner_radius`\"]\n    pub fn rounding(self, corner_radius: impl Into<CornerRadius>) -> Self {\n        self.corner_radius(corner_radius)\n    }\n\n    /// Margin outside the painted frame.\n    ///\n    /// Similar to what is called `margin` in CSS.\n    /// However, egui does NOT do \"Margin Collapse\" like in CSS,\n    /// i.e. when placing two frames next to each other,\n    /// the distance between their borders is the SUM\n    /// of their other margins.\n    /// In CSS the distance would be the MAX of their outer margins.\n    /// Supporting margin collapse is difficult, and would\n    /// requires complicating the already complicated egui layout code.\n    ///\n    /// Consider using [`crate::Spacing::item_spacing`]\n    /// for adding space between widgets.\n    #[inline]\n    pub fn outer_margin(mut self, outer_margin: impl Into<Margin>) -> Self {\n        self.outer_margin = outer_margin.into();\n        self\n    }\n\n    /// Optional drop-shadow behind the frame.\n    #[inline]\n    pub fn shadow(mut self, shadow: Shadow) -> Self {\n        self.shadow = shadow;\n        self\n    }\n\n    /// Opacity multiplier in gamma space.\n    ///\n    /// For instance, multiplying with `0.5`\n    /// will make the frame half transparent.\n    #[inline]\n    pub fn multiply_with_opacity(mut self, opacity: f32) -> Self {\n        self.fill = self.fill.gamma_multiply(opacity);\n        self.stroke.color = self.stroke.color.gamma_multiply(opacity);\n        self.shadow.color = self.shadow.color.gamma_multiply(opacity);\n        self\n    }\n}\n\n/// ## Inspectors\nimpl Frame {\n    /// How much extra space the frame uses up compared to the content.\n    ///\n    /// [`Self::inner_margin`] + [`Self::stroke`]`.width` + [`Self::outer_margin`].\n    #[inline]\n    pub fn total_margin(&self) -> MarginF32 {\n        MarginF32::from(self.inner_margin)\n            + MarginF32::from(self.stroke.width)\n            + MarginF32::from(self.outer_margin)\n    }\n\n    /// Calculate the `fill_rect` from the `content_rect`.\n    ///\n    /// This is the rectangle that is filled with the fill color (inside the stroke, if any).\n    pub fn fill_rect(&self, content_rect: Rect) -> Rect {\n        content_rect + self.inner_margin\n    }\n\n    /// Calculate the `widget_rect` from the `content_rect`.\n    ///\n    /// This is the visible and interactive rectangle.\n    pub fn widget_rect(&self, content_rect: Rect) -> Rect {\n        content_rect + self.inner_margin + MarginF32::from(self.stroke.width)\n    }\n\n    /// Calculate the `outer_rect` from the `content_rect`.\n    ///\n    /// This is what is allocated in the outer [`Ui`], and is what is returned by [`Response::rect`].\n    pub fn outer_rect(&self, content_rect: Rect) -> Rect {\n        content_rect + self.inner_margin + MarginF32::from(self.stroke.width) + self.outer_margin\n    }\n}\n\n// ----------------------------------------------------------------------------\n\npub struct Prepared {\n    /// The frame that was prepared.\n    ///\n    /// The margin has already been read and used,\n    /// but the rest of the fields may be modified.\n    pub frame: Frame,\n\n    /// This is where we will insert the frame shape so it ends up behind the content.\n    where_to_put_background: ShapeIdx,\n\n    /// Add your widgets to this UI so it ends up within the frame.\n    pub content_ui: Ui,\n}\n\nimpl Frame {\n    /// Begin a dynamically colored frame.\n    ///\n    /// This is a more advanced API.\n    /// Usually you want to use [`Self::show`] instead.\n    ///\n    /// See docs for [`Frame`] for an example.\n    pub fn begin(self, ui: &mut Ui) -> Prepared {\n        let where_to_put_background = ui.painter().add(Shape::Noop);\n        let outer_rect_bounds = ui.available_rect_before_wrap();\n\n        let mut max_content_rect = outer_rect_bounds - self.total_margin();\n\n        // Make sure we don't shrink to the negative:\n        max_content_rect.max.x = max_content_rect.max.x.max(max_content_rect.min.x);\n        max_content_rect.max.y = max_content_rect.max.y.max(max_content_rect.min.y);\n\n        let content_ui = ui.new_child(\n            UiBuilder::new()\n                .ui_stack_info(UiStackInfo::new(UiKind::Frame).with_frame(self))\n                .max_rect(max_content_rect),\n        );\n\n        Prepared {\n            frame: self,\n            where_to_put_background,\n            content_ui,\n        }\n    }\n\n    /// Show the given ui surrounded by this frame.\n    pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {\n        self.show_dyn(ui, Box::new(add_contents))\n    }\n\n    /// Show using dynamic dispatch.\n    pub fn show_dyn<'c, R>(\n        self,\n        ui: &mut Ui,\n        add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> InnerResponse<R> {\n        let mut prepared = self.begin(ui);\n        let ret = add_contents(&mut prepared.content_ui);\n        let response = prepared.end(ui);\n        InnerResponse::new(ret, response)\n    }\n\n    /// Paint this frame as a shape.\n    pub fn paint(&self, content_rect: Rect) -> Shape {\n        let Self {\n            inner_margin: _,\n            fill,\n            stroke,\n            corner_radius,\n            outer_margin: _,\n            shadow,\n        } = *self;\n\n        let widget_rect = self.widget_rect(content_rect);\n\n        let frame_shape = Shape::Rect(epaint::RectShape::new(\n            widget_rect,\n            corner_radius,\n            fill,\n            stroke,\n            epaint::StrokeKind::Inside,\n        ));\n\n        if shadow == Default::default() {\n            frame_shape\n        } else {\n            let shadow = shadow.as_shape(widget_rect, corner_radius);\n            Shape::Vec(vec![Shape::from(shadow), frame_shape])\n        }\n    }\n}\n\nimpl Prepared {\n    fn outer_rect(&self) -> Rect {\n        let content_rect = self.content_ui.min_rect();\n        content_rect\n            + self.frame.inner_margin\n            + MarginF32::from(self.frame.stroke.width)\n            + self.frame.outer_margin\n    }\n\n    /// Allocate the space that was used by [`Self::content_ui`].\n    ///\n    /// This MUST be called, or the parent ui will not know how much space this widget used.\n    ///\n    /// This can be called before or after [`Self::paint`].\n    pub fn allocate_space(&self, ui: &mut Ui) -> Response {\n        ui.allocate_rect(self.outer_rect(), Sense::hover())\n    }\n\n    /// Paint the frame.\n    ///\n    /// This can be called before or after [`Self::allocate_space`].\n    pub fn paint(&self, ui: &Ui) {\n        let content_rect = self.content_ui.min_rect();\n        let widget_rect = self.frame.widget_rect(content_rect);\n\n        if ui.is_rect_visible(widget_rect) {\n            let shape = self.frame.paint(content_rect);\n            ui.painter().set(self.where_to_put_background, shape);\n        }\n    }\n\n    /// Convenience for calling [`Self::allocate_space`] and [`Self::paint`].\n    ///\n    /// Returns the outer rect, i.e. including the outer margin.\n    pub fn end(self, ui: &mut Ui) -> Response {\n        self.paint(ui);\n        self.allocate_space(ui)\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/menu.rs",
    "content": "//! Popup menus, context menus and menu bars.\n//!\n//! Show menus via\n//! - [`Popup::menu`] and [`Popup::context_menu`]\n//! - [`Ui::menu_button`], [`MenuButton`] and [`SubMenuButton`]\n//! - [`MenuBar`]\n//! - [`Response::context_menu`]\n//!\n//! See [`MenuBar`] for an example.\n\nuse crate::style::StyleModifier;\nuse crate::{\n    Button, Color32, Context, Frame, Id, InnerResponse, IntoAtoms, Layout, Popup,\n    PopupCloseBehavior, Response, Style, Ui, UiBuilder, UiKind, UiStack, UiStackInfo, Widget as _,\n};\nuse emath::{Align, RectAlign, Vec2, vec2};\nuse epaint::Stroke;\n\n/// Apply a menu style to the [`Style`].\n///\n/// Mainly removes the background stroke and the inactive background fill.\npub fn menu_style(style: &mut Style) {\n    style.spacing.button_padding = vec2(2.0, 0.0);\n    style.visuals.widgets.active.bg_stroke = Stroke::NONE;\n    style.visuals.widgets.open.bg_stroke = Stroke::NONE;\n    style.visuals.widgets.hovered.bg_stroke = Stroke::NONE;\n    style.visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;\n    style.visuals.widgets.inactive.bg_stroke = Stroke::NONE;\n}\n\n/// Find the root [`UiStack`] of the menu.\npub fn find_menu_root(ui: &Ui) -> &UiStack {\n    ui.stack()\n        .iter()\n        .find(|stack| {\n            stack.is_root_ui()\n                || [Some(UiKind::Popup), Some(UiKind::Menu)].contains(&stack.kind())\n                || stack.info.tags.contains(MenuConfig::MENU_CONFIG_TAG)\n        })\n        .expect(\"We should always find the root\")\n}\n\n/// Is this Ui part of a menu?\n///\n/// Returns `false` if this is a menu bar.\n/// Should be used to determine if we should show a menu button or submenu button.\npub fn is_in_menu(ui: &Ui) -> bool {\n    for stack in ui.stack().iter() {\n        if let Some(config) = stack\n            .info\n            .tags\n            .get_downcast::<MenuConfig>(MenuConfig::MENU_CONFIG_TAG)\n        {\n            return !config.bar;\n        }\n        if [Some(UiKind::Popup), Some(UiKind::Menu)].contains(&stack.kind()) {\n            return true;\n        }\n    }\n    false\n}\n\n/// Configuration and style for menus.\n#[derive(Clone, Debug)]\npub struct MenuConfig {\n    /// Is this a menu bar?\n    bar: bool,\n\n    /// If the user clicks, should we close the menu?\n    pub close_behavior: PopupCloseBehavior,\n\n    /// Override the menu style.\n    ///\n    /// Default is [`menu_style`].\n    pub style: StyleModifier,\n}\n\nimpl Default for MenuConfig {\n    fn default() -> Self {\n        Self {\n            close_behavior: PopupCloseBehavior::default(),\n            bar: false,\n            style: menu_style.into(),\n        }\n    }\n}\n\nimpl MenuConfig {\n    /// The tag used to store the menu config in the [`UiStack`].\n    pub const MENU_CONFIG_TAG: &'static str = \"egui_menu_config\";\n\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// If the user clicks, should we close the menu?\n    #[inline]\n    pub fn close_behavior(mut self, close_behavior: PopupCloseBehavior) -> Self {\n        self.close_behavior = close_behavior;\n        self\n    }\n\n    /// Override the menu style.\n    ///\n    /// Default is [`menu_style`].\n    #[inline]\n    pub fn style(mut self, style: impl Into<StyleModifier>) -> Self {\n        self.style = style.into();\n        self\n    }\n\n    fn from_stack(stack: &UiStack) -> Self {\n        stack\n            .info\n            .tags\n            .get_downcast(Self::MENU_CONFIG_TAG)\n            .cloned()\n            .unwrap_or_default()\n    }\n\n    /// Find the config for the current menu.\n    ///\n    /// Returns the default config if no config is found.\n    pub fn find(ui: &Ui) -> Self {\n        find_menu_root(ui)\n            .info\n            .tags\n            .get_downcast(Self::MENU_CONFIG_TAG)\n            .cloned()\n            .unwrap_or_default()\n    }\n}\n\n/// Holds the state of the menu.\n#[derive(Clone)]\npub struct MenuState {\n    /// The currently open sub menu in this menu.\n    pub open_item: Option<Id>,\n    last_visible_pass: u64,\n}\n\nimpl MenuState {\n    pub const ID: &'static str = \"menu_state\";\n\n    /// Find the root of the menu and get the state\n    pub fn from_ui<R>(ui: &Ui, f: impl FnOnce(&mut Self, &UiStack) -> R) -> R {\n        let stack = find_menu_root(ui);\n        Self::from_id(ui.ctx(), stack.id, |state| f(state, stack))\n    }\n\n    /// Get the state via the menus root [`Ui`] id\n    pub fn from_id<R>(ctx: &Context, id: Id, f: impl FnOnce(&mut Self) -> R) -> R {\n        let pass_nr = ctx.cumulative_pass_nr();\n        ctx.data_mut(|data| {\n            let state_id = id.with(Self::ID);\n            let mut state = data.get_temp(state_id).unwrap_or(Self {\n                open_item: None,\n                last_visible_pass: pass_nr,\n            });\n            // If the menu was closed for at least a frame, reset the open item\n            if state.last_visible_pass + 1 < pass_nr {\n                state.open_item = None;\n            }\n            if let Some(item) = state.open_item\n                && data\n                    .get_temp(item.with(Self::ID))\n                    .is_none_or(|item: Self| item.last_visible_pass + 1 < pass_nr)\n            {\n                // If the open item wasn't shown for at least a frame, reset the open item\n                state.open_item = None;\n            }\n            let r = f(&mut state);\n            data.insert_temp(state_id, state);\n            r\n        })\n    }\n\n    pub fn mark_shown(ctx: &Context, id: Id) {\n        let pass_nr = ctx.cumulative_pass_nr();\n        Self::from_id(ctx, id, |state| {\n            state.last_visible_pass = pass_nr;\n        });\n    }\n\n    /// Is the menu with this id the deepest sub menu? (-> no child sub menu is open)\n    ///\n    /// Note: This only returns correct results if called after the menu contents were shown.\n    pub fn is_deepest_open_sub_menu(ctx: &Context, id: Id) -> bool {\n        let pass_nr = ctx.cumulative_pass_nr();\n        let open_item = Self::from_id(ctx, id, |state| state.open_item);\n        // If we have some open item, check if that was actually shown this frame\n        open_item.is_none_or(|submenu_id| {\n            Self::from_id(ctx, submenu_id, |state| state.last_visible_pass != pass_nr)\n        })\n    }\n}\n\n/// Horizontal menu bar where you can add [`MenuButton`]s.\n///\n/// The menu bar goes well in a [`crate::TopBottomPanel::top`],\n/// but can also be placed in a [`crate::Window`].\n/// In the latter case you may want to wrap it in [`Frame`].\n///\n/// ### Example:\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// egui::MenuBar::new().ui(ui, |ui| {\n///     ui.menu_button(\"File\", |ui| {\n///         if ui.button(\"Quit\").clicked() {\n///             ui.send_viewport_cmd(egui::ViewportCommand::Close);\n///         }\n///     });\n/// });\n/// # });\n/// ```\n#[derive(Clone, Debug)]\npub struct MenuBar {\n    config: MenuConfig,\n    style: StyleModifier,\n}\n\n#[deprecated = \"Renamed to `egui::MenuBar`\"]\npub type Bar = MenuBar;\n\nimpl Default for MenuBar {\n    fn default() -> Self {\n        Self {\n            config: MenuConfig::default(),\n            style: menu_style.into(),\n        }\n    }\n}\n\nimpl MenuBar {\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Set the style for buttons in the menu bar.\n    ///\n    /// Doesn't affect the style of submenus, use [`MenuConfig::style`] for that.\n    /// Default is [`menu_style`].\n    #[inline]\n    pub fn style(mut self, style: impl Into<StyleModifier>) -> Self {\n        self.style = style.into();\n        self\n    }\n\n    /// Set the config for submenus.\n    ///\n    /// Note: The config will only be passed when using [`MenuButton`], not via [`Popup::menu`].\n    #[inline]\n    pub fn config(mut self, config: MenuConfig) -> Self {\n        self.config = config;\n        self\n    }\n\n    /// Show the menu bar.\n    #[inline]\n    pub fn ui<R>(self, ui: &mut Ui, content: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {\n        let Self { mut config, style } = self;\n        config.bar = true;\n        // TODO(lucasmerlin): It'd be nice if we had a ui.horizontal_builder or something\n        // So we don't need the nested scope here\n        ui.horizontal(|ui| {\n            ui.scope_builder(\n                UiBuilder::new()\n                    .layout(Layout::left_to_right(Align::Center))\n                    .ui_stack_info(\n                        UiStackInfo::new(UiKind::Menu)\n                            .with_tag_value(MenuConfig::MENU_CONFIG_TAG, config),\n                    ),\n                |ui| {\n                    style.apply(ui.style_mut());\n\n                    // Take full width and fixed height:\n                    let height = ui.spacing().interact_size.y;\n                    ui.set_min_size(vec2(ui.available_width(), height));\n\n                    content(ui)\n                },\n            )\n            .inner\n        })\n    }\n}\n\n/// A thin wrapper around a [`Button`] that shows a [`Popup::menu`] when clicked.\n///\n/// The only thing this does is search for the current menu config (if set via [`MenuBar`]).\n/// If your menu button is not in a [`MenuBar`] it's fine to use [`Ui::button`] and [`Popup::menu`]\n/// directly.\npub struct MenuButton<'a> {\n    pub button: Button<'a>,\n    pub config: Option<MenuConfig>,\n}\n\nimpl<'a> MenuButton<'a> {\n    pub fn new(atoms: impl IntoAtoms<'a>) -> Self {\n        Self::from_button(Button::new(atoms.into_atoms()))\n    }\n\n    /// Set the config for the menu.\n    #[inline]\n    pub fn config(mut self, config: MenuConfig) -> Self {\n        self.config = Some(config);\n        self\n    }\n\n    /// Create a new menu button from a [`Button`].\n    #[inline]\n    pub fn from_button(button: Button<'a>) -> Self {\n        Self {\n            button,\n            config: None,\n        }\n    }\n\n    /// Show the menu button.\n    pub fn ui<R>(\n        self,\n        ui: &mut Ui,\n        content: impl FnOnce(&mut Ui) -> R,\n    ) -> (Response, Option<InnerResponse<R>>) {\n        let response = self.button.ui(ui);\n        let mut config = self.config.unwrap_or_else(|| MenuConfig::find(ui));\n        config.bar = false;\n        let inner = Popup::menu(&response)\n            .close_behavior(config.close_behavior)\n            .style(config.style.clone())\n            .info(\n                UiStackInfo::new(UiKind::Menu).with_tag_value(MenuConfig::MENU_CONFIG_TAG, config),\n            )\n            .show(content);\n        (response, inner)\n    }\n}\n\n/// A submenu button that shows a [`SubMenu`] if a [`Button`] is hovered.\npub struct SubMenuButton<'a> {\n    pub button: Button<'a>,\n    pub sub_menu: SubMenu,\n}\n\nimpl<'a> SubMenuButton<'a> {\n    /// The default right arrow symbol: `\"⏵\"`\n    pub const RIGHT_ARROW: &'static str = \"⏵\";\n\n    pub fn new(atoms: impl IntoAtoms<'a>) -> Self {\n        Self::from_button(Button::new(atoms.into_atoms()).right_text(\"⏵\"))\n    }\n\n    /// Create a new submenu button from a [`Button`].\n    ///\n    /// Use [`Button::right_text`] and [`SubMenuButton::RIGHT_ARROW`] to add the default right\n    /// arrow symbol.\n    pub fn from_button(button: Button<'a>) -> Self {\n        Self {\n            button,\n            sub_menu: SubMenu::default(),\n        }\n    }\n\n    /// Set the config for the submenu.\n    ///\n    /// The close behavior will not affect the current button, but the buttons in the submenu.\n    #[inline]\n    pub fn config(mut self, config: MenuConfig) -> Self {\n        self.sub_menu.config = Some(config);\n        self\n    }\n\n    /// Show the submenu button.\n    pub fn ui<R>(\n        self,\n        ui: &mut Ui,\n        content: impl FnOnce(&mut Ui) -> R,\n    ) -> (Response, Option<InnerResponse<R>>) {\n        let my_id = ui.next_auto_id();\n        let open = MenuState::from_ui(ui, |state, _| {\n            state.open_item == Some(SubMenu::id_from_widget_id(my_id))\n        });\n        let inactive = ui.style().visuals.widgets.inactive;\n        // TODO(lucasmerlin) add `open` function to `Button`\n        if open {\n            ui.style_mut().visuals.widgets.inactive = ui.style().visuals.widgets.open;\n        }\n        let response = self.button.ui(ui);\n        ui.style_mut().visuals.widgets.inactive = inactive;\n\n        let popup_response = self.sub_menu.show(ui, &response, content);\n\n        (response, popup_response)\n    }\n}\n\n/// Show a submenu in a menu.\n///\n/// Useful if you want to make custom menu buttons.\n/// Usually, just use [`MenuButton`] or [`SubMenuButton`] instead.\n#[derive(Clone, Debug, Default)]\npub struct SubMenu {\n    config: Option<MenuConfig>,\n}\n\nimpl SubMenu {\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Set the config for the submenu.\n    ///\n    /// The close behavior will not affect the current button, but the buttons in the submenu.\n    #[inline]\n    pub fn config(mut self, config: MenuConfig) -> Self {\n        self.config = Some(config);\n        self\n    }\n\n    /// Get the id for the submenu from the widget/response id.\n    pub fn id_from_widget_id(widget_id: Id) -> Id {\n        widget_id.with(\"submenu\")\n    }\n\n    /// Show the submenu.\n    ///\n    /// This does some heuristics to check if the `button_response` was the last thing in the\n    /// menu that was hovered/clicked, and if so, shows the submenu.\n    pub fn show<R>(\n        self,\n        ui: &Ui,\n        button_response: &Response,\n        content: impl FnOnce(&mut Ui) -> R,\n    ) -> Option<InnerResponse<R>> {\n        let frame = Frame::menu(ui.style());\n\n        let id = Self::id_from_widget_id(button_response.id);\n\n        // Get the state from the parent menu\n        let (open_item, menu_id, parent_config) = MenuState::from_ui(ui, |state, stack| {\n            (state.open_item, stack.id, MenuConfig::from_stack(stack))\n        });\n\n        let mut menu_config = self.config.unwrap_or_else(|| parent_config.clone());\n        menu_config.bar = false;\n\n        #[expect(clippy::unwrap_used)] // Since we are a child of that ui, this should always exist\n        let menu_root_response = ui.ctx().read_response(menu_id).unwrap();\n\n        let hover_pos = ui.ctx().pointer_hover_pos();\n\n        // We don't care if the user is hovering over the border\n        let menu_rect = menu_root_response.rect - frame.total_margin();\n        let is_hovering_menu = hover_pos.is_some_and(|pos| {\n            ui.ctx().layer_id_at(pos) == Some(menu_root_response.layer_id)\n                && menu_rect.contains(pos)\n        });\n\n        let is_any_open = open_item.is_some();\n        let mut is_open = open_item == Some(id);\n        let mut set_open = None;\n\n        // We expand the button rect so there is no empty space where no menu is shown\n        // TODO(lucasmerlin): Instead, maybe make item_spacing.y 0.0?\n        let button_rect = button_response\n            .rect\n            .expand2(ui.style().spacing.item_spacing / 2.0);\n\n        // In theory some other widget could cover the button and this check would still pass\n        // But since we check if no other menu is open, nothing should be able to cover the button\n        let is_hovered = hover_pos.is_some_and(|pos| button_rect.contains(pos));\n\n        // The clicked handler is there for accessibility (keyboard navigation)\n        let should_open =\n            ui.is_enabled() && (button_response.clicked() || (is_hovered && !is_any_open));\n        if should_open {\n            set_open = Some(true);\n            is_open = true;\n            // Ensure that all other sub menus are closed when we open the menu\n            MenuState::from_id(ui.ctx(), menu_id, |state| {\n                state.open_item = None;\n            });\n        }\n\n        let gap = frame.total_margin().sum().x / 2.0 + 2.0;\n\n        let mut response = button_response.clone();\n        // Expand the button rect so that the button and the first item in the submenu are aligned\n        let expand = Vec2::new(0.0, frame.total_margin().sum().y / 2.0);\n        response.interact_rect = response.interact_rect.expand2(expand);\n\n        let popup_response = Popup::from_response(&response)\n            .id(id)\n            .open(is_open)\n            .align(RectAlign::RIGHT_START)\n            .layout(Layout::top_down_justified(Align::Min))\n            .gap(gap)\n            .style(menu_config.style.clone())\n            .frame(frame)\n            // The close behavior is handled by the menu (see below)\n            .close_behavior(PopupCloseBehavior::IgnoreClicks)\n            .info(\n                UiStackInfo::new(UiKind::Menu)\n                    .with_tag_value(MenuConfig::MENU_CONFIG_TAG, menu_config.clone()),\n            )\n            .show(|ui| {\n                // Ensure our layer stays on top when the button is clicked\n                if button_response.clicked() || button_response.is_pointer_button_down_on() {\n                    ui.ctx().move_to_top(ui.layer_id());\n                }\n                content(ui)\n            });\n\n        if let Some(popup_response) = &popup_response {\n            // If no child sub menu is open means we must be the deepest child sub menu.\n            let is_deepest_submenu = MenuState::is_deepest_open_sub_menu(ui.ctx(), id);\n\n            // If the user clicks and the cursor is not hovering over our menu rect, it's\n            // safe to assume they clicked outside the menu, so we close everything.\n            // If they were to hover some other parent submenu we wouldn't be open.\n            // Only edge case is the user hovering this submenu's button, so we also check\n            // if we clicked outside the parent menu (which we luckily have access to here).\n            let clicked_outside = is_deepest_submenu\n                && popup_response.response.clicked_elsewhere()\n                && menu_root_response.clicked_elsewhere();\n\n            // We never automatically close when a submenu button is clicked, (so menus work\n            // on touch devices)\n            // Luckily we will always be the deepest submenu when a submenu button is clicked,\n            // so the following check is enough.\n            let submenu_button_clicked = button_response.clicked();\n\n            let clicked_inside = is_deepest_submenu\n                && !submenu_button_clicked\n                && response.ctx.input(|i| i.pointer.any_click())\n                && hover_pos.is_some_and(|pos| popup_response.response.interact_rect.contains(pos));\n\n            let click_close = match menu_config.close_behavior {\n                PopupCloseBehavior::CloseOnClick => clicked_outside || clicked_inside,\n                PopupCloseBehavior::CloseOnClickOutside => clicked_outside,\n                PopupCloseBehavior::IgnoreClicks => false,\n            };\n\n            if click_close {\n                set_open = Some(false);\n                ui.close();\n            }\n\n            let is_moving_towards_rect = ui.input(|i| {\n                i.pointer\n                    .is_moving_towards_rect(&popup_response.response.rect)\n            });\n            if is_moving_towards_rect {\n                // We need to repaint while this is true, so we can detect when\n                // the pointer is no longer moving towards the rect\n                ui.request_repaint();\n            }\n            let hovering_other_menu_entry = is_open\n                && !is_hovered\n                && !popup_response.response.contains_pointer()\n                && !is_moving_towards_rect\n                && is_hovering_menu;\n\n            let close_called = popup_response.response.should_close();\n\n            // Close the parent ui to e.g. close the popup from where the submenu was opened\n            if close_called {\n                ui.close();\n            }\n\n            if hovering_other_menu_entry {\n                set_open = Some(false);\n            }\n\n            if ui.will_parent_close() {\n                ui.data_mut(|data| data.remove_by_type::<MenuState>());\n            }\n        }\n\n        if let Some(set_open) = set_open {\n            MenuState::from_id(ui.ctx(), menu_id, |state| {\n                state.open_item = set_open.then_some(id);\n            });\n        }\n\n        popup_response\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/mod.rs",
    "content": "//! Containers are pieces of the UI which wraps other pieces of UI. Examples: [`Window`], [`ScrollArea`], [`Resize`], [`Panel`], etc.\n//!\n//! For instance, a [`Frame`] adds a frame and background to some contained UI.\n\npub(crate) mod area;\nmod close_tag;\npub mod collapsing_header;\nmod combo_box;\npub mod frame;\npub mod menu;\npub mod modal;\npub mod old_popup;\npub mod panel;\nmod popup;\npub(crate) mod resize;\nmod scene;\npub mod scroll_area;\nmod sides;\nmod tooltip;\npub(crate) mod window;\n\npub use {\n    area::{Area, AreaState},\n    close_tag::ClosableTag,\n    collapsing_header::{CollapsingHeader, CollapsingResponse},\n    combo_box::*,\n    frame::Frame,\n    modal::{Modal, ModalResponse},\n    old_popup::*,\n    panel::*,\n    popup::*,\n    resize::Resize,\n    scene::{DragPanButtons, Scene},\n    scroll_area::ScrollArea,\n    sides::Sides,\n    tooltip::*,\n    window::Window,\n};\n"
  },
  {
    "path": "crates/egui/src/containers/modal.rs",
    "content": "use emath::{Align2, Vec2};\n\nuse crate::{\n    Area, Color32, Context, Frame, Id, InnerResponse, Order, Response, Sense, Ui, UiBuilder, UiKind,\n};\n\n/// A modal dialog.\n///\n/// Similar to a [`crate::Window`] but centered and with a backdrop that\n/// blocks input to the rest of the UI.\n///\n/// You can show multiple modals on top of each other. The topmost modal will always be\n/// the most recently shown one.\n/// If multiple modals are newly shown in the same frame, the order of the modals is undefined\n/// (either first or second could be top).\npub struct Modal {\n    pub area: Area,\n    pub backdrop_color: Color32,\n    pub frame: Option<Frame>,\n}\n\nimpl Modal {\n    /// Create a new Modal.\n    ///\n    /// The id is passed to the area.\n    pub fn new(id: Id) -> Self {\n        Self {\n            area: Self::default_area(id),\n            backdrop_color: Color32::from_black_alpha(100),\n            frame: None,\n        }\n    }\n\n    /// Returns an area customized for a modal.\n    ///\n    /// Makes these changes to the default area:\n    /// - sense: hover\n    /// - anchor: center\n    /// - order: foreground\n    pub fn default_area(id: Id) -> Area {\n        Area::new(id)\n            .kind(UiKind::Modal)\n            .sense(Sense::hover())\n            .anchor(Align2::CENTER_CENTER, Vec2::ZERO)\n            .order(Order::Foreground)\n            .interactable(true)\n    }\n\n    /// Set the frame of the modal.\n    ///\n    /// Default is [`Frame::popup`].\n    #[inline]\n    pub fn frame(mut self, frame: Frame) -> Self {\n        self.frame = Some(frame);\n        self\n    }\n\n    /// Set the backdrop color of the modal.\n    ///\n    /// Default is `Color32::from_black_alpha(100)`.\n    #[inline]\n    pub fn backdrop_color(mut self, color: Color32) -> Self {\n        self.backdrop_color = color;\n        self\n    }\n\n    /// Set the area of the modal.\n    ///\n    /// Default is [`Modal::default_area`].\n    #[inline]\n    pub fn area(mut self, area: Area) -> Self {\n        self.area = area;\n        self\n    }\n\n    /// Show the modal.\n    pub fn show<T>(self, ctx: &Context, content: impl FnOnce(&mut Ui) -> T) -> ModalResponse<T> {\n        let Self {\n            area,\n            backdrop_color,\n            frame,\n        } = self;\n\n        let is_top_modal = ctx.memory_mut(|mem| {\n            mem.set_modal_layer(area.layer());\n            mem.top_modal_layer() == Some(area.layer())\n        });\n        let any_popup_open = crate::Popup::is_any_open(ctx);\n        let InnerResponse {\n            inner: (inner, backdrop_response),\n            response,\n        } = area.show(ctx, |ui| {\n            let bg_rect = ui.ctx().content_rect();\n            let bg_sense = Sense::CLICK | Sense::DRAG;\n            let mut backdrop = ui.new_child(UiBuilder::new().sense(bg_sense).max_rect(bg_rect));\n            backdrop.set_min_size(bg_rect.size());\n            ui.painter().rect_filled(bg_rect, 0.0, backdrop_color);\n            let backdrop_response = backdrop.response();\n\n            let frame = frame.unwrap_or_else(|| Frame::popup(ui.style()));\n\n            // We need the extra scope with the sense since frame can't have a sense and since we\n            // need to prevent the clicks from passing through to the backdrop.\n            let inner = ui\n                .scope_builder(UiBuilder::new().sense(Sense::CLICK | Sense::DRAG), |ui| {\n                    frame.show(ui, content).inner\n                })\n                .inner;\n\n            (inner, backdrop_response)\n        });\n\n        ModalResponse {\n            response,\n            backdrop_response,\n            inner,\n            is_top_modal,\n            any_popup_open,\n        }\n    }\n}\n\n/// The response of a modal dialog.\npub struct ModalResponse<T> {\n    /// The response of the modal contents\n    pub response: Response,\n\n    /// The response of the modal backdrop.\n    ///\n    /// A click on this means the user clicked outside the modal,\n    /// in which case you might want to close the modal.\n    pub backdrop_response: Response,\n\n    /// The inner response from the content closure\n    pub inner: T,\n\n    /// Is this the topmost modal?\n    pub is_top_modal: bool,\n\n    /// Is there any popup open?\n    /// We need to check this before the modal contents are shown, so we can know if any popup\n    /// was open when checking if the escape key was clicked.\n    pub any_popup_open: bool,\n}\n\nimpl<T> ModalResponse<T> {\n    /// Should the modal be closed?\n    /// Returns true if:\n    ///  - the backdrop was clicked\n    ///  - this is the topmost modal, no popup is open and the escape key was pressed\n    pub fn should_close(&self) -> bool {\n        let ctx = &self.response.ctx;\n\n        // this is a closure so that `Esc` is consumed only if the modal is topmost\n        let escape_clicked =\n            || ctx.input_mut(|i| i.consume_key(crate::Modifiers::NONE, crate::Key::Escape));\n\n        let ui_close_called = self.response.should_close();\n\n        self.backdrop_response.clicked()\n            || ui_close_called\n            || (self.is_top_modal && !self.any_popup_open && escape_clicked())\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/old_popup.rs",
    "content": "//! Old and deprecated API for popups. Use [`Popup`] instead.\n#![expect(deprecated)]\n\nuse crate::containers::tooltip::Tooltip;\nuse crate::{\n    Align, Context, Id, LayerId, Layout, Popup, PopupAnchor, PopupCloseBehavior, Pos2, Rect,\n    Response, Ui, Widget as _, WidgetText,\n};\nuse emath::RectAlign;\n// ----------------------------------------------------------------------------\n\n/// Show a tooltip at the current pointer position (if any).\n///\n/// Most of the time it is easier to use [`Response::on_hover_ui`].\n///\n/// See also [`show_tooltip_text`].\n///\n/// Returns `None` if the tooltip could not be placed.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # #[expect(deprecated)]\n/// if ui.ui_contains_pointer() {\n///     egui::show_tooltip(ui.ctx(), ui.layer_id(), egui::Id::new(\"my_tooltip\"), |ui| {\n///         ui.label(\"Helpful text\");\n///     });\n/// }\n/// # });\n/// ```\n#[deprecated = \"Use `egui::Tooltip` instead\"]\npub fn show_tooltip<R>(\n    ctx: &Context,\n    parent_layer: LayerId,\n    widget_id: Id,\n    add_contents: impl FnOnce(&mut Ui) -> R,\n) -> Option<R> {\n    show_tooltip_at_pointer(ctx, parent_layer, widget_id, add_contents)\n}\n\n/// Show a tooltip at the current pointer position (if any).\n///\n/// Most of the time it is easier to use [`Response::on_hover_ui`].\n///\n/// See also [`show_tooltip_text`].\n///\n/// Returns `None` if the tooltip could not be placed.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// if ui.ui_contains_pointer() {\n///     egui::show_tooltip_at_pointer(ui.ctx(), ui.layer_id(), egui::Id::new(\"my_tooltip\"), |ui| {\n///         ui.label(\"Helpful text\");\n///     });\n/// }\n/// # });\n/// ```\n#[deprecated = \"Use `egui::Tooltip` instead\"]\npub fn show_tooltip_at_pointer<R>(\n    ctx: &Context,\n    parent_layer: LayerId,\n    widget_id: Id,\n    add_contents: impl FnOnce(&mut Ui) -> R,\n) -> Option<R> {\n    Tooltip::always_open(ctx.clone(), parent_layer, widget_id, PopupAnchor::Pointer)\n        .gap(12.0)\n        .show(add_contents)\n        .map(|response| response.inner)\n}\n\n/// Show a tooltip under the given area.\n///\n/// If the tooltip does not fit under the area, it tries to place it above it instead.\n#[deprecated = \"Use `egui::Tooltip` instead\"]\npub fn show_tooltip_for<R>(\n    ctx: &Context,\n    parent_layer: LayerId,\n    widget_id: Id,\n    widget_rect: &Rect,\n    add_contents: impl FnOnce(&mut Ui) -> R,\n) -> Option<R> {\n    Tooltip::always_open(ctx.clone(), parent_layer, widget_id, *widget_rect)\n        .show(add_contents)\n        .map(|response| response.inner)\n}\n\n/// Show a tooltip at the given position.\n///\n/// Returns `None` if the tooltip could not be placed.\n#[deprecated = \"Use `egui::Tooltip` instead\"]\npub fn show_tooltip_at<R>(\n    ctx: &Context,\n    parent_layer: LayerId,\n    widget_id: Id,\n    suggested_position: Pos2,\n    add_contents: impl FnOnce(&mut Ui) -> R,\n) -> Option<R> {\n    Tooltip::always_open(ctx.clone(), parent_layer, widget_id, suggested_position)\n        .show(add_contents)\n        .map(|response| response.inner)\n}\n\n/// Show some text at the current pointer position (if any).\n///\n/// Most of the time it is easier to use [`Response::on_hover_text`].\n///\n/// See also [`show_tooltip`].\n///\n/// Returns `None` if the tooltip could not be placed.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// if ui.ui_contains_pointer() {\n///     egui::show_tooltip_text(ui.ctx(), ui.layer_id(), egui::Id::new(\"my_tooltip\"), \"Helpful text\");\n/// }\n/// # });\n/// ```\n#[deprecated = \"Use `egui::Tooltip` instead\"]\npub fn show_tooltip_text(\n    ctx: &Context,\n    parent_layer: LayerId,\n    widget_id: Id,\n    text: impl Into<WidgetText>,\n) -> Option<()> {\n    show_tooltip(ctx, parent_layer, widget_id, |ui| {\n        crate::widgets::Label::new(text).ui(ui);\n    })\n}\n\n/// Was this tooltip visible last frame?\n#[deprecated = \"Use `Tooltip::was_tooltip_open_last_frame` instead\"]\npub fn was_tooltip_open_last_frame(ctx: &Context, widget_id: Id) -> bool {\n    Tooltip::was_tooltip_open_last_frame(ctx, widget_id)\n}\n\n/// Indicate whether a popup will be shown above or below the box.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\npub enum AboveOrBelow {\n    Above,\n    Below,\n}\n\n/// Helper for [`popup_above_or_below_widget`].\n#[deprecated = \"Use `egui::Popup` instead\"]\npub fn popup_below_widget<R>(\n    ui: &Ui,\n    popup_id: Id,\n    widget_response: &Response,\n    close_behavior: PopupCloseBehavior,\n    add_contents: impl FnOnce(&mut Ui) -> R,\n) -> Option<R> {\n    popup_above_or_below_widget(\n        ui,\n        popup_id,\n        widget_response,\n        AboveOrBelow::Below,\n        close_behavior,\n        add_contents,\n    )\n}\n\n/// Shows a popup above or below another widget.\n///\n/// Useful for drop-down menus (combo boxes) or suggestion menus under text fields.\n///\n/// The opened popup will have a minimum width matching its parent.\n///\n/// You must open the popup with [`crate::Memory::open_popup`] or  [`crate::Memory::toggle_popup`].\n///\n/// Returns `None` if the popup is not open.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// let response = ui.button(\"Open popup\");\n/// let popup_id = ui.make_persistent_id(\"my_unique_id\");\n/// if response.clicked() {\n///     ui.memory_mut(|mem| mem.toggle_popup(popup_id));\n/// }\n/// let below = egui::AboveOrBelow::Below;\n/// let close_on_click_outside = egui::PopupCloseBehavior::CloseOnClickOutside;\n/// # #[expect(deprecated)]\n/// egui::popup_above_or_below_widget(ui, popup_id, &response, below, close_on_click_outside, |ui| {\n///     ui.set_min_width(200.0); // if you want to control the size\n///     ui.label(\"Some more info, or things you can select:\");\n///     ui.label(\"…\");\n/// });\n/// # });\n/// ```\n#[deprecated = \"Use `egui::Popup` instead\"]\npub fn popup_above_or_below_widget<R>(\n    _parent_ui: &Ui,\n    popup_id: Id,\n    widget_response: &Response,\n    above_or_below: AboveOrBelow,\n    close_behavior: PopupCloseBehavior,\n    add_contents: impl FnOnce(&mut Ui) -> R,\n) -> Option<R> {\n    let response = Popup::from_response(widget_response)\n        .layout(Layout::top_down_justified(Align::LEFT))\n        .open_memory(None)\n        .close_behavior(close_behavior)\n        .id(popup_id)\n        .align(match above_or_below {\n            AboveOrBelow::Above => RectAlign::TOP_START,\n            AboveOrBelow::Below => RectAlign::BOTTOM_START,\n        })\n        .width(widget_response.rect.width())\n        .show(|ui| {\n            ui.set_min_width(ui.available_width());\n            add_contents(ui)\n        })?;\n    Some(response.inner)\n}\n"
  },
  {
    "path": "crates/egui/src/containers/panel.rs",
    "content": "//! Panels are [`Ui`] regions taking up e.g. the left side of a [`Ui`] or screen.\n//!\n//! Panels can either be a child of a [`Ui`] (taking up a portion of the parent)\n//! or be top-level (taking up a portion of the whole screen).\n//!\n//! Together with [`crate::Window`] and [`crate::Area`]:s, top-level panels are\n//! the only places where you can put you widgets.\n//!\n//! The order in which you add panels matter!\n//! The first panel you add will always be the outermost, and the last you add will always be the innermost.\n//!\n//! You must never open one top-level panel from within another panel. Add one panel, then the next.\n//!\n//! ⚠ Always add any [`CentralPanel`] last.\n//!\n//! Add your [`crate::Window`]:s after any top-level panels.\n\nuse emath::{GuiRounding as _, Pos2};\n\nuse crate::{\n    Align, Context, CursorIcon, Frame, Id, InnerResponse, LayerId, Layout, NumExt as _, Rangef,\n    Rect, Sense, Stroke, Ui, UiBuilder, UiKind, UiStackInfo, Vec2, WidgetInfo, WidgetType, lerp,\n    vec2,\n};\n\nfn animate_expansion(ctx: &Context, id: Id, is_expanded: bool) -> f32 {\n    ctx.animate_bool_responsive(id, is_expanded)\n}\n\n/// State regarding panels.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct PanelState {\n    pub rect: Rect,\n}\n\nimpl PanelState {\n    pub fn load(ctx: &Context, bar_id: Id) -> Option<Self> {\n        ctx.data_mut(|d| d.get_persisted(bar_id))\n    }\n\n    /// The size of the panel (from previous frame).\n    pub fn size(&self) -> Vec2 {\n        self.rect.size()\n    }\n\n    fn store(self, ctx: &Context, bar_id: Id) {\n        ctx.data_mut(|d| d.insert_persisted(bar_id, self));\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// [`Left`](VerticalSide::Left) or [`Right`](VerticalSide::Right)\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\nenum VerticalSide {\n    Left,\n    Right,\n}\n\nimpl VerticalSide {\n    pub fn opposite(self) -> Self {\n        match self {\n            Self::Left => Self::Right,\n            Self::Right => Self::Left,\n        }\n    }\n\n    /// `self` is the _fixed_ side.\n    ///\n    /// * Left panels are resized on their right side\n    /// * Right panels are resized on their left side\n    fn set_rect_width(self, rect: &mut Rect, width: f32) {\n        match self {\n            Self::Left => rect.max.x = rect.min.x + width,\n            Self::Right => rect.min.x = rect.max.x - width,\n        }\n    }\n\n    fn sign(self) -> f32 {\n        match self {\n            Self::Left => -1.0,\n            Self::Right => 1.0,\n        }\n    }\n\n    fn side_x(self, rect: Rect) -> f32 {\n        match self {\n            Self::Left => rect.left(),\n            Self::Right => rect.right(),\n        }\n    }\n}\n\n/// [`Top`](HorizontalSide::Top) or [`Bottom`](HorizontalSide::Bottom)\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\nenum HorizontalSide {\n    Top,\n    Bottom,\n}\n\nimpl HorizontalSide {\n    pub fn opposite(self) -> Self {\n        match self {\n            Self::Top => Self::Bottom,\n            Self::Bottom => Self::Top,\n        }\n    }\n\n    /// `self` is the _fixed_ side.\n    ///\n    /// * Top panels are resized on their bottom side\n    /// * Bottom panels are resized upwards\n    fn set_rect_height(self, rect: &mut Rect, height: f32) {\n        match self {\n            Self::Top => rect.max.y = rect.min.y + height,\n            Self::Bottom => rect.min.y = rect.max.y - height,\n        }\n    }\n\n    fn sign(self) -> f32 {\n        match self {\n            Self::Top => -1.0,\n            Self::Bottom => 1.0,\n        }\n    }\n\n    fn side_y(self, rect: Rect) -> f32 {\n        match self {\n            Self::Top => rect.top(),\n            Self::Bottom => rect.bottom(),\n        }\n    }\n}\n\n// Intentionally private because I'm not sure of the naming.\n// TODO(emilk): decide on good names and make public.\n// \"VerticalSide\" and \"HorizontalSide\" feels inverted to me.\n/// [`Horizontal`](PanelSide::Horizontal) or [`Vertical`](PanelSide::Vertical)\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\nenum PanelSide {\n    /// Left or right.\n    Vertical(VerticalSide),\n\n    /// Top or bottom\n    Horizontal(HorizontalSide),\n}\n\nimpl From<HorizontalSide> for PanelSide {\n    fn from(side: HorizontalSide) -> Self {\n        Self::Horizontal(side)\n    }\n}\n\nimpl From<VerticalSide> for PanelSide {\n    fn from(side: VerticalSide) -> Self {\n        Self::Vertical(side)\n    }\n}\n\nimpl PanelSide {\n    pub const LEFT: Self = Self::Vertical(VerticalSide::Left);\n    pub const RIGHT: Self = Self::Vertical(VerticalSide::Right);\n    pub const TOP: Self = Self::Horizontal(HorizontalSide::Top);\n    pub const BOTTOM: Self = Self::Horizontal(HorizontalSide::Bottom);\n\n    /// Resize by keeping the [`self`] side fixed, and moving the opposite side.\n    fn set_rect_size(self, rect: &mut Rect, size: f32) {\n        match self {\n            Self::Vertical(side) => side.set_rect_width(rect, size),\n            Self::Horizontal(side) => side.set_rect_height(rect, size),\n        }\n    }\n\n    fn ui_kind(self) -> UiKind {\n        match self {\n            Self::Vertical(side) => match side {\n                VerticalSide::Left => UiKind::LeftPanel,\n                VerticalSide::Right => UiKind::RightPanel,\n            },\n            Self::Horizontal(side) => match side {\n                HorizontalSide::Top => UiKind::TopPanel,\n                HorizontalSide::Bottom => UiKind::BottomPanel,\n            },\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Intermediate structure to abstract some portion of [`Panel::show_inside`](Panel::show_inside).\nstruct PanelSizer<'a> {\n    panel: &'a Panel,\n    frame: Frame,\n    available_rect: Rect,\n    size: f32,\n    panel_rect: Rect,\n}\n\nimpl<'a> PanelSizer<'a> {\n    fn new(panel: &'a Panel, ui: &Ui) -> Self {\n        let frame = panel\n            .frame\n            .unwrap_or_else(|| Frame::side_top_panel(ui.style()));\n        let available_rect = ui.available_rect_before_wrap();\n        let size = PanelSizer::get_size_from_state_or_default(panel, ui, frame);\n        let panel_rect = PanelSizer::panel_rect(panel, available_rect, size);\n\n        Self {\n            panel,\n            frame,\n            available_rect,\n            size,\n            panel_rect,\n        }\n    }\n\n    fn get_size_from_state_or_default(panel: &Panel, ui: &Ui, frame: Frame) -> f32 {\n        if let Some(state) = PanelState::load(ui.ctx(), panel.id) {\n            match panel.side {\n                PanelSide::Vertical(_) => state.rect.width(),\n                PanelSide::Horizontal(_) => state.rect.height(),\n            }\n        } else {\n            match panel.side {\n                PanelSide::Vertical(_) => panel.default_size.unwrap_or_else(|| {\n                    ui.style().spacing.interact_size.x + frame.inner_margin.sum().x\n                }),\n                PanelSide::Horizontal(_) => panel.default_size.unwrap_or_else(|| {\n                    ui.style().spacing.interact_size.y + frame.inner_margin.sum().y\n                }),\n            }\n        }\n    }\n\n    fn panel_rect(panel: &Panel, available_rect: Rect, mut size: f32) -> Rect {\n        let side = panel.side;\n        let size_range = panel.size_range;\n\n        let mut panel_rect = available_rect;\n\n        match side {\n            PanelSide::Vertical(_) => {\n                size = clamp_to_range(size, size_range).at_most(available_rect.width());\n            }\n            PanelSide::Horizontal(_) => {\n                size = clamp_to_range(size, size_range).at_most(available_rect.height());\n            }\n        }\n        side.set_rect_size(&mut panel_rect, size);\n        panel_rect\n    }\n\n    fn prepare_resizing_response(&mut self, is_resizing: bool, pointer: Option<Pos2>) {\n        let side = self.panel.side;\n        let size_range = self.panel.size_range;\n\n        if is_resizing && let Some(pointer) = pointer {\n            match side {\n                PanelSide::Vertical(side) => {\n                    self.size = (pointer.x - side.side_x(self.panel_rect)).abs();\n                    self.size =\n                        clamp_to_range(self.size, size_range).at_most(self.available_rect.width());\n                }\n                PanelSide::Horizontal(side) => {\n                    self.size = (pointer.y - side.side_y(self.panel_rect)).abs();\n                    self.size =\n                        clamp_to_range(self.size, size_range).at_most(self.available_rect.height());\n                }\n            }\n\n            side.set_rect_size(&mut self.panel_rect, self.size);\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A panel that covers an entire side\n/// ([`left`](Panel::left), [`right`](Panel::right),\n/// [`top`](Panel::top) or [`bottom`](Panel::bottom))\n/// of a [`Ui`] or screen.\n///\n/// The order in which you add panels matter!\n/// The first panel you add will always be the outermost, and the last you add will always be the innermost.\n///\n/// ⚠ Always add any [`CentralPanel`] last.\n///\n/// See the [module level docs](crate::containers::panel) for more details.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// egui::Panel::left(\"my_left_panel\").show_inside(ui, |ui| {\n///    ui.label(\"Hello World!\");\n/// });\n/// # });\n/// ```\n#[must_use = \"You should call .show_inside()\"]\npub struct Panel {\n    side: PanelSide,\n    id: Id,\n    frame: Option<Frame>,\n    resizable: bool,\n    show_separator_line: bool,\n\n    /// The size is defined as being either the width for a Vertical Panel\n    /// or the height for a Horizontal Panel.\n    default_size: Option<f32>,\n\n    /// The size is defined as being either the width for a Vertical Panel\n    /// or the height for a Horizontal Panel.\n    size_range: Rangef,\n}\n\nimpl Panel {\n    /// Create a left panel.\n    ///\n    /// The id should be globally unique, e.g. `Id::new(\"my_left_panel\")`.\n    pub fn left(id: impl Into<Id>) -> Self {\n        Self::new(PanelSide::LEFT, id)\n    }\n\n    /// Create a right panel.\n    ///\n    /// The id should be globally unique, e.g. `Id::new(\"my_right_panel\")`.\n    pub fn right(id: impl Into<Id>) -> Self {\n        Self::new(PanelSide::RIGHT, id)\n    }\n\n    /// Create a top panel.\n    ///\n    /// The id should be globally unique, e.g. `Id::new(\"my_top_panel\")`.\n    ///\n    /// By default this is NOT resizable.\n    pub fn top(id: impl Into<Id>) -> Self {\n        Self::new(PanelSide::TOP, id).resizable(false)\n    }\n\n    /// Create a bottom panel.\n    ///\n    /// The id should be globally unique, e.g. `Id::new(\"my_bottom_panel\")`.\n    ///\n    /// By default this is NOT resizable.\n    pub fn bottom(id: impl Into<Id>) -> Self {\n        Self::new(PanelSide::BOTTOM, id).resizable(false)\n    }\n\n    /// Create a panel.\n    ///\n    /// The id should be globally unique, e.g. `Id::new(\"my_panel\")`.\n    fn new(side: PanelSide, id: impl Into<Id>) -> Self {\n        let default_size: Option<f32> = match side {\n            PanelSide::Vertical(_) => Some(200.0),\n            PanelSide::Horizontal(_) => None,\n        };\n\n        let size_range: Rangef = match side {\n            PanelSide::Vertical(_) => Rangef::new(96.0, f32::INFINITY),\n            PanelSide::Horizontal(_) => Rangef::new(20.0, f32::INFINITY),\n        };\n\n        Self {\n            side,\n            id: id.into(),\n            frame: None,\n            resizable: true,\n            show_separator_line: true,\n            default_size,\n            size_range,\n        }\n    }\n\n    /// Can panel be resized by dragging the edge of it?\n    ///\n    /// Default is `true`.\n    ///\n    /// If you want your panel to be resizable you also need to make the ui use\n    /// the available space.\n    ///\n    /// This can be done by using [`Ui::take_available_space`], or using a\n    /// widget in it that takes up more space as you resize it, such as:\n    /// * Wrapping text ([`Ui::horizontal_wrapped`]).\n    /// * A [`crate::ScrollArea`].\n    /// * A [`crate::Separator`].\n    /// * A [`crate::TextEdit`].\n    /// * …\n    #[inline]\n    pub fn resizable(mut self, resizable: bool) -> Self {\n        self.resizable = resizable;\n        self\n    }\n\n    /// Show a separator line, even when not interacting with it?\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn show_separator_line(mut self, show_separator_line: bool) -> Self {\n        self.show_separator_line = show_separator_line;\n        self\n    }\n\n    /// The initial wrapping width of the [`Panel`], including margins.\n    #[inline]\n    pub fn default_size(mut self, default_size: f32) -> Self {\n        self.default_size = Some(default_size);\n        self.size_range = Rangef::new(\n            self.size_range.min.at_most(default_size),\n            self.size_range.max.at_least(default_size),\n        );\n        self\n    }\n\n    /// Minimum size of the panel, including margins.\n    #[inline]\n    pub fn min_size(mut self, min_size: f32) -> Self {\n        self.size_range = Rangef::new(min_size, self.size_range.max.at_least(min_size));\n        self\n    }\n\n    /// Maximum size of the panel, including margins.\n    #[inline]\n    pub fn max_size(mut self, max_size: f32) -> Self {\n        self.size_range = Rangef::new(self.size_range.min.at_most(max_size), max_size);\n        self\n    }\n\n    /// The allowable size range for the panel, including margins.\n    #[inline]\n    pub fn size_range(mut self, size_range: impl Into<Rangef>) -> Self {\n        let size_range = size_range.into();\n        self.default_size = self\n            .default_size\n            .map(|default_size| clamp_to_range(default_size, size_range));\n        self.size_range = size_range;\n        self\n    }\n\n    /// Enforce this exact size, including margins.\n    #[inline]\n    pub fn exact_size(mut self, size: f32) -> Self {\n        self.default_size = Some(size);\n        self.size_range = Rangef::point(size);\n        self\n    }\n\n    /// Change the background color, margins, etc.\n    #[inline]\n    pub fn frame(mut self, frame: Frame) -> Self {\n        self.frame = Some(frame);\n        self\n    }\n}\n\n// Deprecated\nimpl Panel {\n    #[deprecated = \"Renamed default_size\"]\n    pub fn default_width(self, default_size: f32) -> Self {\n        self.default_size(default_size)\n    }\n\n    #[deprecated = \"Renamed min_size\"]\n    pub fn min_width(self, min_size: f32) -> Self {\n        self.min_size(min_size)\n    }\n\n    #[deprecated = \"Renamed max_size\"]\n    pub fn max_width(self, max_size: f32) -> Self {\n        self.max_size(max_size)\n    }\n\n    #[deprecated = \"Renamed size_range\"]\n    pub fn width_range(self, size_range: impl Into<Rangef>) -> Self {\n        self.size_range(size_range)\n    }\n\n    #[deprecated = \"Renamed exact_size\"]\n    pub fn exact_width(self, size: f32) -> Self {\n        self.exact_size(size)\n    }\n\n    #[deprecated = \"Renamed default_size\"]\n    pub fn default_height(self, default_size: f32) -> Self {\n        self.default_size(default_size)\n    }\n\n    #[deprecated = \"Renamed min_size\"]\n    pub fn min_height(self, min_size: f32) -> Self {\n        self.min_size(min_size)\n    }\n\n    #[deprecated = \"Renamed max_size\"]\n    pub fn max_height(self, max_size: f32) -> Self {\n        self.max_size(max_size)\n    }\n\n    #[deprecated = \"Renamed size_range\"]\n    pub fn height_range(self, size_range: impl Into<Rangef>) -> Self {\n        self.size_range(size_range)\n    }\n\n    #[deprecated = \"Renamed exact_size\"]\n    pub fn exact_height(self, size: f32) -> Self {\n        self.exact_size(size)\n    }\n}\n\n// Public showing methods\nimpl Panel {\n    /// Show the panel inside a [`Ui`].\n    pub fn show_inside<R>(\n        self,\n        ui: &mut Ui,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.show_inside_dyn(ui, Box::new(add_contents))\n    }\n\n    /// Show the panel at the top level.\n    #[deprecated = \"Use show_inside() instead\"]\n    pub fn show<R>(\n        self,\n        ctx: &Context,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.show_dyn(ctx, Box::new(add_contents))\n    }\n\n    /// Show the panel if `is_expanded` is `true`,\n    /// otherwise don't show it, but with a nice animation between collapsed and expanded.\n    #[deprecated = \"Use show_animated_inside() instead\"]\n    pub fn show_animated<R>(\n        self,\n        ctx: &Context,\n        is_expanded: bool,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> Option<InnerResponse<R>> {\n        #![expect(deprecated)]\n\n        let how_expanded = animate_expansion(ctx, self.id.with(\"animation\"), is_expanded);\n\n        let animated_panel = self.get_animated_panel(ctx, is_expanded)?;\n\n        if how_expanded < 1.0 {\n            // Show a fake panel in this in-between animation state:\n            animated_panel.show(ctx, |_ui| {});\n            None\n        } else {\n            // Show the real panel:\n            Some(animated_panel.show(ctx, add_contents))\n        }\n    }\n\n    /// Show the panel if `is_expanded` is `true`,\n    /// otherwise don't show it, but with a nice animation between collapsed and expanded.\n    pub fn show_animated_inside<R>(\n        self,\n        ui: &mut Ui,\n        is_expanded: bool,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> Option<InnerResponse<R>> {\n        let how_expanded = animate_expansion(ui.ctx(), self.id.with(\"animation\"), is_expanded);\n\n        // Get either the fake or the real panel to animate\n        let animated_panel = self.get_animated_panel(ui.ctx(), is_expanded)?;\n\n        if how_expanded < 1.0 {\n            // Show a fake panel in this in-between animation state:\n            animated_panel.show_inside(ui, |_ui| {});\n            None\n        } else {\n            // Show the real panel:\n            Some(animated_panel.show_inside(ui, add_contents))\n        }\n    }\n\n    /// Show either a collapsed or a expanded panel, with a nice animation between.\n    #[deprecated = \"Use show_animated_between_inside() instead\"]\n    pub fn show_animated_between<R>(\n        ctx: &Context,\n        is_expanded: bool,\n        collapsed_panel: Self,\n        expanded_panel: Self,\n        add_contents: impl FnOnce(&mut Ui, f32) -> R,\n    ) -> Option<InnerResponse<R>> {\n        #![expect(deprecated)]\n\n        let how_expanded = animate_expansion(ctx, expanded_panel.id.with(\"animation\"), is_expanded);\n\n        // Get either the fake or the real panel to animate\n        let animated_between_panel =\n            Self::get_animated_between_panel(ctx, is_expanded, collapsed_panel, expanded_panel);\n\n        if 0.0 == how_expanded {\n            Some(animated_between_panel.show(ctx, |ui| add_contents(ui, how_expanded)))\n        } else if how_expanded < 1.0 {\n            // Show animation:\n            animated_between_panel.show(ctx, |ui| add_contents(ui, how_expanded));\n            None\n        } else {\n            Some(animated_between_panel.show(ctx, |ui| add_contents(ui, how_expanded)))\n        }\n    }\n\n    /// Show either a collapsed or a expanded panel, with a nice animation between.\n    pub fn show_animated_between_inside<R>(\n        ui: &mut Ui,\n        is_expanded: bool,\n        collapsed_panel: Self,\n        expanded_panel: Self,\n        add_contents: impl FnOnce(&mut Ui, f32) -> R,\n    ) -> InnerResponse<R> {\n        let how_expanded =\n            animate_expansion(ui.ctx(), expanded_panel.id.with(\"animation\"), is_expanded);\n\n        let animated_between_panel = Self::get_animated_between_panel(\n            ui.ctx(),\n            is_expanded,\n            collapsed_panel,\n            expanded_panel,\n        );\n\n        if 0.0 == how_expanded {\n            animated_between_panel.show_inside(ui, |ui| add_contents(ui, how_expanded))\n        } else if how_expanded < 1.0 {\n            // Show animation:\n            animated_between_panel.show_inside(ui, |ui| add_contents(ui, how_expanded))\n        } else {\n            animated_between_panel.show_inside(ui, |ui| add_contents(ui, how_expanded))\n        }\n    }\n}\n\n// Private methods to support the various show methods\nimpl Panel {\n    /// Show the panel inside a [`Ui`].\n    fn show_inside_dyn<'c, R>(\n        self,\n        ui: &mut Ui,\n        add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> InnerResponse<R> {\n        let side = self.side;\n        let id = self.id;\n        let resizable = self.resizable;\n        let show_separator_line = self.show_separator_line;\n        let size_range = self.size_range;\n\n        // Define the sizing of the panel.\n        let mut panel_sizer = PanelSizer::new(&self, ui);\n\n        // Check for duplicate id\n        ui.ctx()\n            .check_for_id_clash(id, panel_sizer.panel_rect, \"Panel\");\n\n        if self.resizable {\n            // Prepare the resizable panel to avoid frame latency in the resize\n            self.prepare_resizable_panel(&mut panel_sizer, ui);\n        }\n\n        // NOTE(shark98): This must be **after** the resizable preparation, as the size\n        // may change and round_ui() uses the size.\n        panel_sizer.panel_rect = panel_sizer.panel_rect.round_ui();\n\n        let mut panel_ui = ui.new_child(\n            UiBuilder::new()\n                .id_salt(id)\n                .ui_stack_info(UiStackInfo::new(side.ui_kind()))\n                .max_rect(panel_sizer.panel_rect)\n                .layout(Layout::top_down(Align::Min)),\n        );\n        panel_ui.expand_to_include_rect(panel_sizer.panel_rect);\n        panel_ui.set_clip_rect(panel_sizer.panel_rect); // If we overflow, don't do so visibly (#4475)\n\n        let inner_response = panel_sizer.frame.show(&mut panel_ui, |ui| {\n            match side {\n                PanelSide::Vertical(_) => {\n                    ui.set_min_height(ui.max_rect().height()); // Make sure the frame fills the full height\n                    ui.set_min_width(\n                        (size_range.min - panel_sizer.frame.inner_margin.sum().x).at_least(0.0),\n                    );\n                }\n                PanelSide::Horizontal(_) => {\n                    ui.set_min_width(ui.max_rect().width()); // Make the frame fill full width\n                    ui.set_min_height(\n                        (size_range.min - panel_sizer.frame.inner_margin.sum().y).at_least(0.0),\n                    );\n                }\n            }\n\n            add_contents(ui)\n        });\n\n        let rect = inner_response.response.rect;\n\n        {\n            let mut cursor = ui.cursor();\n            match side {\n                PanelSide::Vertical(side) => match side {\n                    VerticalSide::Left => cursor.min.x = rect.max.x,\n                    VerticalSide::Right => cursor.max.x = rect.min.x,\n                },\n                PanelSide::Horizontal(side) => match side {\n                    HorizontalSide::Top => cursor.min.y = rect.max.y,\n                    HorizontalSide::Bottom => cursor.max.y = rect.min.y,\n                },\n            }\n            ui.set_cursor(cursor);\n        }\n\n        ui.expand_to_include_rect(rect);\n\n        let mut resize_hover = false;\n        let mut is_resizing = false;\n        if resizable {\n            // Now we do the actual resize interaction, on top of all the contents,\n            // otherwise its input could be eaten by the contents, e.g. a\n            // `ScrollArea` on either side of the panel boundary.\n            (resize_hover, is_resizing) = self.resize_panel(&panel_sizer, ui);\n        }\n\n        if resize_hover || is_resizing {\n            ui.set_cursor_icon(self.cursor_icon(&panel_sizer));\n        }\n\n        PanelState { rect }.store(ui.ctx(), id);\n\n        {\n            let stroke = if is_resizing {\n                ui.style().visuals.widgets.active.fg_stroke // highly visible\n            } else if resize_hover {\n                ui.style().visuals.widgets.hovered.fg_stroke // highly visible\n            } else if show_separator_line {\n                // TODO(emilk): distinguish resizable from non-resizable\n                ui.style().visuals.widgets.noninteractive.bg_stroke // dim\n            } else {\n                Stroke::NONE\n            };\n            // TODO(emilk): draw line on top of all panels in this ui when https://github.com/emilk/egui/issues/1516 is done\n            match side {\n                PanelSide::Vertical(side) => {\n                    let x = side.opposite().side_x(rect) + 0.5 * side.sign() * stroke.width;\n                    ui.painter()\n                        .vline(x, panel_sizer.panel_rect.y_range(), stroke);\n                }\n                PanelSide::Horizontal(side) => {\n                    let y = side.opposite().side_y(rect) + 0.5 * side.sign() * stroke.width;\n                    ui.painter()\n                        .hline(panel_sizer.panel_rect.x_range(), y, stroke);\n                }\n            }\n        }\n\n        inner_response\n    }\n\n    /// Show the panel at the top level.\n    fn show_dyn<'c, R>(\n        self,\n        ctx: &Context,\n        add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> InnerResponse<R> {\n        #![expect(deprecated)]\n\n        let side = self.side;\n        let available_rect = ctx.available_rect();\n        let mut panel_ui = Ui::new(\n            ctx.clone(),\n            self.id,\n            UiBuilder::new()\n                .layer_id(LayerId::background())\n                .max_rect(available_rect),\n        );\n        panel_ui.set_clip_rect(ctx.content_rect());\n        panel_ui\n            .response()\n            .widget_info(|| WidgetInfo::new(WidgetType::Panel));\n\n        let inner_response = self.show_inside_dyn(&mut panel_ui, add_contents);\n        let rect = inner_response.response.rect;\n\n        match side {\n            PanelSide::Vertical(side) => match side {\n                VerticalSide::Left => ctx.pass_state_mut(|state| {\n                    state.allocate_left_panel(Rect::from_min_max(available_rect.min, rect.max));\n                }),\n                VerticalSide::Right => ctx.pass_state_mut(|state| {\n                    state.allocate_right_panel(Rect::from_min_max(rect.min, available_rect.max));\n                }),\n            },\n            PanelSide::Horizontal(side) => match side {\n                HorizontalSide::Top => {\n                    ctx.pass_state_mut(|state| {\n                        state.allocate_top_panel(Rect::from_min_max(available_rect.min, rect.max));\n                    });\n                }\n                HorizontalSide::Bottom => {\n                    ctx.pass_state_mut(|state| {\n                        state.allocate_bottom_panel(Rect::from_min_max(\n                            rect.min,\n                            available_rect.max,\n                        ));\n                    });\n                }\n            },\n        }\n        inner_response\n    }\n\n    fn prepare_resizable_panel(&self, panel_sizer: &mut PanelSizer<'_>, ui: &Ui) {\n        let resize_id = self.id.with(\"__resize\");\n        let resize_response = ui.ctx().read_response(resize_id);\n\n        if let Some(resize_response) = resize_response {\n            // NOTE(sharky98): The original code was initializing to\n            // false first, but it doesn't seem necessary.\n            let is_resizing = resize_response.dragged();\n            let pointer = resize_response.interact_pointer_pos();\n            panel_sizer.prepare_resizing_response(is_resizing, pointer);\n        }\n    }\n\n    fn resize_panel(&self, panel_sizer: &PanelSizer<'_>, ui: &Ui) -> (bool, bool) {\n        let (resize_x, resize_y, amount): (Rangef, Rangef, Vec2) = match self.side {\n            PanelSide::Vertical(side) => {\n                let resize_x = side.opposite().side_x(panel_sizer.panel_rect);\n                let resize_y = panel_sizer.panel_rect.y_range();\n                (\n                    Rangef::from(resize_x..=resize_x),\n                    resize_y,\n                    vec2(ui.style().interaction.resize_grab_radius_side, 0.0),\n                )\n            }\n            PanelSide::Horizontal(side) => {\n                let resize_x = panel_sizer.panel_rect.x_range();\n                let resize_y = side.opposite().side_y(panel_sizer.panel_rect);\n                (\n                    resize_x,\n                    Rangef::from(resize_y..=resize_y),\n                    vec2(0.0, ui.style().interaction.resize_grab_radius_side),\n                )\n            }\n        };\n\n        let resize_id = self.id.with(\"__resize\");\n        let resize_rect = Rect::from_x_y_ranges(resize_x, resize_y).expand2(amount);\n        let resize_response = ui.interact(resize_rect, resize_id, Sense::drag());\n\n        (resize_response.hovered(), resize_response.dragged())\n    }\n\n    fn cursor_icon(&self, panel_sizer: &PanelSizer<'_>) -> CursorIcon {\n        if panel_sizer.size <= self.size_range.min {\n            match self.side {\n                PanelSide::Vertical(side) => match side {\n                    VerticalSide::Left => CursorIcon::ResizeEast,\n                    VerticalSide::Right => CursorIcon::ResizeWest,\n                },\n                PanelSide::Horizontal(side) => match side {\n                    HorizontalSide::Top => CursorIcon::ResizeSouth,\n                    HorizontalSide::Bottom => CursorIcon::ResizeNorth,\n                },\n            }\n        } else if panel_sizer.size < self.size_range.max {\n            match self.side {\n                PanelSide::Vertical(_) => CursorIcon::ResizeHorizontal,\n                PanelSide::Horizontal(_) => CursorIcon::ResizeVertical,\n            }\n        } else {\n            match self.side {\n                PanelSide::Vertical(side) => match side {\n                    VerticalSide::Left => CursorIcon::ResizeWest,\n                    VerticalSide::Right => CursorIcon::ResizeEast,\n                },\n                PanelSide::Horizontal(side) => match side {\n                    HorizontalSide::Top => CursorIcon::ResizeNorth,\n                    HorizontalSide::Bottom => CursorIcon::ResizeSouth,\n                },\n            }\n        }\n    }\n\n    /// Get the real or fake panel to animate if `is_expanded` is `true`.\n    fn get_animated_panel(self, ctx: &Context, is_expanded: bool) -> Option<Self> {\n        let how_expanded = animate_expansion(ctx, self.id.with(\"animation\"), is_expanded);\n\n        if 0.0 == how_expanded {\n            None\n        } else if how_expanded < 1.0 {\n            // Show a fake panel in this in-between animation state:\n            // TODO(emilk): move the panel out-of-screen instead of changing its width.\n            // Then we can actually paint it as it animates.\n            let expanded_size = Self::animated_size(ctx, &self);\n            let fake_size = how_expanded * expanded_size;\n            Some(\n                Self {\n                    id: self.id.with(\"animating_panel\"),\n                    ..self\n                }\n                .resizable(false)\n                .exact_size(fake_size),\n            )\n        } else {\n            // Show the real panel:\n            Some(self)\n        }\n    }\n\n    /// Get either the collapsed or expended panel to animate.\n    fn get_animated_between_panel(\n        ctx: &Context,\n        is_expanded: bool,\n        collapsed_panel: Self,\n        expanded_panel: Self,\n    ) -> Self {\n        let how_expanded = animate_expansion(ctx, expanded_panel.id.with(\"animation\"), is_expanded);\n\n        if 0.0 == how_expanded {\n            collapsed_panel\n        } else if how_expanded < 1.0 {\n            let collapsed_size = Self::animated_size(ctx, &collapsed_panel);\n            let expanded_size = Self::animated_size(ctx, &expanded_panel);\n\n            let fake_size = lerp(collapsed_size..=expanded_size, how_expanded);\n\n            Self {\n                id: expanded_panel.id.with(\"animating_panel\"),\n                ..expanded_panel\n            }\n            .resizable(false)\n            .exact_size(fake_size)\n        } else {\n            expanded_panel\n        }\n    }\n\n    fn animated_size(ctx: &Context, panel: &Self) -> f32 {\n        let get_rect_state_size = |state: PanelState| match panel.side {\n            PanelSide::Vertical(_) => state.rect.width(),\n            PanelSide::Horizontal(_) => state.rect.height(),\n        };\n\n        let get_spacing_size = || match panel.side {\n            PanelSide::Vertical(_) => ctx.global_style().spacing.interact_size.x,\n            PanelSide::Horizontal(_) => ctx.global_style().spacing.interact_size.y,\n        };\n\n        PanelState::load(ctx, panel.id)\n            .map(get_rect_state_size)\n            .or(panel.default_size)\n            .unwrap_or_else(get_spacing_size)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A panel that covers the remainder of the screen,\n/// i.e. whatever area is left after adding other panels.\n///\n/// This acts very similar to [`Frame::central_panel`], but always expands\n/// to use up all available space.\n///\n/// The order in which you add panels matter!\n/// The first panel you add will always be the outermost, and the last you add will always be the innermost.\n///\n/// ⚠ [`CentralPanel`] must be added after all other panels!\n///\n/// NOTE: Any [`crate::Window`]s and [`crate::Area`]s will cover the top-level [`CentralPanel`].\n///\n/// See the [module level docs](crate::containers::panel) for more details.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// egui::Panel::top(\"my_panel\").show_inside(ui, |ui| {\n///    ui.label(\"Hello World! From `Panel`, that must be before `CentralPanel`!\");\n/// });\n/// egui::CentralPanel::default().show_inside(ui, |ui| {\n///    ui.label(\"Hello World!\");\n/// });\n/// # });\n/// ```\n#[must_use = \"You should call .show_inside()\"]\n#[derive(Default)]\npub struct CentralPanel {\n    frame: Option<Frame>,\n}\n\nimpl CentralPanel {\n    /// A central panel with no margin or background color\n    pub fn no_frame() -> Self {\n        Self {\n            frame: Some(Frame::NONE),\n        }\n    }\n\n    /// A central panel with a background color and some inner margins\n    pub fn default_margins() -> Self {\n        Self { frame: None }\n    }\n\n    /// Change the background color, margins, etc.\n    #[inline]\n    pub fn frame(mut self, frame: Frame) -> Self {\n        self.frame = Some(frame);\n        self\n    }\n\n    /// Show the panel inside a [`Ui`].\n    pub fn show_inside<R>(\n        self,\n        ui: &mut Ui,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.show_inside_dyn(ui, Box::new(add_contents))\n    }\n\n    /// Show the panel inside a [`Ui`].\n    fn show_inside_dyn<'c, R>(\n        self,\n        ui: &mut Ui,\n        add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> InnerResponse<R> {\n        let Self { frame } = self;\n\n        let panel_rect = ui.available_rect_before_wrap();\n        let mut panel_ui = ui.new_child(\n            UiBuilder::new()\n                .ui_stack_info(UiStackInfo::new(UiKind::CentralPanel))\n                .max_rect(panel_rect)\n                .layout(Layout::top_down(Align::Min)),\n        );\n        panel_ui.set_clip_rect(panel_rect); // If we overflow, don't do so visibly (#4475)\n\n        let frame = frame.unwrap_or_else(|| Frame::central_panel(ui.style()));\n        let response = frame.show(&mut panel_ui, |ui| {\n            ui.expand_to_include_rect(ui.max_rect()); // Expand frame to include it all\n            add_contents(ui)\n        });\n\n        // Use up space in the parent:\n        ui.advance_cursor_after_rect(response.response.rect);\n\n        response\n    }\n\n    /// Show the panel at the top level.\n    #[deprecated = \"Use show_inside() instead\"]\n    pub fn show<R>(\n        self,\n        ctx: &Context,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.show_dyn(ctx, Box::new(add_contents))\n    }\n\n    /// Show the panel at the top level.\n    fn show_dyn<'c, R>(\n        self,\n        ctx: &Context,\n        add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> InnerResponse<R> {\n        #![expect(deprecated)]\n\n        let id = Id::new((ctx.viewport_id(), \"central_panel\"));\n\n        let mut panel_ui = Ui::new(\n            ctx.clone(),\n            id,\n            UiBuilder::new()\n                .layer_id(LayerId::background())\n                .max_rect(ctx.available_rect()),\n        );\n        panel_ui.set_clip_rect(ctx.content_rect());\n\n        if false {\n            // TODO(emilk): @lucasmerlin shouldn't we enable this?\n            panel_ui\n                .response()\n                .widget_info(|| WidgetInfo::new(WidgetType::Panel));\n        }\n\n        let inner_response = self.show_inside_dyn(&mut panel_ui, add_contents);\n\n        // Only inform ctx about what we actually used, so we can shrink the native window to fit.\n        ctx.pass_state_mut(|state| state.allocate_central_panel(inner_response.response.rect));\n\n        inner_response\n    }\n}\n\nfn clamp_to_range(x: f32, range: Rangef) -> f32 {\n    let range = range.as_positive();\n    x.clamp(range.min, range.max)\n}\n\n// ----------------------------------------------------------------------------\n\n#[deprecated = \"Use Panel::left or Panel::right instead\"]\npub type SidePanel = super::Panel;\n\n#[deprecated = \"Use Panel::top or Panel::bottom instead\"]\npub type TopBottomPanel = super::Panel;\n"
  },
  {
    "path": "crates/egui/src/containers/popup.rs",
    "content": "#![expect(deprecated)] // This is a new, safe wrapper around the old `Memory::popup` API.\n\nuse std::iter::once;\n\nuse emath::{Align, Pos2, Rect, RectAlign, Vec2, vec2};\n\nuse crate::{\n    Area, AreaState, Context, Frame, Id, InnerResponse, Key, LayerId, Layout, Order, Response,\n    Sense, Ui, UiKind, UiStackInfo,\n    containers::menu::{MenuConfig, MenuState, menu_style},\n    style::StyleModifier,\n};\n\n/// What should we anchor the popup to?\n///\n/// The final position for the popup will be calculated based on [`RectAlign`]\n/// and can be customized with [`Popup::align`] and [`Popup::align_alternatives`].\n/// [`PopupAnchor`] is the parent rect of [`RectAlign`].\n///\n/// For [`PopupAnchor::Pointer`], [`PopupAnchor::PointerFixed`] and [`PopupAnchor::Position`],\n/// the rect will be derived via [`Rect::from_pos`] (so a zero-sized rect at the given position).\n///\n/// The rect should be in global coordinates. `PopupAnchor::from(&response)` will automatically\n/// do this conversion.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum PopupAnchor {\n    /// Show the popup relative to some parent [`Rect`].\n    ParentRect(Rect),\n\n    /// Show the popup relative to the mouse pointer.\n    Pointer,\n\n    /// Remember the mouse position and show the popup relative to that (like a context menu).\n    PointerFixed,\n\n    /// Show the popup relative to a specific position.\n    Position(Pos2),\n}\n\nimpl From<Rect> for PopupAnchor {\n    fn from(rect: Rect) -> Self {\n        Self::ParentRect(rect)\n    }\n}\n\nimpl From<Pos2> for PopupAnchor {\n    fn from(pos: Pos2) -> Self {\n        Self::Position(pos)\n    }\n}\n\nimpl From<&Response> for PopupAnchor {\n    fn from(response: &Response) -> Self {\n        // We use interact_rect so we don't show the popup relative to some clipped point\n        let mut widget_rect = response.interact_rect;\n        if let Some(to_global) = response.ctx.layer_transform_to_global(response.layer_id) {\n            widget_rect = to_global * widget_rect;\n        }\n        Self::ParentRect(widget_rect)\n    }\n}\n\nimpl PopupAnchor {\n    /// Get the rect the popup should be shown relative to.\n    /// Returns `Rect::from_pos` for [`PopupAnchor::Pointer`], [`PopupAnchor::PointerFixed`]\n    /// and [`PopupAnchor::Position`] (so the rect will be zero-sized).\n    pub fn rect(self, popup_id: Id, ctx: &Context) -> Option<Rect> {\n        match self {\n            Self::ParentRect(rect) => Some(rect),\n            Self::Pointer => ctx.pointer_hover_pos().map(Rect::from_pos),\n            Self::PointerFixed => Popup::position_of_id(ctx, popup_id).map(Rect::from_pos),\n            Self::Position(pos) => Some(Rect::from_pos(pos)),\n        }\n    }\n}\n\n/// Determines popup's close behavior\n#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]\npub enum PopupCloseBehavior {\n    /// Popup will be closed on click anywhere, inside or outside the popup.\n    ///\n    /// It is used in [`crate::ComboBox`] and in [`crate::containers::menu`]s.\n    #[default]\n    CloseOnClick,\n\n    /// Popup will be closed if the click happened somewhere else\n    /// but in the popup's body\n    CloseOnClickOutside,\n\n    /// Clicks will be ignored. Popup might be closed manually by calling [`crate::Memory::close_all_popups`]\n    /// or by pressing the escape button\n    IgnoreClicks,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum SetOpenCommand {\n    /// Set the open state to the given value\n    Bool(bool),\n\n    /// Toggle the open state\n    Toggle,\n}\n\nimpl From<bool> for SetOpenCommand {\n    fn from(b: bool) -> Self {\n        Self::Bool(b)\n    }\n}\n\n/// How do we determine if the popup should be open or closed\nenum OpenKind<'a> {\n    /// Always open\n    Open,\n\n    /// Always closed\n    Closed,\n\n    /// Open if the bool is true\n    Bool(&'a mut bool),\n\n    /// Store the open state via [`crate::Memory`]\n    Memory { set: Option<SetOpenCommand> },\n}\n\nimpl OpenKind<'_> {\n    /// Returns `true` if the popup should be open\n    fn is_open(&self, popup_id: Id, ctx: &Context) -> bool {\n        match self {\n            OpenKind::Open => true,\n            OpenKind::Closed => false,\n            OpenKind::Bool(open) => **open,\n            OpenKind::Memory { .. } => Popup::is_id_open(ctx, popup_id),\n        }\n    }\n}\n\n/// Is the popup a popup, tooltip or menu?\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum PopupKind {\n    Popup,\n    Tooltip,\n    Menu,\n}\n\nimpl PopupKind {\n    /// Returns the order to be used with this kind.\n    pub fn order(self) -> Order {\n        match self {\n            Self::Tooltip => Order::Tooltip,\n            Self::Menu | Self::Popup => Order::Foreground,\n        }\n    }\n}\n\nimpl From<PopupKind> for UiKind {\n    fn from(kind: PopupKind) -> Self {\n        match kind {\n            PopupKind::Popup => Self::Popup,\n            PopupKind::Tooltip => Self::Tooltip,\n            PopupKind::Menu => Self::Menu,\n        }\n    }\n}\n\n/// A popup container.\n#[must_use = \"Call `.show()` to actually display the popup\"]\npub struct Popup<'a> {\n    id: Id,\n    ctx: Context,\n    anchor: PopupAnchor,\n    rect_align: RectAlign,\n    alternative_aligns: Option<&'a [RectAlign]>,\n    layer_id: LayerId,\n    open_kind: OpenKind<'a>,\n    close_behavior: PopupCloseBehavior,\n    info: Option<UiStackInfo>,\n    kind: PopupKind,\n\n    /// Gap between the anchor and the popup\n    gap: f32,\n\n    /// Default width passed to the Area\n    width: Option<f32>,\n    sense: Sense,\n    layout: Layout,\n    frame: Option<Frame>,\n    style: StyleModifier,\n}\n\nimpl<'a> Popup<'a> {\n    /// Create a new popup\n    pub fn new(id: Id, ctx: Context, anchor: impl Into<PopupAnchor>, layer_id: LayerId) -> Self {\n        Self {\n            id,\n            ctx,\n            anchor: anchor.into(),\n            open_kind: OpenKind::Open,\n            close_behavior: PopupCloseBehavior::default(),\n            info: None,\n            kind: PopupKind::Popup,\n            layer_id,\n            rect_align: RectAlign::BOTTOM_START,\n            alternative_aligns: None,\n            gap: 0.0,\n            width: None,\n            sense: Sense::click(),\n            layout: Layout::default(),\n            frame: None,\n            style: StyleModifier::default(),\n        }\n    }\n\n    /// Show a popup relative to some widget.\n    /// The popup will be always open.\n    ///\n    /// See [`Self::menu`] and [`Self::context_menu`] for common use cases.\n    pub fn from_response(response: &Response) -> Self {\n        Self::new(\n            Self::default_response_id(response),\n            response.ctx.clone(),\n            response,\n            response.layer_id,\n        )\n    }\n\n    /// Show a popup relative to some widget,\n    /// toggling the open state based on the widget's click state.\n    ///\n    /// See [`Self::menu`] and [`Self::context_menu`] for common use cases.\n    pub fn from_toggle_button_response(button_response: &Response) -> Self {\n        Self::from_response(button_response)\n            .open_memory(button_response.clicked().then_some(SetOpenCommand::Toggle))\n    }\n\n    /// Show a popup when the widget was clicked.\n    /// Sets the layout to `Layout::top_down_justified(Align::Min)`.\n    pub fn menu(button_response: &Response) -> Self {\n        Self::from_toggle_button_response(button_response)\n            .kind(PopupKind::Menu)\n            .layout(Layout::top_down_justified(Align::Min))\n            .style(menu_style)\n            .gap(0.0)\n    }\n\n    /// Show a context menu when the widget was secondary clicked.\n    /// Sets the layout to `Layout::top_down_justified(Align::Min)`.\n    /// In contrast to [`Self::menu`], this will open at the pointer position.\n    pub fn context_menu(response: &Response) -> Self {\n        Self::menu(response)\n            .open_memory(if response.secondary_clicked() {\n                Some(SetOpenCommand::Bool(true))\n            } else if response.clicked() {\n                // Explicitly close the menu if the widget was clicked\n                // Without this, the context menu would stay open if the user clicks the widget\n                Some(SetOpenCommand::Bool(false))\n            } else {\n                None\n            })\n            .at_pointer_fixed()\n    }\n\n    /// Set the kind of the popup. Used for [`Area::kind`] and [`Area::order`].\n    #[inline]\n    pub fn kind(mut self, kind: PopupKind) -> Self {\n        self.kind = kind;\n        self\n    }\n\n    /// Set the [`UiStackInfo`] of the popup's [`Ui`].\n    #[inline]\n    pub fn info(mut self, info: UiStackInfo) -> Self {\n        self.info = Some(info);\n        self\n    }\n\n    /// Set the [`RectAlign`] of the popup relative to the [`PopupAnchor`].\n    /// This is the default position, and will be used if it fits.\n    /// See [`Self::align_alternatives`] for more on this.\n    #[inline]\n    pub fn align(mut self, position_align: RectAlign) -> Self {\n        self.rect_align = position_align;\n        self\n    }\n\n    /// Set alternative positions to try if the default one doesn't fit. Set to an empty slice to\n    /// always use the position you set with [`Self::align`].\n    /// By default, this will try [`RectAlign::symmetries`] and then [`RectAlign::MENU_ALIGNS`].\n    #[inline]\n    pub fn align_alternatives(mut self, alternatives: &'a [RectAlign]) -> Self {\n        self.alternative_aligns = Some(alternatives);\n        self\n    }\n\n    /// Force the popup to be open or closed.\n    #[inline]\n    pub fn open(mut self, open: bool) -> Self {\n        self.open_kind = if open {\n            OpenKind::Open\n        } else {\n            OpenKind::Closed\n        };\n        self\n    }\n\n    /// Store the open state via [`crate::Memory`].\n    /// You can set the state via the first [`SetOpenCommand`] param.\n    #[inline]\n    pub fn open_memory(mut self, set_state: impl Into<Option<SetOpenCommand>>) -> Self {\n        self.open_kind = OpenKind::Memory {\n            set: set_state.into(),\n        };\n        self\n    }\n\n    /// Store the open state via a mutable bool.\n    #[inline]\n    pub fn open_bool(mut self, open: &'a mut bool) -> Self {\n        self.open_kind = OpenKind::Bool(open);\n        self\n    }\n\n    /// Set the close behavior of the popup.\n    ///\n    /// This will do nothing if [`Popup::open`] was called.\n    #[inline]\n    pub fn close_behavior(mut self, close_behavior: PopupCloseBehavior) -> Self {\n        self.close_behavior = close_behavior;\n        self\n    }\n\n    /// Show the popup relative to the pointer.\n    #[inline]\n    pub fn at_pointer(mut self) -> Self {\n        self.anchor = PopupAnchor::Pointer;\n        self\n    }\n\n    /// Remember the pointer position at the time of opening the popup, and show the popup\n    /// relative to that.\n    #[inline]\n    pub fn at_pointer_fixed(mut self) -> Self {\n        self.anchor = PopupAnchor::PointerFixed;\n        self\n    }\n\n    /// Show the popup relative to a specific position.\n    #[inline]\n    pub fn at_position(mut self, position: Pos2) -> Self {\n        self.anchor = PopupAnchor::Position(position);\n        self\n    }\n\n    /// Show the popup relative to the given [`PopupAnchor`].\n    #[inline]\n    pub fn anchor(mut self, anchor: impl Into<PopupAnchor>) -> Self {\n        self.anchor = anchor.into();\n        self\n    }\n\n    /// Set the gap between the anchor and the popup.\n    #[inline]\n    pub fn gap(mut self, gap: f32) -> Self {\n        self.gap = gap;\n        self\n    }\n\n    /// Set the frame of the popup.\n    #[inline]\n    pub fn frame(mut self, frame: Frame) -> Self {\n        self.frame = Some(frame);\n        self\n    }\n\n    /// Set the sense of the popup.\n    #[inline]\n    pub fn sense(mut self, sense: Sense) -> Self {\n        self.sense = sense;\n        self\n    }\n\n    /// Set the layout of the popup.\n    #[inline]\n    pub fn layout(mut self, layout: Layout) -> Self {\n        self.layout = layout;\n        self\n    }\n\n    /// The width that will be passed to [`Area::default_width`].\n    #[inline]\n    pub fn width(mut self, width: f32) -> Self {\n        self.width = Some(width);\n        self\n    }\n\n    /// Set the id of the Area.\n    #[inline]\n    pub fn id(mut self, id: Id) -> Self {\n        self.id = id;\n        self\n    }\n\n    /// Set the style for the popup contents.\n    ///\n    /// Default:\n    /// - is [`menu_style`] for [`Self::menu`] and [`Self::context_menu`]\n    /// - is [`None`] otherwise\n    #[inline]\n    pub fn style(mut self, style: impl Into<StyleModifier>) -> Self {\n        self.style = style.into();\n        self\n    }\n\n    /// Get the [`Context`]\n    pub fn ctx(&self) -> &Context {\n        &self.ctx\n    }\n\n    /// Return the [`PopupAnchor`] of the popup.\n    pub fn get_anchor(&self) -> PopupAnchor {\n        self.anchor\n    }\n\n    /// Return the anchor rect of the popup.\n    ///\n    /// Returns `None` if the anchor is [`PopupAnchor::Pointer`] and there is no pointer.\n    pub fn get_anchor_rect(&self) -> Option<Rect> {\n        self.anchor.rect(self.id, &self.ctx)\n    }\n\n    /// Get the expected rect the popup will be shown in.\n    ///\n    /// Returns `None` if the popup wasn't shown before or anchor is `PopupAnchor::Pointer` and\n    /// there is no pointer.\n    pub fn get_popup_rect(&self) -> Option<Rect> {\n        let size = self.get_expected_size();\n        if let Some(size) = size {\n            self.get_anchor_rect()\n                .map(|anchor| self.get_best_align().align_rect(&anchor, size, self.gap))\n        } else {\n            None\n        }\n    }\n\n    /// Get the id of the popup.\n    pub fn get_id(&self) -> Id {\n        self.id\n    }\n\n    /// Is the popup open?\n    pub fn is_open(&self) -> bool {\n        match &self.open_kind {\n            OpenKind::Open => true,\n            OpenKind::Closed => false,\n            OpenKind::Bool(open) => **open,\n            OpenKind::Memory { .. } => Self::is_id_open(&self.ctx, self.id),\n        }\n    }\n\n    /// Get the expected size of the popup.\n    pub fn get_expected_size(&self) -> Option<Vec2> {\n        AreaState::load(&self.ctx, self.id)?.size\n    }\n\n    /// Calculate the best alignment for the popup, based on the last size and screen rect.\n    pub fn get_best_align(&self) -> RectAlign {\n        let expected_popup_size = self\n            .get_expected_size()\n            .unwrap_or_else(|| vec2(self.width.unwrap_or(0.0), 0.0));\n\n        let Some(anchor_rect) = self.anchor.rect(self.id, &self.ctx) else {\n            return self.rect_align;\n        };\n\n        RectAlign::find_best_align(\n            #[expect(clippy::iter_on_empty_collections)]\n            #[expect(clippy::or_fun_call)]\n            once(self.rect_align).chain(\n                self.alternative_aligns\n                    // Need the empty slice so the iters have the same type so we can unwrap_or\n                    .map(|a| a.iter().copied().chain([].iter().copied()))\n                    .unwrap_or(\n                        self.rect_align\n                            .symmetries()\n                            .iter()\n                            .copied()\n                            .chain(RectAlign::MENU_ALIGNS.iter().copied()),\n                    ),\n            ),\n            self.ctx.content_rect(),\n            anchor_rect,\n            self.gap,\n            expected_popup_size,\n        )\n        .unwrap_or_default()\n    }\n\n    /// Show the popup.\n    ///\n    /// Returns `None` if the popup is not open or anchor is `PopupAnchor::Pointer` and there is\n    /// no pointer.\n    pub fn show<R>(self, content: impl FnOnce(&mut Ui) -> R) -> Option<InnerResponse<R>> {\n        let id = self.id;\n        // When the popup was just opened with a click we don't want to immediately close it based\n        // on the `PopupCloseBehavior`, so we need to remember if the popup was already open on\n        // last frame. A convenient way to check this is to see if we have a response for the `Area`\n        // from last frame:\n        let was_open_last_frame = self.ctx.read_response(id).is_some();\n\n        let hover_pos = self.ctx.pointer_hover_pos();\n        if let OpenKind::Memory { set } = self.open_kind {\n            match set {\n                Some(SetOpenCommand::Bool(open)) => {\n                    if open {\n                        match self.anchor {\n                            PopupAnchor::PointerFixed => {\n                                self.ctx.memory_mut(|mem| mem.open_popup_at(id, hover_pos));\n                            }\n                            _ => Popup::open_id(&self.ctx, id),\n                        }\n                    } else {\n                        Self::close_id(&self.ctx, id);\n                    }\n                }\n                Some(SetOpenCommand::Toggle) => {\n                    Self::toggle_id(&self.ctx, id);\n                }\n                None => {\n                    self.ctx.memory_mut(|mem| mem.keep_popup_open(id));\n                }\n            }\n        }\n\n        if !self.open_kind.is_open(self.id, &self.ctx) {\n            return None;\n        }\n\n        let best_align = self.get_best_align();\n\n        let Popup {\n            id,\n            ctx,\n            anchor,\n            open_kind,\n            close_behavior,\n            kind,\n            info,\n            layer_id,\n            rect_align: _,\n            alternative_aligns: _,\n            gap,\n            width,\n            sense,\n            layout,\n            frame,\n            style,\n        } = self;\n\n        if kind != PopupKind::Tooltip {\n            ctx.pass_state_mut(|fs| {\n                fs.layers\n                    .entry(layer_id)\n                    .or_default()\n                    .open_popups\n                    .insert(id)\n            });\n        }\n\n        let anchor_rect = anchor.rect(id, &ctx)?;\n\n        let (pivot, anchor) = best_align.pivot_pos(&anchor_rect, gap);\n\n        let mut area = Area::new(id)\n            .order(kind.order())\n            .pivot(pivot)\n            .fixed_pos(anchor)\n            .sense(sense)\n            .layout(layout)\n            .info(info.unwrap_or_else(|| {\n                UiStackInfo::new(kind.into()).with_tag_value(\n                    MenuConfig::MENU_CONFIG_TAG,\n                    MenuConfig::new()\n                        .close_behavior(close_behavior)\n                        .style(style.clone()),\n                )\n            }));\n\n        if let Some(width) = width {\n            area = area.default_width(width);\n        }\n\n        let mut response = area.show(&ctx, |ui| {\n            style.apply(ui.style_mut());\n            let frame = frame.unwrap_or_else(|| Frame::popup(ui.style()));\n            frame.show(ui, content).inner\n        });\n\n        // If the popup was just opened with a click, we don't want to immediately close it again.\n        let close_click = was_open_last_frame && ctx.input(|i| i.pointer.any_click());\n\n        let closed_by_click = match close_behavior {\n            PopupCloseBehavior::CloseOnClick => close_click,\n            PopupCloseBehavior::CloseOnClickOutside => {\n                close_click && response.response.clicked_elsewhere()\n            }\n            PopupCloseBehavior::IgnoreClicks => false,\n        };\n\n        // Mark the menu as shown, so the sub menu open state is not reset\n        MenuState::mark_shown(&ctx, id);\n\n        // If a submenu is open, the CloseBehavior is handled there\n        let is_any_submenu_open = !MenuState::is_deepest_open_sub_menu(&response.response.ctx, id);\n\n        let should_close = (!is_any_submenu_open && closed_by_click)\n            || ctx.input(|i| i.key_pressed(Key::Escape))\n            || response.response.should_close();\n\n        if should_close {\n            response.response.set_close();\n        }\n\n        match open_kind {\n            OpenKind::Open | OpenKind::Closed => {}\n            OpenKind::Bool(open) => {\n                if should_close {\n                    *open = false;\n                }\n            }\n            OpenKind::Memory { .. } => {\n                if should_close {\n                    ctx.memory_mut(|mem| mem.close_popup(id));\n                }\n            }\n        }\n\n        Some(response)\n    }\n}\n\n/// ## Static methods\nimpl Popup<'_> {\n    /// The default ID when constructing a popup from the [`Response`] of e.g. a button.\n    pub fn default_response_id(response: &Response) -> Id {\n        response.id.with(\"popup\")\n    }\n\n    /// Is the given popup open?\n    ///\n    /// This assumes the use of either:\n    /// * [`Self::open_memory`]\n    /// * [`Self::from_toggle_button_response`]\n    /// * [`Self::menu`]\n    /// * [`Self::context_menu`]\n    ///\n    /// The popup id should be the same as either you set with [`Self::id`] or the\n    /// default one from [`Self::default_response_id`].\n    pub fn is_id_open(ctx: &Context, popup_id: Id) -> bool {\n        ctx.memory(|mem| mem.is_popup_open(popup_id))\n    }\n\n    /// Is any popup open?\n    ///\n    /// This assumes the egui memory is being used to track the open state of popups.\n    pub fn is_any_open(ctx: &Context) -> bool {\n        ctx.memory(|mem| mem.any_popup_open())\n    }\n\n    /// Open the given popup and close all others.\n    ///\n    /// If you are NOT using [`Popup::show`], you must\n    /// also call [`crate::Memory::keep_popup_open`] as long as\n    /// you're showing the popup.\n    pub fn open_id(ctx: &Context, popup_id: Id) {\n        ctx.memory_mut(|mem| mem.open_popup(popup_id));\n    }\n\n    /// Toggle the given popup between closed and open.\n    ///\n    /// Note: At most, only one popup can be open at a time.\n    pub fn toggle_id(ctx: &Context, popup_id: Id) {\n        ctx.memory_mut(|mem| mem.toggle_popup(popup_id));\n    }\n\n    /// Close all currently open popups.\n    pub fn close_all(ctx: &Context) {\n        ctx.memory_mut(|mem| mem.close_all_popups());\n    }\n\n    /// Close the given popup, if it is open.\n    ///\n    /// See also [`Self::close_all`] if you want to close any / all currently open popups.\n    pub fn close_id(ctx: &Context, popup_id: Id) {\n        ctx.memory_mut(|mem| mem.close_popup(popup_id));\n    }\n\n    /// Get the position for this popup, if it is open.\n    pub fn position_of_id(ctx: &Context, popup_id: Id) -> Option<Pos2> {\n        ctx.memory(|mem| mem.popup_position(popup_id))\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/resize.rs",
    "content": "use crate::{\n    Align2, Color32, Context, CursorIcon, Id, NumExt as _, Rect, Response, Sense, Shape, Ui,\n    UiBuilder, UiKind, UiStackInfo, Vec2, Vec2b, pos2, vec2,\n};\n\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub(crate) struct State {\n    /// This is the size that the user has picked by dragging the resize handles.\n    /// This may be smaller and/or larger than the actual size.\n    /// For instance, the user may have tried to shrink too much (not fitting the contents).\n    /// Or the user requested a large area, but the content don't need that much space.\n    pub(crate) desired_size: Vec2,\n\n    /// Actual size of content last frame\n    last_content_size: Vec2,\n\n    /// Externally requested size (e.g. by Window) for the next frame\n    pub(crate) requested_size: Option<Vec2>,\n}\n\nimpl State {\n    pub fn load(ctx: &Context, id: Id) -> Option<Self> {\n        ctx.data_mut(|d| d.get_persisted(id))\n    }\n\n    pub fn store(self, ctx: &Context, id: Id) {\n        ctx.data_mut(|d| d.insert_persisted(id, self));\n    }\n}\n\n/// A region that can be resized by dragging the bottom right corner.\n#[derive(Clone, Copy, Debug)]\n#[must_use = \"You should call .show()\"]\npub struct Resize {\n    id: Option<Id>,\n    id_salt: Option<Id>,\n\n    /// If false, we are no enabled\n    resizable: Vec2b,\n\n    pub(crate) min_size: Vec2,\n    pub(crate) max_size: Vec2,\n\n    default_size: Vec2,\n\n    with_stroke: bool,\n}\n\nimpl Default for Resize {\n    fn default() -> Self {\n        Self {\n            id: None,\n            id_salt: None,\n            resizable: Vec2b::TRUE,\n            min_size: Vec2::splat(16.0),\n            max_size: Vec2::splat(f32::INFINITY),\n            default_size: vec2(320.0, 128.0), // TODO(emilk): preferred size of [`Resize`] area.\n            with_stroke: true,\n        }\n    }\n}\n\nimpl Resize {\n    /// Assign an explicit and globally unique id.\n    #[inline]\n    pub fn id(mut self, id: Id) -> Self {\n        self.id = Some(id);\n        self\n    }\n\n    /// A source for the unique [`Id`], e.g. `.id_source(\"second_resize_area\")` or `.id_source(loop_index)`.\n    #[inline]\n    #[deprecated = \"Renamed id_salt\"]\n    pub fn id_source(self, id_salt: impl std::hash::Hash) -> Self {\n        self.id_salt(id_salt)\n    }\n\n    /// A source for the unique [`Id`], e.g. `.id_salt(\"second_resize_area\")` or `.id_salt(loop_index)`.\n    #[inline]\n    pub fn id_salt(mut self, id_salt: impl std::hash::Hash) -> Self {\n        self.id_salt = Some(Id::new(id_salt));\n        self\n    }\n\n    /// Preferred / suggested width. Actual width will depend on contents.\n    ///\n    /// Examples:\n    /// * if the contents is text, this will decide where we break long lines.\n    /// * if the contents is a canvas, this decides the width of it,\n    /// * if the contents is some buttons, this is ignored and we will auto-size.\n    #[inline]\n    pub fn default_width(mut self, width: f32) -> Self {\n        self.default_size.x = width;\n        self\n    }\n\n    /// Preferred / suggested height. Actual height will depend on contents.\n    ///\n    /// Examples:\n    /// * if the contents is a [`crate::ScrollArea`] then this decides the maximum size.\n    /// * if the contents is a canvas, this decides the height of it,\n    /// * if the contents is text and buttons, then the `default_height` is ignored\n    ///   and the height is picked automatically..\n    #[inline]\n    pub fn default_height(mut self, height: f32) -> Self {\n        self.default_size.y = height;\n        self\n    }\n\n    #[inline]\n    pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {\n        self.default_size = default_size.into();\n        self\n    }\n\n    /// Won't shrink to smaller than this\n    #[inline]\n    pub fn min_size(mut self, min_size: impl Into<Vec2>) -> Self {\n        self.min_size = min_size.into();\n        self\n    }\n\n    /// Won't shrink to smaller than this\n    #[inline]\n    pub fn min_width(mut self, min_width: f32) -> Self {\n        self.min_size.x = min_width;\n        self\n    }\n\n    /// Won't shrink to smaller than this\n    #[inline]\n    pub fn min_height(mut self, min_height: f32) -> Self {\n        self.min_size.y = min_height;\n        self\n    }\n\n    /// Won't expand to larger than this\n    #[inline]\n    pub fn max_size(mut self, max_size: impl Into<Vec2>) -> Self {\n        self.max_size = max_size.into();\n        self\n    }\n\n    /// Won't expand to larger than this\n    #[inline]\n    pub fn max_width(mut self, max_width: f32) -> Self {\n        self.max_size.x = max_width;\n        self\n    }\n\n    /// Won't expand to larger than this\n    #[inline]\n    pub fn max_height(mut self, max_height: f32) -> Self {\n        self.max_size.y = max_height;\n        self\n    }\n\n    /// Can you resize it with the mouse?\n    ///\n    /// Note that a window can still auto-resize.\n    ///\n    /// Default is `true`.\n    #[inline]\n    pub fn resizable(mut self, resizable: impl Into<Vec2b>) -> Self {\n        self.resizable = resizable.into();\n        self\n    }\n\n    #[inline]\n    pub fn is_resizable(&self) -> Vec2b {\n        self.resizable\n    }\n\n    /// Not manually resizable, just takes the size of its contents.\n    /// Text will not wrap, but will instead make your window width expand.\n    pub fn auto_sized(self) -> Self {\n        self.min_size(Vec2::ZERO)\n            .default_size(Vec2::splat(f32::INFINITY))\n            .resizable(false)\n    }\n\n    #[inline]\n    pub fn fixed_size(mut self, size: impl Into<Vec2>) -> Self {\n        let size = size.into();\n        self.default_size = size;\n        self.min_size = size;\n        self.max_size = size;\n        self.resizable = Vec2b::FALSE;\n        self\n    }\n\n    #[inline]\n    pub fn with_stroke(mut self, with_stroke: bool) -> Self {\n        self.with_stroke = with_stroke;\n        self\n    }\n}\n\nstruct Prepared {\n    id: Id,\n    corner_id: Option<Id>,\n    state: State,\n    content_ui: Ui,\n}\n\nimpl Resize {\n    fn begin(&self, ui: &mut Ui) -> Prepared {\n        let position = ui.available_rect_before_wrap().min;\n        let id = self.id.unwrap_or_else(|| {\n            let id_salt = self.id_salt.unwrap_or_else(|| Id::new(\"resize\"));\n            ui.make_persistent_id(id_salt)\n        });\n\n        let mut state = State::load(ui.ctx(), id).unwrap_or_else(|| {\n            ui.request_repaint(); // counter frame delay\n\n            let default_size = self\n                .default_size\n                .at_least(self.min_size)\n                .at_most(self.max_size)\n                .at_most(\n                    ui.ctx().content_rect().size() - ui.spacing().window_margin.sum(), // hack for windows\n                )\n                .round_ui();\n\n            State {\n                desired_size: default_size,\n                last_content_size: vec2(0.0, 0.0),\n                requested_size: None,\n            }\n        });\n\n        state.desired_size = state\n            .desired_size\n            .at_least(self.min_size)\n            .at_most(self.max_size)\n            .round_ui();\n\n        let mut user_requested_size = state.requested_size.take();\n\n        let corner_id = self.resizable.any().then(|| id.with(\"__resize_corner\"));\n\n        if let Some(corner_id) = corner_id\n            && let Some(corner_response) = ui.ctx().read_response(corner_id)\n            && let Some(pointer_pos) = corner_response.interact_pointer_pos()\n        {\n            // Respond to the interaction early to avoid frame delay.\n            user_requested_size = Some(pointer_pos - position + 0.5 * corner_response.rect.size());\n        }\n\n        if let Some(user_requested_size) = user_requested_size {\n            state.desired_size = user_requested_size;\n        } else {\n            // We are not being actively resized, so auto-expand to include size of last frame.\n            // This prevents auto-shrinking if the contents contain width-filling widgets (separators etc)\n            // but it makes a lot of interactions with [`Window`]s nicer.\n            state.desired_size = state.desired_size.max(state.last_content_size);\n        }\n\n        state.desired_size = state\n            .desired_size\n            .at_least(self.min_size)\n            .at_most(self.max_size);\n\n        // ------------------------------\n\n        let inner_rect = Rect::from_min_size(position, state.desired_size);\n\n        let mut content_clip_rect = inner_rect.expand(ui.visuals().clip_rect_margin);\n\n        // If we pull the resize handle to shrink, we want to TRY to shrink it.\n        // After laying out the contents, we might be much bigger.\n        // In those cases we don't want the clip_rect to be smaller, because\n        // then we will clip the contents of the region even thought the result gets larger. This is simply ugly!\n        // So we use the memory of last_content_size to make the clip rect large enough.\n        content_clip_rect.max = content_clip_rect.max.max(\n            inner_rect.min + state.last_content_size + Vec2::splat(ui.visuals().clip_rect_margin),\n        );\n\n        content_clip_rect = content_clip_rect.intersect(ui.clip_rect()); // Respect parent region\n\n        let mut content_ui = ui.new_child(\n            UiBuilder::new()\n                .ui_stack_info(UiStackInfo::new(UiKind::Resize))\n                .max_rect(inner_rect),\n        );\n        content_ui.set_clip_rect(content_clip_rect);\n\n        Prepared {\n            id,\n            corner_id,\n            state,\n            content_ui,\n        }\n    }\n\n    pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> R {\n        let mut prepared = self.begin(ui);\n        let ret = add_contents(&mut prepared.content_ui);\n        self.end(ui, prepared);\n        ret\n    }\n\n    fn end(self, ui: &mut Ui, prepared: Prepared) {\n        let Prepared {\n            id,\n            corner_id,\n            mut state,\n            content_ui,\n        } = prepared;\n\n        state.last_content_size = content_ui.min_size();\n\n        // ------------------------------\n\n        let mut size = state.last_content_size;\n        for d in 0..2 {\n            if self.with_stroke || self.resizable[d] {\n                // We show how large we are,\n                // so we must follow the contents:\n\n                state.desired_size[d] = state.desired_size[d].max(state.last_content_size[d]);\n\n                // We are as large as we look\n                size[d] = state.desired_size[d];\n            } else {\n                // Probably a window.\n                size[d] = state.last_content_size[d];\n            }\n        }\n        ui.advance_cursor_after_rect(Rect::from_min_size(content_ui.min_rect().min, size));\n\n        // ------------------------------\n\n        let corner_response = if let Some(corner_id) = corner_id {\n            // We do the corner interaction last to place it on top of the content:\n            let corner_size = Vec2::splat(ui.visuals().resize_corner_size);\n            let corner_rect = Rect::from_min_size(\n                content_ui.min_rect().left_top() + size - corner_size,\n                corner_size,\n            );\n            Some(ui.interact(corner_rect, corner_id, Sense::drag()))\n        } else {\n            None\n        };\n\n        // ------------------------------\n\n        if self.with_stroke && corner_response.is_some() {\n            let rect = Rect::from_min_size(content_ui.min_rect().left_top(), state.desired_size);\n            let rect = rect.expand(2.0); // breathing room for content\n            ui.painter().add(Shape::rect_stroke(\n                rect,\n                3.0,\n                ui.visuals().widgets.noninteractive.bg_stroke,\n                epaint::StrokeKind::Inside,\n            ));\n        }\n\n        if let Some(corner_response) = corner_response {\n            paint_resize_corner(ui, &corner_response);\n\n            if corner_response.hovered() || corner_response.dragged() {\n                ui.set_cursor_icon(CursorIcon::ResizeNwSe);\n            }\n        }\n\n        state.store(ui.ctx(), id);\n\n        #[cfg(debug_assertions)]\n        if ui.global_style().debug.show_resize {\n            ui.debug_painter().debug_rect(\n                Rect::from_min_size(content_ui.min_rect().left_top(), state.desired_size),\n                Color32::GREEN,\n                \"desired_size\",\n            );\n            ui.debug_painter().debug_rect(\n                Rect::from_min_size(content_ui.min_rect().left_top(), state.last_content_size),\n                Color32::LIGHT_BLUE,\n                \"last_content_size\",\n            );\n        }\n    }\n}\n\nuse emath::GuiRounding as _;\nuse epaint::Stroke;\n\npub fn paint_resize_corner(ui: &Ui, response: &Response) {\n    let stroke = ui.style().interact(response).fg_stroke;\n    paint_resize_corner_with_style(ui, &response.rect, stroke.color, Align2::RIGHT_BOTTOM);\n}\n\npub fn paint_resize_corner_with_style(\n    ui: &Ui,\n    rect: &Rect,\n    color: impl Into<Color32>,\n    corner: Align2,\n) {\n    let painter = ui.painter();\n    let cp = corner\n        .pos_in_rect(rect)\n        .round_to_pixels(ui.pixels_per_point());\n    let mut w = 2.0;\n    let stroke = Stroke {\n        width: 1.0, // Set width to 1.0 to prevent overlapping\n        color: color.into(),\n    };\n\n    while w <= rect.width() && w <= rect.height() {\n        painter.line_segment(\n            [\n                pos2(cp.x - w * corner.x().to_sign(), cp.y),\n                pos2(cp.x, cp.y - w * corner.y().to_sign()),\n            ],\n            stroke,\n        );\n        w += 4.0;\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/scene.rs",
    "content": "use core::f32;\n\nuse emath::{GuiRounding as _, Pos2};\n\nuse crate::{\n    InnerResponse, LayerId, PointerButton, Rangef, Rect, Response, Sense, Ui, UiBuilder, Vec2,\n    emath::TSTransform,\n};\n\n/// Creates a transformation that fits a given scene rectangle into the available screen size.\n///\n/// The resulting visual scene bounds can be larger, due to letterboxing.\n///\n/// Returns the transformation from `scene` to `global` coordinates.\nfn fit_to_rect_in_scene(\n    rect_in_global: Rect,\n    rect_in_scene: Rect,\n    zoom_range: Rangef,\n) -> TSTransform {\n    // Compute the scale factor to fit the bounding rectangle into the available screen size:\n    let scale = rect_in_global.size() / rect_in_scene.size();\n\n    // Use the smaller of the two scales to ensure the whole rectangle fits on the screen:\n    let scale = scale.min_elem();\n\n    // Clamp scale to what is allowed\n    let scale = zoom_range.clamp(scale);\n\n    // Compute the translation to center the bounding rect in the screen:\n    let center_in_global = rect_in_global.center().to_vec2();\n    let center_scene = rect_in_scene.center().to_vec2();\n\n    // Set the transformation to scale and then translate to center.\n    TSTransform::from_translation(center_in_global - scale * center_scene)\n        * TSTransform::from_scaling(scale)\n}\n\n/// A container that allows you to zoom and pan.\n///\n/// This is similar to [`crate::ScrollArea`] but:\n/// * Supports zooming\n/// * Has no scroll bars\n/// * Has no limits on the scrolling\n#[derive(Clone, Debug)]\n#[must_use = \"You should call .show()\"]\npub struct Scene {\n    zoom_range: Rangef,\n    sense: Sense,\n    max_inner_size: Vec2,\n    drag_pan_buttons: DragPanButtons,\n}\n\n/// Specifies which pointer buttons can be used to pan the scene by dragging.\n#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]\npub struct DragPanButtons(u8);\n\nbitflags::bitflags! {\n    impl DragPanButtons: u8 {\n        /// [PointerButton::Primary]\n        const PRIMARY = 1 << 0;\n\n        /// [PointerButton::Secondary]\n        const SECONDARY = 1 << 1;\n\n        /// [PointerButton::Middle]\n        const MIDDLE = 1 << 2;\n\n        /// [PointerButton::Extra1]\n        const EXTRA_1 = 1 << 3;\n\n        /// [PointerButton::Extra2]\n        const EXTRA_2 = 1 << 4;\n    }\n}\n\nimpl Default for Scene {\n    fn default() -> Self {\n        Self {\n            zoom_range: Rangef::new(f32::EPSILON, 1.0),\n            sense: Sense::click_and_drag(),\n            max_inner_size: Vec2::splat(1000.0),\n            drag_pan_buttons: DragPanButtons::all(),\n        }\n    }\n}\n\nimpl Scene {\n    #[inline]\n    pub fn new() -> Self {\n        Default::default()\n    }\n\n    /// Specify what type of input the scene should respond to.\n    ///\n    /// The default is `Sense::click_and_drag()`.\n    ///\n    /// Set this to `Sense::hover()` to disable panning via clicking and dragging.\n    #[inline]\n    pub fn sense(mut self, sense: Sense) -> Self {\n        self.sense = sense;\n        self\n    }\n\n    /// Set the allowed zoom range.\n    ///\n    /// The default zoom range is `0.0..=1.0`,\n    /// which mean you zan make things arbitrarily small, but you cannot zoom in past a `1:1` ratio.\n    ///\n    /// If you want to allow zooming in, you can set the zoom range to `0.0..=f32::INFINITY`.\n    /// Note that text rendering becomes blurry when you zoom in: <https://github.com/emilk/egui/issues/4813>.\n    #[inline]\n    pub fn zoom_range(mut self, zoom_range: impl Into<Rangef>) -> Self {\n        self.zoom_range = zoom_range.into();\n        self\n    }\n\n    /// Set the maximum size of the inner [`Ui`] that will be created.\n    #[inline]\n    pub fn max_inner_size(mut self, max_inner_size: impl Into<Vec2>) -> Self {\n        self.max_inner_size = max_inner_size.into();\n        self\n    }\n\n    /// Specify which pointer buttons can be used to pan by clicking and dragging.\n    ///\n    /// By default, this is `DragPanButtons::all()`.\n    #[inline]\n    pub fn drag_pan_buttons(mut self, flags: DragPanButtons) -> Self {\n        self.drag_pan_buttons = flags;\n        self\n    }\n\n    /// `scene_rect` contains the view bounds of the inner [`Ui`].\n    ///\n    /// `scene_rect` will be mutated by any panning/zooming done by the user.\n    /// If `scene_rect` is somehow invalid (e.g. `Rect::ZERO`),\n    /// then it will be reset to the inner rect of the inner ui.\n    ///\n    /// You need to store the `scene_rect` in your state between frames.\n    pub fn show<R>(\n        &self,\n        parent_ui: &mut Ui,\n        scene_rect: &mut Rect,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        let (outer_rect, _outer_response) =\n            parent_ui.allocate_exact_size(parent_ui.available_size_before_wrap(), Sense::hover());\n\n        let mut to_global = fit_to_rect_in_scene(outer_rect, *scene_rect, self.zoom_range);\n\n        let scene_rect_was_good =\n            to_global.is_valid() && scene_rect.is_finite() && scene_rect.size() != Vec2::ZERO;\n\n        let mut inner_rect = *scene_rect;\n\n        let ret = self.show_global_transform(parent_ui, outer_rect, &mut to_global, |ui| {\n            let r = add_contents(ui);\n            inner_rect = ui.min_rect();\n            r\n        });\n\n        if ret.response.changed() {\n            // Only update if changed, both to avoid numeric drift,\n            // and to avoid expanding the scene rect unnecessarily.\n            *scene_rect = to_global.inverse() * outer_rect;\n        }\n\n        if !scene_rect_was_good {\n            // Auto-reset if the transformation goes bad somehow (or started bad).\n            // Recalculates transform based on inner_rect, resulting in a rect that's the full size of outer_rect but centered on inner_rect.\n            let to_global = fit_to_rect_in_scene(outer_rect, inner_rect, self.zoom_range);\n            *scene_rect = to_global.inverse() * outer_rect;\n        }\n\n        ret\n    }\n\n    fn show_global_transform<R>(\n        &self,\n        parent_ui: &mut Ui,\n        outer_rect: Rect,\n        to_global: &mut TSTransform,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        // Create a new egui paint layer, where we can draw our contents:\n        let scene_layer_id = LayerId::new(\n            parent_ui.layer_id().order,\n            parent_ui.id().with(\"scene_area\"),\n        );\n\n        // Put the layer directly on-top of the main layer of the ui:\n        parent_ui\n            .ctx()\n            .set_sublayer(parent_ui.layer_id(), scene_layer_id);\n\n        let mut local_ui = parent_ui.new_child(\n            UiBuilder::new()\n                .layer_id(scene_layer_id)\n                .max_rect(Rect::from_min_size(Pos2::ZERO, self.max_inner_size))\n                .sense(self.sense),\n        );\n\n        let mut pan_response = local_ui.response();\n\n        // Update the `to_global` transform based on use interaction:\n        self.register_pan_and_zoom(&local_ui, &mut pan_response, to_global);\n\n        // Set a correct global clip rect:\n        local_ui.set_clip_rect(to_global.inverse() * outer_rect);\n\n        // Tell egui to apply the transform on the layer:\n        local_ui\n            .ctx()\n            .set_transform_layer(scene_layer_id, *to_global);\n\n        // Add the actual contents to the area:\n        let ret = add_contents(&mut local_ui);\n\n        // This ensures we catch clicks/drags/pans anywhere on the background.\n        local_ui.force_set_min_rect((to_global.inverse() * outer_rect).round_ui());\n\n        InnerResponse {\n            response: pan_response,\n            inner: ret,\n        }\n    }\n\n    /// Helper function to handle pan and zoom interactions on a response.\n    pub fn register_pan_and_zoom(&self, ui: &Ui, resp: &mut Response, to_global: &mut TSTransform) {\n        let dragged = self.drag_pan_buttons.iter().any(|button| match button {\n            DragPanButtons::PRIMARY => resp.dragged_by(PointerButton::Primary),\n            DragPanButtons::SECONDARY => resp.dragged_by(PointerButton::Secondary),\n            DragPanButtons::MIDDLE => resp.dragged_by(PointerButton::Middle),\n            DragPanButtons::EXTRA_1 => resp.dragged_by(PointerButton::Extra1),\n            DragPanButtons::EXTRA_2 => resp.dragged_by(PointerButton::Extra2),\n            _ => false,\n        });\n        if dragged {\n            to_global.translation += to_global.scaling * resp.drag_delta();\n            resp.mark_changed();\n        }\n\n        if let Some(mouse_pos) = ui.input(|i| i.pointer.latest_pos())\n            && resp.contains_pointer()\n        {\n            let pointer_in_scene = to_global.inverse() * mouse_pos;\n            let zoom_delta = ui.input(|i| i.zoom_delta());\n            let pan_delta = ui.input(|i| i.smooth_scroll_delta());\n\n            // Most of the time we can return early. This is also important to\n            // avoid `ui_from_scene` to change slightly due to floating point errors.\n            if zoom_delta == 1.0 && pan_delta == Vec2::ZERO {\n                return;\n            }\n\n            if zoom_delta != 1.0 {\n                // Zoom in on pointer, but only if we are not zoomed in or out too far.\n                let zoom_delta = zoom_delta.clamp(\n                    self.zoom_range.min / to_global.scaling,\n                    self.zoom_range.max / to_global.scaling,\n                );\n\n                *to_global = *to_global\n                    * TSTransform::from_translation(pointer_in_scene.to_vec2())\n                    * TSTransform::from_scaling(zoom_delta)\n                    * TSTransform::from_translation(-pointer_in_scene.to_vec2());\n\n                // Clamp to exact zoom range.\n                to_global.scaling = self.zoom_range.clamp(to_global.scaling);\n            }\n\n            // Pan:\n            *to_global = TSTransform::from_translation(pan_delta) * *to_global;\n            resp.mark_changed();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/scroll_area.rs",
    "content": "//! See [`ScrollArea`] for docs.\n\n#![expect(clippy::needless_range_loop)]\n\nuse std::ops::{Add, AddAssign, BitOr, BitOrAssign};\n\nuse emath::GuiRounding as _;\nuse epaint::Margin;\n\nuse crate::{\n    Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,\n    UiKind, UiStackInfo, Vec2, Vec2b, WidgetInfo, emath, epaint, lerp, pass_state, pos2, remap,\n    remap_clamp,\n};\n\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nstruct ScrollingToTarget {\n    animation_time_span: (f64, f64),\n    target_offset: f32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct State {\n    /// Positive offset means scrolling down/right\n    pub offset: Vec2,\n\n    /// If set, quickly but smoothly scroll to this target offset.\n    offset_target: [Option<ScrollingToTarget>; 2],\n\n    /// Were the scroll bars visible last frame?\n    show_scroll: Vec2b,\n\n    /// The content were to large to fit large frame.\n    content_is_too_large: Vec2b,\n\n    /// Did the user interact (hover or drag) the scroll bars last frame?\n    scroll_bar_interaction: Vec2b,\n\n    /// Momentum, used for kinetic scrolling\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    vel: Vec2,\n\n    /// Mouse offset relative to the top of the handle when started moving the handle.\n    scroll_start_offset_from_top_left: [Option<f32>; 2],\n\n    /// Is the scroll sticky. This is true while scroll handle is in the end position\n    /// and remains that way until the user moves the `scroll_handle`. Once unstuck (false)\n    /// it remains false until the scroll touches the end position, which reenables stickiness.\n    scroll_stuck_to_end: Vec2b,\n\n    /// Area that can be dragged. This is the size of the content from the last frame.\n    interact_rect: Option<Rect>,\n}\n\nimpl Default for State {\n    fn default() -> Self {\n        Self {\n            offset: Vec2::ZERO,\n            offset_target: Default::default(),\n            show_scroll: Vec2b::FALSE,\n            content_is_too_large: Vec2b::FALSE,\n            scroll_bar_interaction: Vec2b::FALSE,\n            vel: Vec2::ZERO,\n            scroll_start_offset_from_top_left: [None; 2],\n            scroll_stuck_to_end: Vec2b::TRUE,\n            interact_rect: None,\n        }\n    }\n}\n\nimpl State {\n    pub fn load(ctx: &Context, id: Id) -> Option<Self> {\n        ctx.data_mut(|d| d.get_persisted(id))\n    }\n\n    pub fn store(self, ctx: &Context, id: Id) {\n        ctx.data_mut(|d| d.insert_persisted(id, self));\n    }\n\n    /// Get the current kinetic scrolling velocity.\n    pub fn velocity(&self) -> Vec2 {\n        self.vel\n    }\n}\n\npub struct ScrollAreaOutput<R> {\n    /// What the user closure returned.\n    pub inner: R,\n\n    /// [`Id`] of the [`ScrollArea`].\n    pub id: Id,\n\n    /// The current state of the scroll area.\n    pub state: State,\n\n    /// The size of the content. If this is larger than [`Self::inner_rect`],\n    /// then there was need for scrolling.\n    pub content_size: Vec2,\n\n    /// Where on the screen the content is (excludes scroll bars).\n    pub inner_rect: Rect,\n}\n\n/// Indicate whether the horizontal and vertical scroll bars must be always visible, hidden or visible when needed.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum ScrollBarVisibility {\n    /// Hide scroll bar even if they are needed.\n    ///\n    /// You can still scroll, with the scroll-wheel\n    /// and by dragging the contents, but there is no\n    /// visual indication of how far you have scrolled.\n    AlwaysHidden,\n\n    /// Show scroll bars only when the content size exceeds the container,\n    /// i.e. when there is any need to scroll.\n    ///\n    /// This is the default.\n    VisibleWhenNeeded,\n\n    /// Always show the scroll bar, even if the contents fit in the container\n    /// and there is no need to scroll.\n    AlwaysVisible,\n}\n\nimpl Default for ScrollBarVisibility {\n    #[inline]\n    fn default() -> Self {\n        Self::VisibleWhenNeeded\n    }\n}\n\nimpl ScrollBarVisibility {\n    pub const ALL: [Self; 3] = [\n        Self::AlwaysHidden,\n        Self::VisibleWhenNeeded,\n        Self::AlwaysVisible,\n    ];\n}\n\n/// What is the source of scrolling for a [`ScrollArea`].\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct ScrollSource {\n    /// Scroll the area by dragging a scroll bar.\n    ///\n    /// By default the scroll bars remain visible to show current position.\n    /// To hide them use [`ScrollArea::scroll_bar_visibility()`].\n    pub scroll_bar: bool,\n\n    /// Scroll the area by dragging the contents.\n    pub drag: bool,\n\n    /// Scroll the area by scrolling (or shift scrolling) the mouse wheel with\n    /// the mouse cursor over the [`ScrollArea`].\n    pub mouse_wheel: bool,\n}\n\nimpl Default for ScrollSource {\n    fn default() -> Self {\n        Self::ALL\n    }\n}\n\nimpl ScrollSource {\n    pub const NONE: Self = Self {\n        scroll_bar: false,\n        drag: false,\n        mouse_wheel: false,\n    };\n    pub const ALL: Self = Self {\n        scroll_bar: true,\n        drag: true,\n        mouse_wheel: true,\n    };\n    pub const SCROLL_BAR: Self = Self {\n        scroll_bar: true,\n        drag: false,\n        mouse_wheel: false,\n    };\n    pub const DRAG: Self = Self {\n        scroll_bar: false,\n        drag: true,\n        mouse_wheel: false,\n    };\n    pub const MOUSE_WHEEL: Self = Self {\n        scroll_bar: false,\n        drag: false,\n        mouse_wheel: true,\n    };\n\n    /// Is everything disabled?\n    #[inline]\n    pub fn is_none(&self) -> bool {\n        self == &Self::NONE\n    }\n\n    /// Is anything enabled?\n    #[inline]\n    pub fn any(&self) -> bool {\n        self.scroll_bar | self.drag | self.mouse_wheel\n    }\n\n    /// Is everything enabled?\n    #[inline]\n    pub fn is_all(&self) -> bool {\n        self.scroll_bar & self.drag & self.mouse_wheel\n    }\n}\n\nimpl BitOr for ScrollSource {\n    type Output = Self;\n\n    #[inline]\n    fn bitor(self, rhs: Self) -> Self::Output {\n        Self {\n            scroll_bar: self.scroll_bar | rhs.scroll_bar,\n            drag: self.drag | rhs.drag,\n            mouse_wheel: self.mouse_wheel | rhs.mouse_wheel,\n        }\n    }\n}\n\n#[expect(clippy::suspicious_arithmetic_impl)]\nimpl Add for ScrollSource {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, rhs: Self) -> Self::Output {\n        self | rhs\n    }\n}\n\nimpl BitOrAssign for ScrollSource {\n    #[inline]\n    fn bitor_assign(&mut self, rhs: Self) {\n        *self = *self | rhs;\n    }\n}\n\nimpl AddAssign for ScrollSource {\n    #[inline]\n    fn add_assign(&mut self, rhs: Self) {\n        *self = *self + rhs;\n    }\n}\n\n/// Add vertical and/or horizontal scrolling to a contained [`Ui`].\n///\n/// By default, scroll bars only show up when needed, i.e. when the contents\n/// is larger than the container.\n/// This is controlled by [`Self::scroll_bar_visibility`].\n///\n/// There are two flavors of scroll areas: solid and floating.\n/// Solid scroll bars use up space, reducing the amount of space available\n/// to the contents. Floating scroll bars float on top of the contents, covering it.\n/// You can change the scroll style by changing the [`crate::style::Spacing::scroll`].\n///\n/// ### Coordinate system\n/// * content: size of contents (generally large; that's why we want scroll bars)\n/// * outer: size of scroll area including scroll bar(s)\n/// * inner: excluding scroll bar(s). The area we clip the contents to. Includes `content_margin`.\n///\n/// If the floating scroll bars settings is turned on then `inner == outer`.\n///\n/// ## Example\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// egui::ScrollArea::vertical().show(ui, |ui| {\n///     // Add a lot of widgets here.\n/// });\n/// # });\n/// ```\n///\n/// You can scroll to an element using [`crate::Response::scroll_to_me`], [`Ui::scroll_to_cursor`] and [`Ui::scroll_to_rect`].\n///\n/// ## See also\n/// If you want to allow zooming, use [`crate::Scene`].\n#[derive(Clone, Debug)]\n#[must_use = \"You should call .show()\"]\npub struct ScrollArea {\n    /// Do we have horizontal/vertical scrolling enabled?\n    direction_enabled: Vec2b,\n\n    auto_shrink: Vec2b,\n    max_size: Vec2,\n    min_scrolled_size: Vec2,\n    scroll_bar_visibility: ScrollBarVisibility,\n    scroll_bar_rect: Option<Rect>,\n    id_salt: Option<Id>,\n    offset_x: Option<f32>,\n    offset_y: Option<f32>,\n    on_hover_cursor: Option<CursorIcon>,\n    on_drag_cursor: Option<CursorIcon>,\n    scroll_source: ScrollSource,\n    wheel_scroll_multiplier: Vec2,\n\n    content_margin: Option<Margin>,\n\n    /// If true for vertical or horizontal the scroll wheel will stick to the\n    /// end position until user manually changes position. It will become true\n    /// again once scroll handle makes contact with end.\n    stick_to_end: Vec2b,\n\n    /// If false, `scroll_to_*` functions will not be animated\n    animated: bool,\n}\n\nimpl ScrollArea {\n    /// Create a horizontal scroll area.\n    #[inline]\n    pub fn horizontal() -> Self {\n        Self::new([true, false])\n    }\n\n    /// Create a vertical scroll area.\n    #[inline]\n    pub fn vertical() -> Self {\n        Self::new([false, true])\n    }\n\n    /// Create a bi-directional (horizontal and vertical) scroll area.\n    #[inline]\n    pub fn both() -> Self {\n        Self::new([true, true])\n    }\n\n    /// Create a scroll area where both direction of scrolling is disabled.\n    /// It's unclear why you would want to do this.\n    #[inline]\n    pub fn neither() -> Self {\n        Self::new([false, false])\n    }\n\n    /// Create a scroll area where you decide which axis has scrolling enabled.\n    /// For instance, `ScrollArea::new([true, false])` enables horizontal scrolling.\n    pub fn new(direction_enabled: impl Into<Vec2b>) -> Self {\n        Self {\n            direction_enabled: direction_enabled.into(),\n            auto_shrink: Vec2b::TRUE,\n            max_size: Vec2::INFINITY,\n            min_scrolled_size: Vec2::splat(64.0),\n            scroll_bar_visibility: Default::default(),\n            scroll_bar_rect: None,\n            id_salt: None,\n            offset_x: None,\n            offset_y: None,\n            on_hover_cursor: None,\n            on_drag_cursor: None,\n            scroll_source: ScrollSource::default(),\n            wheel_scroll_multiplier: Vec2::splat(1.0),\n            content_margin: None,\n            stick_to_end: Vec2b::FALSE,\n            animated: true,\n        }\n    }\n\n    /// The maximum width of the outer frame of the scroll area.\n    ///\n    /// Use `f32::INFINITY` if you want the scroll area to expand to fit the surrounding [`Ui`] (default).\n    ///\n    /// See also [`Self::auto_shrink`].\n    #[inline]\n    pub fn max_width(mut self, max_width: f32) -> Self {\n        self.max_size.x = max_width;\n        self\n    }\n\n    /// The maximum height of the outer frame of the scroll area.\n    ///\n    /// Use `f32::INFINITY` if you want the scroll area to expand to fit the surrounding [`Ui`] (default).\n    ///\n    /// See also [`Self::auto_shrink`].\n    #[inline]\n    pub fn max_height(mut self, max_height: f32) -> Self {\n        self.max_size.y = max_height;\n        self\n    }\n\n    /// The minimum width of a horizontal scroll area which requires scroll bars.\n    ///\n    /// The [`ScrollArea`] will only become smaller than this if the content is smaller than this\n    /// (and so we don't require scroll bars).\n    ///\n    /// Default: `64.0`.\n    #[inline]\n    pub fn min_scrolled_width(mut self, min_scrolled_width: f32) -> Self {\n        self.min_scrolled_size.x = min_scrolled_width;\n        self\n    }\n\n    /// The minimum height of a vertical scroll area which requires scroll bars.\n    ///\n    /// The [`ScrollArea`] will only become smaller than this if the content is smaller than this\n    /// (and so we don't require scroll bars).\n    ///\n    /// Default: `64.0`.\n    #[inline]\n    pub fn min_scrolled_height(mut self, min_scrolled_height: f32) -> Self {\n        self.min_scrolled_size.y = min_scrolled_height;\n        self\n    }\n\n    /// Set the visibility of both horizontal and vertical scroll bars.\n    ///\n    /// With `ScrollBarVisibility::VisibleWhenNeeded` (default), the scroll bar will be visible only when needed.\n    #[inline]\n    pub fn scroll_bar_visibility(mut self, scroll_bar_visibility: ScrollBarVisibility) -> Self {\n        self.scroll_bar_visibility = scroll_bar_visibility;\n        self\n    }\n\n    /// Specify within which screen-space rectangle to show the scroll bars.\n    ///\n    /// This can be used to move the scroll bars to a smaller region of the `ScrollArea`,\n    /// for instance if you are painting a sticky header on top of it.\n    #[inline]\n    pub fn scroll_bar_rect(mut self, scroll_bar_rect: Rect) -> Self {\n        self.scroll_bar_rect = Some(scroll_bar_rect);\n        self\n    }\n\n    /// A source for the unique [`Id`], e.g. `.id_source(\"second_scroll_area\")` or `.id_source(loop_index)`.\n    #[inline]\n    #[deprecated = \"Renamed id_salt\"]\n    pub fn id_source(self, id_salt: impl std::hash::Hash) -> Self {\n        self.id_salt(id_salt)\n    }\n\n    /// A source for the unique [`Id`], e.g. `.id_salt(\"second_scroll_area\")` or `.id_salt(loop_index)`.\n    #[inline]\n    pub fn id_salt(mut self, id_salt: impl std::hash::Hash) -> Self {\n        self.id_salt = Some(Id::new(id_salt));\n        self\n    }\n\n    /// Set the horizontal and vertical scroll offset position.\n    ///\n    /// Positive offset means scrolling down/right.\n    ///\n    /// See also: [`Self::vertical_scroll_offset`], [`Self::horizontal_scroll_offset`],\n    /// [`Ui::scroll_to_cursor`](crate::ui::Ui::scroll_to_cursor) and\n    /// [`Response::scroll_to_me`](crate::Response::scroll_to_me)\n    #[inline]\n    pub fn scroll_offset(mut self, offset: Vec2) -> Self {\n        self.offset_x = Some(offset.x);\n        self.offset_y = Some(offset.y);\n        self\n    }\n\n    /// Set the vertical scroll offset position.\n    ///\n    /// Positive offset means scrolling down.\n    ///\n    /// See also: [`Self::scroll_offset`], [`Ui::scroll_to_cursor`](crate::ui::Ui::scroll_to_cursor) and\n    /// [`Response::scroll_to_me`](crate::Response::scroll_to_me)\n    #[inline]\n    pub fn vertical_scroll_offset(mut self, offset: f32) -> Self {\n        self.offset_y = Some(offset);\n        self\n    }\n\n    /// Set the horizontal scroll offset position.\n    ///\n    /// Positive offset means scrolling right.\n    ///\n    /// See also: [`Self::scroll_offset`], [`Ui::scroll_to_cursor`](crate::ui::Ui::scroll_to_cursor) and\n    /// [`Response::scroll_to_me`](crate::Response::scroll_to_me)\n    #[inline]\n    pub fn horizontal_scroll_offset(mut self, offset: f32) -> Self {\n        self.offset_x = Some(offset);\n        self\n    }\n\n    /// Set the cursor used when the mouse pointer is hovering over the [`ScrollArea`].\n    ///\n    /// Only applies if [`Self::scroll_source()`] has set [`ScrollSource::drag`] to `true`.\n    ///\n    /// Any changes to the mouse cursor made within the contents of the [`ScrollArea`] will\n    /// override this setting.\n    #[inline]\n    pub fn on_hover_cursor(mut self, cursor: CursorIcon) -> Self {\n        self.on_hover_cursor = Some(cursor);\n        self\n    }\n\n    /// Set the cursor used when the [`ScrollArea`] is being dragged.\n    ///\n    /// Only applies if [`Self::scroll_source()`] has set [`ScrollSource::drag`] to `true`.\n    ///\n    /// Any changes to the mouse cursor made within the contents of the [`ScrollArea`] will\n    /// override this setting.\n    #[inline]\n    pub fn on_drag_cursor(mut self, cursor: CursorIcon) -> Self {\n        self.on_drag_cursor = Some(cursor);\n        self\n    }\n\n    /// Turn on/off scrolling on the horizontal axis.\n    #[inline]\n    pub fn hscroll(mut self, hscroll: bool) -> Self {\n        self.direction_enabled[0] = hscroll;\n        self\n    }\n\n    /// Turn on/off scrolling on the vertical axis.\n    #[inline]\n    pub fn vscroll(mut self, vscroll: bool) -> Self {\n        self.direction_enabled[1] = vscroll;\n        self\n    }\n\n    /// Turn on/off scrolling on the horizontal/vertical axes.\n    ///\n    /// You can pass in `false`, `true`, `[false, true]` etc.\n    #[inline]\n    pub fn scroll(mut self, direction_enabled: impl Into<Vec2b>) -> Self {\n        self.direction_enabled = direction_enabled.into();\n        self\n    }\n\n    /// Control the scrolling behavior.\n    ///\n    /// * If `true` (default), the scroll area will respond to user scrolling.\n    /// * If `false`, the scroll area will not respond to user scrolling.\n    ///\n    /// This can be used, for example, to optionally freeze scrolling while the user\n    /// is typing text in a [`crate::TextEdit`] widget contained within the scroll area.\n    ///\n    /// This controls both scrolling directions.\n    #[deprecated = \"Use `ScrollArea::scroll_source()\"]\n    #[inline]\n    pub fn enable_scrolling(mut self, enable: bool) -> Self {\n        self.scroll_source = if enable {\n            ScrollSource::ALL\n        } else {\n            ScrollSource::NONE\n        };\n        self\n    }\n\n    /// Can the user drag the scroll area to scroll?\n    ///\n    /// This is useful for touch screens.\n    ///\n    /// If `true`, the [`ScrollArea`] will sense drags.\n    ///\n    /// Default: `true`.\n    #[deprecated = \"Use `ScrollArea::scroll_source()\"]\n    #[inline]\n    pub fn drag_to_scroll(mut self, drag_to_scroll: bool) -> Self {\n        self.scroll_source.drag = drag_to_scroll;\n        self\n    }\n\n    /// What sources does the [`ScrollArea`] use for scrolling the contents.\n    #[inline]\n    pub fn scroll_source(mut self, scroll_source: ScrollSource) -> Self {\n        self.scroll_source = scroll_source;\n        self\n    }\n\n    /// The scroll amount caused by a mouse wheel scroll is multiplied by this amount.\n    ///\n    /// Independent for each scroll direction. Defaults to `Vec2{x: 1.0, y: 1.0}`.\n    ///\n    /// This can invert or effectively disable mouse scrolling.\n    #[inline]\n    pub fn wheel_scroll_multiplier(mut self, multiplier: Vec2) -> Self {\n        self.wheel_scroll_multiplier = multiplier;\n        self\n    }\n\n    /// For each axis, should the containing area shrink if the content is small?\n    ///\n    /// * If `true`, egui will add blank space outside the scroll area.\n    /// * If `false`, egui will add blank space inside the scroll area.\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn auto_shrink(mut self, auto_shrink: impl Into<Vec2b>) -> Self {\n        self.auto_shrink = auto_shrink.into();\n        self\n    }\n\n    /// Should the scroll area animate `scroll_to_*` functions?\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn animated(mut self, animated: bool) -> Self {\n        self.animated = animated;\n        self\n    }\n\n    /// Is any scrolling enabled?\n    pub(crate) fn is_any_scroll_enabled(&self) -> bool {\n        self.direction_enabled[0] || self.direction_enabled[1]\n    }\n\n    /// Extra margin added around the contents.\n    ///\n    /// The scroll bars will be either on top of this margin, or outside of it,\n    /// depending on the value of [`crate::style::ScrollStyle::floating`].\n    ///\n    /// Default: [`crate::style::ScrollStyle::content_margin`].\n    #[inline]\n    pub fn content_margin(mut self, margin: impl Into<Margin>) -> Self {\n        self.content_margin = Some(margin.into());\n        self\n    }\n\n    /// The scroll handle will stick to the rightmost position even while the content size\n    /// changes dynamically. This can be useful to simulate text scrollers coming in from right\n    /// hand side. The scroll handle remains stuck until user manually changes position. Once \"unstuck\"\n    /// it will remain focused on whatever content viewport the user left it on. If the scroll\n    /// handle is dragged all the way to the right it will again become stuck and remain there\n    /// until manually pulled from the end position.\n    #[inline]\n    pub fn stick_to_right(mut self, stick: bool) -> Self {\n        self.stick_to_end[0] = stick;\n        self\n    }\n\n    /// The scroll handle will stick to the bottom position even while the content size\n    /// changes dynamically. This can be useful to simulate terminal UIs or log/info scrollers.\n    /// The scroll handle remains stuck until user manually changes position. Once \"unstuck\"\n    /// it will remain focused on whatever content viewport the user left it on. If the scroll\n    /// handle is dragged to the bottom it will again become stuck and remain there until manually\n    /// pulled from the end position.\n    #[inline]\n    pub fn stick_to_bottom(mut self, stick: bool) -> Self {\n        self.stick_to_end[1] = stick;\n        self\n    }\n}\n\nstruct Prepared {\n    id: Id,\n    state: State,\n\n    auto_shrink: Vec2b,\n\n    /// Does this `ScrollArea` have horizontal/vertical scrolling enabled?\n    direction_enabled: Vec2b,\n\n    /// Smoothly interpolated boolean of whether or not to show the scroll bars.\n    show_bars_factor: Vec2,\n\n    /// How much horizontal and vertical space are used up by the\n    /// width of the vertical bar, and the height of the horizontal bar?\n    ///\n    /// This is always zero for floating scroll bars.\n    ///\n    /// Note that this is a `yx` swizzling of [`Self::show_bars_factor`]\n    /// times the maximum bar with.\n    /// That's because horizontal scroll uses up vertical space,\n    /// and vice versa.\n    current_bar_use: Vec2,\n\n    scroll_bar_visibility: ScrollBarVisibility,\n    scroll_bar_rect: Option<Rect>,\n\n    /// Where on the screen the content is (excludes scroll bars; includes `content_margin`).\n    inner_rect: Rect,\n\n    content_ui: Ui,\n\n    /// Relative coordinates: the offset and size of the view of the inner UI.\n    /// `viewport.min == ZERO` means we scrolled to the top.\n    viewport: Rect,\n\n    scroll_source: ScrollSource,\n    wheel_scroll_multiplier: Vec2,\n    stick_to_end: Vec2b,\n\n    /// If there was a scroll target before the [`ScrollArea`] was added this frame, it's\n    /// not for us to handle so we save it and restore it after this [`ScrollArea`] is done.\n    saved_scroll_target: [Option<pass_state::ScrollTarget>; 2],\n\n    /// The response from dragging the background (if enabled)\n    background_drag_response: Option<Response>,\n\n    animated: bool,\n}\n\nimpl ScrollArea {\n    fn begin(self, ui: &mut Ui) -> Prepared {\n        let Self {\n            direction_enabled,\n            auto_shrink,\n            max_size,\n            min_scrolled_size,\n            scroll_bar_visibility,\n            scroll_bar_rect,\n            id_salt,\n            offset_x,\n            offset_y,\n            on_hover_cursor,\n            on_drag_cursor,\n            scroll_source,\n            wheel_scroll_multiplier,\n            content_margin: _, // Used elsewhere\n            stick_to_end,\n            animated,\n        } = self;\n\n        let ctx = ui.ctx().clone();\n\n        let id_salt = id_salt.unwrap_or_else(|| Id::new(\"scroll_area\"));\n        let id = ui.make_persistent_id(id_salt);\n        ctx.check_for_id_clash(\n            id,\n            Rect::from_min_size(ui.available_rect_before_wrap().min, Vec2::ZERO),\n            \"ScrollArea\",\n        );\n        let mut state = State::load(&ctx, id).unwrap_or_default();\n\n        state.offset.x = offset_x.unwrap_or(state.offset.x);\n        state.offset.y = offset_y.unwrap_or(state.offset.y);\n\n        let show_bars: Vec2b = match scroll_bar_visibility {\n            ScrollBarVisibility::AlwaysHidden => Vec2b::FALSE,\n            ScrollBarVisibility::VisibleWhenNeeded => state.show_scroll,\n            ScrollBarVisibility::AlwaysVisible => direction_enabled,\n        };\n\n        let show_bars_factor = Vec2::new(\n            ctx.animate_bool_responsive(id.with(\"h\"), show_bars[0]),\n            ctx.animate_bool_responsive(id.with(\"v\"), show_bars[1]),\n        );\n\n        let current_bar_use = show_bars_factor.yx() * ui.spacing().scroll.allocated_width();\n\n        let available_outer = ui.available_rect_before_wrap();\n\n        let outer_size = available_outer.size().at_most(max_size);\n\n        let inner_size = {\n            let mut inner_size = outer_size - current_bar_use;\n\n            // Don't go so far that we shrink to zero.\n            // In particular, if we put a [`ScrollArea`] inside of a [`ScrollArea`], the inner\n            // one shouldn't collapse into nothingness.\n            // See https://github.com/emilk/egui/issues/1097\n            for d in 0..2 {\n                if direction_enabled[d] {\n                    inner_size[d] = inner_size[d].max(min_scrolled_size[d]);\n                }\n            }\n            inner_size\n        };\n\n        let inner_rect = Rect::from_min_size(available_outer.min, inner_size);\n\n        let mut content_max_size = inner_size;\n\n        if true {\n            // Tell the inner Ui to *try* to fit the content without needing to scroll,\n            // i.e. better to wrap text and shrink images than showing a horizontal scrollbar!\n        } else {\n            // Tell the inner Ui to use as much space as possible, we can scroll to see it!\n            for d in 0..2 {\n                if direction_enabled[d] {\n                    content_max_size[d] = f32::INFINITY;\n                }\n            }\n        }\n\n        let content_max_rect = Rect::from_min_size(inner_rect.min - state.offset, content_max_size);\n\n        // Round to pixels to avoid widgets appearing to \"float\" when scrolling fractional amounts:\n        let content_max_rect = content_max_rect\n            .round_to_pixels(ui.pixels_per_point())\n            .round_ui();\n\n        let mut content_ui = ui.new_child(\n            UiBuilder::new()\n                .ui_stack_info(UiStackInfo::new(UiKind::ScrollArea))\n                .max_rect(content_max_rect),\n        );\n\n        {\n            // Clip the content, but only when we really need to:\n            let clip_rect_margin = ui.visuals().clip_rect_margin;\n            let mut content_clip_rect = ui.clip_rect();\n            for d in 0..2 {\n                if direction_enabled[d] {\n                    content_clip_rect.min[d] = inner_rect.min[d] - clip_rect_margin;\n                    content_clip_rect.max[d] = inner_rect.max[d] + clip_rect_margin;\n                } else {\n                    // Nice handling of forced resizing beyond the possible:\n                    content_clip_rect.max[d] = ui.clip_rect().max[d] - current_bar_use[d];\n                }\n            }\n            // Make sure we didn't accidentally expand the clip rect\n            content_clip_rect = content_clip_rect.intersect(ui.clip_rect());\n            content_ui.set_clip_rect(content_clip_rect);\n        }\n\n        let viewport = Rect::from_min_size(Pos2::ZERO + state.offset, inner_size);\n        let dt = ui.input(|i| i.stable_dt).at_most(0.1);\n\n        let background_drag_response =\n            if scroll_source.drag && ui.is_enabled() && state.content_is_too_large.any() {\n                // Drag contents to scroll (for touch screens mostly).\n                // We must do this BEFORE adding content to the `ScrollArea`,\n                // or we will steal input from the widgets we contain.\n                let content_response_option = state\n                    .interact_rect\n                    .map(|rect| ui.interact(rect, id.with(\"area\"), Sense::DRAG));\n\n                if content_response_option\n                    .as_ref()\n                    .is_some_and(|response| response.dragged())\n                {\n                    for d in 0..2 {\n                        if direction_enabled[d] {\n                            ui.input(|input| {\n                                state.offset[d] -= input.pointer.delta()[d];\n                            });\n                            state.scroll_stuck_to_end[d] = false;\n                            state.offset_target[d] = None;\n                        }\n                    }\n                } else {\n                    // Apply the cursor velocity to the scroll area when the user releases the drag.\n                    if content_response_option\n                        .as_ref()\n                        .is_some_and(|response| response.drag_stopped())\n                    {\n                        state.vel = direction_enabled.to_vec2()\n                            * ui.input(|input| input.pointer.velocity());\n                    }\n                    for d in 0..2 {\n                        // Kinetic scrolling\n                        let stop_speed = 20.0; // Pixels per second.\n                        let friction_coeff = 1000.0; // Pixels per second squared.\n\n                        let friction = friction_coeff * dt;\n                        if friction > state.vel[d].abs() || state.vel[d].abs() < stop_speed {\n                            state.vel[d] = 0.0;\n                        } else {\n                            state.vel[d] -= friction * state.vel[d].signum();\n                            // Offset has an inverted coordinate system compared to\n                            // the velocity, so we subtract it instead of adding it\n                            state.offset[d] -= state.vel[d] * dt;\n                            ctx.request_repaint();\n                        }\n                    }\n                }\n\n                // Set the desired mouse cursors.\n                if let Some(response) = &content_response_option {\n                    if response.dragged()\n                        && let Some(cursor) = on_drag_cursor\n                    {\n                        ui.set_cursor_icon(cursor);\n                    } else if response.hovered()\n                        && let Some(cursor) = on_hover_cursor\n                    {\n                        ui.set_cursor_icon(cursor);\n                    }\n                }\n\n                content_response_option\n            } else {\n                None\n            };\n\n        // Scroll with an animation if we have a target offset (that hasn't been cleared by the code\n        // above).\n        for d in 0..2 {\n            if let Some(scroll_target) = state.offset_target[d] {\n                state.vel[d] = 0.0;\n\n                if (state.offset[d] - scroll_target.target_offset).abs() < 1.0 {\n                    // Arrived\n                    state.offset[d] = scroll_target.target_offset;\n                    state.offset_target[d] = None;\n                } else {\n                    // Move towards target\n                    let t = emath::interpolation_factor(\n                        scroll_target.animation_time_span,\n                        ui.input(|i| i.time),\n                        dt,\n                        emath::ease_in_ease_out,\n                    );\n                    if t < 1.0 {\n                        state.offset[d] =\n                            emath::lerp(state.offset[d]..=scroll_target.target_offset, t);\n                        ctx.request_repaint();\n                    } else {\n                        // Arrived\n                        state.offset[d] = scroll_target.target_offset;\n                        state.offset_target[d] = None;\n                    }\n                }\n            }\n        }\n\n        let saved_scroll_target = content_ui\n            .ctx()\n            .pass_state_mut(|state| std::mem::take(&mut state.scroll_target));\n\n        Prepared {\n            id,\n            state,\n            auto_shrink,\n            direction_enabled,\n            show_bars_factor,\n            current_bar_use,\n            scroll_bar_visibility,\n            scroll_bar_rect,\n            inner_rect,\n            content_ui,\n            viewport,\n            scroll_source,\n            wheel_scroll_multiplier,\n            stick_to_end,\n            saved_scroll_target,\n            background_drag_response,\n            animated,\n        }\n    }\n\n    /// Show the [`ScrollArea`], and add the contents to the viewport.\n    ///\n    /// If the inner area can be very long, consider using [`Self::show_rows`] instead.\n    pub fn show<R>(\n        self,\n        ui: &mut Ui,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> ScrollAreaOutput<R> {\n        self.show_viewport_dyn(ui, Box::new(|ui, _viewport| add_contents(ui)))\n    }\n\n    /// Efficiently show only the visible part of a large number of rows.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let text_style = egui::TextStyle::Body;\n    /// let row_height = ui.text_style_height(&text_style);\n    /// // let row_height = ui.spacing().interact_size.y; // if you are adding buttons instead of labels.\n    /// let total_rows = 10_000;\n    /// egui::ScrollArea::vertical().show_rows(ui, row_height, total_rows, |ui, row_range| {\n    ///     for row in row_range {\n    ///         let text = format!(\"Row {}/{}\", row + 1, total_rows);\n    ///         ui.label(text);\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    pub fn show_rows<R>(\n        self,\n        ui: &mut Ui,\n        row_height_sans_spacing: f32,\n        total_rows: usize,\n        add_contents: impl FnOnce(&mut Ui, std::ops::Range<usize>) -> R,\n    ) -> ScrollAreaOutput<R> {\n        let spacing = ui.spacing().item_spacing;\n        let row_height_with_spacing = row_height_sans_spacing + spacing.y;\n        self.show_viewport(ui, |ui, viewport| {\n            ui.set_height((row_height_with_spacing * total_rows as f32 - spacing.y).at_least(0.0));\n\n            let mut min_row = (viewport.min.y / row_height_with_spacing).floor() as usize;\n            let mut max_row = (viewport.max.y / row_height_with_spacing).ceil() as usize + 1;\n            if max_row > total_rows {\n                let diff = max_row.saturating_sub(min_row);\n                max_row = total_rows;\n                min_row = total_rows.saturating_sub(diff);\n            }\n\n            let y_min = ui.max_rect().top() + min_row as f32 * row_height_with_spacing;\n            let y_max = ui.max_rect().top() + max_row as f32 * row_height_with_spacing;\n\n            let rect = Rect::from_x_y_ranges(ui.max_rect().x_range(), y_min..=y_max);\n\n            ui.scope_builder(UiBuilder::new().max_rect(rect), |viewport_ui| {\n                viewport_ui.skip_ahead_auto_ids(min_row); // Make sure we get consistent IDs.\n                add_contents(viewport_ui, min_row..max_row)\n            })\n            .inner\n        })\n    }\n\n    /// This can be used to only paint the visible part of the contents.\n    ///\n    /// `add_contents` is given the viewport rectangle, which is the relative view of the content.\n    /// So if the passed rect has min = zero, then show the top left content (the user has not scrolled).\n    pub fn show_viewport<R>(\n        self,\n        ui: &mut Ui,\n        add_contents: impl FnOnce(&mut Ui, Rect) -> R,\n    ) -> ScrollAreaOutput<R> {\n        self.show_viewport_dyn(ui, Box::new(add_contents))\n    }\n\n    fn show_viewport_dyn<'c, R>(\n        self,\n        ui: &mut Ui,\n        add_contents: Box<dyn FnOnce(&mut Ui, Rect) -> R + 'c>,\n    ) -> ScrollAreaOutput<R> {\n        let margin = self\n            .content_margin\n            .unwrap_or_else(|| ui.spacing().scroll.content_margin);\n\n        let mut prepared = self.begin(ui);\n        let id = prepared.id;\n        let inner_rect = prepared.inner_rect;\n\n        let inner = crate::Frame::NONE\n            .inner_margin(margin)\n            .show(&mut prepared.content_ui, |ui| {\n                add_contents(ui, prepared.viewport)\n            })\n            .inner;\n\n        let (content_size, state) = prepared.end(ui);\n        ScrollAreaOutput {\n            inner,\n            id,\n            state,\n            content_size,\n            inner_rect,\n        }\n    }\n}\n\nimpl Prepared {\n    /// Returns content size and state\n    fn end(self, ui: &mut Ui) -> (Vec2, State) {\n        let Self {\n            id,\n            mut state,\n            inner_rect,\n            auto_shrink,\n            direction_enabled,\n            mut show_bars_factor,\n            current_bar_use,\n            scroll_bar_visibility,\n            scroll_bar_rect,\n            content_ui,\n            viewport: _,\n            scroll_source,\n            wheel_scroll_multiplier,\n            stick_to_end,\n            saved_scroll_target,\n            background_drag_response,\n            animated,\n        } = self;\n\n        let content_size = content_ui.min_size();\n\n        let scroll_delta = content_ui\n            .ctx()\n            .pass_state_mut(|state| std::mem::take(&mut state.scroll_delta));\n\n        for d in 0..2 {\n            // PassState::scroll_delta is inverted from the way we apply the delta, so we need to negate it.\n            let mut delta = -scroll_delta.0[d];\n            let mut animation = scroll_delta.1;\n\n            // We always take both scroll targets regardless of which scroll axes are enabled. This\n            // is to avoid them leaking to other scroll areas.\n            let scroll_target = content_ui\n                .ctx()\n                .pass_state_mut(|state| state.scroll_target[d].take());\n\n            if direction_enabled[d] {\n                if let Some(target) = scroll_target {\n                    let pass_state::ScrollTarget {\n                        range,\n                        align,\n                        animation: animation_update,\n                    } = target;\n                    let min = content_ui.min_rect().min[d];\n                    let clip_rect = content_ui.clip_rect();\n                    let visible_range = min..=min + clip_rect.size()[d];\n                    let (start, end) = (range.min, range.max);\n                    let clip_start = clip_rect.min[d];\n                    let clip_end = clip_rect.max[d];\n                    let mut spacing = content_ui.spacing().item_spacing[d];\n\n                    let delta_update = if let Some(align) = align {\n                        let center_factor = align.to_factor();\n\n                        let offset =\n                            lerp(range, center_factor) - lerp(visible_range, center_factor);\n\n                        // Depending on the alignment we need to add or subtract the spacing\n                        spacing *= remap(center_factor, 0.0..=1.0, -1.0..=1.0);\n\n                        offset + spacing - state.offset[d]\n                    } else if start < clip_start && end < clip_end {\n                        -(clip_start - start + spacing).min(clip_end - end - spacing)\n                    } else if end > clip_end && start > clip_start {\n                        (end - clip_end + spacing).min(start - clip_start - spacing)\n                    } else {\n                        // Ui is already in view, no need to adjust scroll.\n                        0.0\n                    };\n\n                    delta += delta_update;\n                    animation = animation_update;\n                }\n\n                if delta != 0.0 {\n                    let target_offset = state.offset[d] + delta;\n\n                    if !animated {\n                        state.offset[d] = target_offset;\n                    } else if let Some(animation) = &mut state.offset_target[d] {\n                        // For instance: the user is continuously calling `ui.scroll_to_cursor`,\n                        // so we don't want to reset the animation, but perhaps update the target:\n                        animation.target_offset = target_offset;\n                    } else {\n                        // The further we scroll, the more time we take.\n                        let now = ui.input(|i| i.time);\n                        let animation_duration = (delta.abs() / animation.points_per_second)\n                            .clamp(animation.duration.min, animation.duration.max);\n                        state.offset_target[d] = Some(ScrollingToTarget {\n                            animation_time_span: (now, now + animation_duration as f64),\n                            target_offset,\n                        });\n                    }\n                    ui.request_repaint();\n                }\n            }\n        }\n\n        // Restore scroll target meant for ScrollAreas up the stack (if any)\n        ui.ctx().pass_state_mut(|state| {\n            for d in 0..2 {\n                if saved_scroll_target[d].is_some() {\n                    state.scroll_target[d] = saved_scroll_target[d].clone();\n                }\n            }\n        });\n\n        let inner_rect = {\n            // At this point this is the available size for the inner rect.\n            let mut inner_size = inner_rect.size();\n\n            for d in 0..2 {\n                inner_size[d] = match (direction_enabled[d], auto_shrink[d]) {\n                    (true, true) => inner_size[d].min(content_size[d]), // shrink scroll area if content is small\n                    (true, false) => inner_size[d], // let scroll area be larger than content; fill with blank space\n                    (false, true) => content_size[d], // Follow the content (expand/contract to fit it).\n                    (false, false) => inner_size[d].max(content_size[d]), // Expand to fit content\n                };\n            }\n\n            Rect::from_min_size(inner_rect.min, inner_size)\n        };\n\n        let outer_rect = Rect::from_min_size(inner_rect.min, inner_rect.size() + current_bar_use);\n\n        let content_is_too_large = Vec2b::new(\n            direction_enabled[0] && inner_rect.width() < content_size.x,\n            direction_enabled[1] && inner_rect.height() < content_size.y,\n        );\n\n        let max_offset = content_size - inner_rect.size();\n\n        // Drag-to-scroll?\n        let is_dragging_background = background_drag_response\n            .as_ref()\n            .is_some_and(|r| r.dragged());\n\n        let is_hovering_outer_rect = ui.rect_contains_pointer(outer_rect)\n            && ui.ctx().dragged_id().is_none()\n            || is_dragging_background;\n\n        if scroll_source.mouse_wheel && ui.is_enabled() && is_hovering_outer_rect {\n            let always_scroll_enabled_direction = ui.style().always_scroll_the_only_direction\n                && direction_enabled[0] != direction_enabled[1];\n            for d in 0..2 {\n                if direction_enabled[d] {\n                    let scroll_delta = ui.input(|input| {\n                        if always_scroll_enabled_direction {\n                            // no bidirectional scrolling; allow horizontal scrolling without pressing shift\n                            input.smooth_scroll_delta()[0] + input.smooth_scroll_delta()[1]\n                        } else {\n                            input.smooth_scroll_delta()[d]\n                        }\n                    });\n                    let scroll_delta = scroll_delta * wheel_scroll_multiplier[d];\n\n                    let scrolling_up = state.offset[d] > 0.0 && scroll_delta > 0.0;\n                    let scrolling_down = state.offset[d] < max_offset[d] && scroll_delta < 0.0;\n\n                    if scrolling_up || scrolling_down {\n                        state.offset[d] -= scroll_delta;\n\n                        // Clear scroll delta so no parent scroll will use it:\n                        ui.input_mut(|input| {\n                            if always_scroll_enabled_direction {\n                                input.smooth_scroll_delta = Vec2::ZERO;\n                            } else {\n                                input.smooth_scroll_delta[d] = 0.0;\n                            }\n                        });\n\n                        state.scroll_stuck_to_end[d] = false;\n                        state.offset_target[d] = None;\n                    }\n                }\n            }\n        }\n\n        let show_scroll_this_frame = match scroll_bar_visibility {\n            ScrollBarVisibility::AlwaysHidden => Vec2b::FALSE,\n            ScrollBarVisibility::VisibleWhenNeeded => content_is_too_large,\n            ScrollBarVisibility::AlwaysVisible => direction_enabled,\n        };\n\n        // Avoid frame delay; start showing scroll bar right away:\n        if show_scroll_this_frame[0] && show_bars_factor.x <= 0.0 {\n            show_bars_factor.x = ui.ctx().animate_bool_responsive(id.with(\"h\"), true);\n        }\n        if show_scroll_this_frame[1] && show_bars_factor.y <= 0.0 {\n            show_bars_factor.y = ui.ctx().animate_bool_responsive(id.with(\"v\"), true);\n        }\n\n        let scroll_style = ui.spacing().scroll;\n\n        // Paint the bars:\n        let scroll_bar_rect = scroll_bar_rect.unwrap_or(inner_rect);\n        for d in 0..2 {\n            // maybe force increase in offset to keep scroll stuck to end position\n            if stick_to_end[d] && state.scroll_stuck_to_end[d] {\n                state.offset[d] = content_size[d] - inner_rect.size()[d];\n            }\n\n            let show_factor = show_bars_factor[d];\n            if show_factor == 0.0 {\n                state.scroll_bar_interaction[d] = false;\n                continue;\n            }\n\n            let interact_id = id.with(d);\n\n            // Margin on either side of the scroll bar:\n            let inner_margin = show_factor * scroll_style.bar_inner_margin;\n            let outer_margin = show_factor * scroll_style.bar_outer_margin;\n\n            // bottom of a horizontal scroll (d==0).\n            // right of a vertical scroll (d==1).\n            let mut max_cross = outer_rect.max[1 - d] - outer_margin;\n\n            if ui.clip_rect().max[1 - d] - outer_margin < max_cross {\n                // Move the scrollbar so it is visible. This is needed in some cases.\n                // For instance:\n                // * When we have a vertical-only scroll area in a top level panel,\n                //   and that panel is not wide enough for the contents.\n                // * When one ScrollArea is nested inside another, and the outer\n                //   is scrolled so that the scroll-bars of the inner ScrollArea (us)\n                //   is outside the clip rectangle.\n                // Really this should use the tighter clip_rect that ignores clip_rect_margin, but we don't store that.\n                // clip_rect_margin is quite a hack. It would be nice to get rid of it.\n                max_cross = ui.clip_rect().max[1 - d] - outer_margin;\n            }\n\n            let full_width = scroll_style.bar_width;\n\n            // The bounding rect of a fully visible bar.\n            // When we hover this area, we should show the full bar:\n            let max_bar_rect = if d == 0 {\n                outer_rect.with_min_y(max_cross - full_width)\n            } else {\n                outer_rect.with_min_x(max_cross - full_width)\n            };\n\n            let sense = if scroll_source.scroll_bar && ui.is_enabled() {\n                Sense::CLICK | Sense::DRAG\n            } else {\n                Sense::hover()\n            };\n\n            // We always sense interaction with the full width, even if we antimate it growing/shrinking.\n            // This is to present a more consistent target for our hit test code,\n            // and to avoid producing jitter in \"thin widget\" heuristics there.\n            // Also: it make sense to detect any hover where the scroll bar _will_ be.\n            let response = ui.interact(max_bar_rect, interact_id, sense);\n\n            response.widget_info(|| WidgetInfo::new(crate::WidgetType::ScrollBar));\n\n            // top/bottom of a horizontal scroll (d==0).\n            // left/rigth of a vertical scroll (d==1).\n            let cross = if scroll_style.floating {\n                let is_hovering_bar_area = response.hovered() || state.scroll_bar_interaction[d];\n\n                let is_hovering_bar_area_t = ui\n                    .ctx()\n                    .animate_bool_responsive(id.with((d, \"bar_hover\")), is_hovering_bar_area);\n\n                let width = show_factor\n                    * lerp(\n                        scroll_style.floating_width..=full_width,\n                        is_hovering_bar_area_t,\n                    );\n\n                let min_cross = max_cross - width;\n                Rangef::new(min_cross, max_cross)\n            } else {\n                let min_cross = inner_rect.max[1 - d] + inner_margin;\n                Rangef::new(min_cross, max_cross)\n            };\n\n            let outer_scroll_bar_rect = if d == 0 {\n                Rect::from_x_y_ranges(scroll_bar_rect.x_range(), cross)\n            } else {\n                Rect::from_x_y_ranges(cross, scroll_bar_rect.y_range())\n            };\n\n            let from_content = |content| {\n                remap_clamp(\n                    content,\n                    0.0..=content_size[d],\n                    scroll_bar_rect.min[d]..=scroll_bar_rect.max[d],\n                )\n            };\n\n            let calculate_handle_rect = |d, offset: &Vec2| {\n                let handle_size = if d == 0 {\n                    from_content(offset.x + inner_rect.width()) - from_content(offset.x)\n                } else {\n                    from_content(offset.y + inner_rect.height()) - from_content(offset.y)\n                }\n                .max(scroll_style.handle_min_length);\n\n                let handle_start_point = remap_clamp(\n                    offset[d],\n                    0.0..=max_offset[d],\n                    scroll_bar_rect.min[d]..=(scroll_bar_rect.max[d] - handle_size),\n                );\n\n                if d == 0 {\n                    Rect::from_min_max(\n                        pos2(handle_start_point, cross.min),\n                        pos2(handle_start_point + handle_size, cross.max),\n                    )\n                } else {\n                    Rect::from_min_max(\n                        pos2(cross.min, handle_start_point),\n                        pos2(cross.max, handle_start_point + handle_size),\n                    )\n                }\n            };\n\n            let handle_rect = calculate_handle_rect(d, &state.offset);\n\n            state.scroll_bar_interaction[d] = response.hovered() || response.dragged();\n\n            if let Some(pointer_pos) = response.interact_pointer_pos() {\n                let scroll_start_offset_from_top_left = state.scroll_start_offset_from_top_left[d]\n                    .get_or_insert_with(|| {\n                        if handle_rect.contains(pointer_pos) {\n                            pointer_pos[d] - handle_rect.min[d]\n                        } else {\n                            let handle_top_pos_at_bottom =\n                                scroll_bar_rect.max[d] - handle_rect.size()[d];\n                            // Calculate the new handle top position, centering the handle on the mouse.\n                            let new_handle_top_pos = (pointer_pos[d] - handle_rect.size()[d] / 2.0)\n                                .clamp(scroll_bar_rect.min[d], handle_top_pos_at_bottom);\n                            pointer_pos[d] - new_handle_top_pos\n                        }\n                    });\n\n                let new_handle_top = pointer_pos[d] - *scroll_start_offset_from_top_left;\n                let handle_travel =\n                    scroll_bar_rect.min[d]..=(scroll_bar_rect.max[d] - handle_rect.size()[d]);\n                state.offset[d] = if handle_travel.start() == handle_travel.end() {\n                    0.0\n                } else {\n                    remap(new_handle_top, handle_travel, 0.0..=max_offset[d])\n                };\n\n                // some manual action taken, scroll not stuck\n                state.scroll_stuck_to_end[d] = false;\n                state.offset_target[d] = None;\n            } else {\n                state.scroll_start_offset_from_top_left[d] = None;\n            }\n\n            let unbounded_offset = state.offset[d];\n            state.offset[d] = state.offset[d].max(0.0);\n            state.offset[d] = state.offset[d].min(max_offset[d]);\n\n            if state.offset[d] != unbounded_offset {\n                state.vel[d] = 0.0;\n            }\n\n            if ui.is_rect_visible(outer_scroll_bar_rect) {\n                // Avoid frame-delay by calculating a new handle rect:\n                let handle_rect = calculate_handle_rect(d, &state.offset);\n\n                let visuals = if scroll_source.scroll_bar && ui.is_enabled() {\n                    // Pick visuals based on interaction with the handle.\n                    // Remember that the response is for the whole scroll bar!\n                    let is_hovering_handle = response.hovered()\n                        && ui.input(|i| {\n                            i.pointer\n                                .latest_pos()\n                                .is_some_and(|p| handle_rect.contains(p))\n                        });\n                    let visuals = ui.visuals();\n                    if response.is_pointer_button_down_on() {\n                        &visuals.widgets.active\n                    } else if is_hovering_handle {\n                        &visuals.widgets.hovered\n                    } else {\n                        &visuals.widgets.inactive\n                    }\n                } else {\n                    &ui.visuals().widgets.inactive\n                };\n\n                let handle_opacity = if scroll_style.floating {\n                    if response.hovered() || response.dragged() {\n                        scroll_style.interact_handle_opacity\n                    } else {\n                        let is_hovering_outer_rect_t = ui.ctx().animate_bool_responsive(\n                            id.with((d, \"is_hovering_outer_rect\")),\n                            is_hovering_outer_rect,\n                        );\n                        lerp(\n                            scroll_style.dormant_handle_opacity\n                                ..=scroll_style.active_handle_opacity,\n                            is_hovering_outer_rect_t,\n                        )\n                    }\n                } else {\n                    1.0\n                };\n\n                let background_opacity = if scroll_style.floating {\n                    if response.hovered() || response.dragged() {\n                        scroll_style.interact_background_opacity\n                    } else if is_hovering_outer_rect {\n                        scroll_style.active_background_opacity\n                    } else {\n                        scroll_style.dormant_background_opacity\n                    }\n                } else {\n                    1.0\n                };\n\n                let handle_color = if scroll_style.foreground_color {\n                    visuals.fg_stroke.color\n                } else {\n                    visuals.bg_fill\n                };\n\n                // Background:\n                ui.painter().add(epaint::Shape::rect_filled(\n                    outer_scroll_bar_rect,\n                    visuals.corner_radius,\n                    ui.visuals()\n                        .extreme_bg_color\n                        .gamma_multiply(background_opacity),\n                ));\n\n                // Handle:\n                ui.painter().add(epaint::Shape::rect_filled(\n                    handle_rect,\n                    visuals.corner_radius,\n                    handle_color.gamma_multiply(handle_opacity),\n                ));\n            }\n        }\n\n        ui.advance_cursor_after_rect(outer_rect);\n\n        if show_scroll_this_frame != state.show_scroll {\n            ui.request_repaint();\n        }\n\n        let available_offset = content_size - inner_rect.size();\n        state.offset = state.offset.min(available_offset);\n        state.offset = state.offset.max(Vec2::ZERO);\n\n        // Is scroll handle at end of content, or is there no scrollbar\n        // yet (not enough content), but sticking is requested? If so, enter sticky mode.\n        // Only has an effect if stick_to_end is enabled but we save in\n        // state anyway so that entering sticky mode at an arbitrary time\n        // has appropriate effect.\n        state.scroll_stuck_to_end = Vec2b::new(\n            (state.offset[0] == available_offset[0])\n                || (self.stick_to_end[0] && available_offset[0] < 0.0),\n            (state.offset[1] == available_offset[1])\n                || (self.stick_to_end[1] && available_offset[1] < 0.0),\n        );\n\n        state.show_scroll = show_scroll_this_frame;\n        state.content_is_too_large = content_is_too_large;\n        state.interact_rect = Some(inner_rect);\n\n        state.store(ui.ctx(), id);\n\n        (content_size, state)\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/sides.rs",
    "content": "use emath::{Align, NumExt as _};\n\nuse crate::{Layout, Ui, UiBuilder};\n\n/// Put some widgets on the left and right sides of a ui.\n///\n/// The result will look like this:\n/// ```text\n///                        parent Ui\n///  ______________________________________________________\n/// |                    |           |                     |  ^\n/// | -> left widgets -> |    gap    | <- right widgets <- |  | height\n/// |____________________|           |_____________________|  v\n/// |                                                      |\n/// |                                                      |\n/// ```\n///\n/// The width of the gap is dynamic, based on the max width of the parent [`Ui`].\n/// When the parent is being auto-sized ([`Ui::is_sizing_pass`]) the gap will be as small as possible.\n///\n/// If the parent is not wide enough to fit all widgets, the parent will be expanded to the right.\n///\n/// The left widgets are added left-to-right.\n/// The right widgets are added right-to-left.\n///\n/// Which side is first depends on the configuration:\n///  - [`Sides::extend`] - left widgets are added first\n///  - [`Sides::shrink_left`] - right widgets are added first\n///  - [`Sides::shrink_right`] - left widgets are added first\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// egui::containers::Sides::new().show(ui,\n///     |ui| {\n///         ui.label(\"Left\");\n///     },\n///     |ui| {\n///         ui.label(\"Right\");\n///     }\n/// );\n/// # });\n/// ```\n#[must_use = \"You should call sides.show()\"]\n#[derive(Clone, Copy, Debug, Default)]\npub struct Sides {\n    height: Option<f32>,\n    spacing: Option<f32>,\n    kind: SidesKind,\n    wrap_mode: Option<crate::TextWrapMode>,\n}\n\n#[derive(Clone, Copy, Debug, Default)]\nenum SidesKind {\n    #[default]\n    Extend,\n    ShrinkLeft,\n    ShrinkRight,\n}\n\nimpl Sides {\n    #[inline]\n    pub fn new() -> Self {\n        Default::default()\n    }\n\n    /// The minimum height of the sides.\n    ///\n    /// The content will be centered vertically within this height.\n    /// The default height is [`crate::Spacing::interact_size`]`.y`.\n    #[inline]\n    pub fn height(mut self, height: f32) -> Self {\n        self.height = Some(height);\n        self\n    }\n\n    /// The horizontal spacing between the left and right UIs.\n    ///\n    /// This is the minimum gap.\n    /// The default is [`crate::Spacing::item_spacing`]`.x`.\n    #[inline]\n    pub fn spacing(mut self, spacing: f32) -> Self {\n        self.spacing = Some(spacing);\n        self\n    }\n\n    /// Try to shrink widgets on the left side.\n    ///\n    /// Right widgets will be added first. The left [`Ui`]s max rect will be limited to the\n    /// remaining space.\n    #[inline]\n    pub fn shrink_left(mut self) -> Self {\n        self.kind = SidesKind::ShrinkLeft;\n        self\n    }\n\n    /// Try to shrink widgets on the right side.\n    ///\n    /// Left widgets will be added first. The right [`Ui`]s max rect will be limited to the\n    /// remaining space.\n    #[inline]\n    pub fn shrink_right(mut self) -> Self {\n        self.kind = SidesKind::ShrinkRight;\n        self\n    }\n\n    /// Extend the left and right sides to fill the available space.\n    ///\n    /// This is the default behavior.\n    /// The left widgets will be added first, followed by the right widgets.\n    #[inline]\n    pub fn extend(mut self) -> Self {\n        self.kind = SidesKind::Extend;\n        self\n    }\n\n    /// The text wrap mode for the shrinking side.\n    ///\n    /// Does nothing if [`Self::extend`] is used (the default).\n    #[inline]\n    pub fn wrap_mode(mut self, wrap_mode: crate::TextWrapMode) -> Self {\n        self.wrap_mode = Some(wrap_mode);\n        self\n    }\n\n    /// Truncate the text on the shrinking side.\n    ///\n    /// This is a shortcut for [`Self::wrap_mode`].\n    /// Does nothing if [`Self::extend`] is used (the default).\n    #[inline]\n    pub fn truncate(mut self) -> Self {\n        self.wrap_mode = Some(crate::TextWrapMode::Truncate);\n        self\n    }\n\n    /// Wrap the text on the shrinking side.\n    ///\n    /// This is a shortcut for [`Self::wrap_mode`].\n    /// Does nothing if [`Self::extend`] is used (the default).\n    #[inline]\n    pub fn wrap(mut self) -> Self {\n        self.wrap_mode = Some(crate::TextWrapMode::Wrap);\n        self\n    }\n\n    pub fn show<RetL, RetR>(\n        self,\n        ui: &mut Ui,\n        add_left: impl FnOnce(&mut Ui) -> RetL,\n        add_right: impl FnOnce(&mut Ui) -> RetR,\n    ) -> (RetL, RetR) {\n        let Self {\n            height,\n            spacing,\n            mut kind,\n            mut wrap_mode,\n        } = self;\n        let height = height.unwrap_or_else(|| ui.spacing().interact_size.y);\n        let spacing = spacing.unwrap_or_else(|| ui.spacing().item_spacing.x);\n\n        let mut top_rect = ui.available_rect_before_wrap();\n        top_rect.max.y = top_rect.min.y + height;\n\n        if ui.is_sizing_pass() {\n            kind = SidesKind::Extend;\n            wrap_mode = None;\n        }\n\n        match kind {\n            SidesKind::ShrinkLeft => {\n                let (right_rect, result_right) = Self::create_ui(\n                    ui,\n                    top_rect,\n                    Layout::right_to_left(Align::Center),\n                    add_right,\n                    None,\n                );\n                let available_width = top_rect.width() - right_rect.width() - spacing;\n                let left_rect_constraint =\n                    top_rect.with_max_x(top_rect.min.x + available_width.at_least(0.0));\n                let (left_rect, result_left) = Self::create_ui(\n                    ui,\n                    left_rect_constraint,\n                    Layout::left_to_right(Align::Center),\n                    add_left,\n                    wrap_mode,\n                );\n\n                ui.advance_cursor_after_rect(left_rect | right_rect);\n                (result_left, result_right)\n            }\n            SidesKind::ShrinkRight => {\n                let (left_rect, result_left) = Self::create_ui(\n                    ui,\n                    top_rect,\n                    Layout::left_to_right(Align::Center),\n                    add_left,\n                    None,\n                );\n                let right_rect_constraint = top_rect.with_min_x(left_rect.max.x + spacing);\n                let (right_rect, result_right) = Self::create_ui(\n                    ui,\n                    right_rect_constraint,\n                    Layout::right_to_left(Align::Center),\n                    add_right,\n                    wrap_mode,\n                );\n\n                ui.advance_cursor_after_rect(left_rect | right_rect);\n                (result_left, result_right)\n            }\n            SidesKind::Extend => {\n                let (left_rect, result_left) = Self::create_ui(\n                    ui,\n                    top_rect,\n                    Layout::left_to_right(Align::Center),\n                    add_left,\n                    None,\n                );\n                let right_max_rect = top_rect.with_min_x(left_rect.max.x);\n                let (right_rect, result_right) = Self::create_ui(\n                    ui,\n                    right_max_rect,\n                    Layout::right_to_left(Align::Center),\n                    add_right,\n                    None,\n                );\n\n                let mut final_rect = left_rect | right_rect;\n                let min_width = left_rect.width() + spacing + right_rect.width();\n\n                if ui.is_sizing_pass() {\n                    final_rect.max.x = left_rect.min.x + min_width;\n                } else {\n                    final_rect.max.x = final_rect.max.x.max(left_rect.min.x + min_width);\n                }\n\n                ui.advance_cursor_after_rect(final_rect);\n                (result_left, result_right)\n            }\n        }\n    }\n\n    fn create_ui<Ret>(\n        ui: &mut Ui,\n        max_rect: emath::Rect,\n        layout: Layout,\n        add_content: impl FnOnce(&mut Ui) -> Ret,\n        wrap_mode: Option<crate::TextWrapMode>,\n    ) -> (emath::Rect, Ret) {\n        let mut child_ui = ui.new_child(UiBuilder::new().max_rect(max_rect).layout(layout));\n        if let Some(wrap_mode) = wrap_mode {\n            child_ui.style_mut().wrap_mode = Some(wrap_mode);\n        }\n        let result = add_content(&mut child_ui);\n        (child_ui.min_rect(), result)\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/tooltip.rs",
    "content": "use crate::pass_state::PerWidgetTooltipState;\nuse crate::{\n    AreaState, Context, Id, InnerResponse, LayerId, Layout, Order, Popup, PopupAnchor, PopupKind,\n    Response, Sense,\n};\nuse emath::Vec2;\n\npub struct Tooltip<'a> {\n    pub popup: Popup<'a>,\n\n    /// The layer of the parent widget.\n    parent_layer: LayerId,\n\n    /// The id of the widget that owns this tooltip.\n    parent_widget: Id,\n}\n\nimpl Tooltip<'_> {\n    /// Show a tooltip that is always open.\n    #[deprecated = \"Use `Tooltip::always_open` instead.\"]\n    pub fn new(\n        parent_widget: Id,\n        ctx: Context,\n        anchor: impl Into<PopupAnchor>,\n        parent_layer: LayerId,\n    ) -> Self {\n        Self {\n            popup: Popup::new(parent_widget, ctx, anchor.into(), parent_layer)\n                .kind(PopupKind::Tooltip)\n                .gap(4.0)\n                .sense(Sense::hover()),\n            parent_layer,\n            parent_widget,\n        }\n    }\n\n    /// Show a tooltip that is always open.\n    pub fn always_open(\n        ctx: Context,\n        parent_layer: LayerId,\n        parent_widget: Id,\n        anchor: impl Into<PopupAnchor>,\n    ) -> Self {\n        let width = ctx.global_style().spacing.tooltip_width;\n        Self {\n            popup: Popup::new(parent_widget, ctx, anchor.into(), parent_layer)\n                .kind(PopupKind::Tooltip)\n                .gap(4.0)\n                .width(width)\n                .sense(Sense::hover()),\n            parent_layer,\n            parent_widget,\n        }\n    }\n\n    /// Show a tooltip for a widget. Always open (as long as this function is called).\n    pub fn for_widget(response: &Response) -> Self {\n        let popup = Popup::from_response(response)\n            .kind(PopupKind::Tooltip)\n            .gap(4.0)\n            .width(response.ctx.global_style().spacing.tooltip_width)\n            .sense(Sense::hover());\n        Self {\n            popup,\n            parent_layer: response.layer_id,\n            parent_widget: response.id,\n        }\n    }\n\n    /// Show a tooltip when hovering an enabled widget.\n    pub fn for_enabled(response: &Response) -> Self {\n        let mut tooltip = Self::for_widget(response);\n        tooltip.popup = tooltip\n            .popup\n            .open(response.enabled() && Self::should_show_tooltip(response, true));\n        tooltip\n    }\n\n    /// Show a tooltip when hovering a disabled widget.\n    pub fn for_disabled(response: &Response) -> Self {\n        let mut tooltip = Self::for_widget(response);\n        tooltip.popup = tooltip\n            .popup\n            .open(!response.enabled() && Self::should_show_tooltip(response, true));\n        tooltip\n    }\n\n    /// Show the tooltip at the pointer position.\n    #[inline]\n    pub fn at_pointer(mut self) -> Self {\n        self.popup = self.popup.at_pointer();\n        self\n    }\n\n    /// Set the gap between the tooltip and the anchor\n    ///\n    /// Default: 5.0\n    #[inline]\n    pub fn gap(mut self, gap: f32) -> Self {\n        self.popup = self.popup.gap(gap);\n        self\n    }\n\n    /// Set the layout of the tooltip\n    #[inline]\n    pub fn layout(mut self, layout: Layout) -> Self {\n        self.popup = self.popup.layout(layout);\n        self\n    }\n\n    /// Set the width of the tooltip\n    #[inline]\n    pub fn width(mut self, width: f32) -> Self {\n        self.popup = self.popup.width(width);\n        self\n    }\n\n    /// Show the tooltip\n    pub fn show<R>(self, content: impl FnOnce(&mut crate::Ui) -> R) -> Option<InnerResponse<R>> {\n        let Self {\n            mut popup,\n            parent_layer,\n            parent_widget,\n        } = self;\n\n        if !popup.is_open() {\n            return None;\n        }\n\n        let rect = popup.get_anchor_rect()?;\n\n        let mut state = popup.ctx().pass_state_mut(|fs| {\n            // Remember that this is the widget showing the tooltip:\n            fs.layers\n                .entry(parent_layer)\n                .or_default()\n                .widget_with_tooltip = Some(parent_widget);\n\n            fs.tooltips\n                .widget_tooltips\n                .get(&parent_widget)\n                .copied()\n                .unwrap_or(PerWidgetTooltipState {\n                    bounding_rect: rect,\n                    tooltip_count: 0,\n                })\n        });\n\n        let tooltip_area_id = Self::tooltip_id(parent_widget, state.tooltip_count);\n        popup = popup.anchor(state.bounding_rect).id(tooltip_area_id);\n\n        let response = popup.show(|ui| {\n            // By default, the text in tooltips aren't selectable.\n            // This means that most tooltips aren't interactable,\n            // which also mean they won't stick around so you can click them.\n            // Only tooltips that have actual interactive stuff (buttons, links, …)\n            // will stick around when you try to click them.\n            ui.style_mut().interaction.selectable_labels = false;\n\n            content(ui)\n        });\n\n        // The popup might not be shown on at_pointer if there is no pointer.\n        if let Some(response) = &response {\n            state.tooltip_count += 1;\n            state.bounding_rect |= response.response.rect;\n            response\n                .response\n                .ctx\n                .pass_state_mut(|fs| fs.tooltips.widget_tooltips.insert(parent_widget, state));\n            Self::remember_that_tooltip_was_shown(&response.response.ctx);\n        }\n\n        response\n    }\n\n    fn when_was_a_toolip_last_shown_id() -> Id {\n        Id::new(\"when_was_a_toolip_last_shown\")\n    }\n\n    pub fn seconds_since_last_tooltip(ctx: &Context) -> f32 {\n        let when_was_a_toolip_last_shown =\n            ctx.data(|d| d.get_temp::<f64>(Self::when_was_a_toolip_last_shown_id()));\n\n        if let Some(when_was_a_toolip_last_shown) = when_was_a_toolip_last_shown {\n            let now = ctx.input(|i| i.time);\n            (now - when_was_a_toolip_last_shown) as f32\n        } else {\n            f32::INFINITY\n        }\n    }\n\n    fn remember_that_tooltip_was_shown(ctx: &Context) {\n        let now = ctx.input(|i| i.time);\n        ctx.data_mut(|data| data.insert_temp::<f64>(Self::when_was_a_toolip_last_shown_id(), now));\n    }\n\n    /// What is the id of the next tooltip for this widget?\n    pub fn next_tooltip_id(ctx: &Context, widget_id: Id) -> Id {\n        let tooltip_count = ctx.pass_state(|fs| {\n            fs.tooltips\n                .widget_tooltips\n                .get(&widget_id)\n                .map_or(0, |state| state.tooltip_count)\n        });\n        Self::tooltip_id(widget_id, tooltip_count)\n    }\n\n    pub fn tooltip_id(widget_id: Id, tooltip_count: usize) -> Id {\n        widget_id.with(tooltip_count)\n    }\n\n    /// Should we show a tooltip for this response?\n    ///\n    /// Argument `allow_interactive_tooltip` controls whether mouse can interact with tooltip that\n    /// contains interactive widgets\n    pub fn should_show_tooltip(response: &Response, allow_interactive_tooltip: bool) -> bool {\n        if response.ctx.memory(|mem| mem.everything_is_visible()) {\n            return true;\n        }\n\n        let any_open_popups = response.ctx.prev_pass_state(|fs| {\n            fs.layers\n                .get(&response.layer_id)\n                .is_some_and(|layer| !layer.open_popups.is_empty())\n        });\n        if any_open_popups {\n            // Hide tooltips if the user opens a popup (menu, combo-box, etc.) in the same layer.\n            return false;\n        }\n\n        let style = response.ctx.global_style();\n\n        let tooltip_delay = style.interaction.tooltip_delay;\n        let tooltip_grace_time = style.interaction.tooltip_grace_time;\n\n        let (\n            time_since_last_scroll,\n            time_since_last_click,\n            time_since_last_pointer_movement,\n            pointer_pos,\n            pointer_dir,\n        ) = response.ctx.input(|i| {\n            (\n                i.time_since_last_scroll(),\n                i.pointer.time_since_last_click(),\n                i.pointer.time_since_last_movement(),\n                i.pointer.hover_pos(),\n                i.pointer.direction(),\n            )\n        });\n\n        if time_since_last_scroll < tooltip_delay {\n            // See https://github.com/emilk/egui/issues/4781\n            // Note that this means we cannot have `ScrollArea`s in a tooltip.\n            response\n                .ctx\n                .request_repaint_after_secs(tooltip_delay - time_since_last_scroll);\n            return false;\n        }\n\n        let is_our_tooltip_open = response.is_tooltip_open();\n\n        if is_our_tooltip_open {\n            // Check if we should automatically stay open:\n\n            let tooltip_id = Self::next_tooltip_id(&response.ctx, response.id);\n            let tooltip_layer_id = LayerId::new(Order::Tooltip, tooltip_id);\n\n            let tooltip_has_interactive_widget = allow_interactive_tooltip\n                && response.ctx.viewport(|vp| {\n                    vp.prev_pass\n                        .widgets\n                        .get_layer(tooltip_layer_id)\n                        .any(|w| w.enabled && w.sense.interactive())\n                });\n\n            if tooltip_has_interactive_widget {\n                // We keep the tooltip open if hovered,\n                // or if the pointer is on its way to it,\n                // so that the user can interact with the tooltip\n                // (i.e. click links that are in it).\n                if let Some(area) = AreaState::load(&response.ctx, tooltip_id) {\n                    let rect = area.rect();\n\n                    if let Some(pos) = pointer_pos {\n                        if rect.contains(pos) {\n                            return true; // hovering interactive tooltip\n                        }\n                        if pointer_dir != Vec2::ZERO\n                            && rect.intersects_ray(pos, pointer_dir.normalized())\n                        {\n                            return true; // on the way to interactive tooltip\n                        }\n                    }\n                }\n            }\n        }\n\n        let clicked_more_recently_than_moved =\n            time_since_last_click < time_since_last_pointer_movement + 0.1;\n        if clicked_more_recently_than_moved {\n            // It is common to click a widget and then rest the mouse there.\n            // It would be annoying to then see a tooltip for it immediately.\n            // Similarly, clicking should hide the existing tooltip.\n            // Only hovering should lead to a tooltip, not clicking.\n            // The offset is only to allow small movement just right after the click.\n            return false;\n        }\n\n        if is_our_tooltip_open {\n            // Check if we should automatically stay open:\n\n            if pointer_pos.is_some_and(|pointer_pos| response.rect.contains(pointer_pos)) {\n                // Handle the case of a big tooltip that covers the widget:\n                return true;\n            }\n        }\n\n        let is_other_tooltip_open = response.ctx.prev_pass_state(|fs| {\n            if let Some(already_open_tooltip) = fs\n                .layers\n                .get(&response.layer_id)\n                .and_then(|layer| layer.widget_with_tooltip)\n            {\n                already_open_tooltip != response.id\n            } else {\n                false\n            }\n        });\n        if is_other_tooltip_open {\n            // We only allow one tooltip per layer. First one wins. It is up to that tooltip to close itself.\n            return false;\n        }\n\n        // Fast early-outs:\n        if response.enabled() {\n            if !response.hovered() || !response.ctx.input(|i| i.pointer.has_pointer()) {\n                return false;\n            }\n        } else if !response\n            .ctx\n            .rect_contains_pointer(response.layer_id, response.rect)\n        {\n            return false;\n        }\n\n        // There is a tooltip_delay before showing the first tooltip,\n        // but once one tooltip is show, moving the mouse cursor to\n        // another widget should show the tooltip for that widget right away.\n\n        // Let the user quickly move over some dead space to hover the next thing\n        let tooltip_was_recently_shown =\n            Self::seconds_since_last_tooltip(&response.ctx) < tooltip_grace_time;\n\n        if !tooltip_was_recently_shown && !is_our_tooltip_open {\n            if style.interaction.show_tooltips_only_when_still {\n                // We only show the tooltip when the mouse pointer is still.\n                if !response\n                    .ctx\n                    .input(|i| i.pointer.is_still() && !i.is_scrolling())\n                {\n                    // wait for mouse to stop\n                    response.ctx.request_repaint();\n                    return false;\n                }\n            }\n\n            let time_since_last_interaction = time_since_last_scroll\n                .min(time_since_last_pointer_movement)\n                .min(time_since_last_click);\n            let time_til_tooltip = tooltip_delay - time_since_last_interaction;\n\n            if 0.0 < time_til_tooltip {\n                // Wait until the mouse has been still for a while\n                response.ctx.request_repaint_after_secs(time_til_tooltip);\n                return false;\n            }\n        }\n\n        // We don't want tooltips of things while we are dragging them,\n        // but we do want tooltips while holding down on an item on a touch screen.\n        if response\n            .ctx\n            .input(|i| i.pointer.any_down() && i.pointer.has_moved_too_much_for_a_click)\n        {\n            return false;\n        }\n\n        // All checks passed: show the tooltip!\n\n        true\n    }\n\n    /// Was this tooltip visible last frame?\n    pub fn was_tooltip_open_last_frame(ctx: &Context, widget_id: Id) -> bool {\n        let primary_tooltip_area_id = Self::tooltip_id(widget_id, 0);\n        ctx.memory(|mem| {\n            mem.areas()\n                .visible_last_frame(&LayerId::new(Order::Tooltip, primary_tooltip_area_id))\n        })\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/containers/window.rs",
    "content": "// WARNING: the code in here is horrible. It is a behemoth that needs breaking up into simpler parts.\n\nuse std::sync::Arc;\n\nuse emath::GuiRounding as _;\nuse epaint::{CornerRadiusF32, RectShape};\n\nuse crate::collapsing_header::CollapsingState;\nuse crate::*;\n\nuse super::scroll_area::{ScrollBarVisibility, ScrollSource};\nuse super::{Area, Frame, Resize, ScrollArea, area, resize};\n\n/// Builder for a floating window which can be dragged, closed, collapsed, resized and scrolled (off by default).\n///\n/// You can customize:\n/// * title\n/// * default, minimum, maximum and/or fixed size, collapsed/expanded\n/// * if the window has a scroll area (off by default)\n/// * if the window can be collapsed (minimized) to just the title bar (yes, by default)\n/// * if there should be a close button (none by default)\n///\n/// ```\n/// # egui::__run_test_ctx(|ctx| {\n/// egui::Window::new(\"My Window\").show(ctx, |ui| {\n///    ui.label(\"Hello World!\");\n/// });\n/// # });\n/// ```\n///\n/// The previous rectangle used by this window can be obtained through [`crate::Memory::area_rect()`].\n///\n/// Note that this is NOT a native OS window.\n/// To create a new native OS window, use [`crate::Context::show_viewport_deferred`].\n#[must_use = \"You should call .show()\"]\npub struct Window<'open> {\n    title: WidgetText,\n    open: Option<&'open mut bool>,\n    area: Area,\n    frame: Option<Frame>,\n    resize: Resize,\n    scroll: ScrollArea,\n    collapsible: bool,\n    default_open: bool,\n    with_title_bar: bool,\n    fade_out: bool,\n}\n\nimpl<'open> Window<'open> {\n    /// The window title is used as a unique [`Id`] and must be unique, and should not change.\n    /// This is true even if you disable the title bar with `.title_bar(false)`.\n    /// If you need a changing title, you must call `window.id(…)` with a fixed id.\n    pub fn new(title: impl Into<WidgetText>) -> Self {\n        let title = title.into().fallback_text_style(TextStyle::Heading);\n        let area = Area::new(Id::new(title.text())).kind(UiKind::Window);\n        Self {\n            title,\n            open: None,\n            area,\n            frame: None,\n            resize: Resize::default()\n                .with_stroke(false)\n                .min_size([96.0, 32.0])\n                .default_size([340.0, 420.0]), // Default inner size of a window\n            scroll: ScrollArea::neither().auto_shrink(false),\n            collapsible: true,\n            default_open: true,\n            with_title_bar: true,\n            fade_out: true,\n        }\n    }\n\n    /// Construct a [`Window`] that follows the given viewport.\n    pub fn from_viewport(id: ViewportId, viewport: ViewportBuilder) -> Self {\n        let ViewportBuilder {\n            title,\n            app_id,\n            inner_size,\n            min_inner_size,\n            max_inner_size,\n            resizable,\n            decorations,\n            title_shown,\n            minimize_button,\n            .. // A lot of things not implemented yet\n        } = viewport;\n\n        let mut window = Self::new(title.or(app_id).unwrap_or_else(String::new)).id(Id::new(id));\n\n        if let Some(inner_size) = inner_size {\n            window = window.default_size(inner_size);\n        }\n        if let Some(min_inner_size) = min_inner_size {\n            window = window.min_size(min_inner_size);\n        }\n        if let Some(max_inner_size) = max_inner_size {\n            window = window.max_size(max_inner_size);\n        }\n        if let Some(resizable) = resizable {\n            window = window.resizable(resizable);\n        }\n        window = window.title_bar(decorations.unwrap_or(true) && title_shown.unwrap_or(true));\n        window = window.collapsible(minimize_button.unwrap_or(true));\n\n        window\n    }\n\n    /// Assign a unique id to the Window. Required if the title changes, or is shared with another window.\n    #[inline]\n    pub fn id(mut self, id: Id) -> Self {\n        self.area = self.area.id(id);\n        self\n    }\n\n    /// Call this to add a close-button to the window title bar.\n    ///\n    /// * If `*open == false`, the window will not be visible.\n    /// * If `*open == true`, the window will have a close button.\n    /// * If the close button is pressed, `*open` will be set to `false`.\n    #[inline]\n    pub fn open(mut self, open: &'open mut bool) -> Self {\n        self.open = Some(open);\n        self\n    }\n\n    /// If `false` the window will be grayed out and non-interactive.\n    #[inline]\n    pub fn enabled(mut self, enabled: bool) -> Self {\n        self.area = self.area.enabled(enabled);\n        self\n    }\n\n    /// If false, clicks goes straight through to what is behind us.\n    ///\n    /// Can be used for semi-invisible areas that the user should be able to click through.\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn interactable(mut self, interactable: bool) -> Self {\n        self.area = self.area.interactable(interactable);\n        self\n    }\n\n    /// If `false` the window will be immovable.\n    #[inline]\n    pub fn movable(mut self, movable: bool) -> Self {\n        self.area = self.area.movable(movable);\n        self\n    }\n\n    /// `order(Order::Foreground)` for a Window that should always be on top\n    #[inline]\n    pub fn order(mut self, order: Order) -> Self {\n        self.area = self.area.order(order);\n        self\n    }\n\n    /// If `true`, quickly fade in the `Window` when it first appears.\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn fade_in(mut self, fade_in: bool) -> Self {\n        self.area = self.area.fade_in(fade_in);\n        self\n    }\n\n    /// If `true`, quickly fade out the `Window` when it closes.\n    ///\n    /// This only works if you use [`Self::open`] to close the window.\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn fade_out(mut self, fade_out: bool) -> Self {\n        self.fade_out = fade_out;\n        self\n    }\n\n    /// Usage: `Window::new(…).mutate(|w| w.resize = w.resize.auto_expand_width(true))`\n    // TODO(emilk): I'm not sure this is a good interface for this.\n    #[inline]\n    pub fn mutate(mut self, mutate: impl Fn(&mut Self)) -> Self {\n        mutate(&mut self);\n        self\n    }\n\n    /// Usage: `Window::new(…).resize(|r| r.auto_expand_width(true))`\n    // TODO(emilk): I'm not sure this is a good interface for this.\n    #[inline]\n    pub fn resize(mut self, mutate: impl Fn(Resize) -> Resize) -> Self {\n        self.resize = mutate(self.resize);\n        self\n    }\n\n    /// Change the background color, margins, etc.\n    #[inline]\n    pub fn frame(mut self, frame: Frame) -> Self {\n        self.frame = Some(frame);\n        self\n    }\n\n    /// Set minimum width of the window.\n    #[inline]\n    pub fn min_width(mut self, min_width: f32) -> Self {\n        self.resize = self.resize.min_width(min_width);\n        self\n    }\n\n    /// Set minimum height of the window.\n    #[inline]\n    pub fn min_height(mut self, min_height: f32) -> Self {\n        self.resize = self.resize.min_height(min_height);\n        self\n    }\n\n    /// Set minimum size of the window, equivalent to calling both `min_width` and `min_height`.\n    #[inline]\n    pub fn min_size(mut self, min_size: impl Into<Vec2>) -> Self {\n        self.resize = self.resize.min_size(min_size);\n        self\n    }\n\n    /// Set maximum width of the window.\n    #[inline]\n    pub fn max_width(mut self, max_width: f32) -> Self {\n        self.resize = self.resize.max_width(max_width);\n        self\n    }\n\n    /// Set maximum height of the window.\n    #[inline]\n    pub fn max_height(mut self, max_height: f32) -> Self {\n        self.resize = self.resize.max_height(max_height);\n        self\n    }\n\n    /// Set maximum size of the window, equivalent to calling both `max_width` and `max_height`.\n    #[inline]\n    pub fn max_size(mut self, max_size: impl Into<Vec2>) -> Self {\n        self.resize = self.resize.max_size(max_size);\n        self\n    }\n\n    /// Set current position of the window.\n    /// If the window is movable it is up to you to keep track of where it moved to!\n    #[inline]\n    pub fn current_pos(mut self, current_pos: impl Into<Pos2>) -> Self {\n        self.area = self.area.current_pos(current_pos);\n        self\n    }\n\n    /// Set initial position of the window.\n    #[inline]\n    pub fn default_pos(mut self, default_pos: impl Into<Pos2>) -> Self {\n        self.area = self.area.default_pos(default_pos);\n        self\n    }\n\n    /// Sets the window position and prevents it from being dragged around.\n    #[inline]\n    pub fn fixed_pos(mut self, pos: impl Into<Pos2>) -> Self {\n        self.area = self.area.fixed_pos(pos);\n        self\n    }\n\n    /// Constrains this window to [`Context::screen_rect`].\n    ///\n    /// To change the area to constrain to, use [`Self::constrain_to`].\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn constrain(mut self, constrain: bool) -> Self {\n        self.area = self.area.constrain(constrain);\n        self\n    }\n\n    /// Constrain the movement of the window to the given rectangle.\n    ///\n    /// For instance: `.constrain_to(ctx.screen_rect())`.\n    #[inline]\n    pub fn constrain_to(mut self, constrain_rect: Rect) -> Self {\n        self.area = self.area.constrain_to(constrain_rect);\n        self\n    }\n\n    /// Where the \"root\" of the window is.\n    ///\n    /// For instance, if you set this to [`Align2::RIGHT_TOP`]\n    /// then [`Self::fixed_pos`] will set the position of the right-top\n    /// corner of the window.\n    ///\n    /// Default: [`Align2::LEFT_TOP`].\n    #[inline]\n    pub fn pivot(mut self, pivot: Align2) -> Self {\n        self.area = self.area.pivot(pivot);\n        self\n    }\n\n    /// Set anchor and distance.\n    ///\n    /// An anchor of `Align2::RIGHT_TOP` means \"put the right-top corner of the window\n    /// in the right-top corner of the screen\".\n    ///\n    /// The offset is added to the position, so e.g. an offset of `[-5.0, 5.0]`\n    /// would move the window left and down from the given anchor.\n    ///\n    /// Anchoring also makes the window immovable.\n    ///\n    /// It is an error to set both an anchor and a position.\n    #[inline]\n    pub fn anchor(mut self, align: Align2, offset: impl Into<Vec2>) -> Self {\n        self.area = self.area.anchor(align, offset);\n        self\n    }\n\n    /// Set initial collapsed state of the window\n    #[inline]\n    pub fn default_open(mut self, default_open: bool) -> Self {\n        self.default_open = default_open;\n        self\n    }\n\n    /// Set initial size of the window.\n    #[inline]\n    pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {\n        let default_size: Vec2 = default_size.into();\n        self.resize = self.resize.default_size(default_size);\n        self.area = self.area.default_size(default_size);\n        self\n    }\n\n    /// Set initial width of the window.\n    #[inline]\n    pub fn default_width(mut self, default_width: f32) -> Self {\n        self.resize = self.resize.default_width(default_width);\n        self.area = self.area.default_width(default_width);\n        self\n    }\n\n    /// Set initial height of the window.\n    #[inline]\n    pub fn default_height(mut self, default_height: f32) -> Self {\n        self.resize = self.resize.default_height(default_height);\n        self.area = self.area.default_height(default_height);\n        self\n    }\n\n    /// Sets the window size and prevents it from being resized by dragging its edges.\n    #[inline]\n    pub fn fixed_size(mut self, size: impl Into<Vec2>) -> Self {\n        self.resize = self.resize.fixed_size(size);\n        self\n    }\n\n    /// Set initial position and size of the window.\n    pub fn default_rect(self, rect: Rect) -> Self {\n        self.default_pos(rect.min).default_size(rect.size())\n    }\n\n    /// Sets the window pos and size and prevents it from being moved and resized by dragging its edges.\n    pub fn fixed_rect(self, rect: Rect) -> Self {\n        self.fixed_pos(rect.min).fixed_size(rect.size())\n    }\n\n    /// Can the user resize the window by dragging its edges?\n    ///\n    /// Note that even if you set this to `false` the window may still auto-resize.\n    ///\n    /// You can set the window to only be resizable in one direction by using\n    /// e.g. `[true, false]` as the argument,\n    /// making the window only resizable in the x-direction.\n    ///\n    /// Default is `true`.\n    #[inline]\n    pub fn resizable(mut self, resizable: impl Into<Vec2b>) -> Self {\n        let resizable = resizable.into();\n        self.resize = self.resize.resizable(resizable);\n        self\n    }\n\n    /// Can the window be collapsed by clicking on its title?\n    #[inline]\n    pub fn collapsible(mut self, collapsible: bool) -> Self {\n        self.collapsible = collapsible;\n        self\n    }\n\n    /// Show title bar on top of the window?\n    /// If `false`, the window will not be collapsible nor have a close-button.\n    #[inline]\n    pub fn title_bar(mut self, title_bar: bool) -> Self {\n        self.with_title_bar = title_bar;\n        self\n    }\n\n    /// Not resizable, just takes the size of its contents.\n    /// Also disabled scrolling.\n    /// Text will not wrap, but will instead make your window width expand.\n    #[inline]\n    pub fn auto_sized(mut self) -> Self {\n        self.resize = self.resize.auto_sized();\n        self.scroll = ScrollArea::neither();\n        self\n    }\n\n    /// Enable/disable horizontal/vertical scrolling. `false` by default.\n    ///\n    /// You can pass in `false`, `true`, `[false, true]` etc.\n    #[inline]\n    pub fn scroll(mut self, scroll: impl Into<Vec2b>) -> Self {\n        self.scroll = self.scroll.scroll(scroll);\n        self\n    }\n\n    /// Enable/disable horizontal scrolling. `false` by default.\n    #[inline]\n    pub fn hscroll(mut self, hscroll: bool) -> Self {\n        self.scroll = self.scroll.hscroll(hscroll);\n        self\n    }\n\n    /// Enable/disable vertical scrolling. `false` by default.\n    #[inline]\n    pub fn vscroll(mut self, vscroll: bool) -> Self {\n        self.scroll = self.scroll.vscroll(vscroll);\n        self\n    }\n\n    /// Enable/disable scrolling on the window by dragging with the pointer. `true` by default.\n    ///\n    /// See [`ScrollArea::drag_to_scroll`] for more.\n    #[inline]\n    pub fn drag_to_scroll(mut self, drag_to_scroll: bool) -> Self {\n        self.scroll = self.scroll.scroll_source(ScrollSource {\n            drag: drag_to_scroll,\n            ..Default::default()\n        });\n        self\n    }\n\n    /// Sets the [`ScrollBarVisibility`] of the window.\n    #[inline]\n    pub fn scroll_bar_visibility(mut self, visibility: ScrollBarVisibility) -> Self {\n        self.scroll = self.scroll.scroll_bar_visibility(visibility);\n        self\n    }\n}\n\nimpl Window<'_> {\n    /// Returns `None` if the window is not open (if [`Window::open`] was called with `&mut false`).\n    /// Returns `Some(InnerResponse { inner: None })` if the window is collapsed.\n    #[inline]\n    pub fn show<R>(\n        self,\n        ctx: &Context,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> Option<InnerResponse<Option<R>>> {\n        self.show_dyn(ctx, Box::new(add_contents))\n    }\n\n    fn show_dyn<'c, R>(\n        self,\n        ctx: &Context,\n        add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> Option<InnerResponse<Option<R>>> {\n        let Window {\n            title,\n            mut open,\n            area,\n            frame,\n            resize,\n            scroll,\n            collapsible,\n            default_open,\n            with_title_bar,\n            fade_out,\n        } = self;\n\n        let style = ctx.global_style();\n\n        let header_color =\n            frame.map_or_else(|| style.visuals.widgets.open.weak_bg_fill, |f| f.fill);\n        let mut window_frame = frame.unwrap_or_else(|| Frame::window(&style));\n\n        let is_explicitly_closed = matches!(open, Some(false));\n        let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible());\n        let opacity = ctx.animate_bool_with_easing(\n            area.id.with(\"fade-out\"),\n            is_open,\n            emath::easing::cubic_out,\n        );\n        if opacity <= 0.0 {\n            return None;\n        }\n\n        let area_id = area.id;\n        let area_layer_id = area.layer();\n        let resize_id = area_id.with(\"resize\");\n        let mut collapsing =\n            CollapsingState::load_with_default_open(ctx, area_id.with(\"collapsing\"), default_open);\n\n        let is_collapsed = with_title_bar && !collapsing.is_open();\n        let possible = PossibleInteractions::new(&area, &resize, is_collapsed);\n\n        let resize = resize.resizable(false); // We resize it manually\n        let mut resize = resize.id(resize_id);\n\n        let on_top = Some(area_layer_id) == ctx.top_layer_id();\n        let mut area = area.begin(ctx);\n\n        area.with_widget_info(|| WidgetInfo::labeled(WidgetType::Window, true, title.text()));\n\n        // Calculate roughly how much larger the full window inner size is compared to the content rect\n        let (title_bar_height_with_margin, title_content_spacing) = if with_title_bar {\n            let title_bar_inner_height = ctx\n                .fonts_mut(|fonts| title.font_height(fonts, &style))\n                .at_least(style.spacing.interact_size.y);\n            let title_bar_inner_height = title_bar_inner_height + window_frame.inner_margin.sum().y;\n            let half_height = (title_bar_inner_height / 2.0).round() as _;\n            window_frame.corner_radius.ne = window_frame.corner_radius.ne.clamp(0, half_height);\n            window_frame.corner_radius.nw = window_frame.corner_radius.nw.clamp(0, half_height);\n\n            let title_content_spacing = if is_collapsed {\n                0.0\n            } else {\n                window_frame.stroke.width\n            };\n            (title_bar_inner_height, title_content_spacing)\n        } else {\n            (0.0, 0.0)\n        };\n\n        {\n            // Prevent window from becoming larger than the constrain rect.\n            let constrain_rect = area.constrain_rect();\n            let max_width = constrain_rect.width();\n            let max_height =\n                constrain_rect.height() - title_bar_height_with_margin - title_content_spacing;\n            resize.max_size.x = resize.max_size.x.min(max_width);\n            resize.max_size.y = resize.max_size.y.min(max_height);\n        }\n\n        // First check for resize to avoid frame delay:\n        let last_frame_outer_rect = area.state().rect();\n        let resize_interaction = do_resize_interaction(\n            ctx,\n            possible,\n            area.id(),\n            area_layer_id,\n            last_frame_outer_rect,\n            window_frame,\n        );\n\n        {\n            let margins = window_frame.total_margin().sum()\n                + vec2(0.0, title_bar_height_with_margin + title_content_spacing);\n\n            resize_response(\n                resize_interaction,\n                ctx,\n                margins,\n                area_layer_id,\n                &mut area,\n                resize_id,\n            );\n        }\n\n        let mut area_content_ui = area.content_ui(ctx);\n        if is_open {\n            // `Area` already takes care of fade-in animations,\n            // so we only need to handle fade-out animations here.\n        } else if fade_out {\n            area_content_ui.multiply_opacity(opacity);\n        }\n\n        let content_inner = {\n            // BEGIN FRAME --------------------------------\n            let mut frame = window_frame.begin(&mut area_content_ui);\n\n            let show_close_button = open.is_some();\n\n            let where_to_put_header_background = &area_content_ui.painter().add(Shape::Noop);\n\n            let title_bar = if with_title_bar {\n                let title_bar = TitleBar::new(\n                    &frame.content_ui,\n                    title,\n                    show_close_button,\n                    collapsible,\n                    window_frame,\n                    title_bar_height_with_margin,\n                );\n                resize.min_size.x = resize.min_size.x.at_least(title_bar.inner_rect.width()); // Prevent making window smaller than title bar width\n\n                frame.content_ui.set_min_size(title_bar.inner_rect.size());\n\n                // Skip the title bar (and separator):\n                if is_collapsed {\n                    frame.content_ui.add_space(title_bar.inner_rect.height());\n                } else {\n                    frame.content_ui.add_space(\n                        title_bar.inner_rect.height()\n                            + title_content_spacing\n                            + window_frame.inner_margin.sum().y,\n                    );\n                }\n\n                Some(title_bar)\n            } else {\n                None\n            };\n\n            let (content_inner, content_response) = collapsing\n                .show_body_unindented(&mut frame.content_ui, |ui| {\n                    resize.show(ui, |ui| {\n                        if scroll.is_any_scroll_enabled() {\n                            scroll.show(ui, add_contents).inner\n                        } else {\n                            add_contents(ui)\n                        }\n                    })\n                })\n                .map_or((None, None), |ir| (Some(ir.inner), Some(ir.response)));\n\n            let outer_rect = frame.end(&mut area_content_ui).rect;\n\n            // Do resize interaction _again_, to move their widget rectangles on TOP of the rest of the window.\n            let resize_interaction = do_resize_interaction(\n                ctx,\n                possible,\n                area.id(),\n                area_layer_id,\n                last_frame_outer_rect,\n                window_frame,\n            );\n\n            paint_resize_corner(\n                &area_content_ui,\n                &possible,\n                outer_rect,\n                &window_frame,\n                resize_interaction,\n            );\n\n            // END FRAME --------------------------------\n\n            if let Some(mut title_bar) = title_bar {\n                title_bar.inner_rect = outer_rect.shrink(window_frame.stroke.width);\n                title_bar.inner_rect.max.y =\n                    title_bar.inner_rect.min.y + title_bar_height_with_margin;\n\n                if on_top && area_content_ui.visuals().window_highlight_topmost {\n                    let mut round =\n                        window_frame.corner_radius - window_frame.stroke.width.round() as u8;\n\n                    if !is_collapsed {\n                        round.se = 0;\n                        round.sw = 0;\n                    }\n\n                    area_content_ui.painter().set(\n                        *where_to_put_header_background,\n                        RectShape::filled(title_bar.inner_rect, round, header_color),\n                    );\n                }\n\n                if false {\n                    ctx.debug_painter().debug_rect(\n                        title_bar.inner_rect,\n                        Color32::LIGHT_BLUE,\n                        \"title_bar.rect\",\n                    );\n                }\n\n                title_bar.ui(\n                    &mut area_content_ui,\n                    &content_response,\n                    open.as_deref_mut(),\n                    &mut collapsing,\n                    collapsible,\n                );\n            }\n\n            collapsing.store(ctx);\n\n            paint_frame_interaction(&area_content_ui, outer_rect, resize_interaction);\n\n            content_inner\n        };\n\n        let full_response = area.end(ctx, area_content_ui);\n\n        if full_response.should_close()\n            && let Some(open) = open\n        {\n            *open = false;\n        }\n\n        let inner_response = InnerResponse {\n            inner: content_inner,\n            response: full_response,\n        };\n        Some(inner_response)\n    }\n}\n\nfn paint_resize_corner(\n    ui: &Ui,\n    possible: &PossibleInteractions,\n    outer_rect: Rect,\n    window_frame: &Frame,\n    i: ResizeInteraction,\n) {\n    let cr = window_frame.corner_radius;\n\n    let (corner, radius, corner_response) = if possible.resize_right && possible.resize_bottom {\n        (Align2::RIGHT_BOTTOM, cr.se, i.right & i.bottom)\n    } else if possible.resize_left && possible.resize_bottom {\n        (Align2::LEFT_BOTTOM, cr.sw, i.left & i.bottom)\n    } else if possible.resize_left && possible.resize_top {\n        (Align2::LEFT_TOP, cr.nw, i.left & i.top)\n    } else if possible.resize_right && possible.resize_top {\n        (Align2::RIGHT_TOP, cr.ne, i.right & i.top)\n    } else {\n        // We're not in two directions, but it is still nice to tell the user\n        // we're resizable by painting the resize corner in the expected place\n        // (i.e. for windows only resizable in one direction):\n        if possible.resize_right || possible.resize_bottom {\n            (Align2::RIGHT_BOTTOM, cr.se, i.right & i.bottom)\n        } else if possible.resize_left || possible.resize_bottom {\n            (Align2::LEFT_BOTTOM, cr.sw, i.left & i.bottom)\n        } else if possible.resize_left || possible.resize_top {\n            (Align2::LEFT_TOP, cr.nw, i.left & i.top)\n        } else if possible.resize_right || possible.resize_top {\n            (Align2::RIGHT_TOP, cr.ne, i.right & i.top)\n        } else {\n            return;\n        }\n    };\n\n    // Adjust the corner offset to accommodate for window rounding\n    let radius = radius as f32;\n    let offset =\n        ((2.0_f32.sqrt() * (1.0 + radius) - radius) * 45.0_f32.to_radians().cos()).max(2.0);\n\n    let stroke = if corner_response.drag {\n        ui.visuals().widgets.active.fg_stroke\n    } else if corner_response.hover {\n        ui.visuals().widgets.hovered.fg_stroke\n    } else {\n        window_frame.stroke\n    };\n\n    let fill_rect = outer_rect.shrink(window_frame.stroke.width);\n    let corner_size = Vec2::splat(ui.visuals().resize_corner_size);\n    let corner_rect = corner.align_size_within_rect(corner_size, fill_rect);\n    let corner_rect = corner_rect.translate(-offset * corner.to_sign()); // move away from corner\n    crate::resize::paint_resize_corner_with_style(ui, &corner_rect, stroke.color, corner);\n}\n\n// ----------------------------------------------------------------------------\n\n/// Which sides can be resized?\n#[derive(Clone, Copy, Debug)]\nstruct PossibleInteractions {\n    // Which sides can we drag to resize or move?\n    resize_left: bool,\n    resize_right: bool,\n    resize_top: bool,\n    resize_bottom: bool,\n}\n\nimpl PossibleInteractions {\n    fn new(area: &Area, resize: &Resize, is_collapsed: bool) -> Self {\n        let movable = area.is_enabled() && area.is_movable();\n        let resizable = resize\n            .is_resizable()\n            .and(area.is_enabled() && !is_collapsed);\n        let pivot = area.get_pivot();\n        Self {\n            resize_left: resizable.x && (movable || pivot.x() != Align::LEFT),\n            resize_right: resizable.x && (movable || pivot.x() != Align::RIGHT),\n            resize_top: resizable.y && (movable || pivot.y() != Align::TOP),\n            resize_bottom: resizable.y && (movable || pivot.y() != Align::BOTTOM),\n        }\n    }\n\n    pub fn resizable(&self) -> bool {\n        self.resize_left || self.resize_right || self.resize_top || self.resize_bottom\n    }\n}\n\n/// Resizing the window edges.\n#[derive(Clone, Copy, Debug)]\nstruct ResizeInteraction {\n    /// Outer rect (outside the stroke)\n    outer_rect: Rect,\n\n    window_frame: Frame,\n\n    left: SideResponse,\n    right: SideResponse,\n    top: SideResponse,\n    bottom: SideResponse,\n}\n\n/// A miniature version of `Response`, for each side of the window.\n#[derive(Clone, Copy, Debug, Default)]\nstruct SideResponse {\n    hover: bool,\n    drag: bool,\n}\n\nimpl SideResponse {\n    pub fn any(&self) -> bool {\n        self.hover || self.drag\n    }\n}\n\nimpl std::ops::BitAnd for SideResponse {\n    type Output = Self;\n\n    fn bitand(self, rhs: Self) -> Self::Output {\n        Self {\n            hover: self.hover && rhs.hover,\n            drag: self.drag && rhs.drag,\n        }\n    }\n}\n\nimpl std::ops::BitOrAssign for SideResponse {\n    fn bitor_assign(&mut self, rhs: Self) {\n        *self = Self {\n            hover: self.hover || rhs.hover,\n            drag: self.drag || rhs.drag,\n        };\n    }\n}\n\nimpl ResizeInteraction {\n    pub fn set_cursor(&self, ctx: &Context) {\n        let left = self.left.any();\n        let right = self.right.any();\n        let top = self.top.any();\n        let bottom = self.bottom.any();\n\n        // TODO(emilk): use one-sided cursors for when we reached the min/max size.\n        if (left && top) || (right && bottom) {\n            ctx.set_cursor_icon(CursorIcon::ResizeNwSe);\n        } else if (right && top) || (left && bottom) {\n            ctx.set_cursor_icon(CursorIcon::ResizeNeSw);\n        } else if left || right {\n            ctx.set_cursor_icon(CursorIcon::ResizeHorizontal);\n        } else if bottom || top {\n            ctx.set_cursor_icon(CursorIcon::ResizeVertical);\n        }\n    }\n\n    pub fn any_hovered(&self) -> bool {\n        self.left.hover || self.right.hover || self.top.hover || self.bottom.hover\n    }\n\n    pub fn any_dragged(&self) -> bool {\n        self.left.drag || self.right.drag || self.top.drag || self.bottom.drag\n    }\n}\n\nfn resize_response(\n    resize_interaction: ResizeInteraction,\n    ctx: &Context,\n    margins: Vec2,\n    area_layer_id: LayerId,\n    area: &mut area::Prepared,\n    resize_id: Id,\n) {\n    let Some(mut new_rect) = move_and_resize_window(ctx, resize_id, &resize_interaction) else {\n        return;\n    };\n\n    if area.constrain() {\n        new_rect = Context::constrain_window_rect_to_area(new_rect, area.constrain_rect());\n    }\n\n    // TODO(emilk): add this to a Window state instead as a command \"move here next frame\"\n    area.state_mut().set_left_top_pos(new_rect.left_top());\n\n    if resize_interaction.any_dragged()\n        && let Some(mut state) = resize::State::load(ctx, resize_id)\n    {\n        state.requested_size = Some(new_rect.size() - margins);\n        state.store(ctx, resize_id);\n    }\n\n    ctx.memory_mut(|mem| mem.areas_mut().move_to_top(area_layer_id));\n}\n\n/// Acts on outer rect (outside the stroke)\nfn move_and_resize_window(ctx: &Context, id: Id, interaction: &ResizeInteraction) -> Option<Rect> {\n    // Used to prevent drift\n    let rect_at_start_of_drag_id = id.with(\"window_rect_at_drag_start\");\n\n    if !interaction.any_dragged() {\n        ctx.data_mut(|data| {\n            data.remove::<Rect>(rect_at_start_of_drag_id);\n        });\n        return None;\n    }\n\n    let total_drag_delta = ctx.input(|i| i.pointer.total_drag_delta())?;\n\n    let rect_at_start_of_drag = ctx.data_mut(|data| {\n        *data.get_temp_mut_or::<Rect>(rect_at_start_of_drag_id, interaction.outer_rect)\n    });\n\n    let mut rect = rect_at_start_of_drag; // prevent drift\n\n    // Put the rect in the center of the stroke:\n    rect = rect.shrink(interaction.window_frame.stroke.width / 2.0);\n\n    if interaction.left.drag {\n        rect.min.x += total_drag_delta.x;\n    } else if interaction.right.drag {\n        rect.max.x += total_drag_delta.x;\n    }\n\n    if interaction.top.drag {\n        rect.min.y += total_drag_delta.y;\n    } else if interaction.bottom.drag {\n        rect.max.y += total_drag_delta.y;\n    }\n\n    // Return to having the rect outside the stroke:\n    rect = rect.expand(interaction.window_frame.stroke.width / 2.0);\n\n    Some(rect.round_ui())\n}\n\nfn do_resize_interaction(\n    ctx: &Context,\n    possible: PossibleInteractions,\n    accessibility_parent: Id,\n    layer_id: LayerId,\n    outer_rect: Rect,\n    window_frame: Frame,\n) -> ResizeInteraction {\n    if !possible.resizable() {\n        return ResizeInteraction {\n            outer_rect,\n            window_frame,\n            left: Default::default(),\n            right: Default::default(),\n            top: Default::default(),\n            bottom: Default::default(),\n        };\n    }\n\n    // The rect that is in the middle of the stroke:\n    let rect = outer_rect.shrink(window_frame.stroke.width / 2.0);\n\n    let side_response = |rect, id| {\n        ctx.register_accesskit_parent(id, accessibility_parent);\n        let response = ctx.create_widget(\n            WidgetRect {\n                layer_id,\n                id,\n                rect,\n                interact_rect: rect,\n                sense: Sense::DRAG, // Don't use Sense::drag() since we don't want these to be focusable\n                enabled: true,\n            },\n            true,\n            InteractOptions {\n                // We call this multiple times.\n                // First to read the result (to avoid frame delay)\n                // and the second time to move it to the top, above the window contents.\n                move_to_top: true,\n            },\n        );\n\n        response.widget_info(|| WidgetInfo::new(crate::WidgetType::ResizeHandle));\n\n        SideResponse {\n            hover: response.hovered(),\n            drag: response.dragged(),\n        }\n    };\n\n    let id = Id::new(layer_id).with(\"edge_drag\");\n\n    let style = ctx.global_style();\n\n    let side_grab_radius = style.interaction.resize_grab_radius_side;\n    let corner_grab_radius = style.interaction.resize_grab_radius_corner;\n\n    let vetrtical_rect = |a: Pos2, b: Pos2| {\n        Rect::from_min_max(a, b).expand2(vec2(side_grab_radius, -corner_grab_radius))\n    };\n    let horizontal_rect = |a: Pos2, b: Pos2| {\n        Rect::from_min_max(a, b).expand2(vec2(-corner_grab_radius, side_grab_radius))\n    };\n    let corner_rect =\n        |center: Pos2| Rect::from_center_size(center, Vec2::splat(2.0 * corner_grab_radius));\n\n    // What are we dragging/hovering?\n    let [mut left, mut right, mut top, mut bottom] = [SideResponse::default(); 4];\n\n    // ----------------------------------------\n    // Check sides first, so that corners are on top, covering the sides (i.e. corners have priority)\n\n    if possible.resize_right {\n        let response = side_response(\n            vetrtical_rect(rect.right_top(), rect.right_bottom()),\n            id.with(\"right\"),\n        );\n        right |= response;\n    }\n    if possible.resize_left {\n        let response = side_response(\n            vetrtical_rect(rect.left_top(), rect.left_bottom()),\n            id.with(\"left\"),\n        );\n        left |= response;\n    }\n    if possible.resize_bottom {\n        let response = side_response(\n            horizontal_rect(rect.left_bottom(), rect.right_bottom()),\n            id.with(\"bottom\"),\n        );\n        bottom |= response;\n    }\n    if possible.resize_top {\n        let response = side_response(\n            horizontal_rect(rect.left_top(), rect.right_top()),\n            id.with(\"top\"),\n        );\n        top |= response;\n    }\n\n    // ----------------------------------------\n    // Now check corners.\n    // We check any corner that has either side resizable,\n    // because we shrink the side resize handled by the corner width.\n    // Also, even if we can only change the width (or height) of a window,\n    // we show one of the corners as a grab-handle, so it makes sense that\n    // the whole corner is grabbable:\n\n    if possible.resize_right || possible.resize_bottom {\n        let response = side_response(corner_rect(rect.right_bottom()), id.with(\"right_bottom\"));\n        if possible.resize_right {\n            right |= response;\n        }\n        if possible.resize_bottom {\n            bottom |= response;\n        }\n    }\n\n    if possible.resize_right || possible.resize_top {\n        let response = side_response(corner_rect(rect.right_top()), id.with(\"right_top\"));\n        if possible.resize_right {\n            right |= response;\n        }\n        if possible.resize_top {\n            top |= response;\n        }\n    }\n\n    if possible.resize_left || possible.resize_bottom {\n        let response = side_response(corner_rect(rect.left_bottom()), id.with(\"left_bottom\"));\n        if possible.resize_left {\n            left |= response;\n        }\n        if possible.resize_bottom {\n            bottom |= response;\n        }\n    }\n\n    if possible.resize_left || possible.resize_top {\n        let response = side_response(corner_rect(rect.left_top()), id.with(\"left_top\"));\n        if possible.resize_left {\n            left |= response;\n        }\n        if possible.resize_top {\n            top |= response;\n        }\n    }\n\n    let interaction = ResizeInteraction {\n        outer_rect,\n        window_frame,\n        left,\n        right,\n        top,\n        bottom,\n    };\n    interaction.set_cursor(ctx);\n    interaction\n}\n\n/// Fill in parts of the window frame when we resize by dragging that part\nfn paint_frame_interaction(ui: &Ui, rect: Rect, interaction: ResizeInteraction) {\n    use epaint::tessellator::path::add_circle_quadrant;\n\n    let visuals = if interaction.any_dragged() {\n        ui.style().visuals.widgets.active\n    } else if interaction.any_hovered() {\n        ui.style().visuals.widgets.hovered\n    } else {\n        return;\n    };\n\n    let [left, right, top, bottom]: [bool; 4];\n\n    if interaction.any_dragged() {\n        left = interaction.left.drag;\n        right = interaction.right.drag;\n        top = interaction.top.drag;\n        bottom = interaction.bottom.drag;\n    } else {\n        left = interaction.left.hover;\n        right = interaction.right.hover;\n        top = interaction.top.hover;\n        bottom = interaction.bottom.hover;\n    }\n\n    let cr = CornerRadiusF32::from(ui.visuals().window_corner_radius);\n\n    // Put the rect in the center of the fixed window stroke:\n    let rect = rect.shrink(interaction.window_frame.stroke.width / 2.0);\n\n    // Make sure the inner part of the stroke is at a pixel boundary:\n    let stroke = visuals.bg_stroke;\n    let half_stroke = stroke.width / 2.0;\n    let rect = rect\n        .shrink(half_stroke)\n        .round_to_pixels(ui.pixels_per_point())\n        .expand(half_stroke);\n\n    let Rect { min, max } = rect;\n\n    let mut points = Vec::new();\n\n    if right && !bottom && !top {\n        points.push(pos2(max.x, min.y + cr.ne));\n        points.push(pos2(max.x, max.y - cr.se));\n    }\n    if right && bottom {\n        points.push(pos2(max.x, min.y + cr.ne));\n        points.push(pos2(max.x, max.y - cr.se));\n        add_circle_quadrant(&mut points, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0);\n    }\n    if bottom {\n        points.push(pos2(max.x - cr.se, max.y));\n        points.push(pos2(min.x + cr.sw, max.y));\n    }\n    if left && bottom {\n        add_circle_quadrant(&mut points, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0);\n    }\n    if left {\n        points.push(pos2(min.x, max.y - cr.sw));\n        points.push(pos2(min.x, min.y + cr.nw));\n    }\n    if left && top {\n        add_circle_quadrant(&mut points, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0);\n    }\n    if top {\n        points.push(pos2(min.x + cr.nw, min.y));\n        points.push(pos2(max.x - cr.ne, min.y));\n    }\n    if right && top {\n        add_circle_quadrant(&mut points, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0);\n        points.push(pos2(max.x, min.y + cr.ne));\n        points.push(pos2(max.x, max.y - cr.se));\n    }\n\n    ui.painter().add(Shape::line(points, stroke));\n}\n\n// ----------------------------------------------------------------------------\n\nstruct TitleBar {\n    window_frame: Frame,\n\n    /// Prepared text in the title\n    title_galley: Arc<Galley>,\n\n    /// Size of the title bar in an expanded state. This size become known only\n    /// after expanding window and painting its content.\n    ///\n    /// Does not include the stroke, nor the separator line between the title bar and the window contents.\n    inner_rect: Rect,\n}\n\nimpl TitleBar {\n    fn new(\n        ui: &Ui,\n        title: WidgetText,\n        show_close_button: bool,\n        collapsible: bool,\n        window_frame: Frame,\n        title_bar_height_with_margin: f32,\n    ) -> Self {\n        if false {\n            ui.debug_painter()\n                .debug_rect(ui.min_rect(), Color32::GREEN, \"outer_min_rect\");\n        }\n\n        let inner_height = title_bar_height_with_margin - window_frame.inner_margin.sum().y;\n\n        let item_spacing = ui.spacing().item_spacing;\n        let button_size = Vec2::splat(ui.spacing().icon_width.at_most(inner_height));\n\n        let left_pad = ((inner_height - button_size.y) / 2.0).round_ui(); // calculated so that the icon is on the diagonal (if window padding is symmetrical)\n\n        let title_galley = title.into_galley(\n            ui,\n            Some(crate::TextWrapMode::Extend),\n            f32::INFINITY,\n            TextStyle::Heading,\n        );\n\n        let minimum_width = if collapsible || show_close_button {\n            // If at least one button is shown we make room for both buttons (since title should be centered):\n            2.0 * (left_pad + button_size.x + item_spacing.x) + title_galley.size().x\n        } else {\n            left_pad + title_galley.size().x + left_pad\n        };\n        let min_inner_size = vec2(minimum_width, inner_height);\n        let min_rect = Rect::from_min_size(ui.min_rect().min, min_inner_size);\n\n        if false {\n            ui.debug_painter()\n                .debug_rect(min_rect, Color32::LIGHT_BLUE, \"min_rect\");\n        }\n\n        Self {\n            window_frame,\n            title_galley,\n            inner_rect: min_rect, // First estimate - will be refined later\n        }\n    }\n\n    /// Finishes painting of the title bar when the window content size already known.\n    ///\n    /// # Parameters\n    ///\n    /// - `ui`:\n    /// - `outer_rect`:\n    /// - `content_response`: if `None`, window is collapsed at this frame, otherwise contains\n    ///   a result of rendering the window content\n    /// - `open`: if `None`, no \"Close\" button will be rendered, otherwise renders and processes\n    ///   the \"Close\" button and writes a `false` if window was closed\n    /// - `collapsing`: holds the current expanding state. Can be changed by double click on the\n    ///   title if `collapsible` is `true`\n    /// - `collapsible`: if `true`, double click on the title bar will be handled for a change\n    ///   of `collapsing` state\n    fn ui(\n        self,\n        ui: &mut Ui,\n        content_response: &Option<Response>,\n        open: Option<&mut bool>,\n        collapsing: &mut CollapsingState,\n        collapsible: bool,\n    ) {\n        let window_frame = self.window_frame;\n        let title_inner_rect = self.inner_rect;\n\n        if false {\n            ui.debug_painter()\n                .debug_rect(self.inner_rect, Color32::RED, \"TitleBar\");\n        }\n\n        if collapsible {\n            // Show collapse-button:\n            let button_center = Align2::LEFT_CENTER\n                .align_size_within_rect(Vec2::splat(self.inner_rect.height()), self.inner_rect)\n                .center();\n            let button_size = Vec2::splat(ui.spacing().icon_width);\n            let button_rect = Rect::from_center_size(button_center, button_size);\n            let button_rect = button_rect.round_ui();\n\n            ui.scope_builder(UiBuilder::new().max_rect(button_rect), |ui| {\n                collapsing.show_default_button_with_size(ui, button_size);\n            });\n        }\n\n        if let Some(open) = open {\n            // Add close button now that we know our full width:\n            if self.close_button_ui(ui).clicked() {\n                *open = false;\n            }\n        }\n\n        let text_pos =\n            emath::align::center_size_in_rect(self.title_galley.size(), title_inner_rect)\n                .left_top();\n        let text_pos = text_pos - self.title_galley.rect.min.to_vec2();\n        ui.painter().galley(\n            text_pos,\n            Arc::clone(&self.title_galley),\n            ui.visuals().text_color(),\n        );\n\n        if let Some(content_response) = &content_response {\n            // Paint separator between title and content:\n            let content_rect = content_response.rect;\n            if false {\n                ui.debug_painter()\n                    .debug_rect(content_rect, Color32::RED, \"content_rect\");\n            }\n            let y = title_inner_rect.bottom() + window_frame.stroke.width / 2.0;\n\n            // To verify the sanity of this, use a very wide window stroke\n            ui.painter()\n                .hline(title_inner_rect.x_range(), y, window_frame.stroke);\n        }\n\n        // Don't cover the close- and collapse buttons:\n        let double_click_rect = title_inner_rect.shrink2(vec2(32.0, 0.0));\n\n        if false {\n            ui.debug_painter()\n                .debug_rect(double_click_rect, Color32::GREEN, \"double_click_rect\");\n        }\n\n        let id = ui.unique_id().with(\"__window_title_bar\");\n\n        if ui\n            .interact(double_click_rect, id, Sense::CLICK)\n            .double_clicked()\n            && collapsible\n        {\n            collapsing.toggle(ui);\n        }\n    }\n\n    /// Paints the \"Close\" button at the right side of the title bar\n    /// and processes clicks on it.\n    ///\n    /// The button is square and its size is determined by the\n    /// [`crate::style::Spacing::icon_width`] setting.\n    fn close_button_ui(&self, ui: &mut Ui) -> Response {\n        let button_center = Align2::RIGHT_CENTER\n            .align_size_within_rect(Vec2::splat(self.inner_rect.height()), self.inner_rect)\n            .center();\n        let button_size = Vec2::splat(ui.spacing().icon_width);\n        let button_rect = Rect::from_center_size(button_center, button_size);\n        let button_rect = button_rect.round_to_pixels(ui.pixels_per_point());\n        close_button(ui, button_rect)\n    }\n}\n\n/// Paints the \"Close\" button of the window and processes clicks on it.\n///\n/// The close button is just an `X` symbol painted by a current stroke\n/// for foreground elements (such as a label text).\n///\n/// # Parameters\n/// - `ui`:\n/// - `rect`: The rectangular area to fit the button in\n///\n/// Returns the result of a click on a button if it was pressed\nfn close_button(ui: &mut Ui, rect: Rect) -> Response {\n    let close_id = ui.auto_id_with(\"window_close_button\");\n    let response = ui.interact(rect, close_id, Sense::click());\n    response\n        .widget_info(|| WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), \"Close window\"));\n\n    ui.expand_to_include_rect(response.rect);\n\n    let visuals = ui.style().interact(&response);\n    let rect = rect.shrink(2.0).expand(visuals.expansion);\n    let stroke = visuals.fg_stroke;\n    ui.painter() // paints \\\n        .line_segment([rect.left_top(), rect.right_bottom()], stroke);\n    ui.painter() // paints /\n        .line_segment([rect.right_top(), rect.left_bottom()], stroke);\n    response\n}\n"
  },
  {
    "path": "crates/egui/src/context.rs",
    "content": "#![warn(missing_docs)] // Let's keep `Context` well-documented.\n\nuse std::{borrow::Cow, cell::RefCell, panic::Location, sync::Arc, time::Duration};\n\nuse emath::GuiRounding as _;\nuse epaint::{\n    ClippedPrimitive, ClippedShape, Color32, ImageData, Pos2, Rect, StrokeKind,\n    TessellationOptions, TextureId, Vec2,\n    emath::{self, TSTransform},\n    mutex::RwLock,\n    stats::PaintStats,\n    tessellator,\n    text::{FontInsert, FontPriority, Fonts, FontsView},\n    vec2,\n};\n\nuse crate::{\n    Align2, CursorIcon, DeferredViewportUiCallback, FontDefinitions, Grid, Id, ImmediateViewport,\n    ImmediateViewportRendererCallback, Key, KeyboardShortcut, Label, LayerId, Memory,\n    ModifierNames, Modifiers, NumExt as _, Order, Painter, RawInput, Response, RichText,\n    SafeAreaInsets, ScrollArea, Sense, Style, TextStyle, TextureHandle, TextureOptions, Ui,\n    UiBuilder, ViewportBuilder, ViewportCommand, ViewportId, ViewportIdMap, ViewportIdPair,\n    ViewportIdSet, ViewportOutput, Visuals, Widget as _, WidgetRect, WidgetText,\n    animation_manager::AnimationManager,\n    containers::{self, area::AreaState},\n    data::output::PlatformOutput,\n    epaint,\n    hit_test::WidgetHits,\n    input_state::{InputState, MultiTouchInfo, PointerEvent, SurrenderFocusOn},\n    interaction::InteractionSnapshot,\n    layers::GraphicLayers,\n    load::{self, Bytes, Loaders, SizedTexture},\n    memory::{Options, Theme},\n    os::OperatingSystem,\n    output::FullOutput,\n    pass_state::PassState,\n    plugin::{self, TypedPluginHandle},\n    resize, response, scroll_area,\n    util::IdTypeMap,\n    viewport::ViewportClass,\n};\n\nuse crate::IdMap;\n\n/// Information given to the backend about when it is time to repaint the ui.\n///\n/// This is given in the callback set by [`Context::set_request_repaint_callback`].\n#[derive(Clone, Copy, Debug)]\npub struct RequestRepaintInfo {\n    /// This is used to specify what viewport that should repaint.\n    pub viewport_id: ViewportId,\n\n    /// Repaint after this duration. If zero, repaint as soon as possible.\n    pub delay: Duration,\n\n    /// The number of fully completed passes, of the entire lifetime of the [`Context`].\n    ///\n    /// This can be compared to [`Context::cumulative_pass_nr`] to see if we we still\n    /// need another repaint (ui pass / frame), or if one has already happened.\n    pub current_cumulative_pass_nr: u64,\n}\n\n// ----------------------------------------------------------------------------\n\nthread_local! {\n    static IMMEDIATE_VIEWPORT_RENDERER: RefCell<Option<Box<ImmediateViewportRendererCallback>>> = Default::default();\n}\n\n// ----------------------------------------------------------------------------\n\nstruct WrappedTextureManager(Arc<RwLock<epaint::TextureManager>>);\n\nimpl Default for WrappedTextureManager {\n    fn default() -> Self {\n        let mut tex_mngr = epaint::textures::TextureManager::default();\n\n        // Will be filled in later\n        let font_id = tex_mngr.alloc(\n            \"egui_font_texture\".into(),\n            epaint::ColorImage::filled([0, 0], Color32::TRANSPARENT).into(),\n            Default::default(),\n        );\n        assert_eq!(\n            font_id,\n            TextureId::default(),\n            \"font id should be equal to TextureId::default(), but was {font_id:?}\",\n        );\n\n        Self(Arc::new(RwLock::new(tex_mngr)))\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Repaint-logic\nimpl ContextImpl {\n    /// This is where we update the repaint logic.\n    fn begin_pass_repaint_logic(&mut self, viewport_id: ViewportId) {\n        let viewport = self.viewports.entry(viewport_id).or_default();\n\n        std::mem::swap(\n            &mut viewport.repaint.prev_causes,\n            &mut viewport.repaint.causes,\n        );\n        viewport.repaint.causes.clear();\n\n        viewport.repaint.prev_pass_paint_delay = viewport.repaint.repaint_delay;\n\n        if viewport.repaint.outstanding == 0 {\n            // We are repainting now, so we can wait a while for the next repaint.\n            viewport.repaint.repaint_delay = Duration::MAX;\n        } else {\n            viewport.repaint.repaint_delay = Duration::ZERO;\n            viewport.repaint.outstanding -= 1;\n            if let Some(callback) = &self.request_repaint_callback {\n                (callback)(RequestRepaintInfo {\n                    viewport_id,\n                    delay: Duration::ZERO,\n                    current_cumulative_pass_nr: viewport.repaint.cumulative_pass_nr,\n                });\n            }\n        }\n    }\n\n    fn request_repaint(&mut self, viewport_id: ViewportId, cause: RepaintCause) {\n        self.request_repaint_after(Duration::ZERO, viewport_id, cause);\n    }\n\n    fn request_repaint_after(\n        &mut self,\n        mut delay: Duration,\n        viewport_id: ViewportId,\n        cause: RepaintCause,\n    ) {\n        let viewport = self.viewports.entry(viewport_id).or_default();\n\n        if delay == Duration::ZERO {\n            // Each request results in two repaints, just to give some things time to settle.\n            // This solves some corner-cases of missing repaints on frame-delayed responses.\n            viewport.repaint.outstanding = 1;\n        } else {\n            // For non-zero delays, we only repaint once, because\n            // otherwise we would just schedule an immediate repaint _now_,\n            // which would then clear the delay and repaint again.\n            // Hovering a tooltip is a good example of a case where we want to repaint after a delay.\n        }\n\n        if let Ok(predicted_frame_time) = Duration::try_from_secs_f32(viewport.input.predicted_dt) {\n            // Make it less likely we over-shoot the target:\n            delay = delay.saturating_sub(predicted_frame_time);\n        }\n\n        viewport.repaint.causes.push(cause);\n\n        // We save some CPU time by only calling the callback if we need to.\n        // If the new delay is greater or equal to the previous lowest,\n        // it means we have already called the callback, and don't need to do it again.\n        if delay < viewport.repaint.repaint_delay {\n            viewport.repaint.repaint_delay = delay;\n\n            if let Some(callback) = &self.request_repaint_callback {\n                (callback)(RequestRepaintInfo {\n                    viewport_id,\n                    delay,\n                    current_cumulative_pass_nr: viewport.repaint.cumulative_pass_nr,\n                });\n            }\n        }\n    }\n\n    #[must_use]\n    fn requested_immediate_repaint_prev_pass(&self, viewport_id: &ViewportId) -> bool {\n        self.viewports\n            .get(viewport_id)\n            .is_some_and(|v| v.repaint.requested_immediate_repaint_prev_pass())\n    }\n\n    #[must_use]\n    fn has_requested_repaint(&self, viewport_id: &ViewportId) -> bool {\n        self.viewports\n            .get(viewport_id)\n            .is_some_and(|v| 0 < v.repaint.outstanding || v.repaint.repaint_delay < Duration::MAX)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// State stored per viewport.\n///\n/// Mostly for internal use.\n/// Things here may move and change without warning.\n#[derive(Default)]\npub struct ViewportState {\n    /// The type of viewport.\n    ///\n    /// This will never be [`ViewportClass::EmbeddedWindow`],\n    /// since those don't result in real viewports.\n    pub class: ViewportClass,\n\n    /// The latest delta\n    pub builder: ViewportBuilder,\n\n    /// The user-code that shows the GUI, used for deferred viewports.\n    ///\n    /// `None` for immediate viewports.\n    pub viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,\n\n    pub input: InputState,\n\n    /// State that is collected during a pass and then cleared.\n    pub this_pass: PassState,\n\n    /// The final [`PassState`] from last pass.\n    ///\n    /// Only read from.\n    pub prev_pass: PassState,\n\n    /// Has this viewport been updated this pass?\n    pub used: bool,\n\n    /// State related to repaint scheduling.\n    repaint: ViewportRepaintInfo,\n\n    // ----------------------\n    // Updated at the start of the pass:\n    //\n    /// Which widgets are under the pointer?\n    pub hits: WidgetHits,\n\n    /// What widgets are being interacted with this pass?\n    ///\n    /// Based on the widgets from last pass, and input in this pass.\n    pub interact_widgets: InteractionSnapshot,\n\n    // ----------------------\n    // The output of a pass:\n    //\n    pub graphics: GraphicLayers,\n    // Most of the things in `PlatformOutput` are not actually viewport dependent.\n    pub output: PlatformOutput,\n    pub commands: Vec<ViewportCommand>,\n\n    // ----------------------\n    // Cross-frame statistics:\n    pub num_multipass_in_row: usize,\n}\n\n/// What called [`Context::request_repaint`] or [`Context::request_discard`]?\n#[derive(Clone, PartialEq, Eq, Hash)]\npub struct RepaintCause {\n    /// What file had the call that requested the repaint?\n    pub file: &'static str,\n\n    /// What line number of the call that requested the repaint?\n    pub line: u32,\n\n    /// Explicit reason; human readable.\n    pub reason: Cow<'static, str>,\n}\n\nimpl std::fmt::Debug for RepaintCause {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}:{} {}\", self.file, self.line, self.reason)\n    }\n}\n\nimpl std::fmt::Display for RepaintCause {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}:{} {}\", self.file, self.line, self.reason)\n    }\n}\n\nimpl RepaintCause {\n    /// Capture the file and line number of the call site.\n    #[expect(clippy::new_without_default)]\n    #[track_caller]\n    pub fn new() -> Self {\n        let caller = Location::caller();\n        Self {\n            file: caller.file(),\n            line: caller.line(),\n            reason: \"\".into(),\n        }\n    }\n\n    /// Capture the file and line number of the call site,\n    /// as well as add a reason.\n    #[track_caller]\n    pub fn new_reason(reason: impl Into<Cow<'static, str>>) -> Self {\n        let caller = Location::caller();\n        Self {\n            file: caller.file(),\n            line: caller.line(),\n            reason: reason.into(),\n        }\n    }\n}\n\n/// Per-viewport state related to repaint scheduling.\nstruct ViewportRepaintInfo {\n    /// Monotonically increasing counter.\n    ///\n    /// Incremented at the end of [`Context::run`].\n    /// This can be smaller than [`Self::cumulative_pass_nr`],\n    /// but never larger.\n    cumulative_frame_nr: u64,\n\n    /// Monotonically increasing counter, counting the number of passes.\n    /// This can be larger than [`Self::cumulative_frame_nr`],\n    /// but never smaller.\n    cumulative_pass_nr: u64,\n\n    /// The duration which the backend will poll for new events\n    /// before forcing another egui update, even if there's no new events.\n    ///\n    /// Also used to suppress multiple calls to the repaint callback during the same pass.\n    ///\n    /// This is also returned in [`crate::ViewportOutput`].\n    repaint_delay: Duration,\n\n    /// While positive, keep requesting repaints. Decrement at the start of each pass.\n    outstanding: u8,\n\n    /// What caused repaints during this pass?\n    causes: Vec<RepaintCause>,\n\n    /// What triggered a repaint the previous pass?\n    /// (i.e: why are we updating now?)\n    prev_causes: Vec<RepaintCause>,\n\n    /// What was the output of `repaint_delay` on the previous pass?\n    ///\n    /// If this was zero, we are repainting as quickly as possible\n    /// (as far as we know).\n    prev_pass_paint_delay: Duration,\n}\n\nimpl Default for ViewportRepaintInfo {\n    fn default() -> Self {\n        Self {\n            cumulative_frame_nr: 0,\n            cumulative_pass_nr: 0,\n\n            // We haven't scheduled a repaint yet.\n            repaint_delay: Duration::MAX,\n\n            // Let's run a couple of frames at the start, because why not.\n            outstanding: 1,\n\n            causes: Default::default(),\n            prev_causes: Default::default(),\n\n            prev_pass_paint_delay: Duration::MAX,\n        }\n    }\n}\n\nimpl ViewportRepaintInfo {\n    pub fn requested_immediate_repaint_prev_pass(&self) -> bool {\n        self.prev_pass_paint_delay == Duration::ZERO\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Default)]\nstruct ContextImpl {\n    fonts: Option<Fonts>,\n    font_definitions: FontDefinitions,\n\n    memory: Memory,\n    animation_manager: AnimationManager,\n\n    plugins: plugin::Plugins,\n    safe_area: SafeAreaInsets,\n\n    /// All viewports share the same texture manager and texture namespace.\n    ///\n    /// In all viewports, [`TextureId::default`] is special, and points to the font atlas.\n    /// The font-atlas texture _may_ be different across viewports, as they may have different\n    /// `pixels_per_point`, so we do special book-keeping for that.\n    /// See <https://github.com/emilk/egui/issues/3664>.\n    tex_manager: WrappedTextureManager,\n\n    /// Set during the pass, becomes active at the start of the next pass.\n    new_zoom_factor: Option<f32>,\n\n    os: OperatingSystem,\n\n    /// How deeply nested are we?\n    viewport_stack: Vec<ViewportIdPair>,\n\n    /// What is the last viewport rendered?\n    last_viewport: ViewportId,\n\n    paint_stats: PaintStats,\n\n    request_repaint_callback: Option<Box<dyn Fn(RequestRepaintInfo) + Send + Sync>>,\n\n    viewport_parents: ViewportIdMap<ViewportId>,\n    viewports: ViewportIdMap<ViewportState>,\n\n    embed_viewports: bool,\n\n    is_accesskit_enabled: bool,\n\n    loaders: Arc<Loaders>,\n}\n\nimpl ContextImpl {\n    fn begin_pass(&mut self, mut new_raw_input: RawInput) {\n        let viewport_id = new_raw_input.viewport_id;\n        let parent_id = new_raw_input\n            .viewports\n            .get(&viewport_id)\n            .and_then(|v| v.parent)\n            .unwrap_or_default();\n        let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent_id);\n\n        if let Some(safe_area) = new_raw_input.safe_area_insets {\n            self.safe_area = safe_area;\n        }\n\n        let is_outermost_viewport = self.viewport_stack.is_empty(); // not necessarily root, just outermost immediate viewport\n        self.viewport_stack.push(ids);\n\n        self.begin_pass_repaint_logic(viewport_id);\n\n        let viewport = self.viewports.entry(viewport_id).or_default();\n\n        if is_outermost_viewport && let Some(new_zoom_factor) = self.new_zoom_factor.take() {\n            let ratio = self.memory.options.zoom_factor / new_zoom_factor;\n            self.memory.options.zoom_factor = new_zoom_factor;\n\n            let input = &viewport.input;\n            // This is a bit hacky, but is required to avoid jitter:\n            let mut rect = input.content_rect();\n            rect.min = (ratio * rect.min.to_vec2()).to_pos2();\n            rect.max = (ratio * rect.max.to_vec2()).to_pos2();\n            new_raw_input.screen_rect = Some(rect);\n            // We should really scale everything else in the input too,\n            // but the `screen_rect` is the most important part.\n        }\n        let native_pixels_per_point = new_raw_input\n            .viewport()\n            .native_pixels_per_point\n            .unwrap_or(1.0);\n        let pixels_per_point = self.memory.options.zoom_factor * native_pixels_per_point;\n\n        let all_viewport_ids: ViewportIdSet = self.all_viewport_ids();\n\n        let viewport = self.viewports.entry(self.viewport_id()).or_default();\n\n        self.memory.begin_pass(&new_raw_input, &all_viewport_ids);\n\n        viewport.input = std::mem::take(&mut viewport.input).begin_pass(\n            new_raw_input,\n            viewport.repaint.requested_immediate_repaint_prev_pass(),\n            pixels_per_point,\n            self.memory.options.input_options,\n        );\n        let repaint_after = viewport.input.wants_repaint_after();\n\n        let content_rect = viewport.input.content_rect();\n\n        viewport.this_pass.begin_pass(content_rect);\n\n        {\n            let mut layers: Vec<LayerId> = viewport.prev_pass.widgets.layer_ids().collect();\n            layers.sort_by(|&a, &b| self.memory.areas().compare_order(a, b));\n\n            viewport.hits = if let Some(pos) = viewport.input.pointer.interact_pos() {\n                let interact_radius = self.memory.options.style().interaction.interact_radius;\n\n                crate::hit_test::hit_test(\n                    &viewport.prev_pass.widgets,\n                    &layers,\n                    &self.memory.to_global,\n                    pos,\n                    interact_radius,\n                )\n            } else {\n                WidgetHits::default()\n            };\n\n            viewport.interact_widgets = crate::interaction::interact(\n                &viewport.interact_widgets,\n                &viewport.prev_pass.widgets,\n                &viewport.hits,\n                &viewport.input,\n                self.memory.interaction_mut(),\n            );\n        }\n\n        // Ensure we register the background area so panels and background ui can catch clicks:\n        self.memory.areas_mut().set_state(\n            LayerId::background(),\n            AreaState {\n                pivot_pos: Some(content_rect.left_top()),\n                pivot: Align2::LEFT_TOP,\n                size: Some(content_rect.size()),\n                interactable: true,\n                last_became_visible_at: None,\n            },\n        );\n\n        if self.is_accesskit_enabled {\n            profiling::scope!(\"accesskit\");\n            use crate::pass_state::AccessKitPassState;\n            let id = crate::accesskit_root_id();\n            let mut root_node = accesskit::Node::new(accesskit::Role::Window);\n            let pixels_per_point = viewport.input.pixels_per_point();\n            root_node.set_transform(accesskit::Affine::scale(pixels_per_point.into()));\n            let mut nodes = IdMap::default();\n            nodes.insert(id, root_node);\n            viewport.this_pass.accesskit_state = Some(AccessKitPassState {\n                nodes,\n                parent_map: IdMap::default(),\n            });\n        }\n\n        self.update_fonts_mut();\n\n        if let Some(delay) = repaint_after {\n            self.request_repaint_after(delay, viewport_id, RepaintCause::new());\n        }\n    }\n\n    /// Load fonts unless already loaded.\n    fn update_fonts_mut(&mut self) {\n        profiling::function_scope!();\n        let input = &self.viewport().input;\n        let max_texture_side = input.max_texture_side;\n\n        if let Some(font_definitions) = self.memory.new_font_definitions.take() {\n            // New font definition loaded, so we need to reload all fonts.\n            self.fonts = None;\n            self.font_definitions = font_definitions;\n\n            log::trace!(\"Loading new font definitions\");\n        }\n\n        if !self.memory.add_fonts.is_empty() {\n            let fonts = self.memory.add_fonts.drain(..);\n            for font in fonts {\n                self.fonts = None; // recreate all the fonts\n                for family in font.families {\n                    let fam = self\n                        .font_definitions\n                        .families\n                        .entry(family.family)\n                        .or_default();\n                    match family.priority {\n                        FontPriority::Highest => fam.insert(0, font.name.clone()),\n                        FontPriority::Lowest => fam.push(font.name.clone()),\n                    }\n                }\n                self.font_definitions\n                    .font_data\n                    .insert(font.name, Arc::new(font.data));\n            }\n\n            log::trace!(\"Adding new fonts\");\n        }\n\n        let Visuals {\n            mut text_options, ..\n        } = self.memory.options.style().visuals;\n        text_options.max_texture_side = max_texture_side;\n\n        let mut is_new = false;\n\n        let fonts = self.fonts.get_or_insert_with(|| {\n            log::trace!(\"Creating new Fonts\");\n\n            is_new = true;\n            profiling::scope!(\"Fonts::new\");\n            Fonts::new(text_options, self.font_definitions.clone())\n        });\n\n        {\n            profiling::scope!(\"Fonts::begin_pass\");\n            fonts.begin_pass(text_options);\n        }\n    }\n\n    fn accesskit_node_builder(&mut self, id: Id) -> Option<&mut accesskit::Node> {\n        let state = self.viewport().this_pass.accesskit_state.as_mut()?;\n        let builders = &mut state.nodes;\n\n        if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {\n            entry.insert(Default::default());\n\n            /// Find the first ancestor that already has an accesskit node.\n            fn find_accesskit_parent(\n                parent_map: &IdMap<Id>,\n                node_map: &IdMap<accesskit::Node>,\n                id: Id,\n            ) -> Option<Id> {\n                if let Some(parent_id) = parent_map.get(&id) {\n                    if node_map.contains_key(parent_id) {\n                        Some(*parent_id)\n                    } else {\n                        find_accesskit_parent(parent_map, node_map, *parent_id)\n                    }\n                } else {\n                    None\n                }\n            }\n\n            let parent_id = find_accesskit_parent(&state.parent_map, builders, id)\n                .unwrap_or_else(crate::accesskit_root_id);\n\n            let parent_builder = builders.get_mut(&parent_id)?;\n            parent_builder.push_child(id.accesskit_id());\n        }\n\n        builders.get_mut(&id)\n    }\n\n    fn pixels_per_point(&mut self) -> f32 {\n        self.viewport().input.pixels_per_point\n    }\n\n    /// Return the `ViewportId` of the current viewport.\n    ///\n    /// For the root viewport this will return [`ViewportId::ROOT`].\n    pub(crate) fn viewport_id(&self) -> ViewportId {\n        self.viewport_stack.last().copied().unwrap_or_default().this\n    }\n\n    /// Return the `ViewportId` of his parent.\n    ///\n    /// For the root viewport this will return [`ViewportId::ROOT`].\n    pub(crate) fn parent_viewport_id(&self) -> ViewportId {\n        let viewport_id = self.viewport_id();\n        *self\n            .viewport_parents\n            .get(&viewport_id)\n            .unwrap_or(&ViewportId::ROOT)\n    }\n\n    fn all_viewport_ids(&self) -> ViewportIdSet {\n        self.viewports\n            .keys()\n            .copied()\n            .chain([ViewportId::ROOT])\n            .collect()\n    }\n\n    /// The current active viewport\n    pub(crate) fn viewport(&mut self) -> &mut ViewportState {\n        self.viewports.entry(self.viewport_id()).or_default()\n    }\n\n    fn viewport_for(&mut self, viewport_id: ViewportId) -> &mut ViewportState {\n        self.viewports.entry(viewport_id).or_default()\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Your handle to egui.\n///\n/// This is the first thing you need when working with egui.\n/// Contains the [`InputState`], [`Memory`], [`PlatformOutput`], and more.\n///\n/// [`Context`] is cheap to clone, and any clones refers to the same mutable data\n/// ([`Context`] uses refcounting internally).\n///\n/// ## Locking\n/// All methods are marked `&self`; [`Context`] has interior mutability protected by an [`RwLock`].\n///\n/// To access parts of a `Context` you need to use some of the helper functions that take closures:\n///\n/// ```\n/// # let ctx = egui::Context::default();\n/// if ctx.input(|i| i.key_pressed(egui::Key::A)) {\n///     ctx.copy_text(\"Hello!\".to_owned());\n/// }\n/// ```\n///\n/// Within such a closure you may NOT recursively lock the same [`Context`], as that can lead to a deadlock.\n/// Therefore it is important that any lock of [`Context`] is short-lived.\n///\n/// These are effectively transactional accesses.\n///\n/// [`Ui`] has many of the same accessor functions, and the same applies there.\n///\n/// ## Example:\n///\n/// ``` no_run\n/// # fn handle_platform_output(_: egui::PlatformOutput) {}\n/// # fn paint(textures_delta: egui::TexturesDelta, _: Vec<egui::ClippedPrimitive>) {}\n/// let mut ctx = egui::Context::default();\n///\n/// // Game loop:\n/// loop {\n///     let raw_input = egui::RawInput::default();\n///     let full_output = ctx.run(raw_input, |ctx| {\n///         egui::CentralPanel::default().show(&ctx, |ui| {\n///             ui.label(\"Hello world!\");\n///             if ui.button(\"Click me\").clicked() {\n///                 // take some action here\n///             }\n///         });\n///     });\n///     handle_platform_output(full_output.platform_output);\n///     let clipped_primitives = ctx.tessellate(full_output.shapes, full_output.pixels_per_point);\n///     paint(full_output.textures_delta, clipped_primitives);\n/// }\n/// ```\n#[derive(Clone)]\npub struct Context(Arc<RwLock<ContextImpl>>);\n\nimpl std::fmt::Debug for Context {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Context\").finish_non_exhaustive()\n    }\n}\n\nimpl std::cmp::PartialEq for Context {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.0, &other.0)\n    }\n}\n\nimpl Default for Context {\n    fn default() -> Self {\n        let ctx_impl = ContextImpl {\n            embed_viewports: true,\n            viewports: std::iter::once((ViewportId::ROOT, ViewportState::default())).collect(),\n            ..Default::default()\n        };\n        let ctx = Self(Arc::new(RwLock::new(ctx_impl)));\n\n        ctx.add_plugin(plugin::CallbackPlugin::default());\n\n        // Register built-in plugins:\n        ctx.add_plugin(crate::debug_text::DebugTextPlugin::default());\n        ctx.add_plugin(crate::text_selection::LabelSelectionState::default());\n        ctx.add_plugin(crate::DragAndDrop::default());\n\n        ctx\n    }\n}\n\nimpl Context {\n    /// Do read-only (shared access) transaction on Context\n    fn read<R>(&self, reader: impl FnOnce(&ContextImpl) -> R) -> R {\n        reader(&self.0.read())\n    }\n\n    /// Do read-write (exclusive access) transaction on Context\n    fn write<R>(&self, writer: impl FnOnce(&mut ContextImpl) -> R) -> R {\n        writer(&mut self.0.write())\n    }\n\n    /// Run the ui code for one frame.\n    ///\n    /// At most [`Options::max_passes`] calls will be issued to `run_ui`,\n    /// and only on the rare occasion that [`Context::request_discard`] is called.\n    /// Usually, it `run_ui` will only be called once.\n    ///\n    /// The [`Ui`] given to the callback will cover the entire [`Self::content_rect`],\n    /// with no margin or background color. Use [`crate::Frame`] to add that.\n    ///\n    /// You can organize your GUI using [`crate::Panel`].\n    ///\n    /// Instead of calling `run_ui`, you can alternatively use [`Self::begin_pass`] and [`Context::end_pass`].\n    ///\n    /// ```\n    /// // One egui context that you keep reusing:\n    /// let mut ctx = egui::Context::default();\n    ///\n    /// // Each frame:\n    /// let input = egui::RawInput::default();\n    /// let full_output = ctx.run_ui(input, |ui| {\n    ///     ui.label(\"Hello egui!\");\n    /// });\n    /// // handle full_output\n    /// ```\n    ///\n    /// ## See also\n    /// * [`Self::run`]\n    #[must_use]\n    pub fn run_ui(&self, new_input: RawInput, mut run_ui: impl FnMut(&mut Ui)) -> FullOutput {\n        self.run_ui_dyn(new_input, &mut run_ui)\n    }\n\n    #[must_use]\n    fn run_ui_dyn(&self, new_input: RawInput, run_ui: &mut dyn FnMut(&mut Ui)) -> FullOutput {\n        let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());\n        #[expect(deprecated)]\n        self.run(new_input, |ctx| {\n            let mut top_ui = Ui::new(\n                ctx.clone(),\n                Id::new((ctx.viewport_id(), \"__top_ui\")),\n                UiBuilder::new()\n                    .layer_id(LayerId::background())\n                    .max_rect(ctx.available_rect()),\n            );\n\n            {\n                plugins.on_begin_pass(&mut top_ui);\n                run_ui(&mut top_ui);\n                plugins.on_end_pass(&mut top_ui);\n            }\n\n            // Inform ctx about what we actually used, so we can shrink the native window to fit.\n            // TODO(emilk): make better use of this somehow\n            ctx.pass_state_mut(|state| state.allocate_central_panel(top_ui.min_rect()));\n        })\n    }\n\n    /// Run the ui code for one frame.\n    ///\n    /// At most [`Options::max_passes`] calls will be issued to `run_ui`,\n    /// and only on the rare occasion that [`Context::request_discard`] is called.\n    /// Usually, it `run_ui` will only be called once.\n    ///\n    /// Put your widgets into a [`crate::Panel`], [`crate::CentralPanel`], [`crate::Window`] or [`crate::Area`].\n    ///\n    /// Instead of calling `run`, you can alternatively use [`Self::begin_pass`] and [`Context::end_pass`].\n    ///\n    /// ```\n    /// // One egui context that you keep reusing:\n    /// let mut ctx = egui::Context::default();\n    ///\n    /// // Each frame:\n    /// let input = egui::RawInput::default();\n    /// let full_output = ctx.run(input, |ctx| {\n    ///     egui::CentralPanel::default().show(&ctx, |ui| {\n    ///         ui.label(\"Hello egui!\");\n    ///     });\n    /// });\n    /// // handle full_output\n    /// ```\n    ///\n    /// ## See also\n    /// * [`Self::run_ui`]\n    #[must_use]\n    #[deprecated = \"Call run_ui instead\"]\n    pub fn run(&self, new_input: RawInput, mut run_ui: impl FnMut(&Self)) -> FullOutput {\n        self.run_dyn(new_input, &mut run_ui)\n    }\n\n    #[must_use]\n    fn run_dyn(&self, mut new_input: RawInput, run_ui: &mut dyn FnMut(&Self)) -> FullOutput {\n        profiling::function_scope!();\n        let viewport_id = new_input.viewport_id;\n        let max_passes = self.write(|ctx| ctx.memory.options.max_passes.get());\n\n        let mut output = FullOutput::default();\n        debug_assert_eq!(\n            output.platform_output.num_completed_passes, 0,\n            \"output must be fresh, but had {} passes\",\n            output.platform_output.num_completed_passes\n        );\n\n        loop {\n            profiling::scope!(\n                \"pass\",\n                output\n                    .platform_output\n                    .num_completed_passes\n                    .to_string()\n                    .as_str()\n            );\n\n            // We must move the `num_passes` (back) to the viewport output so that [`Self::will_discard`]\n            // has access to the latest pass count.\n            self.write(|ctx| {\n                let viewport = ctx.viewport_for(viewport_id);\n                viewport.output.num_completed_passes =\n                    std::mem::take(&mut output.platform_output.num_completed_passes);\n                output.platform_output.request_discard_reasons.clear();\n            });\n\n            self.begin_pass(new_input.take());\n            run_ui(self);\n            output.append(self.end_pass());\n            debug_assert!(\n                0 < output.platform_output.num_completed_passes,\n                \"Completed passes was lower than 0, was {}\",\n                output.platform_output.num_completed_passes\n            );\n\n            if !output.platform_output.requested_discard() {\n                break; // no need for another pass\n            }\n\n            if max_passes <= output.platform_output.num_completed_passes {\n                log::debug!(\n                    \"Ignoring call request_discard, because max_passes={max_passes}. Requested from {:?}\",\n                    output.platform_output.request_discard_reasons\n                );\n\n                break;\n            }\n        }\n\n        self.write(|ctx| {\n            let did_multipass = 1 < output.platform_output.num_completed_passes;\n            let viewport = ctx.viewport_for(viewport_id);\n            if did_multipass {\n                viewport.num_multipass_in_row += 1;\n            } else {\n                viewport.num_multipass_in_row = 0;\n            }\n            viewport.repaint.cumulative_frame_nr += 1;\n        });\n\n        output\n    }\n\n    /// An alternative to calling [`Self::run`].\n    ///\n    /// It is usually better to use [`Self::run`], because\n    /// `run` supports multi-pass layout using [`Self::request_discard`].\n    ///\n    /// ```\n    /// // One egui context that you keep reusing:\n    /// let mut ctx = egui::Context::default();\n    ///\n    /// // Each frame:\n    /// let input = egui::RawInput::default();\n    /// ctx.begin_pass(input);\n    ///\n    /// egui::CentralPanel::default().show(&ctx, |ui| {\n    ///     ui.label(\"Hello egui!\");\n    /// });\n    ///\n    /// let full_output = ctx.end_pass();\n    /// // handle full_output\n    /// ```\n    pub fn begin_pass(&self, mut new_input: RawInput) {\n        profiling::function_scope!();\n\n        let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());\n        plugins.on_input(&mut new_input);\n\n        self.write(|ctx| ctx.begin_pass(new_input));\n    }\n\n    /// See [`Self::begin_pass`].\n    #[deprecated = \"Renamed begin_pass\"]\n    pub fn begin_frame(&self, new_input: RawInput) {\n        self.begin_pass(new_input);\n    }\n}\n\n/// ## Borrows parts of [`Context`]\n/// These functions all lock the [`Context`].\n/// Please see the documentation of [`Context`] for how locking works!\nimpl Context {\n    /// Read-only access to [`InputState`].\n    ///\n    /// Note that this locks the [`Context`].\n    ///\n    /// ```\n    /// # let mut ctx = egui::Context::default();\n    /// ctx.input(|i| {\n    ///     // ⚠️ Using `ctx` (even from other `Arc` reference) again here will lead to a deadlock!\n    /// });\n    ///\n    /// if let Some(pos) = ctx.input(|i| i.pointer.hover_pos()) {\n    ///     // This is fine!\n    /// }\n    /// ```\n    #[inline]\n    pub fn input<R>(&self, reader: impl FnOnce(&InputState) -> R) -> R {\n        self.write(move |ctx| reader(&ctx.viewport().input))\n    }\n\n    /// This will create a `InputState::default()` if there is no input state for that viewport\n    #[inline]\n    pub fn input_for<R>(&self, id: ViewportId, reader: impl FnOnce(&InputState) -> R) -> R {\n        self.write(move |ctx| reader(&ctx.viewport_for(id).input))\n    }\n\n    /// Read-write access to [`InputState`].\n    #[inline]\n    pub fn input_mut<R>(&self, writer: impl FnOnce(&mut InputState) -> R) -> R {\n        self.input_mut_for(self.viewport_id(), writer)\n    }\n\n    /// This will create a `InputState::default()` if there is no input state for that viewport\n    #[inline]\n    pub fn input_mut_for<R>(&self, id: ViewportId, writer: impl FnOnce(&mut InputState) -> R) -> R {\n        self.write(move |ctx| writer(&mut ctx.viewport_for(id).input))\n    }\n\n    /// Read-only access to [`Memory`].\n    #[inline]\n    pub fn memory<R>(&self, reader: impl FnOnce(&Memory) -> R) -> R {\n        self.read(move |ctx| reader(&ctx.memory))\n    }\n\n    /// Read-write access to [`Memory`].\n    #[inline]\n    pub fn memory_mut<R>(&self, writer: impl FnOnce(&mut Memory) -> R) -> R {\n        self.write(move |ctx| writer(&mut ctx.memory))\n    }\n\n    /// Read-only access to [`IdTypeMap`], which stores superficial widget state.\n    #[inline]\n    pub fn data<R>(&self, reader: impl FnOnce(&IdTypeMap) -> R) -> R {\n        self.read(move |ctx| reader(&ctx.memory.data))\n    }\n\n    /// Read-write access to [`IdTypeMap`], which stores superficial widget state.\n    #[inline]\n    pub fn data_mut<R>(&self, writer: impl FnOnce(&mut IdTypeMap) -> R) -> R {\n        self.write(move |ctx| writer(&mut ctx.memory.data))\n    }\n\n    /// Read-write access to [`GraphicLayers`], where painted [`crate::Shape`]s are written to.\n    #[inline]\n    pub fn graphics_mut<R>(&self, writer: impl FnOnce(&mut GraphicLayers) -> R) -> R {\n        self.write(move |ctx| writer(&mut ctx.viewport().graphics))\n    }\n\n    /// Read-only access to [`GraphicLayers`], where painted [`crate::Shape`]s are written to.\n    #[inline]\n    pub fn graphics<R>(&self, reader: impl FnOnce(&GraphicLayers) -> R) -> R {\n        self.write(move |ctx| reader(&ctx.viewport().graphics))\n    }\n\n    /// Read-only access to [`PlatformOutput`].\n    ///\n    /// This is what egui outputs each pass and frame.\n    ///\n    /// ```\n    /// # let mut ctx = egui::Context::default();\n    /// ctx.output_mut(|o| o.cursor_icon = egui::CursorIcon::Progress);\n    /// ```\n    #[inline]\n    pub fn output<R>(&self, reader: impl FnOnce(&PlatformOutput) -> R) -> R {\n        self.write(move |ctx| reader(&ctx.viewport().output))\n    }\n\n    /// Read-write access to [`PlatformOutput`].\n    #[inline]\n    pub fn output_mut<R>(&self, writer: impl FnOnce(&mut PlatformOutput) -> R) -> R {\n        self.write(move |ctx| writer(&mut ctx.viewport().output))\n    }\n\n    /// Read-only access to [`PassState`].\n    ///\n    /// This is only valid during the call to [`Self::run`] (between [`Self::begin_pass`] and [`Self::end_pass`]).\n    #[inline]\n    pub(crate) fn pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {\n        self.write(move |ctx| reader(&ctx.viewport().this_pass))\n    }\n\n    /// Read-write access to [`PassState`].\n    ///\n    /// This is only valid during the call to [`Self::run`] (between [`Self::begin_pass`] and [`Self::end_pass`]).\n    #[inline]\n    pub(crate) fn pass_state_mut<R>(&self, writer: impl FnOnce(&mut PassState) -> R) -> R {\n        self.write(move |ctx| writer(&mut ctx.viewport().this_pass))\n    }\n\n    /// Read-only access to the [`PassState`] from the previous pass.\n    ///\n    /// This is swapped at the end of each pass.\n    #[inline]\n    pub(crate) fn prev_pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {\n        self.write(move |ctx| reader(&ctx.viewport().prev_pass))\n    }\n\n    /// Read-only access to [`Fonts`].\n    ///\n    /// Not valid until first call to [`Context::run()`].\n    /// That's because since we don't know the proper `pixels_per_point` until then.\n    #[inline]\n    pub fn fonts<R>(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R {\n        self.write(move |ctx| {\n            let pixels_per_point = ctx.pixels_per_point();\n            reader(\n                &ctx.fonts\n                    .as_mut()\n                    .expect(\"No fonts available until first call to Context::run()\")\n                    .with_pixels_per_point(pixels_per_point),\n            )\n        })\n    }\n\n    /// Read-write access to [`Fonts`].\n    ///\n    /// Not valid until first call to [`Context::run()`].\n    /// That's because since we don't know the proper `pixels_per_point` until then.\n    #[inline]\n    pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R {\n        self.write(move |ctx| {\n            let pixels_per_point = ctx.pixels_per_point();\n            reader(\n                &mut ctx\n                    .fonts\n                    .as_mut()\n                    .expect(\"No fonts available until first call to Context::run()\")\n                    .with_pixels_per_point(pixels_per_point),\n            )\n        })\n    }\n\n    /// Read-only access to [`Options`].\n    #[inline]\n    pub fn options<R>(&self, reader: impl FnOnce(&Options) -> R) -> R {\n        self.read(move |ctx| reader(&ctx.memory.options))\n    }\n\n    /// Read-write access to [`Options`].\n    #[inline]\n    pub fn options_mut<R>(&self, writer: impl FnOnce(&mut Options) -> R) -> R {\n        self.write(move |ctx| writer(&mut ctx.memory.options))\n    }\n\n    /// Read-only access to [`TessellationOptions`].\n    #[inline]\n    pub fn tessellation_options<R>(&self, reader: impl FnOnce(&TessellationOptions) -> R) -> R {\n        self.read(move |ctx| reader(&ctx.memory.options.tessellation_options))\n    }\n\n    /// Read-write access to [`TessellationOptions`].\n    #[inline]\n    pub fn tessellation_options_mut<R>(\n        &self,\n        writer: impl FnOnce(&mut TessellationOptions) -> R,\n    ) -> R {\n        self.write(move |ctx| writer(&mut ctx.memory.options.tessellation_options))\n    }\n\n    /// If the given [`Id`] has been used previously the same pass at different position,\n    /// then an error will be printed on screen.\n    ///\n    /// This function is already called for all widgets that do any interaction,\n    /// but you can call this from widgets that store state but that does not interact.\n    ///\n    /// The given [`Rect`] should be approximately where the widget will be.\n    /// The most important thing is that [`Rect::min`] is approximately correct,\n    /// because that's where the warning will be painted. If you don't know what size to pick, just pick [`Vec2::ZERO`].\n    pub fn check_for_id_clash(&self, id: Id, new_rect: Rect, what: &str) {\n        let prev_rect = self.pass_state_mut(move |state| state.used_ids.insert(id, new_rect));\n\n        if !self.options(|opt| opt.warn_on_id_clash) {\n            return;\n        }\n\n        let Some(prev_rect) = prev_rect else { return };\n\n        // It is ok to reuse the same ID for e.g. a frame around a widget,\n        // or to check for interaction with the same widget twice:\n        let is_same_rect = prev_rect.expand(0.1).contains_rect(new_rect)\n            || new_rect.expand(0.1).contains_rect(prev_rect);\n        if is_same_rect {\n            return;\n        }\n\n        let show_error = |widget_rect: Rect, text: String| {\n            let content_rect = self.content_rect();\n\n            let text = format!(\"🔥 {text}\");\n            let color = self.global_style().visuals.error_fg_color;\n            let painter = self.debug_painter();\n            painter.rect_stroke(widget_rect, 0.0, (1.0, color), StrokeKind::Outside);\n\n            let below = widget_rect.bottom() + 32.0 < content_rect.bottom();\n\n            let text_rect = if below {\n                painter.debug_text(\n                    widget_rect.left_bottom() + vec2(0.0, 2.0),\n                    Align2::LEFT_TOP,\n                    color,\n                    text,\n                )\n            } else {\n                painter.debug_text(\n                    widget_rect.left_top() - vec2(0.0, 2.0),\n                    Align2::LEFT_BOTTOM,\n                    color,\n                    text,\n                )\n            };\n\n            if let Some(pointer_pos) = self.pointer_hover_pos()\n                && text_rect.contains(pointer_pos)\n            {\n                let tooltip_pos = if below {\n                    text_rect.left_bottom() + vec2(2.0, 4.0)\n                } else {\n                    text_rect.left_top() + vec2(2.0, -4.0)\n                };\n\n                painter.error(\n                        tooltip_pos,\n                        format!(\"Widget is {} this text.\\n\\n\\\n                             ID clashes happens when things like Windows or CollapsingHeaders share names,\\n\\\n                             or when things like Plot and Grid:s aren't given unique id_salt:s.\\n\\n\\\n                             Sometimes the solution is to use ui.push_id.\",\n                                if below { \"above\" } else { \"below\" }),\n                    );\n            }\n        };\n\n        let id_str = id.short_debug_format();\n\n        if prev_rect.min.distance(new_rect.min) < 4.0 {\n            show_error(new_rect, format!(\"Double use of {what} ID {id_str}\"));\n        } else {\n            show_error(prev_rect, format!(\"First use of {what} ID {id_str}\"));\n            show_error(new_rect, format!(\"Second use of {what} ID {id_str}\"));\n        }\n    }\n\n    // ---------------------------------------------------------------------\n\n    /// Create a widget and check for interaction.\n    ///\n    /// If this is not called, the widget doesn't exist.\n    ///\n    /// You should use [`Ui::interact`] instead.\n    ///\n    /// If the widget already exists, its state (sense, Rect, etc) will be updated.\n    ///\n    /// `allow_focus` should usually be true, unless you call this function multiple times with the\n    /// same widget, then `allow_focus` should only be true once (like in [`Ui::new`] (true) and [`Ui::remember_min_rect`] (false)).\n    pub(crate) fn create_widget(\n        &self,\n        w: WidgetRect,\n        allow_focus: bool,\n        options: crate::InteractOptions,\n    ) -> Response {\n        let interested_in_focus = w.enabled\n            && w.sense.is_focusable()\n            && self.memory(|mem| mem.allows_interaction(w.layer_id));\n\n        // Remember this widget\n        self.write(|ctx| {\n            let viewport = ctx.viewport();\n\n            // We add all widgets here, even non-interactive ones,\n            // because we need this list not only for checking for blocking widgets,\n            // but also to know when we have reached the widget we are checking for cover.\n            viewport.this_pass.widgets.insert(w.layer_id, w, options);\n\n            if allow_focus && interested_in_focus {\n                ctx.memory.interested_in_focus(w.id, w.layer_id);\n            }\n        });\n\n        if allow_focus && !interested_in_focus {\n            // Not interested or allowed input:\n            self.memory_mut(|mem| mem.surrender_focus(w.id));\n        }\n\n        if w.sense.interactive() || w.sense.is_focusable() {\n            self.check_for_id_clash(w.id, w.rect, \"widget\");\n        }\n\n        #[allow(clippy::allow_attributes, clippy::let_and_return)]\n        let res = self.get_response(w);\n\n        #[cfg(debug_assertions)]\n        if res.contains_pointer() {\n            let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());\n            plugins.on_widget_under_pointer(self, &w);\n        }\n\n        if allow_focus && w.sense.is_focusable() {\n            // Make sure anything that can receive focus has an AccessKit node.\n            // TODO(mwcampbell): For nodes that are filled from widget info,\n            // some information is written to the node twice.\n            self.accesskit_node_builder(w.id, |builder| res.fill_accesskit_node_common(builder));\n        }\n\n        self.write(|ctx| {\n            use crate::{Align, pass_state::ScrollTarget, style::ScrollAnimation};\n            let viewport = ctx.viewport_for(ctx.viewport_id());\n\n            viewport\n                .input\n                .consume_accesskit_action_requests(res.id, |request| {\n                    use accesskit::Action;\n\n                    // TODO(lucasmerlin): Correctly handle the scroll unit:\n                    // https://github.com/AccessKit/accesskit/blob/e639c0e0d8ccbfd9dff302d972fa06f9766d608e/common/src/lib.rs#L2621\n                    const DISTANCE: f32 = 100.0;\n\n                    match &request.action {\n                        Action::ScrollIntoView => {\n                            viewport.this_pass.scroll_target = [\n                                Some(ScrollTarget::new(\n                                    res.rect.x_range(),\n                                    Some(Align::Center),\n                                    ScrollAnimation::none(),\n                                )),\n                                Some(ScrollTarget::new(\n                                    res.rect.y_range(),\n                                    Some(Align::Center),\n                                    ScrollAnimation::none(),\n                                )),\n                            ];\n                        }\n                        Action::ScrollDown => {\n                            viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::UP;\n                        }\n                        Action::ScrollUp => {\n                            viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::DOWN;\n                        }\n                        Action::ScrollLeft => {\n                            viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::LEFT;\n                        }\n                        Action::ScrollRight => {\n                            viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::RIGHT;\n                        }\n                        _ => return false,\n                    }\n                    true\n                });\n        });\n\n        res\n    }\n\n    /// Read the response of some widget, which may be called _before_ creating the widget (!).\n    ///\n    /// This is because widget interaction happens at the start of the pass, using the widget rects from the previous pass.\n    ///\n    /// If the widget was not visible the previous pass (or this pass), this will return `None`.\n    ///\n    /// If you try to read a [`Ui`]'s response, while still inside, this will return the [`Rect`] from the previous frame.\n    pub fn read_response(&self, id: Id) -> Option<Response> {\n        self.write(|ctx| {\n            let viewport = ctx.viewport();\n            let widget_rect = viewport\n                .this_pass\n                .widgets\n                .get(id)\n                .or_else(|| viewport.prev_pass.widgets.get(id))\n                .copied();\n            widget_rect.map(|mut rect| {\n                // If the Rect is invalid the Ui hasn't registered its final Rect yet.\n                // We return the Rect from last frame instead.\n                if !(rect.rect.is_positive() && rect.rect.is_finite())\n                    && let Some(prev_rect) = viewport.prev_pass.widgets.get(id)\n                {\n                    rect.rect = prev_rect.rect;\n                }\n                rect\n            })\n        })\n        .map(|widget_rect| self.get_response(widget_rect))\n    }\n\n    /// Do all interaction for an existing widget, without (re-)registering it.\n    pub(crate) fn get_response(&self, widget_rect: WidgetRect) -> Response {\n        use response::Flags;\n\n        let WidgetRect {\n            id,\n            layer_id,\n            rect,\n            interact_rect,\n            sense,\n            enabled,\n        } = widget_rect;\n\n        // previous pass + \"highlight next pass\" == \"highlight this pass\"\n        let highlighted = self.prev_pass_state(|fs| fs.highlight_next_pass.contains(&id));\n\n        let mut res = Response {\n            ctx: self.clone(),\n            layer_id,\n            id,\n            rect,\n            interact_rect,\n            sense,\n            flags: Flags::empty(),\n            interact_pointer_pos: None,\n            intrinsic_size: None,\n        };\n\n        res.flags.set(Flags::ENABLED, enabled);\n        res.flags.set(Flags::HIGHLIGHTED, highlighted);\n\n        self.write(|ctx| {\n            let viewport = ctx.viewports.entry(ctx.viewport_id()).or_default();\n\n            res.flags.set(\n                Flags::CONTAINS_POINTER,\n                viewport.interact_widgets.contains_pointer.contains(&id),\n            );\n\n            let input = &viewport.input;\n            let memory = &mut ctx.memory;\n\n            if enabled\n                && sense.senses_click()\n                && memory.has_focus(id)\n                && (input.key_pressed(Key::Space) || input.key_pressed(Key::Enter))\n            {\n                // Space/enter works like a primary click for e.g. selected buttons\n                res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);\n            }\n\n            if enabled\n                && sense.senses_click()\n                && input.has_accesskit_action_request(id, accesskit::Action::Click)\n            {\n                res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);\n            }\n\n            if enabled && sense.senses_click() && Some(id) == viewport.interact_widgets.long_touched\n            {\n                res.flags.set(Flags::LONG_TOUCHED, true);\n            }\n\n            let interaction = memory.interaction();\n\n            res.flags.set(\n                Flags::IS_POINTER_BUTTON_DOWN_ON,\n                interaction.potential_click_id == Some(id)\n                    || interaction.potential_drag_id == Some(id),\n            );\n\n            if res.enabled() {\n                res.flags.set(\n                    Flags::HOVERED,\n                    viewport.interact_widgets.hovered.contains(&id),\n                );\n                res.flags.set(\n                    Flags::DRAGGED,\n                    Some(id) == viewport.interact_widgets.dragged,\n                );\n                res.flags.set(\n                    Flags::DRAG_STARTED,\n                    Some(id) == viewport.interact_widgets.drag_started,\n                );\n                res.flags.set(\n                    Flags::DRAG_STOPPED,\n                    Some(id) == viewport.interact_widgets.drag_stopped,\n                );\n            }\n\n            let clicked = Some(id) == viewport.interact_widgets.clicked;\n            let mut any_press = false;\n\n            for pointer_event in &input.pointer.pointer_events {\n                match pointer_event {\n                    PointerEvent::Moved(_) => {}\n                    PointerEvent::Pressed { .. } => {\n                        any_press = true;\n                    }\n                    PointerEvent::Released { click, .. } => {\n                        if enabled && sense.senses_click() && clicked && click.is_some() {\n                            res.flags.set(Flags::CLICKED, true);\n                        }\n\n                        res.flags.set(Flags::IS_POINTER_BUTTON_DOWN_ON, false);\n                        res.flags.set(Flags::DRAGGED, false);\n                    }\n                }\n            }\n\n            // is_pointer_button_down_on is false when released, but we want interact_pointer_pos\n            // to still work.\n            let is_interacted_with = res.is_pointer_button_down_on()\n                || res.long_touched()\n                || clicked\n                || res.drag_stopped();\n            if is_interacted_with {\n                res.interact_pointer_pos = input.pointer.interact_pos();\n                if let (Some(to_global), Some(pos)) = (\n                    memory.to_global.get(&res.layer_id),\n                    &mut res.interact_pointer_pos,\n                ) {\n                    *pos = to_global.inverse() * *pos;\n                }\n            }\n\n            if input.pointer.any_down() && !is_interacted_with {\n                // We don't hover widgets while interacting with *other* widgets:\n                res.flags.set(Flags::HOVERED, false);\n            }\n\n            let should_surrender_focus = match memory.options.input_options.surrender_focus_on {\n                SurrenderFocusOn::Presses => any_press,\n                SurrenderFocusOn::Clicks => input.pointer.any_click(),\n                SurrenderFocusOn::Never => false,\n            };\n\n            let pointer_clicked_elsewhere = should_surrender_focus && !res.hovered();\n            if pointer_clicked_elsewhere && memory.has_focus(id) {\n                memory.surrender_focus(id);\n            }\n        });\n\n        res\n    }\n\n    /// This is called by [`Response::widget_info`], but can also be called directly.\n    ///\n    /// With some debug flags it will store the widget info in [`crate::WidgetRects`] for later display.\n    #[inline]\n    pub fn register_widget_info(&self, id: Id, make_info: impl Fn() -> crate::WidgetInfo) {\n        #[cfg(debug_assertions)]\n        self.write(|ctx| {\n            if ctx.memory.options.style().debug.show_interactive_widgets {\n                ctx.viewport().this_pass.widgets.set_info(id, make_info());\n            }\n        });\n\n        #[cfg(not(debug_assertions))]\n        {\n            _ = (self, id, make_info);\n        }\n    }\n\n    /// Get a full-screen painter for a new or existing layer\n    pub fn layer_painter(&self, layer_id: LayerId) -> Painter {\n        let content_rect = self.content_rect();\n        Painter::new(self.clone(), layer_id, content_rect)\n    }\n\n    /// Paint on top of _everything_ else (even on top of tooltips and popups).\n    pub fn debug_painter(&self) -> Painter {\n        Self::layer_painter(self, LayerId::debug())\n    }\n\n    /// Print this text next to the cursor at the end of the pass.\n    ///\n    /// If you call this multiple times, the text will be appended.\n    ///\n    /// This only works if compiled with `debug_assertions`.\n    ///\n    /// ```\n    /// # let ctx = egui::Context::default();\n    /// # let state = true;\n    /// ctx.debug_text(format!(\"State: {state:?}\"));\n    /// ```\n    ///\n    /// This is just a convenience for calling [`crate::debug_text::print`].\n    #[track_caller]\n    pub fn debug_text(&self, text: impl Into<WidgetText>) {\n        crate::debug_text::print(self, text);\n    }\n\n    /// What operating system are we running on?\n    ///\n    /// When compiling natively, this is\n    /// figured out from the `target_os`.\n    ///\n    /// For web, this can be figured out from the user-agent,\n    /// and is done so by [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe).\n    pub fn os(&self) -> OperatingSystem {\n        self.read(|ctx| ctx.os)\n    }\n\n    /// Set the operating system we are running on.\n    ///\n    /// If you are writing wasm-based integration for egui you\n    /// may want to set this based on e.g. the user-agent.\n    pub fn set_os(&self, os: OperatingSystem) {\n        self.write(|ctx| ctx.os = os);\n    }\n\n    /// Set the cursor icon.\n    ///\n    /// Equivalent to:\n    /// ```\n    /// # let ctx = egui::Context::default();\n    /// ctx.output_mut(|o| o.cursor_icon = egui::CursorIcon::PointingHand);\n    /// ```\n    pub fn set_cursor_icon(&self, cursor_icon: CursorIcon) {\n        self.output_mut(|o| o.cursor_icon = cursor_icon);\n    }\n\n    /// Add a command to [`PlatformOutput::commands`],\n    /// for the integration to execute at the end of the frame.\n    pub fn send_cmd(&self, cmd: crate::OutputCommand) {\n        self.output_mut(|o| o.commands.push(cmd));\n    }\n\n    /// Open an URL in a browser.\n    ///\n    /// Equivalent to:\n    /// ```\n    /// # let ctx = egui::Context::default();\n    /// # let open_url = egui::OpenUrl::same_tab(\"http://www.example.com\");\n    /// ctx.send_cmd(egui::OutputCommand::OpenUrl(open_url));\n    /// ```\n    pub fn open_url(&self, open_url: crate::OpenUrl) {\n        self.send_cmd(crate::OutputCommand::OpenUrl(open_url));\n    }\n\n    /// Copy the given text to the system clipboard.\n    ///\n    /// Note that in web applications, the clipboard is only accessible in secure contexts (e.g.,\n    /// HTTPS or localhost). If this method is used outside of a secure context, it will log an\n    /// error and do nothing. See <https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts>.\n    pub fn copy_text(&self, text: String) {\n        self.send_cmd(crate::OutputCommand::CopyText(text));\n    }\n\n    /// Copy the given image to the system clipboard.\n    ///\n    /// Note that in web applications, the clipboard is only accessible in secure contexts (e.g.,\n    /// HTTPS or localhost). If this method is used outside of a secure context, it will log an\n    /// error and do nothing. See <https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts>.\n    pub fn copy_image(&self, image: crate::ColorImage) {\n        self.send_cmd(crate::OutputCommand::CopyImage(image));\n    }\n\n    fn can_show_modifier_symbols(&self) -> bool {\n        let ModifierNames {\n            alt,\n            ctrl,\n            shift,\n            mac_cmd,\n            ..\n        } = ModifierNames::SYMBOLS;\n\n        let font_id = TextStyle::Body.resolve(&self.global_style());\n        self.fonts_mut(|f| {\n            let mut font = f.fonts.font(&font_id.family);\n            font.has_glyphs(alt)\n                && font.has_glyphs(ctrl)\n                && font.has_glyphs(shift)\n                && font.has_glyphs(mac_cmd)\n        })\n    }\n\n    /// Format the given modifiers in a human-readable way (e.g. `Ctrl+Shift+X`).\n    pub fn format_modifiers(&self, modifiers: Modifiers) -> String {\n        let os = self.os();\n\n        let is_mac = os.is_mac();\n\n        if is_mac && self.can_show_modifier_symbols() {\n            ModifierNames::SYMBOLS.format(&modifiers, is_mac)\n        } else {\n            ModifierNames::NAMES.format(&modifiers, is_mac)\n        }\n    }\n\n    /// Format the given shortcut in a human-readable way (e.g. `Ctrl+Shift+X`).\n    ///\n    /// Can be used to get the text for [`crate::Button::shortcut_text`].\n    pub fn format_shortcut(&self, shortcut: &KeyboardShortcut) -> String {\n        let os = self.os();\n\n        let is_mac = os.is_mac();\n\n        if is_mac && self.can_show_modifier_symbols() {\n            shortcut.format(&ModifierNames::SYMBOLS, is_mac)\n        } else {\n            shortcut.format(&ModifierNames::NAMES, is_mac)\n        }\n    }\n\n    /// The total number of completed frames.\n    ///\n    /// Starts at zero, and is incremented once at the end of each call to [`Self::run`].\n    ///\n    /// This is always smaller or equal to [`Self::cumulative_pass_nr`].\n    pub fn cumulative_frame_nr(&self) -> u64 {\n        self.cumulative_frame_nr_for(self.viewport_id())\n    }\n\n    /// The total number of completed frames.\n    ///\n    /// Starts at zero, and is incremented once at the end of each call to [`Self::run`].\n    ///\n    /// This is always smaller or equal to [`Self::cumulative_pass_nr_for`].\n    pub fn cumulative_frame_nr_for(&self, id: ViewportId) -> u64 {\n        self.read(|ctx| {\n            ctx.viewports\n                .get(&id)\n                .map(|v| v.repaint.cumulative_frame_nr)\n                .unwrap_or_else(|| {\n                    if cfg!(debug_assertions) {\n                        panic!(\"cumulative_frame_nr_for failed to find the viewport {id:?}\");\n                    } else {\n                        0\n                    }\n                })\n        })\n    }\n\n    /// The total number of completed passes (usually there is one pass per rendered frame).\n    ///\n    /// Starts at zero, and is incremented for each completed pass inside of [`Self::run`] (usually once).\n    ///\n    /// If you instead want to know which pass index this is within the current frame,\n    /// use [`Self::current_pass_index`].\n    pub fn cumulative_pass_nr(&self) -> u64 {\n        self.cumulative_pass_nr_for(self.viewport_id())\n    }\n\n    /// The total number of completed passes (usually there is one pass per rendered frame).\n    ///\n    /// Starts at zero, and is incremented for each completed pass inside of [`Self::run`] (usually once).\n    pub fn cumulative_pass_nr_for(&self, id: ViewportId) -> u64 {\n        self.read(|ctx| {\n            ctx.viewports\n                .get(&id)\n                .map_or(0, |v| v.repaint.cumulative_pass_nr)\n        })\n    }\n\n    /// The index of the current pass in the current frame, starting at zero.\n    ///\n    /// Usually this is zero, but if something called [`Self::request_discard`] to do multi-pass layout,\n    /// then this will be incremented for each pass.\n    ///\n    /// This just reads the value of [`PlatformOutput::num_completed_passes`].\n    ///\n    /// To know the total number of passes ever completed, use [`Self::cumulative_pass_nr`].\n    pub fn current_pass_index(&self) -> usize {\n        self.output(|o| o.num_completed_passes)\n    }\n\n    /// Call this if there is need to repaint the UI, i.e. if you are showing an animation.\n    ///\n    /// If this is called at least once in a frame, then there will be another frame right after this.\n    /// Call as many times as you wish, only one repaint will be issued.\n    ///\n    /// To request repaint with a delay, use [`Self::request_repaint_after`].\n    ///\n    /// If called from outside the UI thread, the UI thread will wake up and run,\n    /// provided the egui integration has set that up via [`Self::set_request_repaint_callback`]\n    /// (this will work on `eframe`).\n    ///\n    /// This will repaint the current viewport.\n    #[track_caller]\n    pub fn request_repaint(&self) {\n        self.request_repaint_of(self.viewport_id());\n    }\n\n    /// Call this if there is need to repaint the UI, i.e. if you are showing an animation.\n    ///\n    /// If this is called at least once in a frame, then there will be another frame right after this.\n    /// Call as many times as you wish, only one repaint will be issued.\n    ///\n    /// To request repaint with a delay, use [`Self::request_repaint_after_for`].\n    ///\n    /// If called from outside the UI thread, the UI thread will wake up and run,\n    /// provided the egui integration has set that up via [`Self::set_request_repaint_callback`]\n    /// (this will work on `eframe`).\n    ///\n    /// This will repaint the specified viewport.\n    #[track_caller]\n    pub fn request_repaint_of(&self, id: ViewportId) {\n        let cause = RepaintCause::new();\n        self.write(|ctx| ctx.request_repaint(id, cause));\n    }\n\n    /// Request repaint after at most the specified duration elapses.\n    ///\n    /// The backend can chose to repaint sooner, for instance if some other code called\n    /// this method with a lower duration, or if new events arrived.\n    ///\n    /// The function can be multiple times, but only the *smallest* duration will be considered.\n    /// So, if the function is called two times with `1 second` and `2 seconds`, egui will repaint\n    /// after `1 second`\n    ///\n    /// This is primarily useful for applications who would like to save battery by avoiding wasted\n    /// redraws when the app is not in focus. But sometimes the GUI of the app might become stale\n    /// and outdated if it is not updated for too long.\n    ///\n    /// Let's say, something like a stopwatch widget that displays the time in seconds. You would waste\n    /// resources repainting multiple times within the same second (when you have no input),\n    /// just calculate the difference of duration between current time and next second change,\n    /// and call this function, to make sure that you are displaying the latest updated time, but\n    /// not wasting resources on needless repaints within the same second.\n    ///\n    /// ### Quirk:\n    /// Duration begins at the next frame. Let's say for example that it's a very inefficient app\n    /// and takes 500 milliseconds per frame at 2 fps. The widget / user might want a repaint in\n    /// next 500 milliseconds. Now, app takes 1000 ms per frame (1 fps) because the backend event\n    /// timeout takes 500 milliseconds AFTER the vsync swap buffer.\n    /// So, it's not that we are requesting repaint within X duration. We are rather timing out\n    /// during app idle time where we are not receiving any new input events.\n    ///\n    /// This repaints the current viewport.\n    #[track_caller]\n    pub fn request_repaint_after(&self, duration: Duration) {\n        self.request_repaint_after_for(duration, self.viewport_id());\n    }\n\n    /// Repaint after this many seconds.\n    ///\n    /// See [`Self::request_repaint_after`] for details.\n    #[track_caller]\n    pub fn request_repaint_after_secs(&self, seconds: f32) {\n        if let Ok(duration) = std::time::Duration::try_from_secs_f32(seconds) {\n            self.request_repaint_after(duration);\n        }\n    }\n\n    /// Request repaint after at most the specified duration elapses.\n    ///\n    /// The backend can chose to repaint sooner, for instance if some other code called\n    /// this method with a lower duration, or if new events arrived.\n    ///\n    /// The function can be multiple times, but only the *smallest* duration will be considered.\n    /// So, if the function is called two times with `1 second` and `2 seconds`, egui will repaint\n    /// after `1 second`\n    ///\n    /// This is primarily useful for applications who would like to save battery by avoiding wasted\n    /// redraws when the app is not in focus. But sometimes the GUI of the app might become stale\n    /// and outdated if it is not updated for too long.\n    ///\n    /// Let's say, something like a stopwatch widget that displays the time in seconds. You would waste\n    /// resources repainting multiple times within the same second (when you have no input),\n    /// just calculate the difference of duration between current time and next second change,\n    /// and call this function, to make sure that you are displaying the latest updated time, but\n    /// not wasting resources on needless repaints within the same second.\n    ///\n    /// ### Quirk:\n    /// Duration begins at the next frame. Let's say for example that it's a very inefficient app\n    /// and takes 500 milliseconds per frame at 2 fps. The widget / user might want a repaint in\n    /// next 500 milliseconds. Now, app takes 1000 ms per frame (1 fps) because the backend event\n    /// timeout takes 500 milliseconds AFTER the vsync swap buffer.\n    /// So, it's not that we are requesting repaint within X duration. We are rather timing out\n    /// during app idle time where we are not receiving any new input events.\n    ///\n    /// This repaints the specified viewport.\n    #[track_caller]\n    pub fn request_repaint_after_for(&self, duration: Duration, id: ViewportId) {\n        let cause = RepaintCause::new();\n        self.write(|ctx| ctx.request_repaint_after(duration, id, cause));\n    }\n\n    /// Was a repaint requested last pass for the current viewport?\n    #[must_use]\n    pub fn requested_repaint_last_pass(&self) -> bool {\n        self.requested_repaint_last_pass_for(&self.viewport_id())\n    }\n\n    /// Was a repaint requested last pass for the given viewport?\n    #[must_use]\n    pub fn requested_repaint_last_pass_for(&self, viewport_id: &ViewportId) -> bool {\n        self.read(|ctx| ctx.requested_immediate_repaint_prev_pass(viewport_id))\n    }\n\n    /// Has a repaint been requested for the current viewport?\n    #[must_use]\n    pub fn has_requested_repaint(&self) -> bool {\n        self.has_requested_repaint_for(&self.viewport_id())\n    }\n\n    /// Has a repaint been requested for the given viewport?\n    #[must_use]\n    pub fn has_requested_repaint_for(&self, viewport_id: &ViewportId) -> bool {\n        self.read(|ctx| ctx.has_requested_repaint(viewport_id))\n    }\n\n    /// Why are we repainting?\n    ///\n    /// This can be helpful in debugging why egui is constantly repainting.\n    pub fn repaint_causes(&self) -> Vec<RepaintCause> {\n        self.read(|ctx| {\n            ctx.viewports\n                .get(&ctx.viewport_id())\n                .map(|v| v.repaint.prev_causes.clone())\n        })\n        .unwrap_or_default()\n    }\n\n    /// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`] or [`Self::request_repaint_after`].\n    ///\n    /// This lets you wake up a sleeping UI thread.\n    ///\n    /// Note that only one callback can be set. Any new call overrides the previous callback.\n    pub fn set_request_repaint_callback(\n        &self,\n        callback: impl Fn(RequestRepaintInfo) + Send + Sync + 'static,\n    ) {\n        let callback = Box::new(callback);\n        self.write(|ctx| ctx.request_repaint_callback = Some(callback));\n    }\n\n    /// Request to discard the visual output of this pass,\n    /// and to immediately do another one.\n    ///\n    /// This can be called to cover up visual glitches during a \"sizing pass\".\n    /// For instance, when a [`crate::Grid`] is first shown we don't yet know the\n    /// width and heights of its columns and rows. egui will do a best guess,\n    /// but it will likely be wrong. Next pass it can read the sizes from the previous\n    /// pass, and from there on the widths will be stable.\n    /// This means the first pass will look glitchy, and ideally should not be shown to the user.\n    /// So [`crate::Grid`] calls [`Self::request_discard`] to cover up this glitches.\n    ///\n    /// There is a limit to how many passes egui will perform, set by [`Options::max_passes`] (default=2).\n    /// Therefore, the request might be declined.\n    ///\n    /// You can check if the current pass will be discarded with [`Self::will_discard`].\n    ///\n    /// You should be very conservative with when you call [`Self::request_discard`],\n    /// as it will cause an extra ui pass, potentially leading to extra CPU use and frame judder.\n    ///\n    /// The given reason should be a human-readable string that explains why `request_discard`\n    /// was called. This will be shown in certain debug situations, to help you figure out\n    /// why a pass was discarded.\n    #[track_caller]\n    pub fn request_discard(&self, reason: impl Into<Cow<'static, str>>) {\n        let cause = RepaintCause::new_reason(reason);\n        self.output_mut(|o| o.request_discard_reasons.push(cause));\n\n        log::trace!(\n            \"request_discard: {}\",\n            if self.will_discard() {\n                \"allowed\"\n            } else {\n                \"denied\"\n            }\n        );\n    }\n\n    /// Will the visual output of this pass be discarded?\n    ///\n    /// If true, you can early-out from expensive graphics operations.\n    ///\n    /// See [`Self::request_discard`] for more.\n    pub fn will_discard(&self) -> bool {\n        self.write(|ctx| {\n            let vp = ctx.viewport();\n            // NOTE: `num_passes` is incremented\n            vp.output.requested_discard()\n                && vp.output.num_completed_passes + 1 < ctx.memory.options.max_passes.get()\n        })\n    }\n}\n\n/// Callbacks\nimpl Context {\n    /// Call the given callback at the start of each pass of each viewport.\n    ///\n    /// This is a convenience wrapper around [`Self::add_plugin`].\n    pub fn on_begin_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {\n        self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {\n            p.on_begin_plugins.push((debug_name, cb));\n        });\n    }\n\n    /// Call the given callback at the end of each pass of each viewport.\n    ///\n    /// This is a convenience wrapper around [`Self::add_plugin`].\n    pub fn on_end_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {\n        self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {\n            p.on_end_plugins.push((debug_name, cb));\n        });\n    }\n\n    /// Register a [`Plugin`](plugin::Plugin)\n    ///\n    /// Plugins are called in the order they are added.\n    ///\n    /// A plugin of the same type can only be added once (further calls with the same type will be ignored).\n    /// This way it's convenient to add plugins in `eframe::run_simple_native`.\n    pub fn add_plugin(&self, plugin: impl plugin::Plugin + 'static) {\n        let handle = plugin::PluginHandle::new(plugin);\n\n        let added = self.write(|ctx| ctx.plugins.add(Arc::clone(&handle)));\n\n        if added {\n            handle.lock().dyn_plugin_mut().setup(self);\n        }\n    }\n\n    /// Call the provided closure with the plugin of type `T`, if it was registered.\n    ///\n    /// Returns `None` if the plugin was not registered.\n    pub fn with_plugin<T: plugin::Plugin + 'static, R>(\n        &self,\n        f: impl FnOnce(&mut T) -> R,\n    ) -> Option<R> {\n        let plugin = self.read(|ctx| ctx.plugins.get(std::any::TypeId::of::<T>()));\n        plugin.map(|plugin| f(plugin.lock().typed_plugin_mut()))\n    }\n\n    /// Get a handle to the plugin of type `T`.\n    ///\n    /// ## Panics\n    /// If the plugin of type `T` was not registered, this will panic.\n    pub fn plugin<T: plugin::Plugin>(&self) -> TypedPluginHandle<T> {\n        if let Some(plugin) = self.plugin_opt() {\n            plugin\n        } else {\n            panic!(\"Plugin of type {:?} not found\", std::any::type_name::<T>());\n        }\n    }\n\n    /// Get a handle to the plugin of type `T`, if it was registered.\n    pub fn plugin_opt<T: plugin::Plugin>(&self) -> Option<TypedPluginHandle<T>> {\n        let plugin = self.read(|ctx| ctx.plugins.get(std::any::TypeId::of::<T>()));\n        plugin.map(TypedPluginHandle::new)\n    }\n\n    /// Get a handle to the plugin of type `T`, or insert its default.\n    pub fn plugin_or_default<T: plugin::Plugin + Default>(&self) -> TypedPluginHandle<T> {\n        if let Some(plugin) = self.plugin_opt() {\n            plugin\n        } else {\n            let default_plugin = T::default();\n            self.add_plugin(default_plugin);\n            self.plugin()\n        }\n    }\n}\n\nimpl Context {\n    /// Tell `egui` which fonts to use.\n    ///\n    /// The default `egui` fonts only support latin and cyrillic alphabets,\n    /// but you can call this to install additional fonts that support e.g. korean characters.\n    ///\n    /// The new fonts will become active at the start of the next pass.\n    /// This will overwrite the existing fonts.\n    pub fn set_fonts(&self, font_definitions: FontDefinitions) {\n        profiling::function_scope!();\n\n        let update_fonts = self.read(|ctx| {\n            // NOTE: this comparison is expensive since it checks TTF data for equality\n            // TODO(valadaptive): add_font only checks the *names* for equality. Change this?\n            ctx.fonts\n                .as_ref()\n                .is_none_or(|fonts| fonts.definitions() != &font_definitions)\n        });\n\n        if update_fonts {\n            self.memory_mut(|mem| mem.new_font_definitions = Some(font_definitions));\n        }\n    }\n\n    /// Tell `egui` which fonts to use.\n    ///\n    /// The default `egui` fonts only support latin and cyrillic alphabets,\n    /// but you can call this to install additional fonts that support e.g. korean characters.\n    ///\n    /// The new font will become active at the start of the next pass.\n    /// This will keep the existing fonts.\n    pub fn add_font(&self, new_font: FontInsert) {\n        profiling::function_scope!();\n\n        let mut update_fonts = true;\n\n        self.read(|ctx| {\n            if let Some(current_fonts) = ctx.fonts.as_ref()\n                && current_fonts\n                    .definitions()\n                    .font_data\n                    .contains_key(&new_font.name)\n            {\n                update_fonts = false; // no need to update\n            }\n        });\n\n        if update_fonts {\n            self.memory_mut(|mem| mem.add_fonts.push(new_font));\n        }\n    }\n\n    /// Does the OS use dark or light mode?\n    /// This is used when the theme preference is set to [`crate::ThemePreference::System`].\n    pub fn system_theme(&self) -> Option<Theme> {\n        self.memory(|mem| mem.options.system_theme)\n    }\n\n    /// The [`Theme`] used to select the appropriate [`Style`] (dark or light)\n    /// used by all subsequent popups, menus, etc.\n    pub fn theme(&self) -> Theme {\n        self.options(|opt| opt.theme())\n    }\n\n    /// The [`Theme`] used to select between dark and light [`Self::style`]\n    /// as the active style used by all subsequent popups, menus, etc.\n    ///\n    /// Example:\n    /// ```\n    /// # let mut ctx = egui::Context::default();\n    /// ctx.set_theme(egui::Theme::Light); // Switch to light mode\n    /// ```\n    pub fn set_theme(&self, theme_preference: impl Into<crate::ThemePreference>) {\n        self.options_mut(|opt| opt.theme_preference = theme_preference.into());\n    }\n\n    /// The currently active [`Style`] used by all subsequent popups, menus, etc.\n    pub fn global_style(&self) -> Arc<Style> {\n        self.options(|opt| Arc::clone(opt.style()))\n    }\n\n    /// The currently active [`Style`] used by all subsequent popups, menus, etc.\n    #[deprecated = \"Renamed to `global_style` to avoid confusion with `ui.style()`\"]\n    pub fn style(&self) -> Arc<Style> {\n        self.options(|opt| Arc::clone(opt.style()))\n    }\n\n    /// Mutate the currently active [`Style`] used by all subsequent popups, menus, etc.\n    /// Use [`Self::all_styles_mut`] to mutate both dark and light mode styles.\n    ///\n    /// Example:\n    /// ```\n    /// # let mut ctx = egui::Context::default();\n    /// ctx.global_style_mut(|style| {\n    ///     style.spacing.item_spacing = egui::vec2(10.0, 20.0);\n    /// });\n    /// ```\n    pub fn global_style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {\n        self.options_mut(|opt| mutate_style(Arc::make_mut(opt.style_mut())));\n    }\n\n    /// Mutate the currently active [`Style`] used by all subsequent popups, menus, etc.\n    /// Use [`Self::all_styles_mut`] to mutate both dark and light mode styles.\n    ///\n    /// Example:\n    /// ```\n    /// # let mut ctx = egui::Context::default();\n    /// ctx.global_style_mut(|style| {\n    ///     style.spacing.item_spacing = egui::vec2(10.0, 20.0);\n    /// });\n    /// ```\n    #[deprecated = \"Renamed to `global_style_mut` to avoid confusion with `ui.style_mut()`\"]\n    pub fn style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {\n        self.options_mut(|opt| mutate_style(Arc::make_mut(opt.style_mut())));\n    }\n\n    /// The currently active [`Style`] used by all new popups, menus, etc.\n    ///\n    /// Use [`Self::all_styles_mut`] to mutate both dark and light mode styles.\n    ///\n    /// You can also change this using [`Self::global_style_mut`].\n    ///\n    /// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].\n    pub fn set_global_style(&self, style: impl Into<Arc<Style>>) {\n        self.options_mut(|opt| *opt.style_mut() = style.into());\n    }\n\n    /// The currently active [`Style`] used by all new popups, menus, etc.\n    ///\n    /// Use [`Self::all_styles_mut`] to mutate both dark and light mode styles.\n    ///\n    /// You can also change this using [`Self::style_mut`].\n    ///\n    /// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].\n    #[deprecated = \"Renamed to `set_global_style` to avoid confusion with `ui.set_style()`\"]\n    pub fn set_style(&self, style: impl Into<Arc<Style>>) {\n        self.options_mut(|opt| *opt.style_mut() = style.into());\n    }\n\n    /// Mutate the [`Style`]s used by all subsequent popups, menus, etc. in both dark and light mode.\n    ///\n    /// Example:\n    /// ```\n    /// # let mut ctx = egui::Context::default();\n    /// ctx.all_styles_mut(|style| {\n    ///     style.spacing.item_spacing = egui::vec2(10.0, 20.0);\n    /// });\n    /// ```\n    pub fn all_styles_mut(&self, mut mutate_style: impl FnMut(&mut Style)) {\n        self.options_mut(|opt| {\n            mutate_style(Arc::make_mut(&mut opt.dark_style));\n            mutate_style(Arc::make_mut(&mut opt.light_style));\n        });\n    }\n\n    /// The [`Style`] used by all subsequent popups, menus, etc.\n    pub fn style_of(&self, theme: Theme) -> Arc<Style> {\n        self.options(|opt| match theme {\n            Theme::Dark => Arc::clone(&opt.dark_style),\n            Theme::Light => Arc::clone(&opt.light_style),\n        })\n    }\n\n    /// Mutate the [`Style`] used by all subsequent popups, menus, etc.\n    ///\n    /// Example:\n    /// ```\n    /// # let mut ctx = egui::Context::default();\n    /// ctx.style_mut_of(egui::Theme::Dark, |style| {\n    ///     style.spacing.item_spacing = egui::vec2(10.0, 20.0);\n    /// });\n    /// ```\n    pub fn style_mut_of(&self, theme: Theme, mutate_style: impl FnOnce(&mut Style)) {\n        self.options_mut(|opt| match theme {\n            Theme::Dark => mutate_style(Arc::make_mut(&mut opt.dark_style)),\n            Theme::Light => mutate_style(Arc::make_mut(&mut opt.light_style)),\n        });\n    }\n\n    /// The [`Style`] used by all new popups, menus, etc.\n    /// Use [`Self::set_theme`] to choose between dark and light mode.\n    ///\n    /// You can also change this using [`Self::style_mut_of`].\n    ///\n    /// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].\n    pub fn set_style_of(&self, theme: Theme, style: impl Into<Arc<Style>>) {\n        let style = style.into();\n        self.options_mut(|opt| match theme {\n            Theme::Dark => opt.dark_style = style,\n            Theme::Light => opt.light_style = style,\n        });\n    }\n\n    /// The [`crate::Visuals`] used by all subsequent popups, menus, etc.\n    ///\n    /// You can also use [`Ui::visuals_mut`] to change the visuals of a single [`Ui`].\n    ///\n    /// Example:\n    /// ```\n    /// # let mut ctx = egui::Context::default();\n    /// ctx.set_visuals_of(egui::Theme::Dark, egui::Visuals { panel_fill: egui::Color32::RED, ..Default::default() });\n    /// ```\n    pub fn set_visuals_of(&self, theme: Theme, visuals: crate::Visuals) {\n        self.style_mut_of(theme, |style| style.visuals = visuals);\n    }\n\n    /// The [`crate::Visuals`] used by all subsequent popups, menus, etc.\n    ///\n    /// You can also use [`Ui::visuals_mut`] to change the visuals of a single [`Ui`].\n    ///\n    /// Example:\n    /// ```\n    /// # let mut ctx = egui::Context::default();\n    /// ctx.set_visuals(egui::Visuals { panel_fill: egui::Color32::RED, ..Default::default() });\n    /// ```\n    pub fn set_visuals(&self, visuals: crate::Visuals) {\n        self.style_mut_of(self.theme(), |style| style.visuals = visuals);\n    }\n\n    /// The number of physical pixels for each logical point.\n    ///\n    /// This is calculated as [`Self::zoom_factor`] * [`Self::native_pixels_per_point`]\n    #[inline(always)]\n    pub fn pixels_per_point(&self) -> f32 {\n        self.input(|i| i.pixels_per_point)\n    }\n\n    /// Set the number of physical pixels for each logical point.\n    /// Will become active at the start of the next pass.\n    ///\n    /// This will actually translate to a call to [`Self::set_zoom_factor`].\n    pub fn set_pixels_per_point(&self, pixels_per_point: f32) {\n        if pixels_per_point != self.pixels_per_point() {\n            self.set_zoom_factor(pixels_per_point / self.native_pixels_per_point().unwrap_or(1.0));\n        }\n    }\n\n    /// The number of physical pixels for each logical point on this monitor.\n    ///\n    /// This is given as input to egui via [`crate::ViewportInfo::native_pixels_per_point`]\n    /// and cannot be changed.\n    #[inline(always)]\n    pub fn native_pixels_per_point(&self) -> Option<f32> {\n        self.input(|i| i.viewport().native_pixels_per_point)\n    }\n\n    /// Global zoom factor of the UI.\n    ///\n    /// This is used to calculate the `pixels_per_point`\n    /// for the UI as `pixels_per_point = zoom_factor * native_pixels_per_point`.\n    ///\n    /// The default is 1.0.\n    /// Make larger to make everything larger.\n    #[inline(always)]\n    pub fn zoom_factor(&self) -> f32 {\n        self.options(|o| o.zoom_factor)\n    }\n\n    /// Sets zoom factor of the UI.\n    /// Will become active at the start of the next pass.\n    ///\n    /// Note that calling this will not update [`Self::zoom_factor`] until the end of the pass.\n    ///\n    /// This is used to calculate the `pixels_per_point`\n    /// for the UI as `pixels_per_point = zoom_fator * native_pixels_per_point`.\n    ///\n    /// The default is 1.0.\n    /// Make larger to make everything larger.\n    ///\n    /// It is better to call this than modifying\n    /// [`Options::zoom_factor`].\n    #[inline(always)]\n    pub fn set_zoom_factor(&self, zoom_factor: f32) {\n        let cause = RepaintCause::new();\n        self.write(|ctx| {\n            if ctx.memory.options.zoom_factor != zoom_factor {\n                ctx.new_zoom_factor = Some(zoom_factor);\n                #[expect(clippy::iter_over_hash_type)]\n                for viewport_id in ctx.all_viewport_ids() {\n                    ctx.request_repaint(viewport_id, cause.clone());\n                }\n            }\n        });\n    }\n\n    /// Allocate a texture.\n    ///\n    /// This is for advanced users.\n    /// Most users should use [`crate::Ui::image`] or [`Self::try_load_texture`]\n    /// instead.\n    ///\n    /// In order to display an image you must convert it to a texture using this function.\n    /// The function will hand over the image data to the egui backend, which will\n    /// upload it to the GPU.\n    ///\n    /// ⚠️ Make sure to only call this ONCE for each image, i.e. NOT in your main GUI code.\n    /// The call is NOT immediate safe.\n    ///\n    /// The given name can be useful for later debugging, and will be visible if you call [`Self::texture_ui`].\n    ///\n    /// For how to load an image, see [`crate::ImageData`] and [`crate::ColorImage::from_rgba_unmultiplied`].\n    ///\n    /// ```\n    /// struct MyImage {\n    ///     texture: Option<egui::TextureHandle>,\n    /// }\n    ///\n    /// impl MyImage {\n    ///     fn ui(&mut self, ui: &mut egui::Ui) {\n    ///         let texture: &egui::TextureHandle = self.texture.get_or_insert_with(|| {\n    ///             // Load the texture only once.\n    ///             ui.ctx().load_texture(\n    ///                 \"my-image\",\n    ///                 egui::ColorImage::example(),\n    ///                 Default::default()\n    ///             )\n    ///         });\n    ///\n    ///         // Show the image:\n    ///         ui.image((texture.id(), texture.size_vec2()));\n    ///     }\n    /// }\n    /// ```\n    ///\n    /// See also [`crate::ImageData`], [`crate::Ui::image`] and [`crate::Image`].\n    pub fn load_texture(\n        &self,\n        name: impl Into<String>,\n        image: impl Into<ImageData>,\n        options: TextureOptions,\n    ) -> TextureHandle {\n        let name = name.into();\n        let image = image.into();\n        let max_texture_side = self.input(|i| i.max_texture_side);\n        debug_assert!(\n            image.width() <= max_texture_side && image.height() <= max_texture_side,\n            \"Texture {:?} has size {}x{}, but the maximum texture side is {}\",\n            name,\n            image.width(),\n            image.height(),\n            max_texture_side\n        );\n        let tex_mngr = self.tex_manager();\n        let tex_id = tex_mngr.write().alloc(name, image, options);\n        TextureHandle::new(tex_mngr, tex_id)\n    }\n\n    /// Low-level texture manager.\n    ///\n    /// In general it is easier to use [`Self::load_texture`] and [`TextureHandle`].\n    ///\n    /// You can show stats about the allocated textures using [`Self::texture_ui`].\n    pub fn tex_manager(&self) -> Arc<RwLock<epaint::textures::TextureManager>> {\n        self.read(|ctx| Arc::clone(&ctx.tex_manager.0))\n    }\n\n    // ---------------------------------------------------------------------\n\n    /// Constrain the position of a window/area so it fits within the provided boundary.\n    pub(crate) fn constrain_window_rect_to_area(window: Rect, area: Rect) -> Rect {\n        let mut pos = window.min;\n\n        // Constrain to screen, unless window is too large to fit:\n        let margin_x = (window.width() - area.width()).at_least(0.0);\n        let margin_y = (window.height() - area.height()).at_least(0.0);\n\n        pos.x = pos.x.at_most(area.right() + margin_x - window.width()); // move left if needed\n        pos.x = pos.x.at_least(area.left() - margin_x); // move right if needed\n        pos.y = pos.y.at_most(area.bottom() + margin_y - window.height()); // move right if needed\n        pos.y = pos.y.at_least(area.top() - margin_y); // move down if needed\n\n        Rect::from_min_size(pos, window.size()).round_ui()\n    }\n}\n\nimpl Context {\n    /// Call at the end of each frame if you called [`Context::begin_pass`].\n    #[must_use]\n    pub fn end_pass(&self) -> FullOutput {\n        profiling::function_scope!();\n\n        if self.options(|o| o.zoom_with_keyboard) {\n            crate::gui_zoom::zoom_with_keyboard(self);\n        }\n\n        for shortcut in self.options(|o| o.quit_shortcuts.clone()) {\n            if self.input_mut(|i| i.consume_shortcut(&shortcut)) {\n                self.send_viewport_cmd(ViewportCommand::Close);\n            }\n        }\n\n        #[cfg(debug_assertions)]\n        self.debug_painting();\n\n        let mut output = self.write(|ctx| ctx.end_pass());\n\n        let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());\n        plugins.on_output(&mut output);\n\n        output\n    }\n\n    /// Call at the end of each frame if you called [`Context::begin_pass`].\n    #[must_use]\n    #[deprecated = \"Renamed end_pass\"]\n    pub fn end_frame(&self) -> FullOutput {\n        self.end_pass()\n    }\n\n    /// Called at the end of the pass.\n    #[cfg(debug_assertions)]\n    fn debug_painting(&self) {\n        #![expect(clippy::iter_over_hash_type)] // ok to be sloppy in debug painting\n\n        let paint_widget = |widget: &WidgetRect, text: &str, color: Color32| {\n            let rect = widget.interact_rect;\n            if rect.is_positive() {\n                let painter = Painter::new(self.clone(), widget.layer_id, Rect::EVERYTHING);\n                painter.debug_rect(rect, color, text);\n            }\n        };\n\n        let paint_widget_id = |id: Id, text: &str, color: Color32| {\n            if let Some(widget) =\n                self.write(|ctx| ctx.viewport().this_pass.widgets.get(id).copied())\n            {\n                let text = format!(\"{text} - {id:?}\");\n                paint_widget(&widget, &text, color);\n            }\n        };\n\n        if self.global_style().debug.show_interactive_widgets {\n            // Show all interactive widgets:\n            let rects = self.write(|ctx| ctx.viewport().this_pass.widgets.clone());\n            for (layer_id, rects) in rects.layers() {\n                let painter = Painter::new(self.clone(), *layer_id, Rect::EVERYTHING);\n                for rect in rects {\n                    if rect.sense.interactive() {\n                        let (color, text) = if rect.sense.senses_click() && rect.sense.senses_drag()\n                        {\n                            (Color32::from_rgb(0x88, 0, 0x88), \"click+drag\")\n                        } else if rect.sense.senses_click() {\n                            (Color32::from_rgb(0x88, 0, 0), \"click\")\n                        } else if rect.sense.senses_drag() {\n                            (Color32::from_rgb(0, 0, 0x88), \"drag\")\n                        } else {\n                            // unreachable since we only show interactive\n                            (Color32::from_rgb(0, 0, 0x88), \"hover\")\n                        };\n                        painter.debug_rect(rect.interact_rect, color, text);\n                    }\n                }\n            }\n\n            // Show the ones actually interacted with:\n            {\n                let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());\n                let InteractionSnapshot {\n                    clicked,\n                    long_touched: _,\n                    drag_started: _,\n                    dragged,\n                    drag_stopped: _,\n                    contains_pointer,\n                    hovered,\n                } = interact_widgets;\n\n                if true {\n                    for &id in &contains_pointer {\n                        paint_widget_id(id, \"contains_pointer\", Color32::BLUE);\n                    }\n\n                    let widget_rects = self.write(|w| w.viewport().this_pass.widgets.clone());\n\n                    let mut contains_pointer: Vec<Id> = contains_pointer.iter().copied().collect();\n                    contains_pointer.sort_by_key(|&id| {\n                        widget_rects\n                            .order(id)\n                            .map(|(layer_id, order_in_layer)| (layer_id.order, order_in_layer))\n                    });\n\n                    let mut debug_text = \"Widgets in order:\\n\".to_owned();\n                    for id in contains_pointer {\n                        let mut widget_text = format!(\"{id:?}\");\n                        if let Some(rect) = widget_rects.get(id) {\n                            widget_text +=\n                                &format!(\" {:?} {:?} {:?}\", rect.layer_id, rect.rect, rect.sense);\n                        }\n                        if let Some(info) = widget_rects.info(id) {\n                            widget_text += &format!(\" {info:?}\");\n                        }\n                        debug_text += &format!(\"{widget_text}\\n\");\n                    }\n                    self.debug_text(debug_text);\n                }\n                if true {\n                    for widget in hovered {\n                        paint_widget_id(widget, \"hovered\", Color32::WHITE);\n                    }\n                }\n                if let Some(widget) = clicked {\n                    paint_widget_id(widget, \"clicked\", Color32::RED);\n                }\n                if let Some(widget) = dragged {\n                    paint_widget_id(widget, \"dragged\", Color32::GREEN);\n                }\n            }\n        }\n\n        if self.global_style().debug.show_widget_hits {\n            let hits = self.write(|ctx| ctx.viewport().hits.clone());\n            let WidgetHits {\n                close,\n                contains_pointer,\n                click,\n                drag,\n            } = hits;\n\n            if false {\n                for widget in &close {\n                    paint_widget(widget, \"close\", Color32::from_gray(70));\n                }\n            }\n            if true {\n                for widget in &contains_pointer {\n                    paint_widget(widget, \"contains_pointer\", Color32::BLUE);\n                }\n            }\n            if let Some(widget) = &click {\n                paint_widget(widget, \"click\", Color32::RED);\n            }\n            if let Some(widget) = &drag {\n                paint_widget(widget, \"drag\", Color32::GREEN);\n            }\n        }\n\n        if self.global_style().debug.show_focused_widget\n            && let Some(focused_id) = self.memory(|mem| mem.focused())\n        {\n            paint_widget_id(focused_id, \"focused\", Color32::PURPLE);\n        }\n\n        if let Some(debug_rect) = self.pass_state_mut(|fs| fs.debug_rect.take()) {\n            debug_rect.paint(&self.debug_painter());\n        }\n\n        let num_multipass_in_row = self.viewport(|vp| vp.num_multipass_in_row);\n        if 3 <= num_multipass_in_row {\n            // If you see this message, it means we've been paying the cost of multi-pass for multiple frames in a row.\n            // This is likely a bug. `request_discard` should only be called in rare situations, when some layout changes.\n\n            let mut warning = format!(\n                \"egui PERF WARNING: request_discard has been called {num_multipass_in_row} frames in a row\"\n            );\n            self.viewport(|vp| {\n                for reason in &vp.output.request_discard_reasons {\n                    warning += &format!(\"\\n  {reason}\");\n                }\n            });\n\n            self.debug_painter()\n                .debug_text(Pos2::ZERO, Align2::LEFT_TOP, Color32::RED, warning);\n        }\n    }\n}\n\nimpl ContextImpl {\n    fn end_pass(&mut self) -> FullOutput {\n        let ended_viewport_id = self.viewport_id();\n        let viewport = self.viewports.entry(ended_viewport_id).or_default();\n        let pixels_per_point = viewport.input.pixels_per_point;\n\n        self.loaders.end_pass(viewport.repaint.cumulative_pass_nr);\n\n        viewport.repaint.cumulative_pass_nr += 1;\n\n        self.memory.end_pass(&viewport.this_pass.used_ids);\n\n        if let Some(fonts) = self.fonts.as_mut() {\n            let tex_mngr = &mut self.tex_manager.0.write();\n            if let Some(font_image_delta) = fonts.font_image_delta() {\n                // A partial font atlas update, e.g. a new glyph has been entered.\n                tex_mngr.set(TextureId::default(), font_image_delta);\n            }\n        }\n\n        // Inform the backend of all textures that have been updated (including font atlas).\n        let textures_delta = self.tex_manager.0.write().take_delta();\n\n        let mut platform_output: PlatformOutput = std::mem::take(&mut viewport.output);\n\n        {\n            profiling::scope!(\"accesskit\");\n            let state = viewport.this_pass.accesskit_state.take();\n            if let Some(state) = state {\n                let root_id = crate::accesskit_root_id().accesskit_id();\n                let nodes = {\n                    state\n                        .nodes\n                        .into_iter()\n                        .map(|(id, node)| (id.accesskit_id(), node))\n                        .collect()\n                };\n                let focus_id = self\n                    .memory\n                    .focused()\n                    .map_or(root_id, |id| id.accesskit_id());\n                platform_output.accesskit_update = Some(accesskit::TreeUpdate {\n                    nodes,\n                    tree: Some(accesskit::Tree::new(root_id)),\n                    tree_id: accesskit::TreeId::ROOT,\n                    focus: focus_id,\n                });\n            }\n        }\n\n        let shapes = viewport\n            .graphics\n            .drain(self.memory.areas().order(), &self.memory.to_global);\n\n        let mut repaint_needed = false;\n\n        if self.memory.options.repaint_on_widget_change {\n            profiling::scope!(\"compare-widget-rects\");\n            #[allow(clippy::allow_attributes, clippy::collapsible_if)] // false positive on wasm\n            if viewport.prev_pass.widgets != viewport.this_pass.widgets {\n                repaint_needed = true; // Some widget has moved\n            }\n        }\n\n        #[cfg(debug_assertions)]\n        let shapes = if self.memory.options.style().debug.warn_if_rect_changes_id {\n            let mut shapes = shapes;\n            warn_if_rect_changes_id(\n                &mut shapes,\n                &viewport.prev_pass.widgets,\n                &viewport.this_pass.widgets,\n            );\n            shapes\n        } else {\n            shapes\n        };\n\n        std::mem::swap(&mut viewport.prev_pass, &mut viewport.this_pass);\n\n        if repaint_needed {\n            self.request_repaint(ended_viewport_id, RepaintCause::new());\n        }\n        //  -------------------\n\n        let all_viewport_ids = self.all_viewport_ids();\n\n        self.last_viewport = ended_viewport_id;\n\n        self.viewports.retain(|&id, viewport| {\n            if id == ViewportId::ROOT {\n                return true; // never remove the root\n            }\n\n            let parent = *self.viewport_parents.entry(id).or_default();\n\n            if !all_viewport_ids.contains(&parent) {\n                log::debug!(\n                    \"Removing viewport {:?} ({:?}): the parent is gone\",\n                    id,\n                    viewport.builder.title\n                );\n\n                return false;\n            }\n\n            let is_our_child = parent == ended_viewport_id && id != ViewportId::ROOT;\n            if is_our_child {\n                if !viewport.used {\n                    log::debug!(\n                        \"Removing viewport {:?} ({:?}): it was never used this pass\",\n                        id,\n                        viewport.builder.title\n                    );\n\n                    return false; // Only keep children that have been updated this pass\n                }\n\n                viewport.used = false; // reset so we can check again next pass\n            }\n\n            true\n        });\n\n        // If we are an immediate viewport, this will resume the previous viewport.\n        self.viewport_stack.pop();\n\n        // The last viewport is not necessarily the root viewport,\n        // just the top _immediate_ viewport.\n        let is_last = self.viewport_stack.is_empty();\n\n        let viewport_output = self\n            .viewports\n            .iter_mut()\n            .map(|(&id, viewport)| {\n                let parent = *self.viewport_parents.entry(id).or_default();\n                let commands = if is_last {\n                    // Let the primary immediate viewport handle the commands of its children too.\n                    // This can make things easier for the backend, as otherwise we may get commands\n                    // that affect a viewport while its egui logic is running.\n                    std::mem::take(&mut viewport.commands)\n                } else {\n                    vec![]\n                };\n\n                (\n                    id,\n                    ViewportOutput {\n                        parent,\n                        class: viewport.class,\n                        builder: viewport.builder.clone(),\n                        viewport_ui_cb: viewport.viewport_ui_cb.clone(),\n                        commands,\n                        repaint_delay: viewport.repaint.repaint_delay,\n                    },\n                )\n            })\n            .collect();\n\n        if is_last {\n            // Remove dead viewports:\n            self.viewports.retain(|id, _| all_viewport_ids.contains(id));\n            debug_assert!(\n                self.viewports.contains_key(&ViewportId::ROOT),\n                \"Bug in egui: we removed the root viewport\"\n            );\n            self.viewport_parents\n                .retain(|id, _| all_viewport_ids.contains(id));\n        } else {\n            let viewport_id = self.viewport_id();\n            self.memory.set_viewport_id(viewport_id);\n        }\n\n        platform_output.num_completed_passes += 1;\n\n        FullOutput {\n            platform_output,\n            textures_delta,\n            shapes,\n            pixels_per_point,\n            viewport_output,\n        }\n    }\n}\n\nimpl Context {\n    /// Tessellate the given shapes into triangle meshes.\n    ///\n    /// `pixels_per_point` is used for feathering (anti-aliasing).\n    /// For this you can use [`FullOutput::pixels_per_point`], [`Self::pixels_per_point`],\n    /// or whatever is appropriate for your viewport.\n    pub fn tessellate(\n        &self,\n        shapes: Vec<ClippedShape>,\n        pixels_per_point: f32,\n    ) -> Vec<ClippedPrimitive> {\n        profiling::function_scope!();\n\n        // A tempting optimization is to reuse the tessellation from last frame if the\n        // shapes are the same, but just comparing the shapes takes about 50% of the time\n        // it takes to tessellate them, so it is not a worth optimization.\n\n        self.write(|ctx| {\n            let tessellation_options = ctx.memory.options.tessellation_options;\n            let texture_atlas = if let Some(fonts) = ctx.fonts.as_ref() {\n                fonts.texture_atlas()\n            } else {\n                log::warn!(\"No font size matching {pixels_per_point} pixels per point found.\");\n                ctx.fonts\n                    .iter()\n                    .next()\n                    .expect(\"No fonts loaded\")\n                    .texture_atlas()\n            };\n\n            let paint_stats = PaintStats::from_shapes(&shapes);\n            let clipped_primitives = {\n                profiling::scope!(\"tessellator::tessellate_shapes\");\n                tessellator::Tessellator::new(\n                    pixels_per_point,\n                    tessellation_options,\n                    texture_atlas.size(),\n                    texture_atlas.prepared_discs(),\n                )\n                .tessellate_shapes(shapes)\n            };\n            ctx.paint_stats = paint_stats.with_clipped_primitives(&clipped_primitives);\n            clipped_primitives\n        })\n    }\n\n    // ---------------------------------------------------------------------\n\n    /// Returns the position and size of the egui area that is safe for content rendering.\n    ///\n    /// Returns [`Self::viewport_rect`] minus areas that might be partially covered by, for example,\n    /// the OS status bar or display notches.\n    ///\n    /// If you want to render behind e.g. the dynamic island on iOS, use [`Self::viewport_rect`].\n    pub fn content_rect(&self) -> Rect {\n        self.input(|i| i.content_rect()).round_ui()\n    }\n\n    /// Returns the position and size of the full area available to egui\n    ///\n    /// This includes reas that might be partially covered by, for example, the OS status bar or\n    /// display notches. See [`Self::content_rect`] to get a rect that is safe for content.\n    ///\n    /// This rectangle includes e.g. the dynamic island on iOS.\n    /// If you want to only render _below_ the that (not behind), then you should use\n    /// [`Self::content_rect`] instead.\n    ///\n    /// See also [`RawInput::safe_area_insets`].\n    pub fn viewport_rect(&self) -> Rect {\n        self.input(|i| i.viewport_rect()).round_ui()\n    }\n\n    /// Position and size of the egui area.\n    #[deprecated(\n        note = \"screen_rect has been split into viewport_rect() and content_rect(). You likely should use content_rect()\"\n    )]\n    pub fn screen_rect(&self) -> Rect {\n        self.input(|i| i.content_rect()).round_ui()\n    }\n\n    /// How much space is still available after panels have been added.\n    #[deprecated = \"Use content_rect (or viewport_rect) instead\"]\n    pub fn available_rect(&self) -> Rect {\n        self.pass_state(|s| s.available_rect()).round_ui()\n    }\n\n    /// How much space is used by windows and the top-level [`Ui`].\n    pub fn globally_used_rect(&self) -> Rect {\n        self.write(|ctx| {\n            let mut used = ctx.viewport().this_pass.used_by_panels;\n            for (_id, window) in ctx.memory.areas().visible_windows() {\n                used |= window.rect();\n            }\n            used.round_ui()\n        })\n    }\n\n    /// How much space is used by windows and the top-level [`Ui`].\n    #[deprecated = \"Renamed to globally_used_rect\"]\n    pub fn used_rect(&self) -> Rect {\n        self.globally_used_rect()\n    }\n\n    /// How much space is used by windows and the top-level [`Ui`].\n    ///\n    /// You can shrink your egui area to this size and still fit all egui components.\n    #[deprecated = \"Use globally_used_rect instead\"]\n    pub fn used_size(&self) -> Vec2 {\n        (self.globally_used_rect().max - Pos2::ZERO).round_ui()\n    }\n\n    // ---------------------------------------------------------------------\n\n    /// Is the pointer (mouse/touch) over any egui area?\n    pub fn is_pointer_over_egui(&self) -> bool {\n        let pointer_pos = self.input(|i| i.pointer.interact_pos());\n        if let Some(pointer_pos) = pointer_pos {\n            if let Some(layer) = self.layer_id_at(pointer_pos) {\n                if layer.order == Order::Background {\n                    !self.pass_state(|state| state.unused_rect.contains(pointer_pos))\n                } else {\n                    true\n                }\n            } else {\n                false\n            }\n        } else {\n            false\n        }\n    }\n\n    /// Is the pointer (mouse/touch) over any egui area?\n    #[deprecated = \"Renamed to is_pointer_over_egui\"]\n    pub fn is_pointer_over_area(&self) -> bool {\n        self.is_pointer_over_egui()\n    }\n\n    /// True if egui is currently interested in the pointer (mouse or touch).\n    ///\n    /// Could be the pointer is hovering over a [`crate::Window`] or the user is dragging a widget.\n    /// If `false`, the pointer is outside of any egui area and so\n    /// you may be interested in what it is doing (e.g. controlling your game).\n    /// Returns `false` if a drag started outside of egui and then moved over an egui area.\n    pub fn egui_wants_pointer_input(&self) -> bool {\n        self.egui_is_using_pointer()\n            || (self.is_pointer_over_egui() && !self.input(|i| i.pointer.any_down()))\n    }\n\n    /// True if egui is currently interested in the pointer (mouse or touch).\n    ///\n    /// Could be the pointer is hovering over a [`crate::Window`] or the user is dragging a widget.\n    /// If `false`, the pointer is outside of any egui area and so\n    /// you may be interested in what it is doing (e.g. controlling your game).\n    /// Returns `false` if a drag started outside of egui and then moved over an egui area.\n    #[deprecated = \"Renamed to egui_wants_pointer_input\"]\n    pub fn wants_pointer_input(&self) -> bool {\n        self.egui_wants_pointer_input()\n    }\n\n    /// Is egui currently using the pointer position (e.g. dragging a slider)?\n    ///\n    /// NOTE: this will return `false` if the pointer is just hovering over an egui area.\n    pub fn egui_is_using_pointer(&self) -> bool {\n        self.memory(|m| m.interaction().is_using_pointer())\n    }\n\n    /// Is egui currently using the pointer position (e.g. dragging a slider)?\n    ///\n    /// NOTE: this will return `false` if the pointer is just hovering over an egui area.\n    #[deprecated = \"Renamed to egui_is_using_pointer\"]\n    pub fn is_using_pointer(&self) -> bool {\n        self.egui_is_using_pointer()\n    }\n\n    /// If `true`, egui is currently listening on text input (e.g. typing text in a [`crate::TextEdit`]).\n    pub fn egui_wants_keyboard_input(&self) -> bool {\n        self.memory(|m| m.focused().is_some())\n    }\n\n    /// If `true`, egui is currently listening on text input (e.g. typing text in a [`crate::TextEdit`]).\n    #[deprecated = \"Renamed to egui_wants_keyboard_input\"]\n    pub fn wants_keyboard_input(&self) -> bool {\n        self.egui_wants_keyboard_input()\n    }\n\n    /// Highlight this widget, to make it look like it is hovered, even if it isn't.\n    ///\n    /// If you call this after the widget has been fully rendered,\n    /// then it won't be highlighted until the next ui pass.\n    ///\n    /// See also [`Response::highlight`].\n    pub fn highlight_widget(&self, id: Id) {\n        self.pass_state_mut(|fs| fs.highlight_next_pass.insert(id));\n    }\n\n    /// Is an egui context menu open?\n    ///\n    /// This only works with the old, deprecated [`crate::menu`] API.\n    #[expect(deprecated)]\n    #[deprecated = \"Use `any_popup_open` instead\"]\n    pub fn is_context_menu_open(&self) -> bool {\n        self.data(|d| {\n            d.get_temp::<crate::menu::BarState>(crate::menu::CONTEXT_MENU_ID_STR.into())\n                .is_some_and(|state| state.has_root())\n        })\n    }\n\n    /// Is a popup or (context) menu open?\n    ///\n    /// Will return false for [`crate::Tooltip`]s (which are technically popups as well).\n    pub fn any_popup_open(&self) -> bool {\n        self.pass_state_mut(|fs| {\n            fs.layers\n                .values()\n                .any(|layer| !layer.open_popups.is_empty())\n        })\n    }\n\n    /// Is a popup or (context) menu open?\n    ///\n    /// Will return false for [`crate::Tooltip`]s (which are technically popups as well).\n    #[deprecated = \"Renamed to any_popup_open\"]\n    pub fn is_popup_open(&self) -> bool {\n        self.pass_state_mut(|fs| {\n            fs.layers\n                .values()\n                .any(|layer| !layer.open_popups.is_empty())\n        })\n    }\n}\n\n// Ergonomic methods to forward some calls often used in 'if let' without holding the borrow\nimpl Context {\n    /// Latest reported pointer position.\n    ///\n    /// When tapping a touch screen, this will be `None`.\n    #[inline(always)]\n    pub fn pointer_latest_pos(&self) -> Option<Pos2> {\n        self.input(|i| i.pointer.latest_pos())\n    }\n\n    /// If it is a good idea to show a tooltip, where is pointer?\n    #[inline(always)]\n    pub fn pointer_hover_pos(&self) -> Option<Pos2> {\n        self.input(|i| i.pointer.hover_pos())\n    }\n\n    /// If you detect a click or drag and want to know where it happened, use this.\n    ///\n    /// Latest position of the mouse, but ignoring any [`crate::Event::PointerGone`]\n    /// if there were interactions this pass.\n    /// When tapping a touch screen, this will be the location of the touch.\n    #[inline(always)]\n    pub fn pointer_interact_pos(&self) -> Option<Pos2> {\n        self.input(|i| i.pointer.interact_pos())\n    }\n\n    /// Calls [`InputState::multi_touch`].\n    pub fn multi_touch(&self) -> Option<MultiTouchInfo> {\n        self.input(|i| i.multi_touch())\n    }\n}\n\nimpl Context {\n    /// Transform the graphics of the given layer.\n    ///\n    /// This will also affect input.\n    /// The direction of the given transform is \"into the global coordinate system\".\n    ///\n    /// This is a sticky setting, remembered from one frame to the next.\n    ///\n    /// Can be used to implement pan and zoom (see relevant demo).\n    ///\n    /// For a temporary transform, use [`Self::transform_layer_shapes`] or\n    /// [`Ui::with_visual_transform`].\n    pub fn set_transform_layer(&self, layer_id: LayerId, transform: TSTransform) {\n        self.memory_mut(|m| {\n            if transform == TSTransform::IDENTITY {\n                m.to_global.remove(&layer_id)\n            } else {\n                m.to_global.insert(layer_id, transform)\n            }\n        });\n    }\n\n    /// Return how to transform the graphics of the given layer into the global coordinate system.\n    ///\n    /// Set this with [`Self::layer_transform_to_global`].\n    pub fn layer_transform_to_global(&self, layer_id: LayerId) -> Option<TSTransform> {\n        self.memory(|m| m.to_global.get(&layer_id).copied())\n    }\n\n    /// Return how to transform the graphics of the global coordinate system into the local coordinate system of the given layer.\n    ///\n    /// This returns the inverse of [`Self::layer_transform_to_global`].\n    pub fn layer_transform_from_global(&self, layer_id: LayerId) -> Option<TSTransform> {\n        self.layer_transform_to_global(layer_id)\n            .map(|t| t.inverse())\n    }\n\n    /// Transform all the graphics at the given layer.\n    ///\n    /// Is used to implement drag-and-drop preview.\n    ///\n    /// This only applied to the existing graphics at the layer, not to new graphics added later.\n    ///\n    /// For a persistent transform, use [`Self::set_transform_layer`] instead.\n    pub fn transform_layer_shapes(&self, layer_id: LayerId, transform: TSTransform) {\n        if transform != TSTransform::IDENTITY {\n            self.graphics_mut(|g| g.entry(layer_id).transform(transform));\n        }\n    }\n\n    /// Top-most layer at the given position.\n    pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {\n        self.memory(|mem| mem.layer_id_at(pos))\n    }\n\n    /// Moves the given area to the top in its [`Order`].\n    ///\n    /// [`crate::Area`]s and [`crate::Window`]s also do this automatically when being clicked on or interacted with.\n    pub fn move_to_top(&self, layer_id: LayerId) {\n        self.memory_mut(|mem| mem.areas_mut().move_to_top(layer_id));\n    }\n\n    /// Mark the `child` layer as a sublayer of `parent`.\n    ///\n    /// Sublayers are moved directly above the parent layer at the end of the frame. This is mainly\n    /// intended for adding a new [`crate::Area`] inside a [`crate::Window`].\n    ///\n    /// This currently only supports one level of nesting. If `parent` is a sublayer of another\n    /// layer, the behavior is unspecified.\n    pub fn set_sublayer(&self, parent: LayerId, child: LayerId) {\n        self.memory_mut(|mem| mem.areas_mut().set_sublayer(parent, child));\n    }\n\n    /// Retrieve the [`LayerId`] of the top level windows.\n    pub fn top_layer_id(&self) -> Option<LayerId> {\n        self.memory(|mem| mem.areas().top_layer_id(Order::Middle))\n    }\n\n    /// Does the given rectangle contain the mouse pointer?\n    ///\n    /// Will return false if some other area is covering the given layer.\n    ///\n    /// The given rectangle is assumed to have been clipped by its parent clip rect.\n    ///\n    /// See also [`Response::contains_pointer`].\n    pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool {\n        let rect = if let Some(to_global) = self.layer_transform_to_global(layer_id) {\n            to_global * rect\n        } else {\n            rect\n        };\n        if !rect.is_positive() {\n            return false;\n        }\n\n        let pointer_pos = self.input(|i| i.pointer.interact_pos());\n        let Some(pointer_pos) = pointer_pos else {\n            return false;\n        };\n\n        if !rect.contains(pointer_pos) {\n            return false;\n        }\n\n        if self.layer_id_at(pointer_pos) != Some(layer_id) {\n            return false;\n        }\n\n        true\n    }\n\n    // ---------------------------------------------------------------------\n\n    /// Whether or not to debug widget layout on hover.\n    #[cfg(debug_assertions)]\n    pub fn debug_on_hover(&self) -> bool {\n        self.options(|opt| opt.style().debug.debug_on_hover)\n    }\n\n    /// Turn on/off whether or not to debug widget layout on hover.\n    #[cfg(debug_assertions)]\n    pub fn set_debug_on_hover(&self, debug_on_hover: bool) {\n        self.all_styles_mut(|style| style.debug.debug_on_hover = debug_on_hover);\n    }\n}\n\n/// ## Animation\nimpl Context {\n    /// Returns a value in the range [0, 1], to indicate \"how on\" this thing is.\n    ///\n    /// The first time called it will return `if value { 1.0 } else { 0.0 }`\n    /// Calling this with `value = true` will always yield a number larger than zero, quickly going towards one.\n    /// Calling this with `value = false` will always yield a number less than one, quickly going towards zero.\n    ///\n    /// The function will call [`Self::request_repaint()`] when appropriate.\n    ///\n    /// The animation time is taken from [`Style::animation_time`].\n    #[track_caller] // To track repaint cause\n    pub fn animate_bool(&self, id: Id, value: bool) -> f32 {\n        let animation_time = self.global_style().animation_time;\n        self.animate_bool_with_time_and_easing(id, value, animation_time, emath::easing::linear)\n    }\n\n    /// Like [`Self::animate_bool`], but uses an easing function that makes the value move\n    /// quickly in the beginning and slow down towards the end.\n    ///\n    /// The exact easing function may come to change in future versions of egui.\n    #[track_caller] // To track repaint cause\n    pub fn animate_bool_responsive(&self, id: Id, value: bool) -> f32 {\n        self.animate_bool_with_easing(id, value, emath::easing::cubic_out)\n    }\n\n    /// Like [`Self::animate_bool`] but allows you to control the easing function.\n    #[track_caller] // To track repaint cause\n    pub fn animate_bool_with_easing(&self, id: Id, value: bool, easing: fn(f32) -> f32) -> f32 {\n        let animation_time = self.global_style().animation_time;\n        self.animate_bool_with_time_and_easing(id, value, animation_time, easing)\n    }\n\n    /// Like [`Self::animate_bool`] but allows you to control the animation time.\n    #[track_caller] // To track repaint cause\n    pub fn animate_bool_with_time(&self, id: Id, target_value: bool, animation_time: f32) -> f32 {\n        self.animate_bool_with_time_and_easing(\n            id,\n            target_value,\n            animation_time,\n            emath::easing::linear,\n        )\n    }\n\n    /// Like [`Self::animate_bool`] but allows you to control the animation time and easing function.\n    ///\n    /// Use e.g. [`emath::easing::quadratic_out`]\n    /// for a responsive start and a slow end.\n    ///\n    /// The easing function flips when `target_value` is `false`,\n    /// so that when going back towards 0.0, we get the reverse behavior.\n    #[track_caller] // To track repaint cause\n    pub fn animate_bool_with_time_and_easing(\n        &self,\n        id: Id,\n        target_value: bool,\n        animation_time: f32,\n        easing: fn(f32) -> f32,\n    ) -> f32 {\n        let animated_value = self.write(|ctx| {\n            ctx.animation_manager.animate_bool(\n                &ctx.viewports.entry(ctx.viewport_id()).or_default().input,\n                animation_time,\n                id,\n                target_value,\n            )\n        });\n\n        let animation_in_progress = 0.0 < animated_value && animated_value < 1.0;\n        if animation_in_progress {\n            self.request_repaint();\n        }\n\n        if target_value {\n            easing(animated_value)\n        } else {\n            1.0 - easing(1.0 - animated_value)\n        }\n    }\n\n    /// Smoothly animate an `f32` value.\n    ///\n    /// At the first call the value is written to memory.\n    /// When it is called with a new value, it linearly interpolates to it in the given time.\n    #[track_caller] // To track repaint cause\n    pub fn animate_value_with_time(&self, id: Id, target_value: f32, animation_time: f32) -> f32 {\n        let animated_value = self.write(|ctx| {\n            ctx.animation_manager.animate_value(\n                &ctx.viewports.entry(ctx.viewport_id()).or_default().input,\n                animation_time,\n                id,\n                target_value,\n            )\n        });\n        let animation_in_progress = animated_value != target_value;\n        if animation_in_progress {\n            self.request_repaint();\n        }\n\n        animated_value\n    }\n\n    /// Clear memory of any animations.\n    pub fn clear_animations(&self) {\n        self.write(|ctx| ctx.animation_manager = Default::default());\n    }\n}\n\nimpl Context {\n    /// Show a ui for settings (style and tessellation options).\n    pub fn settings_ui(&self, ui: &mut Ui) {\n        let prev_options = self.options(|o| o.clone());\n        let mut options = prev_options.clone();\n\n        ui.collapsing(\"🔠 Font tweak\", |ui| {\n            self.fonts_tweak_ui(ui);\n        });\n\n        options.ui(ui);\n\n        if options != prev_options {\n            self.options_mut(move |o| *o = options);\n        }\n    }\n\n    fn fonts_tweak_ui(&self, ui: &mut Ui) {\n        let mut font_definitions = self.write(|ctx| ctx.font_definitions.clone());\n        let mut changed = false;\n\n        for (name, data) in &mut font_definitions.font_data {\n            ui.collapsing(name, |ui| {\n                let mut tweak = data.tweak.clone();\n                if tweak.ui(ui).changed() {\n                    Arc::make_mut(data).tweak = tweak;\n                    changed = true;\n                }\n            });\n        }\n\n        if changed {\n            self.set_fonts(font_definitions);\n        }\n    }\n\n    /// Show the state of egui, including its input and output.\n    pub fn inspection_ui(&self, ui: &mut Ui) {\n        use crate::containers::CollapsingHeader;\n\n        crate::Grid::new(\"egui-inspection-grid\")\n            .num_columns(2)\n            .striped(true)\n            .show(ui, |ui| {\n                ui.label(\"Total ui frames:\");\n                ui.monospace(ui.ctx().cumulative_frame_nr().to_string());\n                ui.end_row();\n\n                ui.label(\"Total ui passes:\");\n                ui.monospace(ui.ctx().cumulative_pass_nr().to_string());\n                ui.end_row();\n\n                ui.label(\"Is using pointer\")\n                    .on_hover_text(\"Is egui currently using the pointer actively (e.g. dragging a slider)?\");\n                ui.monospace(self.egui_is_using_pointer().to_string());\n                ui.end_row();\n\n                ui.label(\"Wants pointer input\")\n                    .on_hover_text(\"Is egui currently interested in the location of the pointer (either because it is in use, or because it is hovering over a window).\");\n                ui.monospace(self.egui_wants_pointer_input().to_string());\n                ui.end_row();\n\n                ui.label(\"Wants keyboard input\").on_hover_text(\"Is egui currently listening for text input?\");\n                ui.monospace(self.egui_wants_keyboard_input().to_string());\n                ui.end_row();\n\n                ui.label(\"Keyboard focus widget\").on_hover_text(\"Is egui currently listening for text input?\");\n                ui.monospace(self.memory(|m| m.focused())\n                    .as_ref()\n                    .map(Id::short_debug_format)\n                    .unwrap_or_default());\n                ui.end_row();\n\n                let pointer_pos = self\n                    .pointer_hover_pos()\n                    .map_or_else(String::new, |pos| format!(\"{pos:?}\"));\n                ui.label(\"Pointer pos\");\n                ui.monospace(pointer_pos);\n                ui.end_row();\n\n                let top_layer = self\n                    .pointer_hover_pos()\n                    .and_then(|pos| self.layer_id_at(pos))\n                    .map_or_else(String::new, |layer| layer.short_debug_format());\n                ui.label(\"Top layer under mouse\");\n                ui.monospace(top_layer);\n                ui.end_row();\n            });\n\n        ui.add_space(16.0);\n\n        ui.label(format!(\n            \"There are {} text galleys in the layout cache\",\n            self.fonts(|f| f.num_galleys_in_cache())\n        ))\n        .on_hover_text(\"This is approximately the number of text strings on screen\");\n        ui.add_space(16.0);\n\n        CollapsingHeader::new(\"🔃 Repaint Causes\")\n            .default_open(false)\n            .show(ui, |ui| {\n                ui.set_min_height(120.0);\n                ui.label(\"What caused egui to repaint:\");\n                ui.add_space(8.0);\n                let causes = ui.ctx().repaint_causes();\n                for cause in causes {\n                    ui.label(cause.to_string());\n                }\n            });\n\n        CollapsingHeader::new(\"📥 Input\")\n            .default_open(false)\n            .show(ui, |ui| {\n                let input = ui.input(|i| i.clone());\n                input.ui(ui);\n            });\n\n        CollapsingHeader::new(\"📊 Paint stats\")\n            .default_open(false)\n            .show(ui, |ui| {\n                let paint_stats = self.read(|ctx| ctx.paint_stats);\n                paint_stats.ui(ui);\n            });\n\n        CollapsingHeader::new(\"🖼 Textures\")\n            .default_open(false)\n            .show(ui, |ui| {\n                self.texture_ui(ui);\n            });\n\n        CollapsingHeader::new(\"🖼 Image loaders\")\n            .default_open(false)\n            .show(ui, |ui| {\n                self.loaders_ui(ui);\n            });\n\n        CollapsingHeader::new(\"🔠 Font texture\")\n            .default_open(false)\n            .show(ui, |ui| {\n                let font_image_size = self.fonts(|f| f.font_image_size());\n                crate::introspection::font_texture_ui(ui, font_image_size);\n            });\n\n        CollapsingHeader::new(\"Label text selection state\")\n            .default_open(false)\n            .show(ui, |ui| {\n                ui.label(format!(\n                    \"{:#?}\",\n                    *ui.ctx()\n                        .plugin::<crate::text_selection::LabelSelectionState>()\n                        .lock()\n                ));\n            });\n\n        CollapsingHeader::new(\"Interaction\")\n            .default_open(false)\n            .show(ui, |ui| {\n                let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());\n                interact_widgets.ui(ui);\n            });\n    }\n\n    /// Show stats about the allocated textures.\n    pub fn texture_ui(&self, ui: &mut crate::Ui) {\n        let tex_mngr = self.tex_manager();\n        let tex_mngr = tex_mngr.read();\n\n        let mut textures: Vec<_> = tex_mngr.allocated().collect();\n        textures.sort_by_key(|(id, _)| *id);\n\n        let mut bytes = 0;\n        for (_, tex) in &textures {\n            bytes += tex.bytes_used();\n        }\n\n        ui.label(format!(\n            \"{} allocated texture(s), using {:.1} MB\",\n            textures.len(),\n            bytes as f64 * 1e-6\n        ));\n        let max_preview_size = vec2(48.0, 32.0);\n\n        let pixels_per_point = self.pixels_per_point();\n\n        ui.group(|ui| {\n            ScrollArea::vertical()\n                .max_height(300.0)\n                .auto_shrink([false, true])\n                .show(ui, |ui| {\n                    ui.style_mut().override_text_style = Some(TextStyle::Monospace);\n                    Grid::new(\"textures\")\n                        .striped(true)\n                        .num_columns(4)\n                        .spacing(vec2(16.0, 2.0))\n                        .min_row_height(max_preview_size.y)\n                        .show(ui, |ui| {\n                            for (&texture_id, meta) in textures {\n                                let [w, h] = meta.size;\n                                let point_size = vec2(w as f32, h as f32) / pixels_per_point;\n\n                                let mut size = point_size;\n                                size *= (max_preview_size.x / size.x).min(1.0);\n                                size *= (max_preview_size.y / size.y).min(1.0);\n                                ui.image(SizedTexture::new(texture_id, size))\n                                    .on_hover_ui(|ui| {\n                                        // show larger on hover\n                                        let max_size = 0.5 * ui.ctx().content_rect().size();\n                                        let mut size = point_size;\n                                        size *= max_size.x / size.x.max(max_size.x);\n                                        size *= max_size.y / size.y.max(max_size.y);\n                                        ui.image(SizedTexture::new(texture_id, size));\n                                    });\n\n                                ui.label(format!(\"{w} x {h}\"));\n                                ui.label(format!(\"{:.3} MB\", meta.bytes_used() as f64 * 1e-6));\n                                ui.label(format!(\"{:?}\", meta.name));\n                                ui.end_row();\n                            }\n                        });\n                });\n        });\n    }\n\n    /// Show stats about different image loaders.\n    pub fn loaders_ui(&self, ui: &mut crate::Ui) {\n        struct LoaderInfo {\n            id: String,\n            byte_size: usize,\n        }\n\n        let mut byte_loaders = vec![];\n        let mut image_loaders = vec![];\n        let mut texture_loaders = vec![];\n\n        {\n            let loaders = self.loaders();\n            let Loaders {\n                include: _,\n                bytes,\n                image,\n                texture,\n            } = loaders.as_ref();\n\n            for loader in bytes.lock().iter() {\n                byte_loaders.push(LoaderInfo {\n                    id: loader.id().to_owned(),\n                    byte_size: loader.byte_size(),\n                });\n            }\n            for loader in image.lock().iter() {\n                image_loaders.push(LoaderInfo {\n                    id: loader.id().to_owned(),\n                    byte_size: loader.byte_size(),\n                });\n            }\n            for loader in texture.lock().iter() {\n                texture_loaders.push(LoaderInfo {\n                    id: loader.id().to_owned(),\n                    byte_size: loader.byte_size(),\n                });\n            }\n        }\n\n        fn loaders_ui(ui: &mut crate::Ui, title: &str, loaders: &[LoaderInfo]) {\n            let heading = format!(\"{} {title} loaders\", loaders.len());\n            crate::CollapsingHeader::new(heading)\n                .default_open(true)\n                .show(ui, |ui| {\n                    Grid::new(\"loaders\")\n                        .striped(true)\n                        .num_columns(2)\n                        .show(ui, |ui| {\n                            ui.label(\"ID\");\n                            ui.label(\"Size\");\n                            ui.end_row();\n\n                            for loader in loaders {\n                                ui.label(&loader.id);\n                                ui.label(format!(\"{:.3} MB\", loader.byte_size as f64 * 1e-6));\n                                ui.end_row();\n                            }\n                        });\n                });\n        }\n\n        loaders_ui(ui, \"byte\", &byte_loaders);\n        loaders_ui(ui, \"image\", &image_loaders);\n        loaders_ui(ui, \"texture\", &texture_loaders);\n    }\n\n    /// Shows the contents of [`Self::memory`].\n    pub fn memory_ui(&self, ui: &mut crate::Ui) {\n        if ui\n            .button(\"Reset all\")\n            .on_hover_text(\"Reset all egui state\")\n            .clicked()\n        {\n            self.memory_mut(|mem| *mem = Default::default());\n        }\n\n        let (num_state, num_serialized) = self.data(|d| (d.len(), d.count_serialized()));\n        ui.label(format!(\n            \"{num_state} widget states stored (of which {num_serialized} are serialized).\"\n        ));\n\n        ui.horizontal(|ui| {\n            ui.label(format!(\n                \"{} areas (panels, windows, popups, …)\",\n                self.memory(|mem| mem.areas().count())\n            ));\n            if ui.button(\"Reset\").clicked() {\n                self.memory_mut(|mem| *mem.areas_mut() = Default::default());\n            }\n        });\n        ui.indent(\"layers\", |ui| {\n            ui.label(\"Layers, ordered back to front.\");\n            let layers_ids: Vec<LayerId> = self.memory(|mem| mem.areas().order().to_vec());\n            for layer_id in layers_ids {\n                if let Some(area) = AreaState::load(self, layer_id.id) {\n                    let is_visible = self.memory(|mem| mem.areas().is_visible(&layer_id));\n                    if !is_visible {\n                        continue;\n                    }\n                    let text = format!(\"{} - {:?}\", layer_id.short_debug_format(), area.rect(),);\n                    // TODO(emilk): `Sense::hover_highlight()`\n                    let response =\n                        ui.add(Label::new(RichText::new(text).monospace()).sense(Sense::click()));\n                    if response.hovered() && is_visible {\n                        ui.debug_painter().debug_rect(area.rect(), Color32::RED, \"\");\n                    }\n                } else {\n                    ui.monospace(layer_id.short_debug_format());\n                }\n            }\n        });\n\n        ui.horizontal(|ui| {\n            ui.label(format!(\n                \"{} collapsing headers\",\n                self.data(|d| d.count::<containers::collapsing_header::InnerState>())\n            ));\n            if ui.button(\"Reset\").clicked() {\n                self.data_mut(|d| d.remove_by_type::<containers::collapsing_header::InnerState>());\n            }\n        });\n\n        #[expect(deprecated)]\n        ui.horizontal(|ui| {\n            ui.label(format!(\n                \"{} menu bars\",\n                self.data(|d| d.count::<crate::menu::BarState>())\n            ));\n            if ui.button(\"Reset\").clicked() {\n                self.data_mut(|d| d.remove_by_type::<crate::menu::BarState>());\n            }\n        });\n\n        ui.horizontal(|ui| {\n            ui.label(format!(\n                \"{} scroll areas\",\n                self.data(|d| d.count::<scroll_area::State>())\n            ));\n            if ui.button(\"Reset\").clicked() {\n                self.data_mut(|d| d.remove_by_type::<scroll_area::State>());\n            }\n        });\n\n        ui.horizontal(|ui| {\n            ui.label(format!(\n                \"{} resize areas\",\n                self.data(|d| d.count::<resize::State>())\n            ));\n            if ui.button(\"Reset\").clicked() {\n                self.data_mut(|d| d.remove_by_type::<resize::State>());\n            }\n        });\n\n        ui.shrink_width_to_current(); // don't let the text below grow this window wider\n        ui.label(\"NOTE: the position of this window cannot be reset from within itself.\");\n\n        ui.collapsing(\"Interaction\", |ui| {\n            let interaction = self.memory(|mem| mem.interaction().clone());\n            interaction.ui(ui);\n        });\n    }\n}\n\nimpl Context {\n    /// Edit the [`Style`].\n    pub fn style_ui(&self, ui: &mut Ui, theme: Theme) {\n        let mut style: Style = (*self.style_of(theme)).clone();\n        style.ui(ui);\n        self.set_style_of(theme, style);\n    }\n}\n\n/// ## Accessibility\nimpl Context {\n    /// If AccessKit support is active for the current frame, get or create\n    /// a node builder with the specified ID and return a mutable reference to it.\n    /// For newly created nodes, the parent is the parent [`Ui`]s ID.\n    /// And an [`Ui`]s parent can be set with [`UiBuilder::accessibility_parent`].\n    ///\n    /// The `Context` lock is held while the given closure is called!\n    ///\n    /// Returns `None` if accesskit is off.\n    // TODO(emilk): consider making both read-only and read-write versions\n    pub fn accesskit_node_builder<R>(\n        &self,\n        id: Id,\n        writer: impl FnOnce(&mut accesskit::Node) -> R,\n    ) -> Option<R> {\n        self.write(|ctx| ctx.accesskit_node_builder(id).map(writer))\n    }\n\n    pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {\n        self.write(|ctx| {\n            if let Some(state) = ctx.viewport().this_pass.accesskit_state.as_mut() {\n                state.parent_map.insert(id, parent_id);\n            }\n        });\n    }\n\n    /// Enable generation of AccessKit tree updates in all future frames.\n    pub fn enable_accesskit(&self) {\n        self.write(|ctx| ctx.is_accesskit_enabled = true);\n    }\n\n    /// Disable generation of AccessKit tree updates in all future frames.\n    pub fn disable_accesskit(&self) {\n        self.write(|ctx| ctx.is_accesskit_enabled = false);\n    }\n}\n\n/// ## Image loading\nimpl Context {\n    /// Associate some static bytes with a `uri`.\n    ///\n    /// The same `uri` may be passed to [`Ui::image`] later to load the bytes as an image.\n    ///\n    /// By convention, the `uri` should start with `bytes://`.\n    /// Following that convention will lead to better error messages.\n    pub fn include_bytes(&self, uri: impl Into<Cow<'static, str>>, bytes: impl Into<Bytes>) {\n        self.loaders().include.insert(uri, bytes);\n    }\n\n    /// Returns `true` if the chain of bytes, image, or texture loaders\n    /// contains a loader with the given `id`.\n    pub fn is_loader_installed(&self, id: &str) -> bool {\n        let loaders = self.loaders();\n\n        loaders.bytes.lock().iter().any(|l| l.id() == id)\n            || loaders.image.lock().iter().any(|l| l.id() == id)\n            || loaders.texture.lock().iter().any(|l| l.id() == id)\n    }\n\n    /// Add a new bytes loader.\n    ///\n    /// It will be tried first, before any already installed loaders.\n    ///\n    /// See [`load`] for more information.\n    pub fn add_bytes_loader(&self, loader: Arc<dyn load::BytesLoader + Send + Sync + 'static>) {\n        self.loaders().bytes.lock().push(loader);\n    }\n\n    /// Add a new image loader.\n    ///\n    /// It will be tried first, before any already installed loaders.\n    ///\n    /// See [`load`] for more information.\n    pub fn add_image_loader(&self, loader: Arc<dyn load::ImageLoader + Send + Sync + 'static>) {\n        self.loaders().image.lock().push(loader);\n    }\n\n    /// Add a new texture loader.\n    ///\n    /// It will be tried first, before any already installed loaders.\n    ///\n    /// See [`load`] for more information.\n    pub fn add_texture_loader(&self, loader: Arc<dyn load::TextureLoader + Send + Sync + 'static>) {\n        self.loaders().texture.lock().push(loader);\n    }\n\n    /// Release all memory and textures related to the given image URI.\n    ///\n    /// If you attempt to load the image again, it will be reloaded from scratch.\n    /// Also this cancels any ongoing loading of the image.\n    pub fn forget_image(&self, uri: &str) {\n        use load::BytesLoader as _;\n\n        profiling::function_scope!();\n\n        let loaders = self.loaders();\n\n        loaders.include.forget(uri);\n        for loader in loaders.bytes.lock().iter() {\n            loader.forget(uri);\n        }\n        for loader in loaders.image.lock().iter() {\n            loader.forget(uri);\n        }\n        for loader in loaders.texture.lock().iter() {\n            loader.forget(uri);\n        }\n    }\n\n    /// Release all memory and textures related to images used in [`Ui::image`] or [`crate::Image`].\n    ///\n    /// If you attempt to load any images again, they will be reloaded from scratch.\n    pub fn forget_all_images(&self) {\n        use load::BytesLoader as _;\n\n        profiling::function_scope!();\n\n        let loaders = self.loaders();\n\n        loaders.include.forget_all();\n        for loader in loaders.bytes.lock().iter() {\n            loader.forget_all();\n        }\n        for loader in loaders.image.lock().iter() {\n            loader.forget_all();\n        }\n        for loader in loaders.texture.lock().iter() {\n            loader.forget_all();\n        }\n    }\n\n    /// Try loading the bytes from the given uri using any available bytes loaders.\n    ///\n    /// Loaders are expected to cache results, so that this call is immediate-mode safe.\n    ///\n    /// This calls the loaders one by one in the order in which they were registered.\n    /// If a loader returns [`LoadError::NotSupported`][not_supported],\n    /// then the next loader is called. This process repeats until all loaders have\n    /// been exhausted, at which point this returns [`LoadError::NotSupported`][not_supported].\n    ///\n    /// # Errors\n    /// This may fail with:\n    /// - [`LoadError::NotSupported`][not_supported] if none of the registered loaders support loading the given `uri`.\n    /// - [`LoadError::Loading`][custom] if one of the loaders _does_ support loading the `uri`, but the loading process failed.\n    ///\n    /// ⚠ May deadlock if called from within a `BytesLoader`!\n    ///\n    /// [not_supported]: crate::load::LoadError::NotSupported\n    /// [custom]: crate::load::LoadError::Loading\n    pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult {\n        profiling::function_scope!(uri);\n\n        let loaders = self.loaders();\n        let bytes_loaders = loaders.bytes.lock();\n\n        // Try most recently added loaders first (hence `.rev()`)\n        for loader in bytes_loaders.iter().rev() {\n            let result = loader.load(self, uri);\n            match result {\n                Err(load::LoadError::NotSupported) => {}\n                _ => return result,\n            }\n        }\n\n        Err(load::LoadError::NoMatchingBytesLoader)\n    }\n\n    /// Try loading the image from the given uri using any available image loaders.\n    ///\n    /// Loaders are expected to cache results, so that this call is immediate-mode safe.\n    ///\n    /// This calls the loaders one by one in the order in which they were registered.\n    /// If a loader returns [`LoadError::NotSupported`][not_supported],\n    /// then the next loader is called. This process repeats until all loaders have\n    /// been exhausted, at which point this returns [`LoadError::NotSupported`][not_supported].\n    ///\n    /// # Errors\n    /// This may fail with:\n    /// - [`LoadError::NoImageLoaders`][no_image_loaders] if tbere are no registered image loaders.\n    /// - [`LoadError::NotSupported`][not_supported] if none of the registered loaders support loading the given `uri`.\n    /// - [`LoadError::Loading`][custom] if one of the loaders _does_ support loading the `uri`, but the loading process failed.\n    ///\n    /// ⚠ May deadlock if called from within an `ImageLoader`!\n    ///\n    /// [no_image_loaders]: crate::load::LoadError::NoImageLoaders\n    /// [not_supported]: crate::load::LoadError::NotSupported\n    /// [custom]: crate::load::LoadError::Loading\n    pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult {\n        profiling::function_scope!(uri);\n\n        let loaders = self.loaders();\n        let image_loaders = loaders.image.lock();\n        if image_loaders.is_empty() {\n            return Err(load::LoadError::NoImageLoaders);\n        }\n\n        let mut format = None;\n\n        // Try most recently added loaders first (hence `.rev()`)\n        for loader in image_loaders.iter().rev() {\n            match loader.load(self, uri, size_hint) {\n                Err(load::LoadError::NotSupported) => {}\n                Err(load::LoadError::FormatNotSupported { detected_format }) => {\n                    format = format.or(detected_format);\n                }\n                result => return result,\n            }\n        }\n\n        Err(load::LoadError::NoMatchingImageLoader {\n            detected_format: format,\n        })\n    }\n\n    /// Try loading the texture from the given uri using any available texture loaders.\n    ///\n    /// Loaders are expected to cache results, so that this call is immediate-mode safe.\n    ///\n    /// This calls the loaders one by one in the order in which they were registered.\n    /// If a loader returns [`LoadError::NotSupported`][not_supported],\n    /// then the next loader is called. This process repeats until all loaders have\n    /// been exhausted, at which point this returns [`LoadError::NotSupported`][not_supported].\n    ///\n    /// # Errors\n    /// This may fail with:\n    /// - [`LoadError::NotSupported`][not_supported] if none of the registered loaders support loading the given `uri`.\n    /// - [`LoadError::Loading`][custom] if one of the loaders _does_ support loading the `uri`, but the loading process failed.\n    ///\n    /// ⚠ May deadlock if called from within a `TextureLoader`!\n    ///\n    /// [not_supported]: crate::load::LoadError::NotSupported\n    /// [custom]: crate::load::LoadError::Loading\n    pub fn try_load_texture(\n        &self,\n        uri: &str,\n        texture_options: TextureOptions,\n        size_hint: load::SizeHint,\n    ) -> load::TextureLoadResult {\n        profiling::function_scope!(uri);\n\n        let loaders = self.loaders();\n        let texture_loaders = loaders.texture.lock();\n\n        // Try most recently added loaders first (hence `.rev()`)\n        for loader in texture_loaders.iter().rev() {\n            match loader.load(self, uri, texture_options, size_hint) {\n                Err(load::LoadError::NotSupported) => {}\n                result => return result,\n            }\n        }\n\n        Err(load::LoadError::NoMatchingTextureLoader)\n    }\n\n    /// The loaders of bytes, images, and textures.\n    pub fn loaders(&self) -> Arc<Loaders> {\n        self.read(|this| Arc::clone(&this.loaders))\n    }\n\n    /// Returns `true` if any image is currently being loaded.\n    pub fn has_pending_images(&self) -> bool {\n        self.read(|this| {\n            this.loaders.image.lock().iter().any(|i| i.has_pending())\n                || this.loaders.bytes.lock().iter().any(|i| i.has_pending())\n        })\n    }\n}\n\n/// ## Viewports\nimpl Context {\n    /// Return the `ViewportId` of the current viewport.\n    ///\n    /// If this is the root viewport, this will return [`ViewportId::ROOT`].\n    ///\n    /// Don't use this outside of `Self::run`, or after `Self::end_pass`.\n    pub fn viewport_id(&self) -> ViewportId {\n        self.read(|ctx| ctx.viewport_id())\n    }\n\n    /// Return the `ViewportId` of his parent.\n    ///\n    /// If this is the root viewport, this will return [`ViewportId::ROOT`].\n    ///\n    /// Don't use this outside of `Self::run`, or after `Self::end_pass`.\n    pub fn parent_viewport_id(&self) -> ViewportId {\n        self.read(|ctx| ctx.parent_viewport_id())\n    }\n\n    /// Read the state of the current viewport.\n    pub fn viewport<R>(&self, reader: impl FnOnce(&ViewportState) -> R) -> R {\n        self.write(|ctx| reader(ctx.viewport()))\n    }\n\n    /// Read the state of a specific current viewport.\n    pub fn viewport_for<R>(\n        &self,\n        viewport_id: ViewportId,\n        reader: impl FnOnce(&ViewportState) -> R,\n    ) -> R {\n        self.write(|ctx| reader(ctx.viewport_for(viewport_id)))\n    }\n\n    /// For integrations: Set this to render a sync viewport.\n    ///\n    /// This will only set the callback for the current thread,\n    /// which most likely should be the main thread.\n    ///\n    /// When an immediate viewport is created with [`Self::show_viewport_immediate`] it will be rendered by this function.\n    ///\n    /// When called, the integration needs to:\n    /// * Check if there already is a window for this viewport id, and if not open one\n    /// * Set the window attributes (position, size, …) based on [`ImmediateViewport::builder`].\n    /// * Call [`Context::run`] with [`ImmediateViewport::viewport_ui_cb`].\n    /// * Handle the output from [`Context::run`], including rendering\n    pub fn set_immediate_viewport_renderer(\n        callback: impl for<'a> Fn(&Self, ImmediateViewport<'a>) + 'static,\n    ) {\n        let callback = Box::new(callback);\n        IMMEDIATE_VIEWPORT_RENDERER.with(|render_sync| {\n            render_sync.replace(Some(callback));\n        });\n    }\n\n    /// If `true`, [`Self::show_viewport_deferred`] and [`Self::show_viewport_immediate`] will\n    /// embed the new viewports inside the existing one, instead of spawning a new native window.\n    ///\n    /// `eframe` sets this to `false` on supported platforms, but the default value is `true`.\n    pub fn embed_viewports(&self) -> bool {\n        self.read(|ctx| ctx.embed_viewports)\n    }\n\n    /// If `true`, [`Self::show_viewport_deferred`] and [`Self::show_viewport_immediate`] will\n    /// embed the new viewports inside the existing one, instead of spawning a new native window.\n    ///\n    /// `eframe` sets this to `false` on supported platforms, but the default value is `true`.\n    pub fn set_embed_viewports(&self, value: bool) {\n        self.write(|ctx| ctx.embed_viewports = value);\n    }\n\n    /// Send a command to the current viewport.\n    ///\n    /// This lets you affect the current viewport, e.g. resizing the window.\n    pub fn send_viewport_cmd(&self, command: ViewportCommand) {\n        self.send_viewport_cmd_to(self.viewport_id(), command);\n    }\n\n    /// Send a command to a specific viewport.\n    ///\n    /// This lets you affect another viewport, e.g. resizing its window.\n    pub fn send_viewport_cmd_to(&self, id: ViewportId, command: ViewportCommand) {\n        self.request_repaint_of(id);\n\n        if command.requires_parent_repaint() {\n            self.request_repaint_of(self.parent_viewport_id());\n        }\n\n        self.write(|ctx| ctx.viewport_for(id).commands.push(command));\n    }\n\n    /// Show a deferred viewport, creating a new native window, if possible.\n    ///\n    /// The given id must be unique for each viewport.\n    ///\n    /// You need to call this each pass when the child viewport should exist.\n    ///\n    /// You can check if the user wants to close the viewport by checking the\n    /// [`crate::ViewportInfo::close_requested`] flags found in [`crate::InputState::viewport`].\n    ///\n    /// The given callback will be called whenever the child viewport needs repainting,\n    /// e.g. on an event or when [`Self::request_repaint`] is called.\n    /// This means it may be called multiple times, for instance while the\n    /// parent viewport (the caller) is sleeping but the child viewport is animating.\n    ///\n    /// You will need to wrap your viewport state in an `Arc<RwLock<T>>` or `Arc<Mutex<T>>`.\n    /// When this is called again with the same id in `ViewportBuilder` the render function for that viewport will be updated.\n    ///\n    /// You can also use [`Self::show_viewport_immediate`], which uses a simpler `FnOnce`\n    /// with no need for `Send` or `Sync`. The downside is that it will require\n    /// the parent viewport (the caller) to repaint anytime the child is repainted,\n    /// and vice versa.\n    ///\n    /// If [`Context::embed_viewports`] is `true` (e.g. if the current egui\n    /// backend does not support multiple viewports), the given callback\n    /// will be called immediately, embedding the new viewport in the current one,\n    /// inside of a [`crate::Window`].\n    /// You can know by checking for [`ViewportClass::EmbeddedWindow`].\n    ///\n    /// See [`crate::viewport`] for more information about viewports.\n    pub fn show_viewport_deferred(\n        &self,\n        new_viewport_id: ViewportId,\n        viewport_builder: ViewportBuilder,\n        viewport_ui_cb: impl Fn(&mut Ui, ViewportClass) + Send + Sync + 'static,\n    ) {\n        profiling::function_scope!();\n\n        if self.embed_viewports() {\n            crate::Window::from_viewport(new_viewport_id, viewport_builder).show(self, |ui| {\n                viewport_ui_cb(ui, ViewportClass::EmbeddedWindow);\n            });\n        } else {\n            self.write(|ctx| {\n                ctx.viewport_parents\n                    .insert(new_viewport_id, ctx.viewport_id());\n\n                let viewport = ctx.viewports.entry(new_viewport_id).or_default();\n                viewport.class = ViewportClass::Deferred;\n                viewport.builder = viewport_builder;\n                viewport.used = true;\n                viewport.viewport_ui_cb = Some(Arc::new(move |ui| {\n                    (viewport_ui_cb)(ui, ViewportClass::Deferred);\n                }));\n            });\n        }\n    }\n\n    /// Show an immediate viewport, creating a new native window, if possible.\n    ///\n    /// This is the easier type of viewport to use, but it is less performant\n    /// as it requires both parent and child to repaint if any one of them needs repainting,\n    /// which effectively produce double work for two viewports, and triple work for three viewports, etc.\n    /// To avoid this, use [`Self::show_viewport_deferred`] instead.\n    ///\n    /// The given id must be unique for each viewport.\n    ///\n    /// You need to call this each pass when the child viewport should exist.\n    ///\n    /// You can check if the user wants to close the viewport by checking the\n    /// [`crate::ViewportInfo::close_requested`] flags found in [`crate::InputState::viewport`].\n    ///\n    /// The given ui function will be called immediately.\n    /// This may only be called on the main thread.\n    /// This call will pause the current viewport and render the child viewport in its own window.\n    /// This means that the child viewport will not be repainted when the parent viewport is repainted, and vice versa.\n    ///\n    /// If [`Context::embed_viewports`] is `true` (e.g. if the current egui\n    /// backend does not support multiple viewports), the given callback\n    /// will be called immediately, embedding the new viewport in the current one,\n    /// inside of a [`crate::Window`].\n    /// You can know by checking for [`ViewportClass::EmbeddedWindow`].\n    ///\n    /// See [`crate::viewport`] for more information about viewports.\n    pub fn show_viewport_immediate<T>(\n        &self,\n        new_viewport_id: ViewportId,\n        builder: ViewportBuilder,\n        mut viewport_ui_cb: impl FnMut(&mut Ui, ViewportClass) -> T,\n    ) -> T {\n        profiling::function_scope!();\n\n        if self.embed_viewports() {\n            return self.show_embedded_viewport(new_viewport_id, builder, |ui| {\n                viewport_ui_cb(ui, ViewportClass::EmbeddedWindow)\n            });\n        }\n\n        IMMEDIATE_VIEWPORT_RENDERER.with(|immediate_viewport_renderer| {\n            let immediate_viewport_renderer = immediate_viewport_renderer.borrow();\n            let Some(immediate_viewport_renderer) = immediate_viewport_renderer.as_ref() else {\n                // This egui backend does not support multiple viewports.\n                return self.show_embedded_viewport(new_viewport_id, builder, |ui| {\n                    viewport_ui_cb(ui, ViewportClass::EmbeddedWindow)\n                });\n            };\n\n            let ids = self.write(|ctx| {\n                let parent_viewport_id = ctx.viewport_id();\n\n                ctx.viewport_parents\n                    .insert(new_viewport_id, parent_viewport_id);\n\n                let viewport = ctx.viewports.entry(new_viewport_id).or_default();\n                viewport.builder = builder.clone();\n                viewport.used = true;\n                viewport.viewport_ui_cb = None; // it is immediate\n\n                ViewportIdPair::from_self_and_parent(new_viewport_id, parent_viewport_id)\n            });\n\n            let mut out = None;\n            {\n                let out = &mut out;\n\n                let viewport = ImmediateViewport {\n                    ids,\n                    builder,\n                    viewport_ui_cb: Box::new(move |ui| {\n                        *out = Some((viewport_ui_cb)(ui, ViewportClass::Immediate));\n                    }),\n                };\n\n                immediate_viewport_renderer(self, viewport);\n            }\n\n            out.expect(\n                \"egui backend is implemented incorrectly - the user callback was never called\",\n            )\n        })\n    }\n\n    fn show_embedded_viewport<T>(\n        &self,\n        new_viewport_id: ViewportId,\n        builder: ViewportBuilder,\n        viewport_ui_cb: impl FnOnce(&mut Ui) -> T,\n    ) -> T {\n        crate::Window::from_viewport(new_viewport_id, builder)\n            .collapsible(false)\n            .show(self, |ui| viewport_ui_cb(ui))\n            .unwrap_or_else(|| panic!(\"Window did not show\"))\n            .inner\n            .unwrap_or_else(|| panic!(\"Window was collapsed\"))\n    }\n}\n\n/// ## Interaction\nimpl Context {\n    /// Read you what widgets are currently being interacted with.\n    pub fn interaction_snapshot<R>(&self, reader: impl FnOnce(&InteractionSnapshot) -> R) -> R {\n        self.write(|w| reader(&w.viewport().interact_widgets))\n    }\n\n    /// The widget currently being dragged, if any.\n    ///\n    /// For widgets that sense both clicks and drags, this will\n    /// not be set until the mouse cursor has moved a certain distance.\n    ///\n    /// NOTE: if the widget was released this pass, this will be `None`.\n    /// Use [`Self::drag_stopped_id`] instead.\n    pub fn dragged_id(&self) -> Option<Id> {\n        self.interaction_snapshot(|i| i.dragged)\n    }\n\n    /// Is this specific widget being dragged?\n    ///\n    /// A widget that sense both clicks and drags is only marked as \"dragged\"\n    /// when the mouse has moved a bit.\n    ///\n    /// See also: [`crate::Response::dragged`].\n    pub fn is_being_dragged(&self, id: Id) -> bool {\n        self.dragged_id() == Some(id)\n    }\n\n    /// This widget just started being dragged this pass.\n    ///\n    /// The same widget should also be found in [`Self::dragged_id`].\n    pub fn drag_started_id(&self) -> Option<Id> {\n        self.interaction_snapshot(|i| i.drag_started)\n    }\n\n    /// This widget was being dragged, but was released this pass.\n    pub fn drag_stopped_id(&self) -> Option<Id> {\n        self.interaction_snapshot(|i| i.drag_stopped)\n    }\n\n    /// Set which widget is being dragged.\n    pub fn set_dragged_id(&self, id: Id) {\n        self.write(|ctx| {\n            let vp = ctx.viewport();\n            let i = &mut vp.interact_widgets;\n            if i.dragged != Some(id) {\n                i.drag_stopped = i.dragged.or(i.drag_stopped);\n                i.dragged = Some(id);\n                i.drag_started = Some(id);\n            }\n\n            ctx.memory.interaction_mut().potential_drag_id = Some(id);\n        });\n    }\n\n    /// Stop dragging any widget.\n    pub fn stop_dragging(&self) {\n        self.write(|ctx| {\n            let vp = ctx.viewport();\n            let i = &mut vp.interact_widgets;\n            if i.dragged.is_some() {\n                i.drag_stopped = i.dragged;\n                i.dragged = None;\n            }\n\n            ctx.memory.interaction_mut().potential_drag_id = None;\n        });\n    }\n\n    /// Is something else being dragged?\n    ///\n    /// Returns true if we are dragging something, but not the given widget.\n    #[inline(always)]\n    pub fn dragging_something_else(&self, not_this: Id) -> bool {\n        let dragged = self.dragged_id();\n        dragged.is_some() && dragged != Some(not_this)\n    }\n}\n\n#[test]\nfn context_impl_send_sync() {\n    fn assert_send_sync<T: Send + Sync>() {}\n    assert_send_sync::<Context>();\n}\n\n/// Check if any [`Rect`] appears with different [`Id`]s between two passes.\n///\n/// This helps detect cases where the same screen area is claimed by different widget ids\n/// across passes, which is often a sign of id instability.\n#[cfg(debug_assertions)]\nfn warn_if_rect_changes_id(\n    out_shapes: &mut Vec<ClippedShape>,\n    prev_widgets: &crate::WidgetRects,\n    new_widgets: &crate::WidgetRects,\n) {\n    profiling::function_scope!();\n\n    use std::collections::BTreeMap;\n\n    /// A wrapper around [`Rect`] that implements [`Ord`] using the bit representation of its floats.\n    #[derive(Clone, Copy, PartialEq, Eq)]\n    struct OrderedRect(Rect);\n\n    impl PartialOrd for OrderedRect {\n        fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {\n            Some(self.cmp(other))\n        }\n    }\n\n    impl Ord for OrderedRect {\n        fn cmp(&self, other: &Self) -> std::cmp::Ordering {\n            let lhs = self.0;\n            let rhs = other.0;\n            lhs.min\n                .x\n                .to_bits()\n                .cmp(&rhs.min.x.to_bits())\n                .then(lhs.min.y.to_bits().cmp(&rhs.min.y.to_bits()))\n                .then(lhs.max.x.to_bits().cmp(&rhs.max.x.to_bits()))\n                .then(lhs.max.y.to_bits().cmp(&rhs.max.y.to_bits()))\n        }\n    }\n\n    fn create_lookup<'a>(\n        widgets: impl Iterator<Item = &'a WidgetRect>,\n    ) -> BTreeMap<OrderedRect, Vec<&'a WidgetRect>> {\n        let mut lookup: BTreeMap<OrderedRect, Vec<&'a WidgetRect>> = BTreeMap::default();\n        for w in widgets {\n            lookup.entry(OrderedRect(w.rect)).or_default().push(w);\n        }\n        lookup\n    }\n\n    for (layer_id, new_layer_widgets) in new_widgets.layers() {\n        let prev = create_lookup(prev_widgets.get_layer(*layer_id));\n        let new = create_lookup(new_layer_widgets.iter());\n\n        for (hashable_rect, new_at_rect) in new {\n            let Some(prev_at_rect) = prev.get(&hashable_rect) else {\n                continue; // this rect did not exist in the previous pass\n            };\n\n            if prev_at_rect\n                .iter()\n                .any(|w| new_at_rect.iter().any(|nw| nw.id == w.id))\n            {\n                continue; // at least one id stayed the same, so this is not an id change\n            }\n\n            // Only warn if at least one of the previous ids is gone from this layer entirely.\n            // If they all still exist (just at a different rect), then the rect match\n            // is just a coincidence caused by widgets shifting (e.g. a window being dragged).\n            if prev_at_rect.iter().all(|w| new_widgets.contains(w.id)) {\n                continue;\n            }\n\n            let rect = new_at_rect[0].rect;\n\n            log::warn!(\n                \"Widget rect {rect:?} changed id between passes: prev ids: {:?}, new ids: {:?}\",\n                prev_at_rect\n                    .iter()\n                    .map(|w| w.id.short_debug_format())\n                    .collect::<Vec<_>>(),\n                new_at_rect\n                    .iter()\n                    .map(|w| w.id.short_debug_format())\n                    .collect::<Vec<_>>(),\n            );\n            out_shapes.push(ClippedShape {\n                clip_rect: Rect::EVERYTHING,\n                shape: epaint::Shape::rect_stroke(\n                    rect,\n                    0,\n                    (2.0, Color32::RED),\n                    StrokeKind::Outside,\n                ),\n            });\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::Context;\n\n    #[test]\n    fn test_single_pass() {\n        let ctx = Context::default();\n        ctx.options_mut(|o| o.max_passes = 1.try_into().unwrap());\n\n        // A single call, no request to discard:\n        {\n            let mut num_calls = 0;\n            let output = ctx.run_ui(Default::default(), |ui| {\n                num_calls += 1;\n                assert_eq!(ui.output(|o| o.num_completed_passes), 0);\n                assert!(!ui.output(|o| o.requested_discard()));\n                assert!(!ui.will_discard());\n            });\n            assert_eq!(num_calls, 1);\n            assert_eq!(output.platform_output.num_completed_passes, 1);\n            assert!(!output.platform_output.requested_discard());\n        }\n\n        // A single call, with a denied request to discard:\n        {\n            let mut num_calls = 0;\n            let output = ctx.run_ui(Default::default(), |ui| {\n                num_calls += 1;\n                ui.request_discard(\"test\");\n                assert!(!ui.will_discard(), \"The request should have been denied\");\n            });\n            assert_eq!(num_calls, 1);\n            assert_eq!(output.platform_output.num_completed_passes, 1);\n            assert!(\n                output.platform_output.requested_discard(),\n                \"The request should be reported\"\n            );\n            assert_eq!(\n                output\n                    .platform_output\n                    .request_discard_reasons\n                    .first()\n                    .unwrap()\n                    .reason,\n                \"test\"\n            );\n        }\n    }\n\n    #[test]\n    fn test_dual_pass() {\n        let ctx = Context::default();\n        ctx.options_mut(|o| o.max_passes = 2.try_into().unwrap());\n\n        // Normal single pass:\n        {\n            let mut num_calls = 0;\n            let output = ctx.run_ui(Default::default(), |ui| {\n                assert_eq!(ui.output(|o| o.num_completed_passes), 0);\n                assert!(!ui.output(|o| o.requested_discard()));\n                assert!(!ui.will_discard());\n                num_calls += 1;\n            });\n            assert_eq!(num_calls, 1);\n            assert_eq!(output.platform_output.num_completed_passes, 1);\n            assert!(!output.platform_output.requested_discard());\n        }\n\n        // Request discard once:\n        {\n            let mut num_calls = 0;\n            let output = ctx.run_ui(Default::default(), |ui| {\n                assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);\n\n                assert!(!ui.will_discard());\n                if num_calls == 0 {\n                    ui.request_discard(\"test\");\n                    assert!(ui.will_discard());\n                }\n\n                num_calls += 1;\n            });\n            assert_eq!(num_calls, 2);\n            assert_eq!(output.platform_output.num_completed_passes, 2);\n            assert!(\n                !output.platform_output.requested_discard(),\n                \"The request should have been cleared when fulfilled\"\n            );\n        }\n\n        // Request discard twice:\n        {\n            let mut num_calls = 0;\n            let output = ctx.run_ui(Default::default(), |ui| {\n                assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);\n\n                assert!(!ui.will_discard());\n                ui.request_discard(\"test\");\n                if num_calls == 0 {\n                    assert!(ui.will_discard(), \"First request granted\");\n                } else {\n                    assert!(!ui.will_discard(), \"Second request should be denied\");\n                }\n\n                num_calls += 1;\n            });\n            assert_eq!(num_calls, 2);\n            assert_eq!(output.platform_output.num_completed_passes, 2);\n            assert!(\n                output.platform_output.requested_discard(),\n                \"The unfulfilled request should be reported\"\n            );\n        }\n    }\n\n    #[test]\n    fn test_multi_pass() {\n        let ctx = Context::default();\n        ctx.options_mut(|o| o.max_passes = 10.try_into().unwrap());\n\n        // Request discard three times:\n        {\n            let mut num_calls = 0;\n            let output = ctx.run_ui(Default::default(), |ui| {\n                assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);\n\n                assert!(!ui.will_discard());\n                if num_calls <= 2 {\n                    ui.request_discard(\"test\");\n                    assert!(ui.will_discard());\n                }\n\n                num_calls += 1;\n            });\n            assert_eq!(num_calls, 4);\n            assert_eq!(output.platform_output.num_completed_passes, 4);\n            assert!(\n                !output.platform_output.requested_discard(),\n                \"The request should have been cleared when fulfilled\"\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/data/input.rs",
    "content": "//! The input needed by egui.\n\nuse epaint::{ColorImage, MarginF32};\n\nuse crate::{\n    Key, OrderedViewportIdMap, Theme, ViewportId, ViewportIdMap,\n    emath::{Pos2, Rect, Vec2},\n};\n\n/// What the integrations provides to egui at the start of each frame.\n///\n/// Set the values that make sense, leave the rest at their `Default::default()`.\n///\n/// You can check if `egui` is using the inputs using\n/// [`crate::Context::wants_pointer_input`] and [`crate::Context::wants_keyboard_input`].\n///\n/// All coordinates are in points (logical pixels) with origin (0, 0) in the top left .corner.\n///\n/// Ii \"points\" can be calculated from native physical pixels\n/// using `pixels_per_point` = [`crate::Context::zoom_factor`] * `native_pixels_per_point`;\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct RawInput {\n    /// The id of the active viewport.\n    pub viewport_id: ViewportId,\n\n    /// Information about all egui viewports.\n    pub viewports: ViewportIdMap<ViewportInfo>,\n\n    /// The insets used to only render content in a mobile safe area\n    ///\n    /// `None` will be treated as \"same as last frame\"\n    pub safe_area_insets: Option<SafeAreaInsets>,\n\n    /// Position and size of the area that egui should use, in points.\n    /// Usually you would set this to\n    ///\n    /// `Some(Rect::from_min_size(Default::default(), screen_size_in_points))`.\n    ///\n    /// but you could also constrain egui to some smaller portion of your window if you like.\n    ///\n    /// `None` will be treated as \"same as last frame\", with the default being a very big area.\n    pub screen_rect: Option<Rect>,\n\n    /// Maximum size of one side of the font texture.\n    ///\n    /// Ask your graphics drivers about this. This corresponds to `GL_MAX_TEXTURE_SIZE`.\n    ///\n    /// The default is a very small (but very portable) 2048.\n    pub max_texture_side: Option<usize>,\n\n    /// Monotonically increasing time, in seconds. Relative to whatever. Used for animations.\n    /// If `None` is provided, egui will assume a time delta of `predicted_dt` (default 1/60 seconds).\n    pub time: Option<f64>,\n\n    /// Should be set to the expected time between frames when painting at vsync speeds.\n    /// The default for this is 1/60.\n    /// Can safely be left at its default value.\n    pub predicted_dt: f32,\n\n    /// Which modifier keys are down at the start of the frame?\n    pub modifiers: Modifiers,\n\n    /// In-order events received this frame.\n    ///\n    /// There is currently no way to know if egui handles a particular event,\n    /// but you can check if egui is using the keyboard with [`crate::Context::wants_keyboard_input`]\n    /// and/or the pointer (mouse/touch) with [`crate::Context::is_using_pointer`].\n    pub events: Vec<Event>,\n\n    /// Dragged files hovering over egui.\n    pub hovered_files: Vec<HoveredFile>,\n\n    /// Dragged files dropped into egui.\n    ///\n    /// Note: when using `eframe` on Windows, this will always be empty if drag-and-drop support has\n    /// been disabled in [`crate::viewport::ViewportBuilder`].\n    pub dropped_files: Vec<DroppedFile>,\n\n    /// The native window has the keyboard focus (i.e. is receiving key presses).\n    ///\n    /// False when the user alt-tab away from the application, for instance.\n    pub focused: bool,\n\n    /// Does the OS use dark or light mode?\n    ///\n    /// `None` means \"don't know\".\n    pub system_theme: Option<Theme>,\n}\n\nimpl Default for RawInput {\n    fn default() -> Self {\n        Self {\n            viewport_id: ViewportId::ROOT,\n            viewports: std::iter::once((ViewportId::ROOT, Default::default())).collect(),\n            screen_rect: None,\n            max_texture_side: None,\n            time: None,\n            predicted_dt: 1.0 / 60.0,\n            modifiers: Modifiers::default(),\n            events: vec![],\n            hovered_files: Default::default(),\n            dropped_files: Default::default(),\n            focused: true, // integrations opt into global focus tracking\n            system_theme: None,\n            safe_area_insets: Default::default(),\n        }\n    }\n}\n\nimpl RawInput {\n    /// Info about the active viewport\n    #[inline]\n    pub fn viewport(&self) -> &ViewportInfo {\n        self.viewports.get(&self.viewport_id).expect(\"Failed to find current viewport in egui RawInput. This is the fault of the egui backend\")\n    }\n\n    /// Helper: move volatile (deltas and events), clone the rest.\n    ///\n    /// * [`Self::hovered_files`] is cloned.\n    /// * [`Self::dropped_files`] is moved.\n    pub fn take(&mut self) -> Self {\n        Self {\n            viewport_id: self.viewport_id,\n            viewports: self\n                .viewports\n                .iter_mut()\n                .map(|(id, info)| (*id, info.take()))\n                .collect(),\n            screen_rect: self.screen_rect.take(),\n            safe_area_insets: self.safe_area_insets.take(),\n            max_texture_side: self.max_texture_side.take(),\n            time: self.time,\n            predicted_dt: self.predicted_dt,\n            modifiers: self.modifiers,\n            events: std::mem::take(&mut self.events),\n            hovered_files: self.hovered_files.clone(),\n            dropped_files: std::mem::take(&mut self.dropped_files),\n            focused: self.focused,\n            system_theme: self.system_theme,\n        }\n    }\n\n    /// Add on new input.\n    pub fn append(&mut self, newer: Self) {\n        let Self {\n            viewport_id: viewport_ids,\n            viewports,\n            screen_rect,\n            max_texture_side,\n            time,\n            predicted_dt,\n            modifiers,\n            mut events,\n            mut hovered_files,\n            mut dropped_files,\n            focused,\n            system_theme,\n            safe_area_insets: safe_area,\n        } = newer;\n\n        self.viewport_id = viewport_ids;\n        self.viewports = viewports;\n        self.screen_rect = screen_rect.or(self.screen_rect);\n        self.max_texture_side = max_texture_side.or(self.max_texture_side);\n        self.time = time; // use latest time\n        self.predicted_dt = predicted_dt; // use latest dt\n        self.modifiers = modifiers; // use latest\n        self.events.append(&mut events);\n        self.hovered_files.append(&mut hovered_files);\n        self.dropped_files.append(&mut dropped_files);\n        self.focused = focused;\n        self.system_theme = system_theme;\n        self.safe_area_insets = safe_area;\n    }\n}\n\n/// An input event from the backend into egui, about a specific [viewport](crate::viewport).\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum ViewportEvent {\n    /// The user clicked the close-button on the window, or similar.\n    ///\n    /// If this is the root viewport, the application will exit\n    /// after this frame unless you send a\n    /// [`crate::ViewportCommand::CancelClose`] command.\n    ///\n    /// If this is not the root viewport,\n    /// it is up to the user to hide this viewport the next frame.\n    ///\n    /// This even will wake up both the child and parent viewport.\n    Close,\n}\n\n/// Information about the current viewport, given as input each frame.\n///\n/// `None` means \"unknown\".\n///\n/// All units are in ui \"points\", which can be calculated from native physical pixels\n/// using `pixels_per_point` = [`crate::Context::zoom_factor`] * `[Self::native_pixels_per_point`];\n#[derive(Clone, Debug, Default, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct ViewportInfo {\n    /// Parent viewport, if known.\n    pub parent: Option<crate::ViewportId>,\n\n    /// Name of the viewport, if known.\n    pub title: Option<String>,\n\n    pub events: Vec<ViewportEvent>,\n\n    /// The OS native pixels-per-point.\n    ///\n    /// This should always be set, if known.\n    ///\n    /// On web this takes browser scaling into account,\n    /// and corresponds to [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) in JavaScript.\n    pub native_pixels_per_point: Option<f32>,\n\n    /// Current monitor size in egui points.\n    pub monitor_size: Option<Vec2>,\n\n    /// The inner rectangle of the native window, in monitor space and ui points scale.\n    ///\n    /// This is the content rectangle of the viewport.\n    ///\n    /// **`eframe` notes**:\n    ///\n    /// On Android / Wayland, this will always be `None` since getting the\n    /// position of the window is not possible.\n    pub inner_rect: Option<Rect>,\n\n    /// The outer rectangle of the native window, in monitor space and ui points scale.\n    ///\n    /// This is the content rectangle plus decoration chrome.\n    ///\n    /// **`eframe` notes**:\n    ///\n    /// On Android / Wayland, this will always be `None` since getting the\n    /// position of the window is not possible.\n    pub outer_rect: Option<Rect>,\n\n    /// Are we minimized?\n    pub minimized: Option<bool>,\n\n    /// Are we maximized?\n    pub maximized: Option<bool>,\n\n    /// Are we in fullscreen mode?\n    pub fullscreen: Option<bool>,\n\n    /// Is the window focused and able to receive input?\n    ///\n    /// This should be the same as [`RawInput::focused`].\n    pub focused: Option<bool>,\n\n    /// Is the window fully occluded (completely covered) by another window?\n    ///\n    /// Not all platforms support this.\n    /// On platforms that don't, this will be `None` or `Some(false)`.\n    pub occluded: Option<bool>,\n}\n\nimpl ViewportInfo {\n    /// Is the window considered visible for rendering purposes?\n    ///\n    /// A window is not visible if it is minimized or occluded.\n    /// When not visible, the UI is not painted and rendering is skipped,\n    /// but application logic may still be executed by some integrations.\n    pub fn visible(&self) -> Option<bool> {\n        match (self.minimized, self.occluded) {\n            (Some(true), _) | (_, Some(true)) => Some(false),\n            (Some(false), Some(false)) => Some(true),\n            (_, None) | (None, _) => None,\n        }\n    }\n\n    /// This viewport has been told to close.\n    ///\n    /// If this is the root viewport, the application will exit\n    /// after this frame unless you send a\n    /// [`crate::ViewportCommand::CancelClose`] command.\n    ///\n    /// If this is not the root viewport,\n    /// it is up to the user to hide this viewport the next frame.\n    pub fn close_requested(&self) -> bool {\n        self.events.contains(&ViewportEvent::Close)\n    }\n\n    /// Helper: move [`Self::events`], clone the other fields.\n    pub fn take(&mut self) -> Self {\n        Self {\n            parent: self.parent,\n            title: self.title.clone(),\n            events: std::mem::take(&mut self.events),\n            native_pixels_per_point: self.native_pixels_per_point,\n            monitor_size: self.monitor_size,\n            inner_rect: self.inner_rect,\n            outer_rect: self.outer_rect,\n            minimized: self.minimized,\n            maximized: self.maximized,\n            fullscreen: self.fullscreen,\n            focused: self.focused,\n            occluded: self.occluded,\n        }\n    }\n\n    pub fn ui(&self, ui: &mut crate::Ui) {\n        let Self {\n            parent,\n            title,\n            events,\n            native_pixels_per_point,\n            monitor_size,\n            inner_rect,\n            outer_rect,\n            minimized,\n            maximized,\n            fullscreen,\n            focused,\n            occluded,\n        } = self;\n\n        crate::Grid::new(\"viewport_info\").show(ui, |ui| {\n            ui.label(\"Parent:\");\n            ui.label(opt_as_str(parent));\n            ui.end_row();\n\n            ui.label(\"Title:\");\n            ui.label(opt_as_str(title));\n            ui.end_row();\n\n            ui.label(\"Events:\");\n            ui.label(format!(\"{events:?}\"));\n            ui.end_row();\n\n            ui.label(\"Native pixels-per-point:\");\n            ui.label(opt_as_str(native_pixels_per_point));\n            ui.end_row();\n\n            ui.label(\"Monitor size:\");\n            ui.label(opt_as_str(monitor_size));\n            ui.end_row();\n\n            ui.label(\"Inner rect:\");\n            ui.label(opt_rect_as_string(inner_rect));\n            ui.end_row();\n\n            ui.label(\"Outer rect:\");\n            ui.label(opt_rect_as_string(outer_rect));\n            ui.end_row();\n\n            ui.label(\"Minimized:\");\n            ui.label(opt_as_str(minimized));\n            ui.end_row();\n\n            ui.label(\"Maximized:\");\n            ui.label(opt_as_str(maximized));\n            ui.end_row();\n\n            ui.label(\"Fullscreen:\");\n            ui.label(opt_as_str(fullscreen));\n            ui.end_row();\n\n            ui.label(\"Focused:\");\n            ui.label(opt_as_str(focused));\n            ui.end_row();\n\n            ui.label(\"Occluded:\");\n            ui.label(opt_as_str(occluded));\n            ui.end_row();\n\n            let visible = self.visible();\n\n            ui.label(\"Visible:\");\n            ui.label(opt_as_str(&visible));\n            ui.end_row();\n\n            fn opt_rect_as_string(v: &Option<Rect>) -> String {\n                v.as_ref().map_or(String::new(), |r| {\n                    format!(\"Pos: {:?}, size: {:?}\", r.min, r.size())\n                })\n            }\n\n            fn opt_as_str<T: std::fmt::Debug>(v: &Option<T>) -> String {\n                v.as_ref().map_or(String::new(), |v| format!(\"{v:?}\"))\n            }\n        });\n    }\n}\n\n/// A file about to be dropped into egui.\n#[derive(Clone, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct HoveredFile {\n    /// Set by the `egui-winit` backend.\n    pub path: Option<std::path::PathBuf>,\n\n    /// With the `eframe` web backend, this is set to the mime-type of the file (if available).\n    pub mime: String,\n}\n\n/// A file dropped into egui.\n#[derive(Clone, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct DroppedFile {\n    /// Set by the `egui-winit` backend.\n    pub path: Option<std::path::PathBuf>,\n\n    /// Name of the file. Set by the `eframe` web backend.\n    pub name: String,\n\n    /// With the `eframe` web backend, this is set to the mime-type of the file (if available).\n    pub mime: String,\n\n    /// Set by the `eframe` web backend.\n    pub last_modified: Option<std::time::SystemTime>,\n\n    /// Set by the `eframe` web backend.\n    pub bytes: Option<std::sync::Arc<[u8]>>,\n}\n\n/// An input event generated by the integration.\n///\n/// This only covers events that egui cares about.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum Event {\n    /// The integration detected a \"copy\" event (e.g. Cmd+C).\n    Copy,\n\n    /// The integration detected a \"cut\" event (e.g. Cmd+X).\n    Cut,\n\n    /// The integration detected a \"paste\" event (e.g. Cmd+V).\n    Paste(String),\n\n    /// Text input, e.g. via keyboard.\n    ///\n    /// When the user presses enter/return, do not send a [`Text`](Event::Text) (just [`Key::Enter`]).\n    Text(String),\n\n    /// A key was pressed or released.\n    Key {\n        /// Most of the time, it's the logical key, heeding the active keymap -- for instance, if the user has Dvorak\n        /// keyboard layout, it will be taken into account.\n        ///\n        /// If it's impossible to determine the logical key on desktop platforms (say, in case of non-Latin letters),\n        /// `key` falls back to the value of the corresponding physical key. This is necessary for proper work of\n        /// standard shortcuts that only respond to Latin-based bindings (such as `Ctrl` + `V`).\n        key: Key,\n\n        /// The physical key, corresponding to the actual position on the keyboard.\n        ///\n        /// This ignores keymaps, so it is not recommended to use this.\n        /// The only thing it makes sense for is things like games,\n        /// where e.g. the physical location of WSAD on QWERTY should always map to movement,\n        /// even if the user is using Dvorak or AZERTY.\n        ///\n        /// `eframe` does not (yet) implement this on web.\n        physical_key: Option<Key>,\n\n        /// Was it pressed or released?\n        pressed: bool,\n\n        /// If this is a `pressed` event, is it a key-repeat?\n        ///\n        /// On many platforms, holding down a key produces many repeated \"pressed\" events for it, so called key-repeats.\n        /// Sometimes you will want to ignore such events, and this lets you do that.\n        ///\n        /// egui will automatically detect such repeat events and mark them as such here.\n        /// Therefore, if you are writing an egui integration, you do not need to set this (just set it to `false`).\n        repeat: bool,\n\n        /// The state of the modifier keys at the time of the event.\n        modifiers: Modifiers,\n    },\n\n    /// The mouse or touch moved to a new place.\n    PointerMoved(Pos2),\n\n    /// The mouse moved, the units are unspecified.\n    /// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.\n    /// `PointerMoved` and `MouseMoved` can be sent at the same time.\n    /// This event is optional. If the integration can not determine unfiltered motion it should not send this event.\n    MouseMoved(Vec2),\n\n    /// A mouse button was pressed or released (or a touch started or stopped).\n    PointerButton {\n        /// Where is the pointer?\n        pos: Pos2,\n\n        /// What mouse button? For touches, use [`PointerButton::Primary`].\n        button: PointerButton,\n\n        /// Was it the button/touch pressed this frame, or released?\n        pressed: bool,\n\n        /// The state of the modifier keys at the time of the event.\n        modifiers: Modifiers,\n    },\n\n    /// The mouse left the screen, or the last/primary touch input disappeared.\n    ///\n    /// This means there is no longer a cursor on the screen for hovering etc.\n    ///\n    /// On touch-up first send `PointerButton{pressed: false, …}` followed by `PointerLeft`.\n    PointerGone,\n\n    /// Zoom scale factor this frame (e.g. from a pinch gesture).\n    ///\n    /// * `zoom = 1`: no change.\n    /// * `zoom < 1`: pinch together\n    /// * `zoom > 1`: pinch spread\n    ///\n    /// Note that egui also implement zooming by holding `Ctrl` and scrolling the mouse wheel,\n    /// so integration need NOT emit this `Zoom` event in those cases, just [`Self::MouseWheel`].\n    ///\n    /// As a user, check [`crate::InputState::smooth_scroll_delta`] to see if the user did any zooming this frame.\n    Zoom(f32),\n\n    /// Rotation in radians this frame, measuring clockwise (e.g. from a rotation gesture).\n    Rotate(f32),\n\n    /// IME Event\n    Ime(ImeEvent),\n\n    /// On touch screens, report this *in addition to*\n    /// [`Self::PointerMoved`], [`Self::PointerButton`], [`Self::PointerGone`]\n    Touch {\n        /// Hashed device identifier (if available; may be zero).\n        /// Can be used to separate touches from different devices.\n        device_id: TouchDeviceId,\n\n        /// Unique identifier of a finger/pen. Value is stable from touch down\n        /// to lift-up\n        id: TouchId,\n\n        /// One of: start move end cancel.\n        phase: TouchPhase,\n\n        /// Position of the touch (or where the touch was last detected)\n        pos: Pos2,\n\n        /// Describes how hard the touch device was pressed. May always be `None` if the platform does\n        /// not support pressure sensitivity.\n        /// The value is in the range from 0.0 (no pressure) to 1.0 (maximum pressure).\n        force: Option<f32>,\n    },\n\n    /// A raw mouse wheel event as sent by the backend.\n    ///\n    /// Used for scrolling.\n    MouseWheel {\n        /// The unit of `delta`: points, lines, or pages.\n        unit: MouseWheelUnit,\n\n        /// The direction of the vector indicates how to move the _content_ that is being viewed.\n        /// So if you get positive values, the content being viewed should move to the right and down,\n        /// revealing new things to the left and up.\n        ///\n        /// A positive X-value indicates the content is being moved right,\n        /// as when swiping right on a touch-screen or track-pad with natural scrolling.\n        ///\n        /// A positive Y-value indicates the content is being moved down,\n        /// as when swiping down on a touch-screen or track-pad with natural scrolling.\n        delta: Vec2,\n\n        /// The phase of the scroll, useful for trackpads.\n        ///\n        /// If unknown set this to [`TouchPhase::Move`].\n        phase: TouchPhase,\n\n        /// The state of the modifier keys at the time of the event.\n        modifiers: Modifiers,\n    },\n\n    /// The native window gained or lost focused (e.g. the user clicked alt-tab).\n    WindowFocused(bool),\n\n    /// An assistive technology (e.g. screen reader) requested an action.\n    AccessKitActionRequest(accesskit::ActionRequest),\n\n    /// The reply of a screenshot requested with [`crate::ViewportCommand::Screenshot`].\n    Screenshot {\n        viewport_id: crate::ViewportId,\n\n        /// Whatever was passed to [`crate::ViewportCommand::Screenshot`].\n        user_data: crate::UserData,\n\n        image: std::sync::Arc<ColorImage>,\n    },\n}\n\n/// IME event.\n///\n/// See <https://docs.rs/winit/latest/winit/event/enum.Ime.html>\n#[derive(Clone, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum ImeEvent {\n    /// Notifies when the IME was enabled.\n    Enabled,\n\n    /// A new IME candidate is being suggested.\n    Preedit(String),\n\n    /// IME composition ended with this final result.\n    Commit(String),\n\n    /// Notifies when the IME was disabled.\n    Disabled,\n}\n\n/// Mouse button (or similar for touch input)\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum PointerButton {\n    /// The primary mouse button is usually the left one.\n    Primary = 0,\n\n    /// The secondary mouse button is usually the right one,\n    /// and most often used for context menus or other optional things.\n    Secondary = 1,\n\n    /// The tertiary mouse button is usually the middle mouse button (e.g. clicking the scroll wheel).\n    Middle = 2,\n\n    /// The first extra mouse button on some mice. In web typically corresponds to the Browser back button.\n    Extra1 = 3,\n\n    /// The second extra mouse button on some mice. In web typically corresponds to the Browser forward button.\n    Extra2 = 4,\n}\n\n/// Number of pointer buttons supported by egui, i.e. the number of possible states of [`PointerButton`].\npub const NUM_POINTER_BUTTONS: usize = 5;\n\n/// State of the modifier keys. These must be fed to egui.\n///\n/// The best way to compare [`Modifiers`] is by using [`Modifiers::matches_logically`] or [`Modifiers::matches_exact`].\n///\n/// To access the [`Modifiers`] you can use the [`crate::Context::input`] function\n///\n/// ```rust\n/// # let ctx = egui::Context::default();\n/// let modifiers = ctx.input(|i| i.modifiers);\n/// ```\n///\n/// NOTE: For cross-platform uses, ALT+SHIFT is a bad combination of modifiers\n/// as on mac that is how you type special characters,\n/// so those key presses are usually not reported to egui.\n#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Modifiers {\n    /// Either of the alt keys are down (option ⌥ on Mac).\n    pub alt: bool,\n\n    /// Either of the control keys are down.\n    /// When checking for keyboard shortcuts, consider using [`Self::command`] instead.\n    pub ctrl: bool,\n\n    /// Either of the shift keys are down.\n    pub shift: bool,\n\n    /// The Mac ⌘ Command key. Should always be set to `false` on other platforms.\n    pub mac_cmd: bool,\n\n    /// On Windows and Linux, set this to the same value as `ctrl`.\n    /// On Mac, this should be set whenever one of the ⌘ Command keys are down (same as `mac_cmd`).\n    /// This is so that egui can, for instance, select all text by checking for `command + A`\n    /// and it will work on both Mac and Windows.\n    pub command: bool,\n}\n\nimpl std::fmt::Debug for Modifiers {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        if self.is_none() {\n            return write!(f, \"Modifiers::NONE\");\n        }\n\n        let Self {\n            alt,\n            ctrl,\n            shift,\n            mac_cmd,\n            command,\n        } = *self;\n\n        let mut debug = f.debug_struct(\"Modifiers\");\n        if alt {\n            debug.field(\"alt\", &true);\n        }\n        if ctrl {\n            debug.field(\"ctrl\", &true);\n        }\n        if shift {\n            debug.field(\"shift\", &true);\n        }\n        if mac_cmd {\n            debug.field(\"mac_cmd\", &true);\n        }\n        if command {\n            debug.field(\"command\", &true);\n        }\n        debug.finish()\n    }\n}\n\nimpl Modifiers {\n    pub const NONE: Self = Self {\n        alt: false,\n        ctrl: false,\n        shift: false,\n        mac_cmd: false,\n        command: false,\n    };\n\n    pub const ALT: Self = Self {\n        alt: true,\n        ctrl: false,\n        shift: false,\n        mac_cmd: false,\n        command: false,\n    };\n    pub const CTRL: Self = Self {\n        alt: false,\n        ctrl: true,\n        shift: false,\n        mac_cmd: false,\n        command: false,\n    };\n    pub const SHIFT: Self = Self {\n        alt: false,\n        ctrl: false,\n        shift: true,\n        mac_cmd: false,\n        command: false,\n    };\n\n    /// The Mac ⌘ Command key\n    pub const MAC_CMD: Self = Self {\n        alt: false,\n        ctrl: false,\n        shift: false,\n        mac_cmd: true,\n        command: false,\n    };\n\n    /// On Mac: ⌘ Command key, elsewhere: Ctrl key\n    pub const COMMAND: Self = Self {\n        alt: false,\n        ctrl: false,\n        shift: false,\n        mac_cmd: false,\n        command: true,\n    };\n\n    /// ```\n    /// # use egui::Modifiers;\n    /// assert_eq!(\n    ///     Modifiers::CTRL | Modifiers::ALT,\n    ///     Modifiers { ctrl: true, alt: true, ..Default::default() }\n    /// );\n    /// assert_eq!(\n    ///     Modifiers::ALT.plus(Modifiers::CTRL),\n    ///     Modifiers::CTRL.plus(Modifiers::ALT),\n    /// );\n    /// assert_eq!(\n    ///     Modifiers::CTRL | Modifiers::ALT,\n    ///     Modifiers::CTRL.plus(Modifiers::ALT),\n    /// );\n    /// ```\n    #[inline]\n    pub const fn plus(self, rhs: Self) -> Self {\n        Self {\n            alt: self.alt | rhs.alt,\n            ctrl: self.ctrl | rhs.ctrl,\n            shift: self.shift | rhs.shift,\n            mac_cmd: self.mac_cmd | rhs.mac_cmd,\n            command: self.command | rhs.command,\n        }\n    }\n\n    #[inline]\n    pub fn is_none(&self) -> bool {\n        self == &Self::default()\n    }\n\n    #[inline]\n    pub fn any(&self) -> bool {\n        !self.is_none()\n    }\n\n    #[inline]\n    pub fn all(&self) -> bool {\n        self.alt && self.ctrl && self.shift && self.command\n    }\n\n    /// Is shift the only pressed button?\n    #[inline]\n    pub fn shift_only(&self) -> bool {\n        self.shift && !(self.alt || self.command)\n    }\n\n    /// true if only [`Self::ctrl`] or only [`Self::mac_cmd`] is pressed.\n    #[inline]\n    pub fn command_only(&self) -> bool {\n        !self.alt && !self.shift && self.command\n    }\n\n    /// Checks that the `ctrl/cmd` matches, and that the `shift/alt` of the argument is a subset\n    /// of the pressed key (`self`).\n    ///\n    /// This means that if the pattern has not set `shift`, then `self` can have `shift` set or not.\n    ///\n    /// The reason is that many logical keys require `shift` or `alt` on some keyboard layouts.\n    /// For instance, in order to press `+` on an English keyboard, you need to press `shift` and `=`,\n    /// but a Swedish keyboard has dedicated `+` key.\n    /// So if you want to make a [`KeyboardShortcut`] looking for `Cmd` + `+`, it makes sense\n    /// to ignore the shift key.\n    /// Similarly, the `Alt` key is sometimes used to type special characters.\n    ///\n    /// However, if the pattern (the argument) explicitly requires the `shift` or `alt` keys\n    /// to be pressed, then they must be pressed.\n    ///\n    /// # Example:\n    /// ```\n    /// # use egui::Modifiers;\n    /// # let pressed_modifiers = Modifiers::default();\n    /// if pressed_modifiers.matches_logically(Modifiers::ALT | Modifiers::SHIFT) {\n    ///     // Alt and Shift are pressed, but not ctrl/command\n    /// }\n    /// ```\n    ///\n    /// ## Behavior:\n    /// ```\n    /// # use egui::Modifiers;\n    /// assert!(Modifiers::CTRL.matches_logically(Modifiers::CTRL));\n    /// assert!(!Modifiers::CTRL.matches_logically(Modifiers::CTRL | Modifiers::SHIFT));\n    /// assert!((Modifiers::CTRL | Modifiers::SHIFT).matches_logically(Modifiers::CTRL));\n    /// assert!((Modifiers::CTRL | Modifiers::COMMAND).matches_logically(Modifiers::CTRL));\n    /// assert!((Modifiers::CTRL | Modifiers::COMMAND).matches_logically(Modifiers::COMMAND));\n    /// assert!((Modifiers::MAC_CMD | Modifiers::COMMAND).matches_logically(Modifiers::COMMAND));\n    /// assert!(!Modifiers::COMMAND.matches_logically(Modifiers::MAC_CMD));\n    /// ```\n    pub fn matches_logically(&self, pattern: Self) -> bool {\n        if pattern.alt && !self.alt {\n            return false;\n        }\n        if pattern.shift && !self.shift {\n            return false;\n        }\n\n        self.cmd_ctrl_matches(pattern)\n    }\n\n    /// Check for equality but with proper handling of [`Self::command`].\n    ///\n    /// `self` here are the currently pressed modifiers,\n    /// and the argument the pattern we are testing for.\n    ///\n    /// Note that this will require the `shift` and `alt` keys match, even though\n    /// these modifiers are sometimes required to produce some logical keys.\n    /// For instance, to press `+` on an English keyboard, you need to press `shift` and `=`,\n    /// but on a Swedish keyboard you can press the dedicated `+` key.\n    /// Therefore, you often want to use [`Self::matches_logically`] instead.\n    ///\n    /// # Example:\n    /// ```\n    /// # use egui::Modifiers;\n    /// # let pressed_modifiers = Modifiers::default();\n    /// if pressed_modifiers.matches_exact(Modifiers::ALT | Modifiers::SHIFT) {\n    ///     // Alt and Shift are pressed, and nothing else\n    /// }\n    /// ```\n    ///\n    /// ## Behavior:\n    /// ```\n    /// # use egui::Modifiers;\n    /// assert!(Modifiers::CTRL.matches_exact(Modifiers::CTRL));\n    /// assert!(!Modifiers::CTRL.matches_exact(Modifiers::CTRL | Modifiers::SHIFT));\n    /// assert!(!(Modifiers::CTRL | Modifiers::SHIFT).matches_exact(Modifiers::CTRL));\n    /// assert!((Modifiers::CTRL | Modifiers::COMMAND).matches_exact(Modifiers::CTRL));\n    /// assert!((Modifiers::CTRL | Modifiers::COMMAND).matches_exact(Modifiers::COMMAND));\n    /// assert!((Modifiers::MAC_CMD | Modifiers::COMMAND).matches_exact(Modifiers::COMMAND));\n    /// assert!(!Modifiers::COMMAND.matches_exact(Modifiers::MAC_CMD));\n    /// ```\n    pub fn matches_exact(&self, pattern: Self) -> bool {\n        // alt and shift must always match the pattern:\n        if pattern.alt != self.alt || pattern.shift != self.shift {\n            return false;\n        }\n\n        self.cmd_ctrl_matches(pattern)\n    }\n\n    /// Check if any of the modifiers match exactly.\n    ///\n    /// Returns true if the same modifier is pressed in `self` as in `pattern`,\n    /// for at least one modifier.\n    ///\n    /// ## Behavior:\n    /// ```\n    /// # use egui::Modifiers;\n    /// assert!(Modifiers::CTRL.matches_any(Modifiers::CTRL));\n    /// assert!(Modifiers::CTRL.matches_any(Modifiers::CTRL | Modifiers::SHIFT));\n    /// assert!((Modifiers::CTRL | Modifiers::SHIFT).matches_any(Modifiers::CTRL));\n    /// ```\n    pub fn matches_any(&self, pattern: Self) -> bool {\n        if self.alt && pattern.alt {\n            return true;\n        }\n        if self.shift && pattern.shift {\n            return true;\n        }\n        if self.ctrl && pattern.ctrl {\n            return true;\n        }\n        if self.mac_cmd && pattern.mac_cmd {\n            return true;\n        }\n        if (self.mac_cmd || self.command || self.ctrl) && pattern.command {\n            return true;\n        }\n        false\n    }\n\n    /// Checks only cmd/ctrl, not alt/shift.\n    ///\n    /// `self` here are the currently pressed modifiers,\n    /// and the argument the pattern we are testing for.\n    ///\n    /// This takes care to properly handle the difference between\n    /// [`Self::ctrl`], [`Self::command`] and [`Self::mac_cmd`].\n    pub fn cmd_ctrl_matches(&self, pattern: Self) -> bool {\n        if pattern.mac_cmd {\n            // Mac-specific match:\n            if !self.mac_cmd {\n                return false;\n            }\n            if pattern.ctrl != self.ctrl {\n                return false;\n            }\n            return true;\n        }\n\n        if !pattern.ctrl && !pattern.command {\n            // the pattern explicitly doesn't want any ctrl/command:\n            return !self.ctrl && !self.command;\n        }\n\n        // if the pattern is looking for command, then `ctrl` may or may not be set depending on platform.\n        // if the pattern is looking for `ctrl`, then `command` may or may not be set depending on platform.\n\n        if pattern.ctrl && !self.ctrl {\n            return false;\n        }\n        if pattern.command && !self.command {\n            return false;\n        }\n\n        true\n    }\n\n    /// Whether another set of modifiers is contained in this set of modifiers with proper handling of [`Self::command`].\n    ///\n    /// ```\n    /// # use egui::Modifiers;\n    /// assert!(Modifiers::default().contains(Modifiers::default()));\n    /// assert!(Modifiers::CTRL.contains(Modifiers::default()));\n    /// assert!(Modifiers::CTRL.contains(Modifiers::CTRL));\n    /// assert!(Modifiers::CTRL.contains(Modifiers::COMMAND));\n    /// assert!(Modifiers::MAC_CMD.contains(Modifiers::COMMAND));\n    /// assert!(Modifiers::COMMAND.contains(Modifiers::MAC_CMD));\n    /// assert!(Modifiers::COMMAND.contains(Modifiers::CTRL));\n    /// assert!(!(Modifiers::ALT | Modifiers::CTRL).contains(Modifiers::SHIFT));\n    /// assert!((Modifiers::CTRL | Modifiers::SHIFT).contains(Modifiers::CTRL));\n    /// assert!(!Modifiers::CTRL.contains(Modifiers::CTRL | Modifiers::SHIFT));\n    /// ```\n    pub fn contains(&self, query: Self) -> bool {\n        if query == Self::default() {\n            return true;\n        }\n\n        let Self {\n            alt,\n            ctrl,\n            shift,\n            mac_cmd,\n            command,\n        } = *self;\n\n        if alt && query.alt {\n            return self.contains(Self {\n                alt: false,\n                ..query\n            });\n        }\n        if shift && query.shift {\n            return self.contains(Self {\n                shift: false,\n                ..query\n            });\n        }\n\n        if (ctrl || command) && (query.ctrl || query.command) {\n            return self.contains(Self {\n                command: false,\n                ctrl: false,\n                ..query\n            });\n        }\n        if (mac_cmd || command) && (query.mac_cmd || query.command) {\n            return self.contains(Self {\n                mac_cmd: false,\n                command: false,\n                ..query\n            });\n        }\n\n        false\n    }\n}\n\nimpl std::ops::BitOr for Modifiers {\n    type Output = Self;\n\n    #[inline]\n    fn bitor(self, rhs: Self) -> Self {\n        self.plus(rhs)\n    }\n}\n\nimpl std::ops::BitOrAssign for Modifiers {\n    #[inline]\n    fn bitor_assign(&mut self, rhs: Self) {\n        *self = *self | rhs;\n    }\n}\n\nimpl Modifiers {\n    pub fn ui(&self, ui: &mut crate::Ui) {\n        ui.label(ModifierNames::NAMES.format(self, ui.ctx().os().is_mac()));\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Names of different modifier keys.\n///\n/// Used to name modifiers.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub struct ModifierNames<'a> {\n    pub is_short: bool,\n\n    pub alt: &'a str,\n    pub ctrl: &'a str,\n    pub shift: &'a str,\n    pub mac_cmd: &'a str,\n    pub mac_alt: &'a str,\n\n    /// What goes between the names\n    pub concat: &'a str,\n}\n\nimpl ModifierNames<'static> {\n    /// ⌥ ⌃ ⇧ ⌘ - NOTE: not supported by the default egui font.\n    pub const SYMBOLS: Self = Self {\n        is_short: true,\n        alt: \"⌥\",\n        ctrl: \"⌃\",\n        shift: \"⇧\",\n        mac_cmd: \"⌘\",\n        mac_alt: \"⌥\",\n        concat: \"\",\n    };\n\n    /// Alt, Ctrl, Shift, Cmd\n    pub const NAMES: Self = Self {\n        is_short: false,\n        alt: \"Alt\",\n        ctrl: \"Ctrl\",\n        shift: \"Shift\",\n        mac_cmd: \"Cmd\",\n        mac_alt: \"Option\",\n        concat: \"+\",\n    };\n}\n\nimpl ModifierNames<'_> {\n    pub fn format(&self, modifiers: &Modifiers, is_mac: bool) -> String {\n        let mut s = String::new();\n\n        let mut append_if = |modifier_is_active, modifier_name| {\n            if modifier_is_active {\n                if !s.is_empty() {\n                    s += self.concat;\n                }\n                s += modifier_name;\n            }\n        };\n\n        if is_mac {\n            append_if(modifiers.ctrl, self.ctrl);\n            append_if(modifiers.shift, self.shift);\n            append_if(modifiers.alt, self.mac_alt);\n            append_if(modifiers.mac_cmd || modifiers.command, self.mac_cmd);\n        } else {\n            append_if(modifiers.ctrl || modifiers.command, self.ctrl);\n            append_if(modifiers.alt, self.alt);\n            append_if(modifiers.shift, self.shift);\n        }\n\n        s\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A keyboard shortcut, e.g. `Ctrl+Alt+W`.\n///\n/// Can be used with [`crate::InputState::consume_shortcut`]\n/// and [`crate::Context::format_shortcut`].\n#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct KeyboardShortcut {\n    pub modifiers: Modifiers,\n\n    pub logical_key: Key,\n}\n\nimpl KeyboardShortcut {\n    pub const fn new(modifiers: Modifiers, logical_key: Key) -> Self {\n        Self {\n            modifiers,\n            logical_key,\n        }\n    }\n\n    pub fn format(&self, names: &ModifierNames<'_>, is_mac: bool) -> String {\n        let mut s = names.format(&self.modifiers, is_mac);\n        if !s.is_empty() {\n            s += names.concat;\n        }\n        if names.is_short {\n            s += self.logical_key.symbol_or_name();\n        } else {\n            s += self.logical_key.name();\n        }\n        s\n    }\n}\n\n#[test]\nfn format_kb_shortcut() {\n    let cmd_shift_f = KeyboardShortcut::new(Modifiers::COMMAND | Modifiers::SHIFT, Key::F);\n    assert_eq!(\n        cmd_shift_f.format(&ModifierNames::NAMES, false),\n        \"Ctrl+Shift+F\"\n    );\n    assert_eq!(\n        cmd_shift_f.format(&ModifierNames::NAMES, true),\n        \"Shift+Cmd+F\"\n    );\n    assert_eq!(cmd_shift_f.format(&ModifierNames::SYMBOLS, false), \"⌃⇧F\");\n    assert_eq!(cmd_shift_f.format(&ModifierNames::SYMBOLS, true), \"⇧⌘F\");\n}\n\n// ----------------------------------------------------------------------------\n\nimpl RawInput {\n    pub fn ui(&self, ui: &mut crate::Ui) {\n        let Self {\n            viewport_id,\n            viewports,\n            screen_rect,\n            max_texture_side,\n            time,\n            predicted_dt,\n            modifiers,\n            events,\n            hovered_files,\n            dropped_files,\n            focused,\n            system_theme,\n            safe_area_insets: safe_area,\n        } = self;\n\n        ui.label(format!(\"Active viewport: {viewport_id:?}\"));\n        let ordered_viewports = viewports\n            .iter()\n            .map(|(id, value)| (*id, value))\n            .collect::<OrderedViewportIdMap<_>>();\n        for (id, viewport) in ordered_viewports {\n            ui.group(|ui| {\n                ui.label(format!(\"Viewport {id:?}\"));\n                ui.push_id(id, |ui| {\n                    viewport.ui(ui);\n                });\n            });\n        }\n        ui.label(format!(\"screen_rect: {screen_rect:?} points\"));\n\n        ui.label(format!(\"max_texture_side: {max_texture_side:?}\"));\n        if let Some(time) = time {\n            ui.label(format!(\"time: {time:.3} s\"));\n        } else {\n            ui.label(\"time: None\");\n        }\n        ui.label(format!(\"predicted_dt: {:.1} ms\", 1e3 * predicted_dt));\n        ui.label(format!(\"modifiers: {modifiers:#?}\"));\n        ui.label(format!(\"hovered_files: {}\", hovered_files.len()));\n        ui.label(format!(\"dropped_files: {}\", dropped_files.len()));\n        ui.label(format!(\"focused: {focused}\"));\n        ui.label(format!(\"system_theme: {system_theme:?}\"));\n        ui.label(format!(\"safe_area: {safe_area:?}\"));\n        ui.scope(|ui| {\n            ui.set_min_height(150.0);\n            ui.label(format!(\"events: {events:#?}\"))\n                .on_hover_text(\"key presses etc\");\n        });\n    }\n}\n\n/// this is a `u64` as values of this kind can always be obtained by hashing\n#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct TouchDeviceId(pub u64);\n\n/// Unique identification of a touch occurrence (finger or pen or …).\n/// A Touch ID is valid until the finger is lifted.\n/// A new ID is used for the next touch.\n#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct TouchId(pub u64);\n\n/// In what phase a touch event is in.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum TouchPhase {\n    /// User just placed a touch point on the touch surface\n    Start,\n\n    /// User moves a touch point along the surface. This event is also sent when\n    /// any attributes (position, force, …) of the touch point change.\n    Move,\n\n    /// User lifted the finger or pen from the surface, or slid off the edge of\n    /// the surface\n    End,\n\n    /// Touch operation has been disrupted by something (various reasons are possible,\n    /// maybe a pop-up alert or any other kind of interruption which may not have\n    /// been intended by the user)\n    Cancel,\n}\n\n/// The unit associated with the numeric value of a mouse wheel event\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum MouseWheelUnit {\n    /// Number of ui points (logical pixels)\n    Point,\n\n    /// Number of lines\n    Line,\n\n    /// Number of pages\n    Page,\n}\n\nimpl From<u64> for TouchId {\n    fn from(id: u64) -> Self {\n        Self(id)\n    }\n}\n\nimpl From<i32> for TouchId {\n    fn from(id: i32) -> Self {\n        Self(id as u64)\n    }\n}\n\nimpl From<u32> for TouchId {\n    fn from(id: u32) -> Self {\n        Self(id as u64)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n// TODO(emilk): generalize this to a proper event filter.\n/// Controls which events that a focused widget will have exclusive access to.\n///\n/// Currently this only controls a few special keyboard events,\n/// but in the future this `struct` should be extended into a full callback thing.\n///\n/// Any events not covered by the filter are given to the widget, but are not exclusive.\n#[derive(Clone, Copy, Debug)]\npub struct EventFilter {\n    /// If `true`, pressing tab will act on the widget,\n    /// and NOT move focus away from the focused widget.\n    ///\n    /// Default: `false`\n    pub tab: bool,\n\n    /// If `true`, pressing horizontal arrows will act on the\n    /// widget, and NOT move focus away from the focused widget.\n    ///\n    /// Default: `false`\n    pub horizontal_arrows: bool,\n\n    /// If `true`, pressing vertical arrows will act on the\n    /// widget, and NOT move focus away from the focused widget.\n    ///\n    /// Default: `false`\n    pub vertical_arrows: bool,\n\n    /// If `true`, pressing escape will act on the widget,\n    /// and NOT surrender focus from the focused widget.\n    ///\n    /// Default: `false`\n    pub escape: bool,\n}\n\n#[expect(clippy::derivable_impls)] // let's be explicit\nimpl Default for EventFilter {\n    fn default() -> Self {\n        Self {\n            tab: false,\n            horizontal_arrows: false,\n            vertical_arrows: false,\n            escape: false,\n        }\n    }\n}\n\nimpl EventFilter {\n    pub fn matches(&self, event: &Event) -> bool {\n        if let Event::Key { key, .. } = event {\n            match key {\n                crate::Key::Tab => self.tab,\n                crate::Key::ArrowUp | crate::Key::ArrowDown => self.vertical_arrows,\n                crate::Key::ArrowRight | crate::Key::ArrowLeft => self.horizontal_arrows,\n                crate::Key::Escape => self.escape,\n                _ => true,\n            }\n        } else {\n            true\n        }\n    }\n}\n\n/// The 'safe area' insets of the screen\n///\n/// This represents the area taken up by the status bar, navigation controls, notches,\n/// or any other items that obscure parts of the screen.\n#[derive(Debug, PartialEq, Copy, Clone, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct SafeAreaInsets(pub MarginF32);\n\nimpl std::ops::Sub<SafeAreaInsets> for Rect {\n    type Output = Self;\n\n    fn sub(self, rhs: SafeAreaInsets) -> Self::Output {\n        self - rhs.0\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/data/key.rs",
    "content": "/// Keyboard keys.\n///\n/// egui usually uses logical keys, i.e. after applying any user keymap.\\\n// See comment at the end of `Key { … }` on how to add new keys.\n#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum Key {\n    // ----------------------------------------------\n    // Commands:\n    ArrowDown,\n    ArrowLeft,\n    ArrowRight,\n    ArrowUp,\n\n    Escape,\n    Tab,\n    Backspace,\n    Enter,\n    Space,\n\n    Insert,\n    Delete,\n    Home,\n    End,\n    PageUp,\n    PageDown,\n\n    Copy,\n    Cut,\n    Paste,\n\n    // ----------------------------------------------\n    // Punctuation:\n    /// `:`\n    Colon,\n\n    /// `,`\n    Comma,\n\n    /// `\\`\n    Backslash,\n\n    /// `/`\n    Slash,\n\n    /// `|`, a vertical bar\n    Pipe,\n\n    /// `?`\n    Questionmark,\n\n    // '!'\n    Exclamationmark,\n\n    // `[`\n    OpenBracket,\n\n    // `]`\n    CloseBracket,\n\n    // `{`\n    OpenCurlyBracket,\n\n    // `}`\n    CloseCurlyBracket,\n\n    /// Also known as \"backquote\" or \"grave\"\n    Backtick,\n\n    /// `-`\n    Minus,\n\n    /// `.`\n    Period,\n\n    /// `+`\n    Plus,\n\n    /// `=`\n    Equals,\n\n    /// `;`\n    Semicolon,\n\n    /// `'`\n    Quote,\n\n    // ----------------------------------------------\n    // Digits:\n    /// `0` (from main row or numpad)\n    Num0,\n\n    /// `1` (from main row or numpad)\n    Num1,\n\n    /// `2` (from main row or numpad)\n    Num2,\n\n    /// `3` (from main row or numpad)\n    Num3,\n\n    /// `4` (from main row or numpad)\n    Num4,\n\n    /// `5` (from main row or numpad)\n    Num5,\n\n    /// `6` (from main row or numpad)\n    Num6,\n\n    /// `7` (from main row or numpad)\n    Num7,\n\n    /// `8` (from main row or numpad)\n    Num8,\n\n    /// `9` (from main row or numpad)\n    Num9,\n\n    // ----------------------------------------------\n    // Letters:\n    A, // Used for cmd+A (select All)\n    B,\n    C, // |CMD COPY|\n    D, // |CMD BOOKMARK|\n    E, // |CMD SEARCH|\n    F, // |CMD FIND firefox & chrome|\n    G, // |CMD FIND chrome|\n    H, // |CMD History|\n    I, // italics\n    J, // |CMD SEARCH firefox/DOWNLOAD chrome|\n    K, // Used for ctrl+K (delete text after cursor)\n    L,\n    M,\n    N,\n    O, // |CMD OPEN|\n    P, // |CMD PRINT|\n    Q,\n    R, // |CMD REFRESH|\n    S, // |CMD SAVE|\n    T, // |CMD TAB|\n    U, // Used for ctrl+U (delete text before cursor)\n    V, // |CMD PASTE|\n    W, // Used for ctrl+W (delete previous word)\n    X, // |CMD CUT|\n    Y,\n    Z, // |CMD UNDO|\n\n    // ----------------------------------------------\n    // Function keys:\n    F1,\n    F2,\n    F3,\n    F4,\n    F5, // |CMD REFRESH|\n    F6,\n    F7,\n    F8,\n    F9,\n    F10,\n    F11,\n    F12,\n    F13,\n    F14,\n    F15,\n    F16,\n    F17,\n    F18,\n    F19,\n    F20,\n    F21,\n    F22,\n    F23,\n    F24,\n    F25,\n    F26,\n    F27,\n    F28,\n    F29,\n    F30,\n    F31,\n    F32,\n    F33,\n    F34,\n    F35,\n\n    /// Back navigation key from multimedia keyboard.\n    /// Android sends this key on Back button press.\n    /// Does not work on Web.\n    BrowserBack,\n    // When adding keys, remember to also update:\n    // * crates/egui-winit/src/lib.rs\n    // * Key::ALL\n    // * Key::from_name\n    // You should test that it works using the \"Input Event History\" window in the egui demo app.\n    // Make sure to test both natively and on web!\n    // Also: don't add keys last; add them to the group they best belong to.\n}\n\nimpl Key {\n    /// All egui keys\n    pub const ALL: &'static [Self] = &[\n        // Commands:\n        Self::ArrowDown,\n        Self::ArrowLeft,\n        Self::ArrowRight,\n        Self::ArrowUp,\n        Self::Escape,\n        Self::Tab,\n        Self::Backspace,\n        Self::Enter,\n        Self::Insert,\n        Self::Delete,\n        Self::Home,\n        Self::End,\n        Self::PageUp,\n        Self::PageDown,\n        Self::Copy,\n        Self::Cut,\n        Self::Paste,\n        // Punctuation:\n        Self::Space,\n        Self::Colon,\n        Self::Comma,\n        Self::Minus,\n        Self::Period,\n        Self::Plus,\n        Self::Equals,\n        Self::Semicolon,\n        Self::OpenBracket,\n        Self::CloseBracket,\n        Self::OpenCurlyBracket,\n        Self::CloseCurlyBracket,\n        Self::Backtick,\n        Self::Backslash,\n        Self::Slash,\n        Self::Pipe,\n        Self::Questionmark,\n        Self::Exclamationmark,\n        Self::Quote,\n        // Digits:\n        Self::Num0,\n        Self::Num1,\n        Self::Num2,\n        Self::Num3,\n        Self::Num4,\n        Self::Num5,\n        Self::Num6,\n        Self::Num7,\n        Self::Num8,\n        Self::Num9,\n        // Letters:\n        Self::A,\n        Self::B,\n        Self::C,\n        Self::D,\n        Self::E,\n        Self::F,\n        Self::G,\n        Self::H,\n        Self::I,\n        Self::J,\n        Self::K,\n        Self::L,\n        Self::M,\n        Self::N,\n        Self::O,\n        Self::P,\n        Self::Q,\n        Self::R,\n        Self::S,\n        Self::T,\n        Self::U,\n        Self::V,\n        Self::W,\n        Self::X,\n        Self::Y,\n        Self::Z,\n        // Function keys:\n        Self::F1,\n        Self::F2,\n        Self::F3,\n        Self::F4,\n        Self::F5,\n        Self::F6,\n        Self::F7,\n        Self::F8,\n        Self::F9,\n        Self::F10,\n        Self::F11,\n        Self::F12,\n        Self::F13,\n        Self::F14,\n        Self::F15,\n        Self::F16,\n        Self::F17,\n        Self::F18,\n        Self::F19,\n        Self::F20,\n        Self::F21,\n        Self::F22,\n        Self::F23,\n        Self::F24,\n        Self::F25,\n        Self::F26,\n        Self::F27,\n        Self::F28,\n        Self::F29,\n        Self::F30,\n        Self::F31,\n        Self::F32,\n        Self::F33,\n        Self::F34,\n        Self::F35,\n        // Navigation keys:\n        Self::BrowserBack,\n    ];\n\n    /// Converts `\"A\"` to `Key::A`, `Space` to `Key::Space`, etc.\n    ///\n    /// Makes sense for logical keys.\n    ///\n    /// This will parse the output of both [`Self::name`] and [`Self::symbol_or_name`],\n    /// but will also parse single characters, so that both `\"-\"` and `\"Minus\"` will return `Key::Minus`.\n    ///\n    /// This should support both the names generated in a web browser,\n    /// and by winit. Please test on both with `eframe`.\n    pub fn from_name(key: &str) -> Option<Self> {\n        Some(match key {\n            \"⏷\" | \"ArrowDown\" | \"Down\" => Self::ArrowDown,\n            \"⏴\" | \"ArrowLeft\" | \"Left\" => Self::ArrowLeft,\n            \"⏵\" | \"ArrowRight\" | \"Right\" => Self::ArrowRight,\n            \"⏶\" | \"ArrowUp\" | \"Up\" => Self::ArrowUp,\n\n            \"Escape\" | \"Esc\" => Self::Escape,\n            \"Tab\" => Self::Tab,\n            \"Backspace\" => Self::Backspace,\n            \"Enter\" | \"Return\" => Self::Enter,\n\n            \"Help\" | \"Insert\" => Self::Insert,\n            \"Delete\" => Self::Delete,\n            \"Home\" => Self::Home,\n            \"End\" => Self::End,\n            \"PageUp\" => Self::PageUp,\n            \"PageDown\" => Self::PageDown,\n\n            \"Copy\" => Self::Copy,\n            \"Cut\" => Self::Cut,\n            \"Paste\" => Self::Paste,\n\n            \" \" | \"Space\" => Self::Space,\n            \":\" | \"Colon\" => Self::Colon,\n            \",\" | \"Comma\" => Self::Comma,\n            \"-\" | \"−\" | \"Minus\" => Self::Minus,\n            \".\" | \"Period\" => Self::Period,\n            \"+\" | \"Plus\" => Self::Plus,\n            \"=\" | \"Equal\" | \"Equals\" | \"NumpadEqual\" => Self::Equals,\n            \";\" | \"Semicolon\" => Self::Semicolon,\n            \"\\\\\" | \"Backslash\" => Self::Backslash,\n            \"/\" | \"Slash\" => Self::Slash,\n            \"|\" | \"Pipe\" => Self::Pipe,\n            \"?\" | \"Questionmark\" => Self::Questionmark,\n            \"!\" | \"Exclamationmark\" => Self::Exclamationmark,\n            \"[\" | \"OpenBracket\" => Self::OpenBracket,\n            \"]\" | \"CloseBracket\" => Self::CloseBracket,\n            \"{\" | \"OpenCurlyBracket\" => Self::OpenCurlyBracket,\n            \"}\" | \"CloseCurlyBracket\" => Self::CloseCurlyBracket,\n            \"`\" | \"Backtick\" | \"Backquote\" | \"Grave\" => Self::Backtick,\n            \"'\" | \"Quote\" => Self::Quote,\n\n            \"0\" | \"Digit0\" | \"Numpad0\" => Self::Num0,\n            \"1\" | \"Digit1\" | \"Numpad1\" => Self::Num1,\n            \"2\" | \"Digit2\" | \"Numpad2\" => Self::Num2,\n            \"3\" | \"Digit3\" | \"Numpad3\" => Self::Num3,\n            \"4\" | \"Digit4\" | \"Numpad4\" => Self::Num4,\n            \"5\" | \"Digit5\" | \"Numpad5\" => Self::Num5,\n            \"6\" | \"Digit6\" | \"Numpad6\" => Self::Num6,\n            \"7\" | \"Digit7\" | \"Numpad7\" => Self::Num7,\n            \"8\" | \"Digit8\" | \"Numpad8\" => Self::Num8,\n            \"9\" | \"Digit9\" | \"Numpad9\" => Self::Num9,\n\n            \"a\" | \"A\" => Self::A,\n            \"b\" | \"B\" => Self::B,\n            \"c\" | \"C\" => Self::C,\n            \"d\" | \"D\" => Self::D,\n            \"e\" | \"E\" => Self::E,\n            \"f\" | \"F\" => Self::F,\n            \"g\" | \"G\" => Self::G,\n            \"h\" | \"H\" => Self::H,\n            \"i\" | \"I\" => Self::I,\n            \"j\" | \"J\" => Self::J,\n            \"k\" | \"K\" => Self::K,\n            \"l\" | \"L\" => Self::L,\n            \"m\" | \"M\" => Self::M,\n            \"n\" | \"N\" => Self::N,\n            \"o\" | \"O\" => Self::O,\n            \"p\" | \"P\" => Self::P,\n            \"q\" | \"Q\" => Self::Q,\n            \"r\" | \"R\" => Self::R,\n            \"s\" | \"S\" => Self::S,\n            \"t\" | \"T\" => Self::T,\n            \"u\" | \"U\" => Self::U,\n            \"v\" | \"V\" => Self::V,\n            \"w\" | \"W\" => Self::W,\n            \"x\" | \"X\" => Self::X,\n            \"y\" | \"Y\" => Self::Y,\n            \"z\" | \"Z\" => Self::Z,\n\n            \"F1\" => Self::F1,\n            \"F2\" => Self::F2,\n            \"F3\" => Self::F3,\n            \"F4\" => Self::F4,\n            \"F5\" => Self::F5,\n            \"F6\" => Self::F6,\n            \"F7\" => Self::F7,\n            \"F8\" => Self::F8,\n            \"F9\" => Self::F9,\n            \"F10\" => Self::F10,\n            \"F11\" => Self::F11,\n            \"F12\" => Self::F12,\n            \"F13\" => Self::F13,\n            \"F14\" => Self::F14,\n            \"F15\" => Self::F15,\n            \"F16\" => Self::F16,\n            \"F17\" => Self::F17,\n            \"F18\" => Self::F18,\n            \"F19\" => Self::F19,\n            \"F20\" => Self::F20,\n            \"F21\" => Self::F21,\n            \"F22\" => Self::F22,\n            \"F23\" => Self::F23,\n            \"F24\" => Self::F24,\n            \"F25\" => Self::F25,\n            \"F26\" => Self::F26,\n            \"F27\" => Self::F27,\n            \"F28\" => Self::F28,\n            \"F29\" => Self::F29,\n            \"F30\" => Self::F30,\n            \"F31\" => Self::F31,\n            \"F32\" => Self::F32,\n            \"F33\" => Self::F33,\n            \"F34\" => Self::F34,\n            \"F35\" => Self::F35,\n\n            \"BrowserBack\" => Self::BrowserBack,\n\n            _ => return None,\n        })\n    }\n\n    /// Emoji or name representing the key\n    pub fn symbol_or_name(self) -> &'static str {\n        // TODO(emilk): add support for more unicode symbols (see for instance https://wincent.com/wiki/Unicode_representations_of_modifier_keys).\n        // Before we do we must first make sure they are supported in `Fonts` though,\n        // so perhaps this functions needs to take a `supports_character: impl Fn(char) -> bool` or something.\n        match self {\n            Self::ArrowDown => \"⏷\",\n            Self::ArrowLeft => \"⏴\",\n            Self::ArrowRight => \"⏵\",\n            Self::ArrowUp => \"⏶\",\n\n            Self::Colon => \":\",\n            Self::Comma => \",\",\n            Self::Minus => crate::MINUS_CHAR_STR,\n            Self::Period => \".\",\n            Self::Plus => \"+\",\n            Self::Equals => \"=\",\n            Self::Semicolon => \";\",\n            Self::Backslash => \"\\\\\",\n            Self::Slash => \"/\",\n            Self::Pipe => \"|\",\n            Self::Questionmark => \"?\",\n            Self::Exclamationmark => \"!\",\n            Self::OpenBracket => \"[\",\n            Self::CloseBracket => \"]\",\n            Self::OpenCurlyBracket => \"{\",\n            Self::CloseCurlyBracket => \"}\",\n            Self::Backtick => \"`\",\n\n            _ => self.name(),\n        }\n    }\n\n    /// Human-readable English name.\n    pub fn name(self) -> &'static str {\n        match self {\n            Self::ArrowDown => \"Down\",\n            Self::ArrowLeft => \"Left\",\n            Self::ArrowRight => \"Right\",\n            Self::ArrowUp => \"Up\",\n\n            Self::Escape => \"Escape\",\n            Self::Tab => \"Tab\",\n            Self::Backspace => \"Backspace\",\n            Self::Enter => \"Enter\",\n\n            Self::Insert => \"Insert\",\n            Self::Delete => \"Delete\",\n            Self::Home => \"Home\",\n            Self::End => \"End\",\n            Self::PageUp => \"PageUp\",\n            Self::PageDown => \"PageDown\",\n\n            Self::Copy => \"Copy\",\n            Self::Cut => \"Cut\",\n            Self::Paste => \"Paste\",\n\n            Self::Space => \"Space\",\n            Self::Colon => \"Colon\",\n            Self::Comma => \"Comma\",\n            Self::Minus => \"Minus\",\n            Self::Period => \"Period\",\n            Self::Plus => \"Plus\",\n            Self::Equals => \"Equals\",\n            Self::Semicolon => \"Semicolon\",\n            Self::Backslash => \"Backslash\",\n            Self::Slash => \"Slash\",\n            Self::Pipe => \"Pipe\",\n            Self::Questionmark => \"Questionmark\",\n            Self::Exclamationmark => \"Exclamationmark\",\n            Self::OpenBracket => \"OpenBracket\",\n            Self::CloseBracket => \"CloseBracket\",\n            Self::OpenCurlyBracket => \"OpenCurlyBracket\",\n            Self::CloseCurlyBracket => \"CloseCurlyBracket\",\n            Self::Backtick => \"Backtick\",\n            Self::Quote => \"Quote\",\n\n            Self::Num0 => \"0\",\n            Self::Num1 => \"1\",\n            Self::Num2 => \"2\",\n            Self::Num3 => \"3\",\n            Self::Num4 => \"4\",\n            Self::Num5 => \"5\",\n            Self::Num6 => \"6\",\n            Self::Num7 => \"7\",\n            Self::Num8 => \"8\",\n            Self::Num9 => \"9\",\n\n            Self::A => \"A\",\n            Self::B => \"B\",\n            Self::C => \"C\",\n            Self::D => \"D\",\n            Self::E => \"E\",\n            Self::F => \"F\",\n            Self::G => \"G\",\n            Self::H => \"H\",\n            Self::I => \"I\",\n            Self::J => \"J\",\n            Self::K => \"K\",\n            Self::L => \"L\",\n            Self::M => \"M\",\n            Self::N => \"N\",\n            Self::O => \"O\",\n            Self::P => \"P\",\n            Self::Q => \"Q\",\n            Self::R => \"R\",\n            Self::S => \"S\",\n            Self::T => \"T\",\n            Self::U => \"U\",\n            Self::V => \"V\",\n            Self::W => \"W\",\n            Self::X => \"X\",\n            Self::Y => \"Y\",\n            Self::Z => \"Z\",\n            Self::F1 => \"F1\",\n            Self::F2 => \"F2\",\n            Self::F3 => \"F3\",\n            Self::F4 => \"F4\",\n            Self::F5 => \"F5\",\n            Self::F6 => \"F6\",\n            Self::F7 => \"F7\",\n            Self::F8 => \"F8\",\n            Self::F9 => \"F9\",\n            Self::F10 => \"F10\",\n            Self::F11 => \"F11\",\n            Self::F12 => \"F12\",\n            Self::F13 => \"F13\",\n            Self::F14 => \"F14\",\n            Self::F15 => \"F15\",\n            Self::F16 => \"F16\",\n            Self::F17 => \"F17\",\n            Self::F18 => \"F18\",\n            Self::F19 => \"F19\",\n            Self::F20 => \"F20\",\n            Self::F21 => \"F21\",\n            Self::F22 => \"F22\",\n            Self::F23 => \"F23\",\n            Self::F24 => \"F24\",\n            Self::F25 => \"F25\",\n            Self::F26 => \"F26\",\n            Self::F27 => \"F27\",\n            Self::F28 => \"F28\",\n            Self::F29 => \"F29\",\n            Self::F30 => \"F30\",\n            Self::F31 => \"F31\",\n            Self::F32 => \"F32\",\n            Self::F33 => \"F33\",\n            Self::F34 => \"F34\",\n            Self::F35 => \"F35\",\n\n            Self::BrowserBack => \"BrowserBack\",\n        }\n    }\n}\n\n#[test]\nfn test_key_from_name() {\n    assert_eq!(\n        Key::ALL.len(),\n        Key::BrowserBack as usize + 1,\n        \"Some keys are missing in Key::ALL\"\n    );\n\n    for &key in Key::ALL {\n        let name = key.name();\n        assert_eq!(\n            Key::from_name(name),\n            Some(key),\n            \"Failed to roundtrip {key:?} from name {name:?}\"\n        );\n\n        let symbol = key.symbol_or_name();\n        assert_eq!(\n            Key::from_name(symbol),\n            Some(key),\n            \"Failed to roundtrip {key:?} from symbol {symbol:?}\"\n        );\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/data/mod.rs",
    "content": "//! All the data sent between egui and the backend\n\npub mod input;\nmod key;\npub mod output;\nmod user_data;\n\npub use key::Key;\npub use user_data::UserData;\n"
  },
  {
    "path": "crates/egui/src/data/output.rs",
    "content": "//! All the data egui returns to the backend at the end of each frame.\n\nuse crate::{OrderedViewportIdMap, RepaintCause, ViewportOutput, WidgetType};\n\n/// What egui emits each frame from [`crate::Context::run`].\n///\n/// The backend should use this.\n#[derive(Clone, Default)]\npub struct FullOutput {\n    /// Non-rendering related output.\n    pub platform_output: PlatformOutput,\n\n    /// Texture changes since last frame (including the font texture).\n    ///\n    /// The backend needs to apply [`crate::TexturesDelta::set`] _before_ painting,\n    /// and free any texture in [`crate::TexturesDelta::free`] _after_ painting.\n    ///\n    /// It is assumed that all egui viewports share the same painter and texture namespace.\n    pub textures_delta: epaint::textures::TexturesDelta,\n\n    /// What to paint.\n    ///\n    /// You can use [`crate::Context::tessellate`] to turn this into triangles.\n    pub shapes: Vec<epaint::ClippedShape>,\n\n    /// The number of physical pixels per logical ui point, for the viewport that was updated.\n    ///\n    /// You can pass this to [`crate::Context::tessellate`] together with [`Self::shapes`].\n    pub pixels_per_point: f32,\n\n    /// All the active viewports, including the root.\n    ///\n    /// It is up to the integration to spawn a native window for each viewport,\n    /// and to close any window that no longer has a viewport in this map.\n    pub viewport_output: OrderedViewportIdMap<ViewportOutput>,\n}\n\nimpl FullOutput {\n    /// Add on new output.\n    pub fn append(&mut self, newer: Self) {\n        use std::collections::btree_map::Entry;\n\n        let Self {\n            platform_output,\n            textures_delta,\n            shapes,\n            pixels_per_point,\n            viewport_output,\n        } = newer;\n\n        self.platform_output.append(platform_output);\n        self.textures_delta.append(textures_delta);\n        self.shapes = shapes; // Only paint the latest\n        self.pixels_per_point = pixels_per_point; // Use latest\n\n        for (id, new_viewport) in viewport_output {\n            match self.viewport_output.entry(id) {\n                Entry::Vacant(entry) => {\n                    entry.insert(new_viewport);\n                }\n                Entry::Occupied(mut entry) => {\n                    entry.get_mut().append(new_viewport);\n                }\n            }\n        }\n    }\n}\n\n/// Information about text being edited.\n///\n/// Useful for IME.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct IMEOutput {\n    /// Where the [`crate::TextEdit`] is located on screen.\n    pub rect: crate::Rect,\n\n    /// Where the primary cursor is.\n    ///\n    /// This is a very thin rectangle.\n    pub cursor_rect: crate::Rect,\n}\n\n/// Commands that the egui integration should execute at the end of a frame.\n///\n/// Commands that are specific to a viewport should be put in [`crate::ViewportCommand`] instead.\n#[derive(Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum OutputCommand {\n    /// Put this text to the system clipboard.\n    ///\n    /// This is often a response to [`crate::Event::Copy`] or [`crate::Event::Cut`].\n    CopyText(String),\n\n    /// Put this image to the system clipboard.\n    CopyImage(crate::ColorImage),\n\n    /// Open this url in a browser.\n    OpenUrl(OpenUrl),\n}\n\n/// The non-rendering part of what egui emits each frame.\n///\n/// You can access (and modify) this with [`crate::Context::output`].\n///\n/// The backend should use this.\n#[derive(Default, Clone, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct PlatformOutput {\n    /// Commands that the egui integration should execute at the end of a frame.\n    pub commands: Vec<OutputCommand>,\n\n    /// Set the cursor to this icon.\n    pub cursor_icon: CursorIcon,\n\n    /// Events that may be useful to e.g. a screen reader.\n    pub events: Vec<OutputEvent>,\n\n    /// Is there a mutable [`TextEdit`](crate::TextEdit) under the cursor?\n    /// Use by `eframe` web to show/hide mobile keyboard and IME agent.\n    pub mutable_text_under_cursor: bool,\n\n    /// This is set if, and only if, the user is currently editing text.\n    ///\n    /// Useful for IME.\n    pub ime: Option<IMEOutput>,\n\n    /// The difference in the widget tree since last frame.\n    ///\n    /// NOTE: this needs to be per-viewport.\n    pub accesskit_update: Option<accesskit::TreeUpdate>,\n\n    /// How many ui passes is this the sum of?\n    ///\n    /// See [`crate::Context::request_discard`] for details.\n    ///\n    /// This is incremented at the END of each frame,\n    /// so this will be `0` for the first pass.\n    pub num_completed_passes: usize,\n\n    /// Was [`crate::Context::request_discard`] called during the latest pass?\n    ///\n    /// If so, what was the reason(s) for it?\n    ///\n    /// If empty, there was never any calls.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub request_discard_reasons: Vec<RepaintCause>,\n}\n\nimpl PlatformOutput {\n    /// This can be used by a text-to-speech system to describe the events (if any).\n    pub fn events_description(&self) -> String {\n        // only describe last event:\n        if let Some(event) = self.events.iter().next_back() {\n            match event {\n                OutputEvent::Clicked(widget_info)\n                | OutputEvent::DoubleClicked(widget_info)\n                | OutputEvent::TripleClicked(widget_info)\n                | OutputEvent::FocusGained(widget_info)\n                | OutputEvent::TextSelectionChanged(widget_info)\n                | OutputEvent::ValueChanged(widget_info) => {\n                    return widget_info.description();\n                }\n            }\n        }\n        Default::default()\n    }\n\n    /// Add on new output.\n    pub fn append(&mut self, newer: Self) {\n        let Self {\n            mut commands,\n            cursor_icon,\n            mut events,\n            mutable_text_under_cursor,\n            ime,\n            accesskit_update,\n            num_completed_passes,\n            mut request_discard_reasons,\n        } = newer;\n\n        self.commands.append(&mut commands);\n        self.cursor_icon = cursor_icon;\n        self.events.append(&mut events);\n        self.mutable_text_under_cursor = mutable_text_under_cursor;\n        self.ime = ime.or(self.ime);\n        self.num_completed_passes += num_completed_passes;\n        self.request_discard_reasons\n            .append(&mut request_discard_reasons);\n\n        // egui produces a complete AccessKit tree for each frame, so overwrite rather than append:\n        self.accesskit_update = accesskit_update;\n    }\n\n    /// Take everything ephemeral (everything except `cursor_icon` currently)\n    pub fn take(&mut self) -> Self {\n        let taken = std::mem::take(self);\n        self.cursor_icon = taken.cursor_icon; // everything else is ephemeral\n        taken\n    }\n\n    /// Was [`crate::Context::request_discard`] called?\n    pub fn requested_discard(&self) -> bool {\n        !self.request_discard_reasons.is_empty()\n    }\n}\n\n/// What URL to open, and how.\n///\n/// Use with [`crate::Context::open_url`].\n#[derive(Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct OpenUrl {\n    pub url: String,\n\n    /// If `true`, open the url in a new tab.\n    /// If `false` open it in the same tab.\n    /// Only matters when in a web browser.\n    pub new_tab: bool,\n}\n\nimpl OpenUrl {\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn same_tab(url: impl ToString) -> Self {\n        Self {\n            url: url.to_string(),\n            new_tab: false,\n        }\n    }\n\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn new_tab(url: impl ToString) -> Self {\n        Self {\n            url: url.to_string(),\n            new_tab: true,\n        }\n    }\n}\n\n/// Types of attention to request from a user when a native window is not in focus.\n///\n/// See [winit's documentation][user_attention_type] for platform-specific meaning of the attention types.\n///\n/// [user_attention_type]: https://docs.rs/winit/latest/winit/window/enum.UserAttentionType.html\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum UserAttentionType {\n    /// Request an elevated amount of animations and flair for the window and the task bar or dock icon.\n    Critical,\n\n    /// Request a standard amount of attention-grabbing actions.\n    Informational,\n\n    /// Reset the attention request and interrupt related animations and flashes.\n    Reset,\n}\n\n/// A mouse cursor icon.\n///\n/// egui emits a [`CursorIcon`] in [`PlatformOutput`] each frame as a request to the integration.\n///\n/// Loosely based on <https://developer.mozilla.org/en-US/docs/Web/CSS/cursor>.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum CursorIcon {\n    /// Normal cursor icon, whatever that is.\n    #[default]\n    Default,\n\n    /// Show no cursor\n    None,\n\n    // ------------------------------------\n    // Links and status:\n    /// A context menu is available\n    ContextMenu,\n\n    /// Question mark\n    Help,\n\n    /// Pointing hand, used for e.g. web links\n    PointingHand,\n\n    /// Shows that processing is being done, but that the program is still interactive.\n    Progress,\n\n    /// Not yet ready, try later.\n    Wait,\n\n    // ------------------------------------\n    // Selection:\n    /// Hover a cell in a table\n    Cell,\n\n    /// For precision work\n    Crosshair,\n\n    /// Text caret, e.g. \"Click here to edit text\"\n    Text,\n\n    /// Vertical text caret, e.g. \"Click here to edit vertical text\"\n    VerticalText,\n\n    // ------------------------------------\n    // Drag-and-drop:\n    /// Indicated an alias, e.g. a shortcut\n    Alias,\n\n    /// Indicate that a copy will be made\n    Copy,\n\n    /// Omnidirectional move icon (e.g. arrows in all cardinal directions)\n    Move,\n\n    /// Can't drop here\n    NoDrop,\n\n    /// Forbidden\n    NotAllowed,\n\n    /// The thing you are hovering can be grabbed\n    Grab,\n\n    /// You are grabbing the thing you are hovering\n    Grabbing,\n\n    // ------------------------------------\n    /// Something can be scrolled in any direction (panned).\n    AllScroll,\n\n    // ------------------------------------\n    // Resizing in two directions:\n    /// Horizontal resize `-` to make something wider or more narrow (left to/from right)\n    ResizeHorizontal,\n\n    /// Diagonal resize `/` (right-up to/from left-down)\n    ResizeNeSw,\n\n    /// Diagonal resize `\\` (left-up to/from right-down)\n    ResizeNwSe,\n\n    /// Vertical resize `|` (up-down or down-up)\n    ResizeVertical,\n\n    // ------------------------------------\n    // Resizing in one direction:\n    /// Resize something rightwards (e.g. when dragging the right-most edge of something)\n    ResizeEast,\n\n    /// Resize something down and right (e.g. when dragging the bottom-right corner of something)\n    ResizeSouthEast,\n\n    /// Resize something downwards (e.g. when dragging the bottom edge of something)\n    ResizeSouth,\n\n    /// Resize something down and left (e.g. when dragging the bottom-left corner of something)\n    ResizeSouthWest,\n\n    /// Resize something leftwards (e.g. when dragging the left edge of something)\n    ResizeWest,\n\n    /// Resize something up and left (e.g. when dragging the top-left corner of something)\n    ResizeNorthWest,\n\n    /// Resize something up (e.g. when dragging the top edge of something)\n    ResizeNorth,\n\n    /// Resize something up and right (e.g. when dragging the top-right corner of something)\n    ResizeNorthEast,\n\n    // ------------------------------------\n    /// Resize a column\n    ResizeColumn,\n\n    /// Resize a row\n    ResizeRow,\n\n    // ------------------------------------\n    // Zooming:\n    /// Enhance!\n    ZoomIn,\n\n    /// Let's get a better overview\n    ZoomOut,\n}\n\nimpl CursorIcon {\n    pub const ALL: [Self; 35] = [\n        Self::Default,\n        Self::None,\n        Self::ContextMenu,\n        Self::Help,\n        Self::PointingHand,\n        Self::Progress,\n        Self::Wait,\n        Self::Cell,\n        Self::Crosshair,\n        Self::Text,\n        Self::VerticalText,\n        Self::Alias,\n        Self::Copy,\n        Self::Move,\n        Self::NoDrop,\n        Self::NotAllowed,\n        Self::Grab,\n        Self::Grabbing,\n        Self::AllScroll,\n        Self::ResizeHorizontal,\n        Self::ResizeNeSw,\n        Self::ResizeNwSe,\n        Self::ResizeVertical,\n        Self::ResizeEast,\n        Self::ResizeSouthEast,\n        Self::ResizeSouth,\n        Self::ResizeSouthWest,\n        Self::ResizeWest,\n        Self::ResizeNorthWest,\n        Self::ResizeNorth,\n        Self::ResizeNorthEast,\n        Self::ResizeColumn,\n        Self::ResizeRow,\n        Self::ZoomIn,\n        Self::ZoomOut,\n    ];\n}\n\n/// Things that happened during this frame that the integration may be interested in.\n///\n/// In particular, these events may be useful for accessibility, i.e. for screen readers.\n#[derive(Clone, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum OutputEvent {\n    /// A widget was clicked.\n    Clicked(WidgetInfo),\n\n    /// A widget was double-clicked.\n    DoubleClicked(WidgetInfo),\n\n    /// A widget was triple-clicked.\n    TripleClicked(WidgetInfo),\n\n    /// A widget gained keyboard focus (by tab key).\n    FocusGained(WidgetInfo),\n\n    /// Text selection was updated.\n    TextSelectionChanged(WidgetInfo),\n\n    /// A widget's value changed.\n    ValueChanged(WidgetInfo),\n}\n\nimpl OutputEvent {\n    pub fn widget_info(&self) -> &WidgetInfo {\n        match self {\n            Self::Clicked(info)\n            | Self::DoubleClicked(info)\n            | Self::TripleClicked(info)\n            | Self::FocusGained(info)\n            | Self::TextSelectionChanged(info)\n            | Self::ValueChanged(info) => info,\n        }\n    }\n}\n\nimpl std::fmt::Debug for OutputEvent {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Clicked(wi) => write!(f, \"Clicked({wi:?})\"),\n            Self::DoubleClicked(wi) => write!(f, \"DoubleClicked({wi:?})\"),\n            Self::TripleClicked(wi) => write!(f, \"TripleClicked({wi:?})\"),\n            Self::FocusGained(wi) => write!(f, \"FocusGained({wi:?})\"),\n            Self::TextSelectionChanged(wi) => write!(f, \"TextSelectionChanged({wi:?})\"),\n            Self::ValueChanged(wi) => write!(f, \"ValueChanged({wi:?})\"),\n        }\n    }\n}\n\n/// Describes a widget such as a [`crate::Button`] or a [`crate::TextEdit`].\n#[derive(Clone, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct WidgetInfo {\n    /// The type of widget this is.\n    pub typ: WidgetType,\n\n    /// Whether the widget is enabled.\n    pub enabled: bool,\n\n    /// The text on labels, buttons, checkboxes etc.\n    pub label: Option<String>,\n\n    /// The contents of some editable text (for [`TextEdit`](crate::TextEdit) fields).\n    pub current_text_value: Option<String>,\n\n    /// The previous text value.\n    pub prev_text_value: Option<String>,\n\n    /// The current value of checkboxes and radio buttons.\n    pub selected: Option<bool>,\n\n    /// The current value of sliders etc.\n    pub value: Option<f64>,\n\n    /// Selected range of characters in [`Self::current_text_value`].\n    pub text_selection: Option<std::ops::RangeInclusive<usize>>,\n\n    /// The hint text for text edit fields.\n    pub hint_text: Option<String>,\n}\n\nimpl std::fmt::Debug for WidgetInfo {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let Self {\n            typ,\n            enabled,\n            label,\n            current_text_value: text_value,\n            prev_text_value,\n            selected,\n            value,\n            text_selection,\n            hint_text,\n        } = self;\n\n        let mut s = f.debug_struct(\"WidgetInfo\");\n\n        s.field(\"typ\", typ);\n\n        if !enabled {\n            s.field(\"enabled\", enabled);\n        }\n\n        if let Some(label) = label {\n            s.field(\"label\", label);\n        }\n        if let Some(text_value) = text_value {\n            s.field(\"text_value\", text_value);\n        }\n        if let Some(prev_text_value) = prev_text_value {\n            s.field(\"prev_text_value\", prev_text_value);\n        }\n        if let Some(selected) = selected {\n            s.field(\"selected\", selected);\n        }\n        if let Some(value) = value {\n            s.field(\"value\", value);\n        }\n        if let Some(text_selection) = text_selection {\n            s.field(\"text_selection\", text_selection);\n        }\n        if let Some(hint_text) = hint_text {\n            s.field(\"hint_text\", hint_text);\n        }\n\n        s.finish()\n    }\n}\n\nimpl WidgetInfo {\n    pub fn new(typ: WidgetType) -> Self {\n        Self {\n            typ,\n            enabled: true,\n            label: None,\n            current_text_value: None,\n            prev_text_value: None,\n            selected: None,\n            value: None,\n            text_selection: None,\n            hint_text: None,\n        }\n    }\n\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn labeled(typ: WidgetType, enabled: bool, label: impl ToString) -> Self {\n        Self {\n            enabled,\n            label: Some(label.to_string()),\n            ..Self::new(typ)\n        }\n    }\n\n    /// checkboxes, radio-buttons etc\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn selected(typ: WidgetType, enabled: bool, selected: bool, label: impl ToString) -> Self {\n        Self {\n            enabled,\n            label: Some(label.to_string()),\n            selected: Some(selected),\n            ..Self::new(typ)\n        }\n    }\n\n    pub fn drag_value(enabled: bool, value: f64) -> Self {\n        Self {\n            enabled,\n            value: Some(value),\n            ..Self::new(WidgetType::DragValue)\n        }\n    }\n\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn slider(enabled: bool, value: f64, label: impl ToString) -> Self {\n        let label = label.to_string();\n        Self {\n            enabled,\n            label: if label.is_empty() { None } else { Some(label) },\n            value: Some(value),\n            ..Self::new(WidgetType::Slider)\n        }\n    }\n\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn text_edit(\n        enabled: bool,\n        prev_text_value: impl ToString,\n        text_value: impl ToString,\n        hint_text: impl ToString,\n    ) -> Self {\n        let text_value = text_value.to_string();\n        let prev_text_value = prev_text_value.to_string();\n        let hint_text = hint_text.to_string();\n        let prev_text_value = if text_value == prev_text_value {\n            None\n        } else {\n            Some(prev_text_value)\n        };\n        Self {\n            enabled,\n            current_text_value: Some(text_value),\n            prev_text_value,\n            hint_text: Some(hint_text),\n            ..Self::new(WidgetType::TextEdit)\n        }\n    }\n\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn text_selection_changed(\n        enabled: bool,\n        text_selection: std::ops::RangeInclusive<usize>,\n        current_text_value: impl ToString,\n    ) -> Self {\n        Self {\n            enabled,\n            text_selection: Some(text_selection),\n            current_text_value: Some(current_text_value.to_string()),\n            ..Self::new(WidgetType::TextEdit)\n        }\n    }\n\n    /// This can be used by a text-to-speech system to describe the widget.\n    pub fn description(&self) -> String {\n        let Self {\n            typ,\n            enabled,\n            label,\n            current_text_value: text_value,\n            prev_text_value: _,\n            selected,\n            value,\n            text_selection: _,\n            hint_text: _,\n        } = self;\n\n        // TODO(emilk): localization\n        let widget_type = match typ {\n            WidgetType::Link => \"link\",\n            WidgetType::TextEdit => \"text edit\",\n            WidgetType::Button => \"button\",\n            WidgetType::Checkbox => \"checkbox\",\n            WidgetType::RadioButton => \"radio\",\n            WidgetType::RadioGroup => \"radio group\",\n            WidgetType::SelectableLabel => \"selectable\",\n            WidgetType::ComboBox => \"combo\",\n            WidgetType::Slider => \"slider\",\n            WidgetType::DragValue => \"drag value\",\n            WidgetType::ColorButton => \"color button\",\n            WidgetType::Image => \"image\",\n            WidgetType::CollapsingHeader => \"collapsing header\",\n            WidgetType::Panel => \"panel\",\n            WidgetType::ProgressIndicator => \"progress indicator\",\n            WidgetType::Window => \"window\",\n            WidgetType::ScrollBar => \"scroll bar\",\n            WidgetType::ResizeHandle => \"resize handle\",\n            WidgetType::Label | WidgetType::Other => \"\",\n        };\n\n        let mut description = widget_type.to_owned();\n\n        if let Some(selected) = selected {\n            if *typ == WidgetType::Checkbox {\n                let state = if *selected { \"checked\" } else { \"unchecked\" };\n                description = format!(\"{state} {description}\");\n            } else {\n                description += if *selected { \"selected\" } else { \"\" };\n            }\n        }\n\n        if let Some(label) = label {\n            description = format!(\"{label}: {description}\");\n        }\n\n        if typ == &WidgetType::TextEdit {\n            let text = if let Some(text_value) = text_value {\n                if text_value.is_empty() {\n                    \"blank\".into()\n                } else {\n                    text_value.clone()\n                }\n            } else {\n                \"blank\".into()\n            };\n            description = format!(\"{text}: {description}\");\n        }\n\n        if let Some(value) = value {\n            description += \" \";\n            description += &value.to_string();\n        }\n\n        if !enabled {\n            description += \": disabled\";\n        }\n        description.trim().to_owned()\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/data/user_data.rs",
    "content": "use std::{any::Any, sync::Arc};\n\n/// A wrapper around `dyn Any`, used for passing custom user data\n/// to [`crate::ViewportCommand::Screenshot`].\n#[derive(Clone, Debug, Default)]\npub struct UserData {\n    /// A user value given to the screenshot command,\n    /// that will be returned in [`crate::Event::Screenshot`].\n    pub data: Option<Arc<dyn Any + Send + Sync>>,\n}\n\nimpl UserData {\n    /// You can also use [`Self::default`].\n    pub fn new(user_info: impl Any + Send + Sync) -> Self {\n        Self {\n            data: Some(Arc::new(user_info)),\n        }\n    }\n}\n\nimpl PartialEq for UserData {\n    fn eq(&self, other: &Self) -> bool {\n        match (&self.data, &other.data) {\n            (Some(a), Some(b)) => Arc::ptr_eq(a, b),\n            (None, None) => true,\n            _ => false,\n        }\n    }\n}\n\nimpl Eq for UserData {}\n\nimpl std::hash::Hash for UserData {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.data.as_ref().map(Arc::as_ptr).hash(state);\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl serde::Serialize for UserData {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        serializer.serialize_none() // can't serialize an `Any`\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl<'de> serde::Deserialize<'de> for UserData {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        struct UserDataVisitor;\n\n        impl serde::de::Visitor<'_> for UserDataVisitor {\n            type Value = UserData;\n\n            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n                formatter.write_str(\"a None value\")\n            }\n\n            fn visit_none<E>(self) -> Result<UserData, E>\n            where\n                E: serde::de::Error,\n            {\n                Ok(UserData::default())\n            }\n        }\n\n        deserializer.deserialize_option(UserDataVisitor)\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/debug_text.rs",
    "content": "//! This is an example of how to create a plugin for egui.\n//!\n//! A plugin is a struct that implements the [`Plugin`] trait and holds some state.\n//! The plugin is registered with the [`Context`] using [`Context::add_plugin`]\n//! to get callbacks on certain events ([`Plugin::on_begin_pass`], [`Plugin::on_end_pass`]).\n\nuse crate::{\n    Align, Align2, Color32, Context, FontFamily, FontId, Plugin, Rect, Shape, Ui, Vec2, WidgetText,\n    text,\n};\n\n/// Print this text next to the cursor at the end of the pass.\n///\n/// If you call this multiple times, the text will be appended.\n///\n/// This only works if compiled with `debug_assertions`.\n///\n/// ```\n/// # let ctx = &egui::Context::default();\n/// # let state = true;\n/// egui::debug_text::print(ctx, format!(\"State: {state:?}\"));\n/// ```\n#[track_caller]\npub fn print(ctx: &Context, text: impl Into<WidgetText>) {\n    if !cfg!(debug_assertions) {\n        return;\n    }\n\n    let location = std::panic::Location::caller();\n    let location = format!(\"{}:{}\", location.file(), location.line());\n\n    let plugin = ctx.plugin::<DebugTextPlugin>();\n    let mut state = plugin.lock();\n    state.entries.push(Entry {\n        location,\n        text: text.into(),\n    });\n}\n\n#[derive(Clone)]\nstruct Entry {\n    location: String,\n    text: WidgetText,\n}\n\n/// A plugin for easily showing debug-text on-screen.\n///\n/// This is a built-in plugin in egui, automatically registered during [`Context`] creation.\n#[derive(Clone, Default)]\npub struct DebugTextPlugin {\n    // This gets re-filled every pass.\n    entries: Vec<Entry>,\n}\n\nimpl Plugin for DebugTextPlugin {\n    fn debug_name(&self) -> &'static str {\n        \"DebugTextPlugin\"\n    }\n\n    fn on_end_pass(&mut self, ui: &mut Ui) {\n        let entries = std::mem::take(&mut self.entries);\n        Self::paint_entries(ui, entries);\n    }\n}\n\nimpl DebugTextPlugin {\n    fn paint_entries(ctx: &Context, entries: Vec<Entry>) {\n        if entries.is_empty() {\n            return;\n        }\n\n        // Show debug-text next to the cursor.\n        let mut pos = ctx\n            .input(|i| i.pointer.latest_pos())\n            .unwrap_or_else(|| ctx.content_rect().center())\n            + 8.0 * Vec2::Y;\n\n        let painter = ctx.debug_painter();\n        let where_to_put_background = painter.add(Shape::Noop);\n\n        let mut bounding_rect = Rect::from_points(&[pos]);\n\n        let color = Color32::GRAY;\n        let font_id = FontId::new(10.0, FontFamily::Proportional);\n\n        for Entry { location, text } in entries {\n            {\n                // Paint location to left of `pos`:\n                let location_galley =\n                    ctx.fonts_mut(|f| f.layout(location, font_id.clone(), color, f32::INFINITY));\n                let location_rect =\n                    Align2::RIGHT_TOP.anchor_size(pos - 4.0 * Vec2::X, location_galley.size());\n                painter.galley(location_rect.min, location_galley, color);\n                bounding_rect |= location_rect;\n            }\n\n            {\n                // Paint `text` to right of `pos`:\n                let available_width = ctx.content_rect().max.x - pos.x;\n                let galley = text.into_galley_impl(\n                    ctx,\n                    &ctx.global_style(),\n                    text::TextWrapping::wrap_at_width(available_width),\n                    font_id.clone().into(),\n                    Align::TOP,\n                );\n                let rect = Align2::LEFT_TOP.anchor_size(pos, galley.size());\n                painter.galley(rect.min, galley, color);\n                bounding_rect |= rect;\n            }\n\n            pos.y = bounding_rect.max.y + 4.0;\n        }\n\n        painter.set(\n            where_to_put_background,\n            Shape::rect_filled(\n                bounding_rect.expand(4.0),\n                2.0,\n                Color32::from_black_alpha(192),\n            ),\n        );\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/drag_and_drop.rs",
    "content": "use std::{any::Any, sync::Arc};\n\nuse crate::{Context, CursorIcon, Plugin, Ui};\n\n/// Plugin for tracking drag-and-drop payload.\n///\n/// This plugin stores the current drag-and-drop payload internally and handles\n/// automatic cleanup when the drag operation ends (via Escape key or mouse release).\n///\n/// This is a low-level API. For a higher-level API, see:\n/// - [`crate::Ui::dnd_drag_source`]\n/// - [`crate::Ui::dnd_drop_zone`]\n/// - [`crate::Response::dnd_set_drag_payload`]\n/// - [`crate::Response::dnd_hover_payload`]\n/// - [`crate::Response::dnd_release_payload`]\n///\n/// This is a built-in plugin in egui, automatically registered during [`Context`] creation.\n///\n/// See [this example](https://github.com/emilk/egui/blob/main/crates/egui_demo_lib/src/demo/drag_and_drop.rs).\n#[doc(alias = \"drag and drop\")]\n#[derive(Clone, Default)]\npub struct DragAndDrop {\n    /// The current drag-and-drop payload, if any. Automatically cleared when drag ends.\n    payload: Option<Arc<dyn Any + Send + Sync>>,\n}\n\nimpl Plugin for DragAndDrop {\n    fn debug_name(&self) -> &'static str {\n        \"DragAndDrop\"\n    }\n\n    /// Interrupt drag-and-drop if the user presses the escape key.\n    ///\n    /// This needs to happen at frame start so we can properly capture the escape key.\n    fn on_begin_pass(&mut self, ui: &mut Ui) {\n        let has_any_payload = self.payload.is_some();\n\n        if has_any_payload {\n            let abort_dnd_due_to_escape_key =\n                ui.input_mut(|i| i.consume_key(crate::Modifiers::NONE, crate::Key::Escape));\n\n            if abort_dnd_due_to_escape_key {\n                self.payload = None;\n            }\n        }\n    }\n\n    /// Interrupt drag-and-drop if the user releases the mouse button.\n    ///\n    /// This is a catch-all safety net in case user code doesn't capture the drag payload itself.\n    /// This must happen at end-of-frame such that we don't shadow the mouse release event from user\n    /// code.\n    fn on_end_pass(&mut self, ui: &mut Ui) {\n        let has_any_payload = self.payload.is_some();\n\n        if has_any_payload {\n            let abort_dnd_due_to_mouse_release = ui.input_mut(|i| i.pointer.any_released());\n\n            if abort_dnd_due_to_mouse_release {\n                self.payload = None;\n            } else {\n                // We set the cursor icon only if its default, as the user code might have\n                // explicitly set it already.\n                ui.output_mut(|o| {\n                    if o.cursor_icon == CursorIcon::Default {\n                        o.cursor_icon = CursorIcon::Grabbing;\n                    }\n                });\n            }\n        }\n    }\n}\n\nimpl DragAndDrop {\n    /// Set a drag-and-drop payload.\n    ///\n    /// This can be read by [`Self::payload`] until the pointer is released.\n    pub fn set_payload<Payload>(ctx: &Context, payload: Payload)\n    where\n        Payload: Any + Send + Sync,\n    {\n        ctx.plugin::<Self>().lock().payload = Some(Arc::new(payload));\n    }\n\n    /// Clears the payload, setting it to `None`.\n    pub fn clear_payload(ctx: &Context) {\n        ctx.plugin::<Self>().lock().payload = None;\n    }\n\n    /// Retrieve the payload, if any.\n    ///\n    /// Returns `None` if there is no payload, or if it is not of the requested type.\n    ///\n    /// Returns `Some` both during a drag and on the frame the pointer is released\n    /// (if there is a payload).\n    pub fn payload<Payload>(ctx: &Context) -> Option<Arc<Payload>>\n    where\n        Payload: Any + Send + Sync,\n    {\n        Arc::clone(ctx.plugin::<Self>().lock().payload.as_ref()?)\n            .downcast()\n            .ok()\n    }\n\n    /// Retrieve and clear the payload, if any.\n    ///\n    /// Returns `None` if there is no payload, or if it is not of the requested type.\n    ///\n    /// Returns `Some` both during a drag and on the frame the pointer is released\n    /// (if there is a payload).\n    pub fn take_payload<Payload>(ctx: &Context) -> Option<Arc<Payload>>\n    where\n        Payload: Any + Send + Sync,\n    {\n        ctx.plugin::<Self>().lock().payload.take()?.downcast().ok()\n    }\n\n    /// Are we carrying a payload of the given type?\n    ///\n    /// Returns `true` both during a drag and on the frame the pointer is released\n    /// (if there is a payload).\n    pub fn has_payload_of_type<Payload>(ctx: &Context) -> bool\n    where\n        Payload: Any + Send + Sync,\n    {\n        Self::payload::<Payload>(ctx).is_some()\n    }\n\n    /// Are we carrying a payload?\n    ///\n    /// Returns `true` both during a drag and on the frame the pointer is released\n    /// (if there is a payload).\n    pub fn has_any_payload(ctx: &Context) -> bool {\n        ctx.plugin::<Self>().lock().payload.is_some()\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/grid.rs",
    "content": "use std::sync::Arc;\n\nuse emath::GuiRounding as _;\n\nuse crate::{\n    Align2, Color32, Context, Id, InnerResponse, NumExt as _, Painter, Rect, Region, Style, Ui,\n    UiBuilder, Vec2, vec2,\n};\n\n#[cfg(debug_assertions)]\nuse crate::Stroke;\n\n#[derive(Clone, Debug, Default, PartialEq)]\npub(crate) struct State {\n    col_widths: Vec<f32>,\n    row_heights: Vec<f32>,\n}\n\nimpl State {\n    pub fn load(ctx: &Context, id: Id) -> Option<Self> {\n        ctx.data_mut(|d| d.get_temp(id))\n    }\n\n    pub fn store(self, ctx: &Context, id: Id) {\n        // We don't persist Grids, because\n        // A) there are potentially a lot of them, using up a lot of space (and therefore serialization time)\n        // B) if the code changes, the grid _should_ change, and not remember old sizes\n        ctx.data_mut(|d| d.insert_temp(id, self));\n    }\n\n    fn set_min_col_width(&mut self, col: usize, width: f32) {\n        self.col_widths\n            .resize(self.col_widths.len().max(col + 1), 0.0);\n        self.col_widths[col] = self.col_widths[col].max(width);\n    }\n\n    fn set_min_row_height(&mut self, row: usize, height: f32) {\n        self.row_heights\n            .resize(self.row_heights.len().max(row + 1), 0.0);\n        self.row_heights[row] = self.row_heights[row].max(height);\n    }\n\n    fn col_width(&self, col: usize) -> Option<f32> {\n        self.col_widths.get(col).copied()\n    }\n\n    fn row_height(&self, row: usize) -> Option<f32> {\n        self.row_heights.get(row).copied()\n    }\n\n    fn full_width(&self, x_spacing: f32) -> f32 {\n        self.col_widths.iter().sum::<f32>()\n            + (self.col_widths.len().at_least(1) - 1) as f32 * x_spacing\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n// type alias for boxed function to determine row color during grid generation\ntype ColorPickerFn = Box<dyn Send + Sync + Fn(usize, &Style) -> Option<Color32>>;\n\npub(crate) struct GridLayout {\n    ctx: Context,\n    style: std::sync::Arc<Style>,\n    id: Id,\n\n    /// First frame (no previous know state).\n    is_first_frame: bool,\n\n    /// State previous frame (if any).\n    /// This can be used to predict future sizes of cells.\n    prev_state: State,\n\n    /// State accumulated during the current frame.\n    curr_state: State,\n    initial_available: Rect,\n\n    // Options:\n    num_columns: Option<usize>,\n    spacing: Vec2,\n    min_cell_size: Vec2,\n    max_cell_size: Vec2,\n    color_picker: Option<ColorPickerFn>,\n\n    // Cursor:\n    col: usize,\n    row: usize,\n}\n\nimpl GridLayout {\n    pub(crate) fn new(ui: &Ui, id: Id, prev_state: Option<State>) -> Self {\n        let is_first_frame = prev_state.is_none();\n        let prev_state = prev_state.unwrap_or_default();\n\n        // TODO(emilk): respect current layout\n\n        let initial_available = ui.placer().max_rect().intersect(ui.cursor());\n        debug_assert!(\n            initial_available.min.x.is_finite(),\n            \"Grid not yet available for right-to-left layouts\"\n        );\n\n        ui.ctx().check_for_id_clash(id, initial_available, \"Grid\");\n\n        Self {\n            ctx: ui.ctx().clone(),\n            style: Arc::clone(ui.style()),\n            id,\n            is_first_frame,\n            prev_state,\n            curr_state: State::default(),\n            initial_available,\n\n            num_columns: None,\n            spacing: ui.spacing().item_spacing,\n            min_cell_size: ui.spacing().interact_size,\n            max_cell_size: Vec2::INFINITY,\n            color_picker: None,\n\n            col: 0,\n            row: 0,\n        }\n    }\n}\n\nimpl GridLayout {\n    fn prev_col_width(&self, col: usize) -> f32 {\n        self.prev_state\n            .col_width(col)\n            .unwrap_or(self.min_cell_size.x)\n    }\n\n    fn prev_row_height(&self, row: usize) -> f32 {\n        self.prev_state\n            .row_height(row)\n            .unwrap_or(self.min_cell_size.y)\n    }\n\n    pub(crate) fn wrap_text(&self) -> bool {\n        self.max_cell_size.x.is_finite()\n    }\n\n    pub(crate) fn available_rect(&self, region: &Region) -> Rect {\n        let is_last_column = Some(self.col + 1) == self.num_columns;\n\n        let width = if is_last_column {\n            // The first frame we don't really know the widths of the previous columns,\n            // so returning a big available width here can cause trouble.\n            if self.is_first_frame {\n                self.curr_state\n                    .col_width(self.col)\n                    .unwrap_or(self.min_cell_size.x)\n            } else {\n                (self.initial_available.right() - region.cursor.left())\n                    .at_most(self.max_cell_size.x)\n            }\n        } else if self.max_cell_size.x.is_finite() {\n            // TODO(emilk): should probably heed `prev_state` here too\n            self.max_cell_size.x\n        } else {\n            // If we want to allow width-filling widgets like [`Separator`] in one of the first cells\n            // then we need to make sure they don't spill out of the first cell:\n            self.prev_state\n                .col_width(self.col)\n                .or_else(|| self.curr_state.col_width(self.col))\n                .unwrap_or(self.min_cell_size.x)\n        };\n\n        // If something above was wider, we can be wider:\n        let width = width.max(self.curr_state.col_width(self.col).unwrap_or(0.0));\n\n        let available = region.max_rect.intersect(region.cursor);\n\n        let height = region.max_rect.max.y - available.top();\n        let height = height\n            .at_least(self.min_cell_size.y)\n            .at_most(self.max_cell_size.y);\n\n        Rect::from_min_size(available.min, vec2(width, height))\n    }\n\n    pub(crate) fn next_cell(&self, cursor: Rect, child_size: Vec2) -> Rect {\n        let width = self.prev_state.col_width(self.col).unwrap_or(0.0);\n        let height = self.prev_row_height(self.row);\n        let size = child_size.max(vec2(width, height));\n        Rect::from_min_size(cursor.min, size).round_ui()\n    }\n\n    #[expect(clippy::unused_self)]\n    pub(crate) fn align_size_within_rect(&self, size: Vec2, frame: Rect) -> Rect {\n        // TODO(emilk): allow this alignment to be customized\n        Align2::LEFT_CENTER\n            .align_size_within_rect(size, frame)\n            .round_ui()\n    }\n\n    pub(crate) fn justify_and_align(&self, frame: Rect, size: Vec2) -> Rect {\n        self.align_size_within_rect(size, frame)\n    }\n\n    pub(crate) fn advance(&mut self, cursor: &mut Rect, _frame_rect: Rect, widget_rect: Rect) {\n        #[cfg(debug_assertions)]\n        {\n            let debug_expand_width = self.style.debug.show_expand_width;\n            let debug_expand_height = self.style.debug.show_expand_height;\n            if debug_expand_width || debug_expand_height {\n                let rect = widget_rect;\n                let too_wide = rect.width() > self.prev_col_width(self.col);\n                let too_high = rect.height() > self.prev_row_height(self.row);\n\n                if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {\n                    let painter = self.ctx.debug_painter();\n                    painter.rect_stroke(\n                        rect,\n                        0.0,\n                        (1.0, Color32::LIGHT_BLUE),\n                        crate::StrokeKind::Inside,\n                    );\n\n                    let stroke = Stroke::new(2.5, Color32::from_rgb(200, 0, 0));\n                    let paint_line_seg = |a, b| painter.line_segment([a, b], stroke);\n\n                    if debug_expand_width && too_wide {\n                        paint_line_seg(rect.left_top(), rect.left_bottom());\n                        paint_line_seg(rect.left_center(), rect.right_center());\n                        paint_line_seg(rect.right_top(), rect.right_bottom());\n                    }\n                }\n            }\n        }\n\n        self.curr_state\n            .set_min_col_width(self.col, widget_rect.width().max(self.min_cell_size.x));\n        self.curr_state\n            .set_min_row_height(self.row, widget_rect.height().max(self.min_cell_size.y));\n\n        cursor.min.x += self.prev_col_width(self.col) + self.spacing.x;\n        self.col += 1;\n    }\n\n    fn paint_row(&self, cursor: &Rect, painter: &Painter) {\n        // handle row color painting based on color-picker function\n        let Some(color_picker) = self.color_picker.as_ref() else {\n            return;\n        };\n        let Some(row_color) = color_picker(self.row, &self.style) else {\n            return;\n        };\n        let Some(height) = self.prev_state.row_height(self.row) else {\n            return;\n        };\n        // Paint background for coming row:\n        let size = Vec2::new(self.prev_state.full_width(self.spacing.x), height);\n        let rect = Rect::from_min_size(cursor.min, size);\n        let rect = rect.expand2(0.5 * self.spacing.y * Vec2::Y);\n        let rect = rect.expand2(2.0 * Vec2::X); // HACK: just looks better with some spacing on the sides\n\n        painter.rect_filled(rect, 2.0, row_color);\n    }\n\n    pub(crate) fn end_row(&mut self, cursor: &mut Rect, painter: &Painter) {\n        cursor.min.x = self.initial_available.min.x;\n        cursor.min.y += self.spacing.y;\n        cursor.min.y += self\n            .curr_state\n            .row_height(self.row)\n            .unwrap_or(self.min_cell_size.y);\n\n        self.col = 0;\n        self.row += 1;\n\n        self.paint_row(cursor, painter);\n    }\n\n    pub(crate) fn save(&self) {\n        // We need to always save state on the first frame, otherwise request_discard\n        // would be called repeatedly (see #5132)\n        if self.curr_state != self.prev_state || self.is_first_frame {\n            self.curr_state.clone().store(&self.ctx, self.id);\n            self.ctx.request_repaint();\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A simple grid layout.\n///\n/// The cells are always laid out left to right, top-down.\n/// The contents of each cell will be aligned to the left and center.\n///\n/// If you want to add multiple widgets to a cell you need to group them with\n/// [`Ui::horizontal`], [`Ui::vertical`] etc.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// egui::Grid::new(\"some_unique_id\").show(ui, |ui| {\n///     ui.label(\"First row, first column\");\n///     ui.label(\"First row, second column\");\n///     ui.end_row();\n///\n///     ui.label(\"Second row, first column\");\n///     ui.label(\"Second row, second column\");\n///     ui.label(\"Second row, third column\");\n///     ui.end_row();\n///\n///     ui.horizontal(|ui| { ui.label(\"Same\"); ui.label(\"cell\"); });\n///     ui.label(\"Third row, second column\");\n///     ui.end_row();\n/// });\n/// # });\n/// ```\n#[must_use = \"You should call .show()\"]\npub struct Grid {\n    id_salt: Id,\n    num_columns: Option<usize>,\n    min_col_width: Option<f32>,\n    min_row_height: Option<f32>,\n    max_cell_size: Vec2,\n    spacing: Option<Vec2>,\n    start_row: usize,\n    color_picker: Option<ColorPickerFn>,\n}\n\nimpl Grid {\n    /// Create a new [`Grid`] with a locally unique identifier.\n    pub fn new(id_salt: impl std::hash::Hash) -> Self {\n        Self {\n            id_salt: Id::new(id_salt),\n            num_columns: None,\n            min_col_width: None,\n            min_row_height: None,\n            max_cell_size: Vec2::INFINITY,\n            spacing: None,\n            start_row: 0,\n            color_picker: None,\n        }\n    }\n\n    /// Setting this will allow for dynamic coloring of rows of the grid object\n    #[inline]\n    pub fn with_row_color<F>(mut self, color_picker: F) -> Self\n    where\n        F: Send + Sync + Fn(usize, &Style) -> Option<Color32> + 'static,\n    {\n        self.color_picker = Some(Box::new(color_picker));\n        self\n    }\n\n    /// Setting this will allow the last column to expand to take up the rest of the space of the parent [`Ui`].\n    #[inline]\n    pub fn num_columns(mut self, num_columns: usize) -> Self {\n        self.num_columns = Some(num_columns);\n        self\n    }\n\n    /// If `true`, add a subtle background color to every other row.\n    ///\n    /// This can make a table easier to read.\n    /// Default is whatever is in [`crate::Visuals::striped`].\n    pub fn striped(self, striped: bool) -> Self {\n        if striped {\n            self.with_row_color(striped_row_color)\n        } else {\n            // Explicitly set the row color to nothing.\n            // Needed so that when the style.visuals.striped value is checked later on,\n            // it is clear that the user does not want stripes on this specific Grid.\n            self.with_row_color(|_row: usize, _style: &Style| None)\n        }\n    }\n\n    /// Set minimum width of each column.\n    /// Default: [`crate::style::Spacing::interact_size`]`.x`.\n    #[inline]\n    pub fn min_col_width(mut self, min_col_width: f32) -> Self {\n        self.min_col_width = Some(min_col_width);\n        self\n    }\n\n    /// Set minimum height of each row.\n    /// Default: [`crate::style::Spacing::interact_size`]`.y`.\n    #[inline]\n    pub fn min_row_height(mut self, min_row_height: f32) -> Self {\n        self.min_row_height = Some(min_row_height);\n        self\n    }\n\n    /// Set soft maximum width (wrapping width) of each column.\n    #[inline]\n    pub fn max_col_width(mut self, max_col_width: f32) -> Self {\n        self.max_cell_size.x = max_col_width;\n        self\n    }\n\n    /// Set spacing between columns/rows.\n    /// Default: [`crate::style::Spacing::item_spacing`].\n    #[inline]\n    pub fn spacing(mut self, spacing: impl Into<Vec2>) -> Self {\n        self.spacing = Some(spacing.into());\n        self\n    }\n\n    /// Change which row number the grid starts on.\n    /// This can be useful when you have a large [`crate::Grid`] inside of [`crate::ScrollArea::show_rows`].\n    #[inline]\n    pub fn start_row(mut self, start_row: usize) -> Self {\n        self.start_row = start_row;\n        self\n    }\n}\n\nimpl Grid {\n    pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {\n        self.show_dyn(ui, Box::new(add_contents))\n    }\n\n    fn show_dyn<'c, R>(\n        self,\n        ui: &mut Ui,\n        add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> InnerResponse<R> {\n        let Self {\n            id_salt,\n            num_columns,\n            min_col_width,\n            min_row_height,\n            max_cell_size,\n            spacing,\n            start_row,\n            mut color_picker,\n        } = self;\n        let min_col_width = min_col_width.unwrap_or_else(|| ui.spacing().interact_size.x);\n        let min_row_height = min_row_height.unwrap_or_else(|| ui.spacing().interact_size.y);\n        let spacing = spacing.unwrap_or_else(|| ui.spacing().item_spacing);\n        if color_picker.is_none() && ui.visuals().striped {\n            color_picker = Some(Box::new(striped_row_color));\n        }\n\n        let id = ui.make_persistent_id(id_salt);\n        let prev_state = State::load(ui.ctx(), id);\n\n        // Each grid cell is aligned LEFT_CENTER.\n        // If somebody wants to wrap more things inside a cell,\n        // then we should pick a default layout that matches that alignment,\n        // which we do here:\n        let max_rect = ui.cursor().intersect(ui.max_rect());\n\n        let mut ui_builder = UiBuilder::new().max_rect(max_rect);\n        if prev_state.is_none() {\n            // The initial frame will be glitchy, because we don't know the sizes of things to come.\n\n            if ui.is_visible() {\n                // Try to cover up the glitchy initial frame:\n                ui.request_discard(\"new Grid\");\n            }\n\n            // Hide the ui this frame, and make things as narrow as possible:\n            ui_builder = ui_builder.sizing_pass().invisible();\n        }\n\n        ui.scope_builder(ui_builder, |ui| {\n            ui.horizontal(|ui| {\n                let is_color = color_picker.is_some();\n                let grid = GridLayout {\n                    num_columns,\n                    color_picker,\n                    min_cell_size: vec2(min_col_width, min_row_height),\n                    max_cell_size,\n                    spacing,\n                    row: start_row,\n                    ..GridLayout::new(ui, id, prev_state)\n                };\n\n                // paint first incoming row\n                if is_color {\n                    let cursor = ui.cursor();\n                    let painter = ui.painter();\n                    grid.paint_row(&cursor, painter);\n                }\n\n                ui.set_grid(grid);\n                let r = add_contents(ui);\n                ui.save_grid();\n                r\n            })\n            .inner\n        })\n    }\n}\n\nfn striped_row_color(row: usize, style: &Style) -> Option<Color32> {\n    if row % 2 == 1 {\n        return Some(style.visuals.faint_bg_color);\n    }\n    None\n}\n"
  },
  {
    "path": "crates/egui/src/gui_zoom.rs",
    "content": "//! Helpers for zooming the whole GUI of an app (changing [`Context::pixels_per_point`]).\n//!\nuse crate::{Button, Context, Key, KeyboardShortcut, Modifiers, Ui};\n\n/// The suggested keyboard shortcuts for global gui zooming.\npub mod kb_shortcuts {\n    use super::{Key, KeyboardShortcut, Modifiers};\n\n    /// Primary keyboard shortcut for zooming in (`Cmd` + `+`).\n    pub const ZOOM_IN: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Plus);\n\n    /// Secondary keyboard shortcut for zooming in (`Cmd` + `=`).\n    ///\n    /// On an English keyboard `+` is `Shift` + `=`,\n    /// but it is annoying to have to press shift.\n    /// So most browsers also allow `Cmd` + `=` for zooming in.\n    /// We do the same.\n    pub const ZOOM_IN_SECONDARY: KeyboardShortcut =\n        KeyboardShortcut::new(Modifiers::COMMAND, Key::Equals);\n\n    /// Keyboard shortcut for zooming in (`Cmd` + `-`).\n    pub const ZOOM_OUT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Minus);\n\n    /// Keyboard shortcut for resetting zoom in (`Cmd` + `0`).\n    pub const ZOOM_RESET: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Num0);\n}\n\n/// Let the user scale the GUI (change [`Context::zoom_factor`]) by pressing\n/// Cmd+Plus, Cmd+Minus or Cmd+0, just like in a browser.\n///\n/// By default, [`crate::Context`] calls this function at the end of each frame,\n/// controllable by [`crate::Options::zoom_with_keyboard`].\npub(crate) fn zoom_with_keyboard(ctx: &Context) {\n    if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_RESET)) {\n        ctx.set_zoom_factor(1.0);\n    } else {\n        if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_IN))\n            || ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_IN_SECONDARY))\n        {\n            zoom_in(ctx);\n        }\n        if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_OUT)) {\n            zoom_out(ctx);\n        }\n    }\n}\n\nconst MIN_ZOOM_FACTOR: f32 = 0.2;\nconst MAX_ZOOM_FACTOR: f32 = 5.0;\n\n/// Make everything larger by increasing [`Context::zoom_factor`].\npub fn zoom_in(ctx: &Context) {\n    let mut zoom_factor = ctx.zoom_factor();\n    zoom_factor += 0.1;\n    zoom_factor = zoom_factor.clamp(MIN_ZOOM_FACTOR, MAX_ZOOM_FACTOR);\n    zoom_factor = (zoom_factor * 10.).round() / 10.;\n    ctx.set_zoom_factor(zoom_factor);\n}\n\n/// Make everything smaller by decreasing [`Context::zoom_factor`].\npub fn zoom_out(ctx: &Context) {\n    let mut zoom_factor = ctx.zoom_factor();\n    zoom_factor -= 0.1;\n    zoom_factor = zoom_factor.clamp(MIN_ZOOM_FACTOR, MAX_ZOOM_FACTOR);\n    zoom_factor = (zoom_factor * 10.).round() / 10.;\n    ctx.set_zoom_factor(zoom_factor);\n}\n\n/// Show buttons for zooming the ui.\n///\n/// This is meant to be called from within a menu (See [`Ui::menu_button`]).\npub fn zoom_menu_buttons(ui: &mut Ui) {\n    fn button(ctx: &Context, text: &str, shortcut: &KeyboardShortcut) -> Button<'static> {\n        let btn = Button::new(text);\n        let zoom_with_keyboard = ctx.options(|o| o.zoom_with_keyboard);\n        if zoom_with_keyboard {\n            btn.shortcut_text(ctx.format_shortcut(shortcut))\n        } else {\n            btn\n        }\n    }\n\n    if ui\n        .add_enabled(\n            ui.ctx().zoom_factor() < MAX_ZOOM_FACTOR,\n            button(ui.ctx(), \"Zoom In\", &kb_shortcuts::ZOOM_IN),\n        )\n        .clicked()\n    {\n        zoom_in(ui.ctx());\n    }\n\n    if ui\n        .add_enabled(\n            ui.ctx().zoom_factor() > MIN_ZOOM_FACTOR,\n            button(ui.ctx(), \"Zoom Out\", &kb_shortcuts::ZOOM_OUT),\n        )\n        .clicked()\n    {\n        zoom_out(ui.ctx());\n    }\n\n    if ui\n        .add_enabled(\n            ui.ctx().zoom_factor() != 1.0,\n            button(ui.ctx(), \"Reset Zoom\", &kb_shortcuts::ZOOM_RESET),\n        )\n        .clicked()\n    {\n        ui.ctx().set_zoom_factor(1.0);\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/hit_test.rs",
    "content": "use ahash::HashMap;\n\nuse emath::TSTransform;\n\nuse crate::{LayerId, Pos2, Sense, WidgetRect, WidgetRects, ahash, emath, id::IdSet};\n\n/// Result of a hit-test against [`WidgetRects`].\n///\n/// Answers the question \"what is under the mouse pointer?\".\n///\n/// Note that this doesn't care if the mouse button is pressed or not,\n/// or if we're currently already dragging something.\n#[derive(Clone, Debug, Default)]\npub struct WidgetHits {\n    /// All widgets close to the pointer, back-to-front.\n    ///\n    /// This is a superset of all other widgets in this struct.\n    pub close: Vec<WidgetRect>,\n\n    /// All widgets that contains the pointer, back-to-front.\n    ///\n    /// i.e. both a Window and the Button in it can contain the pointer.\n    ///\n    /// Some of these may be widgets in a layer below the top-most layer.\n    ///\n    /// This will be used for hovering.\n    pub contains_pointer: Vec<WidgetRect>,\n\n    /// If the user would start a clicking now, this is what would be clicked.\n    ///\n    /// This is the top one under the pointer, or closest one of the top-most.\n    pub click: Option<WidgetRect>,\n\n    /// If the user would start a dragging now, this is what would be dragged.\n    ///\n    /// This is the top one under the pointer, or closest one of the top-most.\n    pub drag: Option<WidgetRect>,\n}\n\n/// Find the top or closest widgets to the given position,\n/// none which is closer than `search_radius`.\npub fn hit_test(\n    widgets: &WidgetRects,\n    layer_order: &[LayerId],\n    layer_to_global: &HashMap<LayerId, TSTransform>,\n    pos: Pos2,\n    search_radius: f32,\n) -> WidgetHits {\n    profiling::function_scope!();\n\n    let search_radius_sq = search_radius * search_radius;\n\n    // Transform the position into the local coordinate space of each layer:\n    let pos_in_layers: HashMap<LayerId, Pos2> = layer_to_global\n        .iter()\n        .map(|(layer_id, to_global)| (*layer_id, to_global.inverse() * pos))\n        .collect();\n\n    let mut closest_dist_sq = f32::INFINITY;\n    let mut closest_hit = None;\n\n    // First pass: find the few widgets close to the given position, sorted back-to-front.\n    let mut close: Vec<WidgetRect> = layer_order\n        .iter()\n        .filter(|layer| layer.order.allow_interaction())\n        .flat_map(|&layer_id| widgets.get_layer(layer_id))\n        .filter(|&w| {\n            if w.interact_rect.is_negative() || w.interact_rect.any_nan() {\n                return false;\n            }\n\n            let pos_in_layer = pos_in_layers.get(&w.layer_id).copied().unwrap_or(pos);\n            // TODO(emilk): we should probably do the distance testing in global space instead\n            let dist_sq = w.interact_rect.distance_sq_to_pos(pos_in_layer);\n\n            // In tie, pick last = topmost.\n            if dist_sq <= closest_dist_sq {\n                closest_dist_sq = dist_sq;\n                closest_hit = Some(w);\n            }\n\n            dist_sq <= search_radius_sq\n        })\n        .copied()\n        .collect();\n\n    // Transform to global coordinates:\n    for hit in &mut close {\n        if let Some(to_global) = layer_to_global.get(&hit.layer_id).copied() {\n            *hit = hit.transform(to_global);\n        }\n    }\n\n    close.retain(|rect| !rect.interact_rect.any_nan()); // Protect against bad input and transforms\n\n    // When using layer transforms it is common to stack layers close to each other.\n    // For instance, you may have a resize-separator on a panel, with two\n    // transform-layers on either side.\n    // The resize-separator is technically in a layer _behind_ the transform-layers,\n    // but the user doesn't perceive it as such.\n    // So how do we handle this case?\n    //\n    // If we just allow interactions with ALL close widgets,\n    // then we might accidentally allow clicks through windows and other bad stuff.\n    //\n    // Let's try this:\n    // * Set up a hit-area (based on search_radius)\n    // * Iterate over all hits top-to-bottom\n    //   * Stop if any hit covers the whole hit-area, otherwise keep going\n    //   * Collect the layers ids in a set\n    // * Remove all widgets not in the above layer set\n    //\n    // This will most often result in only one layer,\n    // but if the pointer is at the edge of a layer, we might include widgets in\n    // a layer behind it.\n\n    let mut included_layers: ahash::HashSet<LayerId> = Default::default();\n    for hit in close.iter().rev() {\n        included_layers.insert(hit.layer_id);\n        let hit_covers_search_area = contains_circle(hit.interact_rect, pos, search_radius);\n        if hit_covers_search_area {\n            break; // nothing behind this layer could ever be interacted with\n        }\n    }\n\n    close.retain(|hit| included_layers.contains(&hit.layer_id));\n\n    // If a widget is disabled, treat it as if it isn't sensing anything.\n    // This simplifies the code in `hit_test_on_close` so it doesn't have to check\n    // the `enabled` flag everywhere:\n    for w in &mut close {\n        if !w.enabled {\n            w.sense -= Sense::CLICK;\n            w.sense -= Sense::DRAG;\n        }\n    }\n\n    // Find widgets which are hidden behind another widget and discard them.\n    // This is the case when a widget fully contains another widget and is on a different layer.\n    // It prevents \"hovering through\" widgets when there is a clickable widget behind.\n\n    let mut hidden = IdSet::default();\n    for (i, current) in close.iter().enumerate().rev() {\n        for next in &close[i + 1..] {\n            if next.interact_rect.contains_rect(current.interact_rect)\n                && current.layer_id != next.layer_id\n            {\n                hidden.insert(current.id);\n            }\n        }\n    }\n\n    close.retain(|c| !hidden.contains(&c.id));\n\n    let mut hits = hit_test_on_close(&close, pos);\n\n    hits.contains_pointer = close\n        .iter()\n        .filter(|widget| widget.interact_rect.contains(pos))\n        .copied()\n        .collect();\n\n    hits.close = close;\n\n    {\n        // Undo the to_global-transform we applied earlier,\n        // go back to local layer-coordinates:\n\n        let restore_widget_rect = |w: &mut WidgetRect| {\n            *w = widgets.get(w.id).copied().unwrap_or(*w);\n        };\n\n        for wr in &mut hits.close {\n            restore_widget_rect(wr);\n        }\n        for wr in &mut hits.contains_pointer {\n            restore_widget_rect(wr);\n        }\n        if let Some(wr) = &mut hits.drag {\n            debug_assert!(\n                wr.sense.senses_drag(),\n                \"We should only return drag hits if they sense drag\"\n            );\n            restore_widget_rect(wr);\n        }\n        if let Some(wr) = &mut hits.click {\n            debug_assert!(\n                wr.sense.senses_click(),\n                \"We should only return click hits if they sense click\"\n            );\n            restore_widget_rect(wr);\n        }\n    }\n\n    hits\n}\n\n/// Returns true if the rectangle contains the whole circle.\nfn contains_circle(interact_rect: emath::Rect, pos: Pos2, radius: f32) -> bool {\n    interact_rect.shrink(radius).contains(pos)\n}\n\nfn hit_test_on_close(close: &[WidgetRect], pos: Pos2) -> WidgetHits {\n    #![expect(clippy::collapsible_else_if)]\n\n    // First find the best direct hits:\n    let hit_click = find_closest_within(\n        close.iter().copied().filter(|w| w.sense.senses_click()),\n        pos,\n        0.0,\n    );\n    let hit_drag = find_closest_within(\n        close.iter().copied().filter(|w| w.sense.senses_drag()),\n        pos,\n        0.0,\n    );\n\n    match (hit_click, hit_drag) {\n        (None, None) => {\n            // No direct hit on anything. Find the closest interactive widget.\n\n            let closest = find_closest(\n                close\n                    .iter()\n                    .copied()\n                    .filter(|w| w.sense.senses_click() || w.sense.senses_drag()),\n                pos,\n            );\n\n            if let Some(closest) = closest {\n                WidgetHits {\n                    click: closest.sense.senses_click().then_some(closest),\n                    drag: closest.sense.senses_drag().then_some(closest),\n                    ..Default::default()\n                }\n            } else {\n                // Found nothing\n                WidgetHits {\n                    click: None,\n                    drag: None,\n                    ..Default::default()\n                }\n            }\n        }\n\n        (None, Some(hit_drag)) => {\n            // We have a perfect hit on a drag, but not on click.\n\n            // We have a direct hit on something that implements drag.\n            // This could be a big background thing, like a `ScrollArea` background,\n            // or a moveable window.\n            // It could also be something small, like a slider, or panel resize handle.\n\n            let closest_click = find_closest(\n                close.iter().copied().filter(|w| w.sense.senses_click()),\n                pos,\n            );\n            if let Some(closest_click) = closest_click {\n                if closest_click.sense.senses_drag() {\n                    // We have something close that sense both clicks and drag.\n                    // Should we use it over the direct drag-hit?\n                    if hit_drag\n                        .interact_rect\n                        .contains_rect(closest_click.interact_rect)\n                    {\n                        // This is a smaller thing on a big background - help the user hit it,\n                        // and ignore the big drag background.\n                        WidgetHits {\n                            click: Some(closest_click),\n                            drag: Some(closest_click),\n                            ..Default::default()\n                        }\n                    } else {\n                        // The drag-widget is separate from the click-widget,\n                        // so return only the drag-widget\n                        WidgetHits {\n                            click: None,\n                            drag: Some(hit_drag),\n                            ..Default::default()\n                        }\n                    }\n                } else {\n                    // This is a close pure-click widget.\n                    // However, we should be careful to only return two different widgets\n                    // when it is absolutely not going to confuse the user.\n                    if hit_drag\n                        .interact_rect\n                        .contains_rect(closest_click.interact_rect)\n                    {\n                        // The drag widget is a big background thing (scroll area),\n                        // so returning a separate click widget should not be confusing\n                        WidgetHits {\n                            click: Some(closest_click),\n                            drag: Some(hit_drag),\n                            ..Default::default()\n                        }\n                    } else {\n                        // The two widgets are just two normal small widgets close to each other.\n                        // Highlighting both would be very confusing.\n                        WidgetHits {\n                            click: None,\n                            drag: Some(hit_drag),\n                            ..Default::default()\n                        }\n                    }\n                }\n            } else {\n                // No close clicks.\n                // Maybe there is a close drag widget, that is a smaller\n                // widget floating on top of a big background?\n                // If so, it would be nice to help the user click that.\n                let closest_drag = find_closest(\n                    close\n                        .iter()\n                        .copied()\n                        .filter(|w| w.sense.senses_drag() && w.id != hit_drag.id),\n                    pos,\n                );\n\n                if let Some(closest_drag) = closest_drag\n                    && hit_drag\n                        .interact_rect\n                        .contains_rect(closest_drag.interact_rect)\n                {\n                    // `hit_drag` is a big background thing and `closest_drag` is something small on top of it.\n                    // Be helpful and return the small things:\n                    return WidgetHits {\n                        click: None,\n                        drag: Some(closest_drag),\n                        ..Default::default()\n                    };\n                }\n\n                WidgetHits {\n                    click: None,\n                    drag: Some(hit_drag),\n                    ..Default::default()\n                }\n            }\n        }\n\n        (Some(hit_click), None) => {\n            // We have a perfect hit on a click-widget, but not on a drag-widget.\n            //\n            // Note that we don't look for a close drag widget in this case,\n            // because I can't think of a case where that would be helpful.\n            // This is in contrast with the opposite case,\n            // where when hovering directly over a drag-widget (like a big ScrollArea),\n            // we look for close click-widgets (e.g. buttons).\n            // This is because big background drag-widgets (ScrollArea, Window) are common,\n            // but big clickable things aren't.\n            // Even if they were, I think it would be confusing for a user if clicking\n            // a drag-only widget would click something _behind_ it.\n\n            WidgetHits {\n                click: Some(hit_click),\n                drag: None,\n                ..Default::default()\n            }\n        }\n\n        (Some(hit_click), Some(hit_drag)) => {\n            // We have a perfect hit on both click and drag. Which is the topmost?\n            #[expect(clippy::unwrap_used)]\n            let click_idx = close.iter().position(|w| *w == hit_click).unwrap();\n\n            #[expect(clippy::unwrap_used)]\n            let drag_idx = close.iter().position(|w| *w == hit_drag).unwrap();\n\n            let click_is_on_top_of_drag = drag_idx < click_idx;\n            if click_is_on_top_of_drag {\n                if hit_click.sense.senses_drag() {\n                    // The top thing senses both clicks and drags.\n                    WidgetHits {\n                        click: Some(hit_click),\n                        drag: Some(hit_click),\n                        ..Default::default()\n                    }\n                } else {\n                    // They are interested in different things,\n                    // and click is on top. Report both hits,\n                    // e.g. the top Button and the ScrollArea behind it.\n                    WidgetHits {\n                        click: Some(hit_click),\n                        drag: Some(hit_drag),\n                        ..Default::default()\n                    }\n                }\n            } else {\n                if hit_drag.sense.senses_click() {\n                    // The top thing senses both clicks and drags.\n                    WidgetHits {\n                        click: Some(hit_drag),\n                        drag: Some(hit_drag),\n                        ..Default::default()\n                    }\n                } else {\n                    // The top things senses only drags,\n                    // so we ignore the click-widget, because it would be confusing\n                    // if clicking a drag-widget would actually click something else below it.\n                    WidgetHits {\n                        click: None,\n                        drag: Some(hit_drag),\n                        ..Default::default()\n                    }\n                }\n            }\n        }\n    }\n}\n\nfn find_closest(widgets: impl Iterator<Item = WidgetRect>, pos: Pos2) -> Option<WidgetRect> {\n    find_closest_within(widgets, pos, f32::INFINITY)\n}\n\nfn find_closest_within(\n    widgets: impl Iterator<Item = WidgetRect>,\n    pos: Pos2,\n    max_dist: f32,\n) -> Option<WidgetRect> {\n    let mut closest: Option<WidgetRect> = None;\n    let mut closest_dist_sq = max_dist * max_dist;\n    for widget in widgets {\n        if widget.interact_rect.is_negative() {\n            continue;\n        }\n\n        let dist_sq = widget.interact_rect.distance_sq_to_pos(pos);\n\n        // In case of a tie, take the last one = the one on top.\n        if dist_sq <= closest_dist_sq {\n            closest_dist_sq = dist_sq;\n            closest = Some(widget);\n        }\n    }\n\n    closest\n}\n\n#[cfg(test)]\nmod tests {\n    #![expect(clippy::print_stdout)]\n\n    use emath::{Rect, pos2, vec2};\n\n    use crate::{Id, Sense};\n\n    use super::*;\n\n    fn wr(id: Id, sense: Sense, rect: Rect) -> WidgetRect {\n        WidgetRect {\n            id,\n            layer_id: LayerId::background(),\n            rect,\n            interact_rect: rect,\n            sense,\n            enabled: true,\n        }\n    }\n\n    #[test]\n    fn buttons_on_window() {\n        let widgets = vec![\n            wr(\n                Id::new(\"bg-area\"),\n                Sense::drag(),\n                Rect::from_min_size(pos2(0.0, 0.0), vec2(100.0, 100.0)),\n            ),\n            wr(\n                Id::new(\"click\"),\n                Sense::click(),\n                Rect::from_min_size(pos2(10.0, 10.0), vec2(10.0, 10.0)),\n            ),\n            wr(\n                Id::new(\"click-and-drag\"),\n                Sense::click_and_drag(),\n                Rect::from_min_size(pos2(100.0, 10.0), vec2(10.0, 10.0)),\n            ),\n        ];\n\n        // Perfect hit:\n        let hits = hit_test_on_close(&widgets, pos2(15.0, 15.0));\n        assert_eq!(hits.click.unwrap().id, Id::new(\"click\"));\n        assert_eq!(hits.drag.unwrap().id, Id::new(\"bg-area\"));\n\n        // Close hit:\n        let hits = hit_test_on_close(&widgets, pos2(5.0, 5.0));\n        assert_eq!(hits.click.unwrap().id, Id::new(\"click\"));\n        assert_eq!(hits.drag.unwrap().id, Id::new(\"bg-area\"));\n\n        // Perfect hit:\n        let hits = hit_test_on_close(&widgets, pos2(105.0, 15.0));\n        assert_eq!(hits.click.unwrap().id, Id::new(\"click-and-drag\"));\n        assert_eq!(hits.drag.unwrap().id, Id::new(\"click-and-drag\"));\n\n        // Close hit - should still ignore the drag-background so as not to confuse the user:\n        let hits = hit_test_on_close(&widgets, pos2(105.0, 5.0));\n        assert_eq!(hits.click.unwrap().id, Id::new(\"click-and-drag\"));\n        assert_eq!(hits.drag.unwrap().id, Id::new(\"click-and-drag\"));\n    }\n\n    #[test]\n    fn thin_resize_handle_next_to_label() {\n        let widgets = vec![\n            wr(\n                Id::new(\"bg-area\"),\n                Sense::drag(),\n                Rect::from_min_size(pos2(0.0, 0.0), vec2(100.0, 100.0)),\n            ),\n            wr(\n                Id::new(\"bg-left-label\"),\n                Sense::click_and_drag(),\n                Rect::from_min_size(pos2(0.0, 0.0), vec2(40.0, 100.0)),\n            ),\n            wr(\n                Id::new(\"thin-drag-handle\"),\n                Sense::drag(),\n                Rect::from_min_size(pos2(30.0, 0.0), vec2(70.0, 100.0)),\n            ),\n            wr(\n                Id::new(\"fg-right-label\"),\n                Sense::click_and_drag(),\n                Rect::from_min_size(pos2(60.0, 0.0), vec2(50.0, 100.0)),\n            ),\n        ];\n\n        for (i, w) in widgets.iter().enumerate() {\n            println!(\"Widget {i}: {:?}\", w.id);\n        }\n\n        // In the middle of the bg-left-label:\n        let hits = hit_test_on_close(&widgets, pos2(25.0, 50.0));\n        assert_eq!(hits.click.unwrap().id, Id::new(\"bg-left-label\"));\n        assert_eq!(hits.drag.unwrap().id, Id::new(\"bg-left-label\"));\n\n        // On both the left click-and-drag and thin handle, but the thin handle is on top and should win:\n        let hits = hit_test_on_close(&widgets, pos2(35.0, 50.0));\n        assert_eq!(hits.click, None);\n        assert_eq!(hits.drag.unwrap().id, Id::new(\"thin-drag-handle\"));\n\n        // Only on the thin-drag-handle:\n        let hits = hit_test_on_close(&widgets, pos2(50.0, 50.0));\n        assert_eq!(hits.click, None);\n        assert_eq!(hits.drag.unwrap().id, Id::new(\"thin-drag-handle\"));\n\n        // On both the thin handle and right label. The label is on top and should win\n        let hits = hit_test_on_close(&widgets, pos2(65.0, 50.0));\n        assert_eq!(hits.click.unwrap().id, Id::new(\"fg-right-label\"));\n        assert_eq!(hits.drag.unwrap().id, Id::new(\"fg-right-label\"));\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/id.rs",
    "content": "// TODO(emilk): have separate types `PositionId` and `UniqueId`. ?\n\nuse std::num::NonZeroU64;\n\n/// egui tracks widgets frame-to-frame using [`Id`]s.\n///\n/// For instance, if you start dragging a slider one frame, egui stores\n/// the sliders [`Id`] as the current active id so that next frame when\n/// you move the mouse the same slider changes, even if the mouse has\n/// moved outside the slider.\n///\n/// For some widgets [`Id`]s are also used to persist some state about the\n/// widgets, such as Window position or whether not a collapsing header region is open.\n///\n/// This implies that the [`Id`]s must be unique.\n///\n/// For simple things like sliders and buttons that don't have any memory and\n/// doesn't move we can use the location of the widget as a source of identity.\n/// For instance, a slider only needs a unique and persistent ID while you are\n/// dragging the slider. As long as it is still while moving, that is fine.\n///\n/// For things that need to persist state even after moving (windows, collapsing headers)\n/// the location of the widgets is obviously not good enough. For instance,\n/// a collapsing region needs to remember whether or not it is open even\n/// if the layout next frame is different and the collapsing is not lower down\n/// on the screen.\n///\n/// Then there are widgets that need no identifiers at all, like labels,\n/// because they have no state nor are interacted with.\n///\n/// This is niche-optimized to that `Option<Id>` is the same size as `Id`.\n#[derive(Clone, Copy, Hash, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Id(NonZeroU64);\n\nimpl nohash_hasher::IsEnabled for Id {}\n\nimpl Id {\n    /// A special [`Id`], in particular as a key to [`crate::Memory::data`]\n    /// for when there is no particular widget to attach the data.\n    ///\n    /// The null [`Id`] is still a valid id to use in all circumstances,\n    /// though obviously it will lead to a lot of collisions if you do use it!\n    pub const NULL: Self = Self(NonZeroU64::MAX);\n\n    #[inline]\n    const fn from_hash(hash: u64) -> Self {\n        if let Some(nonzero) = NonZeroU64::new(hash) {\n            Self(nonzero)\n        } else {\n            Self(NonZeroU64::MIN) // The hash was exactly zero (very bad luck)\n        }\n    }\n\n    /// Generate a new [`Id`] by hashing some source (e.g. a string or integer).\n    pub fn new(source: impl std::hash::Hash) -> Self {\n        Self::from_hash(ahash::RandomState::with_seeds(1, 2, 3, 4).hash_one(source))\n    }\n\n    /// Generate a new [`Id`] by hashing the parent [`Id`] and the given argument.\n    pub fn with(self, child: impl std::hash::Hash) -> Self {\n        use std::hash::{BuildHasher as _, Hasher as _};\n        let mut hasher = ahash::RandomState::with_seeds(1, 2, 3, 4).build_hasher();\n        hasher.write_u64(self.0.get());\n        child.hash(&mut hasher);\n        Self::from_hash(hasher.finish())\n    }\n\n    /// Short and readable summary\n    pub fn short_debug_format(&self) -> String {\n        format!(\"{:04X}\", self.value() as u16)\n    }\n\n    /// The inner value of the [`Id`].\n    ///\n    /// This is a high-entropy hash, or [`Self::NULL`].\n    #[inline(always)]\n    pub fn value(&self) -> u64 {\n        self.0.get()\n    }\n\n    pub fn accesskit_id(&self) -> accesskit::NodeId {\n        self.value().into()\n    }\n\n    /// Create a new [`Id`] from a high-entropy value. No hashing is done.\n    ///\n    /// This can be useful if you have an [`Id`] that was converted to some other type\n    /// (e.g. accesskit::NodeId) and you want to convert it back to an [`Id`].\n    ///\n    /// # Safety\n    /// You need to ensure that the value is high-entropy since it might be used in\n    /// a [`IdSet`] or [`IdMap`], which rely on the assumption that [`Id`]s have good entropy.\n    ///\n    /// The method is not unsafe in terms of memory safety.\n    ///\n    /// # Panics\n    /// If the value is zero, this will panic.\n    #[doc(hidden)]\n    #[expect(unsafe_code)]\n    pub unsafe fn from_high_entropy_bits(value: u64) -> Self {\n        Self(NonZeroU64::new(value).expect(\"Id must be non-zero.\"))\n    }\n}\n\nimpl std::fmt::Debug for Id {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{:04X}\", self.value() as u16)\n    }\n}\n\n/// Convenience\nimpl From<&'static str> for Id {\n    #[inline]\n    fn from(string: &'static str) -> Self {\n        Self::new(string)\n    }\n}\n\nimpl From<String> for Id {\n    #[inline]\n    fn from(string: String) -> Self {\n        Self::new(string)\n    }\n}\n\n#[test]\nfn id_size() {\n    assert_eq!(std::mem::size_of::<Id>(), 8);\n    assert_eq!(std::mem::size_of::<Option<Id>>(), 8);\n}\n\n// ----------------------------------------------------------------------------\n\n/// `IdSet` is a `HashSet<Id>` optimized by knowing that [`Id`] has good entropy, and doesn't need more hashing.\npub type IdSet = nohash_hasher::IntSet<Id>;\n\n/// `IdMap<V>` is a `HashMap<Id, V>` optimized by knowing that [`Id`] has good entropy, and doesn't need more hashing.\npub type IdMap<V> = nohash_hasher::IntMap<Id, V>;\n"
  },
  {
    "path": "crates/egui/src/input_state/mod.rs",
    "content": "mod touch_state;\nmod wheel_state;\n\nuse crate::{\n    SafeAreaInsets,\n    emath::{NumExt as _, Pos2, Rect, Vec2, vec2},\n    util::History,\n};\nuse crate::{\n    data::input::{\n        Event, EventFilter, KeyboardShortcut, Modifiers, NUM_POINTER_BUTTONS, PointerButton,\n        RawInput, TouchDeviceId, ViewportInfo,\n    },\n    input_state::wheel_state::WheelState,\n};\nuse std::{\n    collections::{BTreeMap, HashSet},\n    time::Duration,\n};\n\npub use crate::Key;\npub use touch_state::MultiTouchInfo;\nuse touch_state::TouchState;\n\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum SurrenderFocusOn {\n    /// Surrender focus if the user _presses_ somewhere outside the focused widget.\n    Presses,\n\n    /// Surrender focus if the user _clicks_ somewhere outside the focused widget.\n    #[default]\n    Clicks,\n\n    /// Never surrender focus.\n    Never,\n}\n\nimpl SurrenderFocusOn {\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        ui.horizontal(|ui| {\n            ui.selectable_value(self, Self::Presses, \"Presses\")\n                .on_hover_text(\n                    \"Surrender focus if the user presses somewhere outside the focused widget.\",\n                );\n            ui.selectable_value(self, Self::Clicks, \"Clicks\")\n                .on_hover_text(\n                    \"Surrender focus if the user clicks somewhere outside the focused widget.\",\n                );\n            ui.selectable_value(self, Self::Never, \"Never\")\n                .on_hover_text(\"Never surrender focus.\");\n        });\n    }\n}\n\n/// Options for input state handling.\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct InputOptions {\n    /// Multiplier for the scroll speed when reported in [`crate::MouseWheelUnit::Line`]s.\n    pub line_scroll_speed: f32,\n\n    /// Controls the speed at which we zoom in when doing ctrl/cmd + scroll.\n    pub scroll_zoom_speed: f32,\n\n    /// After a pointer-down event, if the pointer moves more than this, it won't become a click.\n    pub max_click_dist: f32,\n\n    /// If the pointer is down for longer than this it will no longer register as a click.\n    ///\n    /// If a touch is held for this many seconds while still, then it will register as a\n    /// \"long-touch\" which is equivalent to a secondary click.\n    ///\n    /// This is to support \"press and hold for context menu\" on touch screens.\n    pub max_click_duration: f64,\n\n    /// The new pointer press must come within this many seconds from previous pointer release\n    /// for double click (or when this value is doubled, triple click) to count.\n    pub max_double_click_delay: f64,\n\n    /// When this modifier is down, all scroll events are treated as zoom events.\n    ///\n    /// The default is CTRL/CMD, and it is STRONGLY recommended to NOT change this.\n    pub zoom_modifier: Modifiers,\n\n    /// When this modifier is down, all scroll events are treated as horizontal scrolls,\n    /// and when combined with [`Self::zoom_modifier`] it will result in zooming\n    /// on only the horizontal axis.\n    ///\n    /// The default is SHIFT, and it is STRONGLY recommended to NOT change this.\n    pub horizontal_scroll_modifier: Modifiers,\n\n    /// When this modifier is down, all scroll events are treated as vertical scrolls,\n    /// and when combined with [`Self::zoom_modifier`] it will result in zooming\n    /// on only the vertical axis.\n    pub vertical_scroll_modifier: Modifiers,\n\n    /// When should we surrender focus from the focused widget?\n    pub surrender_focus_on: SurrenderFocusOn,\n}\n\nimpl Default for InputOptions {\n    fn default() -> Self {\n        // TODO(emilk): figure out why these constants need to be different on web and on native (winit).\n        let is_web = cfg!(target_arch = \"wasm32\");\n        let line_scroll_speed = if is_web {\n            8.0\n        } else {\n            40.0 // Scroll speed decided by consensus: https://github.com/emilk/egui/issues/461\n        };\n\n        Self {\n            line_scroll_speed,\n            scroll_zoom_speed: 1.0 / 200.0,\n            max_click_dist: 6.0,\n            max_click_duration: 0.8,\n            max_double_click_delay: 0.3,\n            zoom_modifier: Modifiers::COMMAND,\n            horizontal_scroll_modifier: Modifiers::SHIFT,\n            vertical_scroll_modifier: Modifiers::ALT,\n            surrender_focus_on: SurrenderFocusOn::default(),\n        }\n    }\n}\n\nimpl InputOptions {\n    /// Show the options in the ui.\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        let Self {\n            line_scroll_speed,\n            scroll_zoom_speed,\n            max_click_dist,\n            max_click_duration,\n            max_double_click_delay,\n            zoom_modifier,\n            horizontal_scroll_modifier,\n            vertical_scroll_modifier,\n            surrender_focus_on,\n        } = self;\n        crate::Grid::new(\"InputOptions\")\n            .num_columns(2)\n            .striped(true)\n            .show(ui, |ui| {\n                ui.label(\"Line scroll speed\");\n                ui.add(crate::DragValue::new(line_scroll_speed).range(0.0..=f32::INFINITY))\n                    .on_hover_text(\n                        \"How many lines to scroll with each tick of the mouse wheel\",\n                    );\n                ui.end_row();\n\n                ui.label(\"Scroll zoom speed\");\n                ui.add(\n                    crate::DragValue::new(scroll_zoom_speed)\n                        .range(0.0..=f32::INFINITY)\n                        .speed(0.001),\n                )\n                .on_hover_text(\"How fast to zoom with ctrl/cmd + scroll\");\n                ui.end_row();\n\n                ui.label(\"Max click distance\");\n                ui.add(crate::DragValue::new(max_click_dist).range(0.0..=f32::INFINITY))\n                    .on_hover_text(\n                        \"If the pointer moves more than this, it won't become a click\",\n                    );\n                ui.end_row();\n\n                ui.label(\"Max click duration\");\n                ui.add(\n                    crate::DragValue::new(max_click_duration)\n                        .range(0.1..=f64::INFINITY)\n                        .speed(0.1),\n                    )\n                    .on_hover_text(\n                        \"If the pointer is down for longer than this it will no longer register as a click\",\n                    );\n                ui.end_row();\n\n                ui.label(\"Max double click delay\");\n                ui.add(\n                    crate::DragValue::new(max_double_click_delay)\n                        .range(0.01..=f64::INFINITY)\n                        .speed(0.1),\n                )\n                .on_hover_text(\"Max time interval for double click to count\");\n                ui.end_row();\n\n                ui.label(\"zoom_modifier\");\n                zoom_modifier.ui(ui);\n                ui.end_row();\n\n                ui.label(\"horizontal_scroll_modifier\");\n                horizontal_scroll_modifier.ui(ui);\n                ui.end_row();\n\n                ui.label(\"vertical_scroll_modifier\");\n                vertical_scroll_modifier.ui(ui);\n                ui.end_row();\n\n                ui.label(\"surrender_focus_on\");\n                surrender_focus_on.ui(ui);\n                ui.end_row();\n\n            });\n    }\n}\n\n/// Input state that egui updates each frame.\n///\n/// You can access this with [`crate::Context::input`].\n///\n/// You can check if `egui` is using the inputs using\n/// [`crate::Context::wants_pointer_input`] and [`crate::Context::wants_keyboard_input`].\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct InputState {\n    /// The raw input we got this frame from the backend.\n    pub raw: RawInput,\n\n    /// State of the mouse or simple touch gestures which can be mapped to mouse operations.\n    pub pointer: PointerState,\n\n    /// State of touches, except those covered by `PointerState` (like clicks and drags).\n    /// (We keep a separate [`TouchState`] for each encountered touch device.)\n    touch_states: BTreeMap<TouchDeviceId, TouchState>,\n\n    // ----------------------------------------------\n    // Scrolling:\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    wheel: WheelState,\n\n    /// How many points the user scrolled, smoothed over a few frames.\n    ///\n    /// The delta dictates how the _content_ should move.\n    ///\n    /// A positive X-value indicates the content is being moved right,\n    /// as when swiping right on a touch-screen or track-pad with natural scrolling.\n    ///\n    /// A positive Y-value indicates the content is being moved down,\n    /// as when swiping down on a touch-screen or track-pad with natural scrolling.\n    ///\n    /// [`crate::ScrollArea`] will both read and write to this field, so that\n    /// at the end of the frame this will be zero if a scroll-area consumed the delta.\n    pub smooth_scroll_delta: Vec2,\n\n    /// Zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).\n    ///\n    /// * `zoom = 1`: no change.\n    /// * `zoom < 1`: pinch together\n    /// * `zoom > 1`: pinch spread\n    zoom_factor_delta: f32,\n\n    /// Rotation in radians this frame, measuring clockwise (e.g. from a rotation gesture).\n    rotation_radians: f32,\n\n    // ----------------------------------------------\n    /// Position and size of the egui area.\n    ///\n    /// This is including the area that may be covered by the `safe_area_insets`.\n    viewport_rect: Rect,\n\n    /// The safe area insets, subtracted from the `viewport_rect` in [`Self::content_rect`].\n    safe_area_insets: SafeAreaInsets,\n\n    /// Also known as device pixel ratio, > 1 for high resolution screens.\n    pub pixels_per_point: f32,\n\n    /// Maximum size of one side of a texture.\n    ///\n    /// This depends on the backend.\n    pub max_texture_side: usize,\n\n    /// Time in seconds. Relative to whatever. Used for animation.\n    pub time: f64,\n\n    /// Time since last frame, in seconds.\n    ///\n    /// This can be very unstable in reactive mode (when we don't paint each frame).\n    /// For animations it is therefore better to use [`Self::stable_dt`].\n    pub unstable_dt: f32,\n\n    /// Estimated time until next frame (provided we repaint right away).\n    ///\n    /// Used for animations to get instant feedback (avoid frame delay).\n    /// Should be set to the expected time between frames when painting at vsync speeds.\n    ///\n    /// On most integrations this has a fixed value of `1.0 / 60.0`, so it is not a very accurate estimate.\n    pub predicted_dt: f32,\n\n    /// Time since last frame (in seconds), but gracefully handles the first frame after sleeping in reactive mode.\n    ///\n    /// In reactive mode (available in e.g. `eframe`), `egui` only updates when there is new input\n    /// or something is animating.\n    /// This can lead to large gaps of time (sleep), leading to large [`Self::unstable_dt`].\n    ///\n    /// If `egui` requested a repaint the previous frame, then `egui` will use\n    /// `stable_dt = unstable_dt;`, but if `egui` did not not request a repaint last frame,\n    /// then `egui` will assume `unstable_dt` is too large, and will use\n    /// `stable_dt = predicted_dt;`.\n    ///\n    /// This means that for the first frame after a sleep,\n    /// `stable_dt` will be a prediction of the delta-time until the next frame,\n    /// and in all other situations this will be an accurate measurement of time passed\n    /// since the previous frame.\n    ///\n    /// Note that a frame can still stall for various reasons, so `stable_dt` can\n    /// still be unusually large in some situations.\n    ///\n    /// When animating something, it is recommended that you use something like\n    /// `stable_dt.min(0.1)` - this will give you smooth animations when the framerate is good\n    /// (even in reactive mode), but will avoid large jumps when framerate is bad,\n    /// and will effectively slow down the animation when FPS drops below 10.\n    pub stable_dt: f32,\n\n    /// The native window has the keyboard focus (i.e. is receiving key presses).\n    ///\n    /// False when the user alt-tab away from the application, for instance.\n    pub focused: bool,\n\n    /// Which modifier keys are down at the start of the frame?\n    pub modifiers: Modifiers,\n\n    /// The keys that are currently being held down.\n    ///\n    /// Keys released this frame are NOT considered down.\n    pub keys_down: HashSet<Key>,\n\n    /// In-order events received this frame\n    pub events: Vec<Event>,\n\n    /// Input state management configuration.\n    ///\n    /// This gets copied from `egui::Options` at the start of each frame for convenience.\n    options: InputOptions,\n}\n\nimpl Default for InputState {\n    fn default() -> Self {\n        Self {\n            raw: Default::default(),\n            pointer: Default::default(),\n            touch_states: Default::default(),\n\n            wheel: Default::default(),\n            smooth_scroll_delta: Vec2::ZERO,\n            zoom_factor_delta: 1.0,\n            rotation_radians: 0.0,\n\n            viewport_rect: Rect::from_min_size(Default::default(), vec2(10_000.0, 10_000.0)),\n            safe_area_insets: Default::default(),\n            pixels_per_point: 1.0,\n            max_texture_side: 2048,\n            time: 0.0,\n            unstable_dt: 1.0 / 60.0,\n            predicted_dt: 1.0 / 60.0,\n            stable_dt: 1.0 / 60.0,\n            focused: false,\n            modifiers: Default::default(),\n            keys_down: Default::default(),\n            events: Default::default(),\n            options: Default::default(),\n        }\n    }\n}\n\nimpl InputState {\n    #[must_use]\n    pub fn begin_pass(\n        mut self,\n        mut new: RawInput,\n        requested_immediate_repaint_prev_frame: bool,\n        pixels_per_point: f32,\n        options: InputOptions,\n    ) -> Self {\n        profiling::function_scope!();\n\n        let time = new.time.unwrap_or(self.time + new.predicted_dt as f64);\n        let unstable_dt = (time - self.time) as f32;\n\n        let stable_dt = if requested_immediate_repaint_prev_frame {\n            // we should have had a repaint straight away,\n            // so this should be trustable.\n            unstable_dt\n        } else {\n            new.predicted_dt\n        };\n\n        let safe_area_insets = new.safe_area_insets.unwrap_or(self.safe_area_insets);\n        let viewport_rect = new.screen_rect.unwrap_or(self.viewport_rect);\n        self.create_touch_states_for_new_devices(&new.events);\n        for touch_state in self.touch_states.values_mut() {\n            touch_state.begin_pass(time, &new, self.pointer.interact_pos);\n        }\n        let pointer = self.pointer.begin_pass(time, &new, options);\n\n        let mut keys_down = self.keys_down;\n        let mut zoom_factor_delta = 1.0; // TODO(emilk): smoothing for zoom factor\n        let mut rotation_radians = 0.0;\n\n        self.wheel.smooth_wheel_delta = Vec2::ZERO;\n\n        for event in &mut new.events {\n            match event {\n                Event::Key {\n                    key,\n                    pressed,\n                    repeat,\n                    ..\n                } => {\n                    if *pressed {\n                        let first_press = keys_down.insert(*key);\n                        *repeat = !first_press;\n                    } else {\n                        keys_down.remove(key);\n                    }\n                }\n                Event::MouseWheel {\n                    unit,\n                    delta,\n                    phase,\n                    modifiers,\n                } => {\n                    self.wheel.on_wheel_event(\n                        viewport_rect,\n                        &options,\n                        time,\n                        *unit,\n                        *delta,\n                        *phase,\n                        *modifiers,\n                    );\n                }\n                Event::Zoom(factor) => {\n                    zoom_factor_delta *= *factor;\n                }\n                Event::Rotate(radians) => {\n                    rotation_radians += *radians;\n                }\n                Event::WindowFocused(false) => {\n                    // Example: pressing `Cmd+S` brings up a save-dialog (e.g. using rfd),\n                    // but we get no key-up event for the `S` key (in winit).\n                    // This leads to `S` being mistakenly marked as down when we switch back to the app.\n                    // So we take the safe route and just clear all the keys and modifiers when\n                    // the app loses focus.\n                    keys_down.clear();\n                }\n                _ => {}\n            }\n        }\n\n        let mut smooth_scroll_delta = Vec2::ZERO;\n\n        {\n            let dt = stable_dt.at_most(0.1);\n            self.wheel.after_events(time, dt);\n\n            let is_zoom = self.wheel.modifiers.matches_any(options.zoom_modifier);\n\n            if is_zoom {\n                zoom_factor_delta *= (options.scroll_zoom_speed\n                    * (self.wheel.smooth_wheel_delta.x + self.wheel.smooth_wheel_delta.y))\n                    .exp();\n            } else {\n                smooth_scroll_delta = self.wheel.smooth_wheel_delta;\n            }\n        }\n\n        Self {\n            pointer,\n            touch_states: self.touch_states,\n\n            wheel: self.wheel,\n            smooth_scroll_delta,\n            zoom_factor_delta,\n            rotation_radians,\n\n            viewport_rect,\n            safe_area_insets,\n            pixels_per_point,\n            max_texture_side: new.max_texture_side.unwrap_or(self.max_texture_side),\n            time,\n            unstable_dt,\n            predicted_dt: new.predicted_dt,\n            stable_dt,\n            focused: new.focused,\n            modifiers: new.modifiers,\n            keys_down,\n            events: new.events.clone(), // TODO(emilk): remove clone() and use raw.events\n            raw: new,\n            options,\n        }\n    }\n\n    /// Info about the active viewport\n    #[inline]\n    pub fn viewport(&self) -> &ViewportInfo {\n        self.raw.viewport()\n    }\n\n    /// Returns the region of the screen that is safe for content rendering\n    ///\n    /// Returns the `viewport_rect` with the `safe_area_insets` removed.\n    ///\n    /// If you want to render behind e.g. the dynamic island on iOS, use [`Self::viewport_rect`].\n    ///\n    /// See also [`RawInput::safe_area_insets`].\n    #[inline(always)]\n    pub fn content_rect(&self) -> Rect {\n        self.viewport_rect - self.safe_area_insets\n    }\n\n    /// Returns the full area available to egui, including parts that might be partially covered,\n    /// for example, by the OS status bar or notches (see [`Self::safe_area_insets`]).\n    ///\n    /// Usually you want to use [`Self::content_rect`] instead.\n    ///\n    /// This rectangle includes e.g. the dynamic island on iOS.\n    /// If you want to only render _below_ the that (not behind), then you should use\n    /// [`Self::content_rect`] instead.\n    ///\n    /// See also [`RawInput::safe_area_insets`].\n    pub fn viewport_rect(&self) -> Rect {\n        self.viewport_rect\n    }\n\n    /// Position and size of the egui area.\n    #[deprecated(\n        note = \"screen_rect has been split into viewport_rect() and content_rect(). You likely should use content_rect()\"\n    )]\n    pub fn screen_rect(&self) -> Rect {\n        self.content_rect()\n    }\n\n    /// Get the safe area insets.\n    ///\n    /// This represents the area of the screen covered by status bars, navigation controls, notches,\n    /// or other items that obscure part of the screen.\n    ///\n    /// See [`Self::content_rect`] to get the `viewport_rect` with the safe area insets removed.\n    pub fn safe_area_insets(&self) -> SafeAreaInsets {\n        self.safe_area_insets\n    }\n\n    /// How many points the user scrolled, smoothed over a few frames.\n    ///\n    /// The delta dictates how the _content_ should move.\n    ///\n    /// A positive X-value indicates the content is being moved right,\n    /// as when swiping right on a touch-screen or track-pad with natural scrolling.\n    ///\n    /// A positive Y-value indicates the content is being moved down,\n    /// as when swiping down on a touch-screen or track-pad with natural scrolling.\n    ///\n    /// [`crate::ScrollArea`] will both read and write to this field, so that\n    /// at the end of the frame this will be zero if a scroll-area consumed the delta.\n    pub fn smooth_scroll_delta(&self) -> Vec2 {\n        self.smooth_scroll_delta\n    }\n\n    /// Uniform zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).\n    /// * `zoom = 1`: no change\n    /// * `zoom < 1`: pinch together\n    /// * `zoom > 1`: pinch spread\n    ///\n    /// If your application supports non-proportional zooming,\n    /// then you probably want to use [`Self::zoom_delta_2d`] instead.\n    #[inline(always)]\n    pub fn zoom_delta(&self) -> f32 {\n        // If a multi touch gesture is detected, it measures the exact and linear proportions of\n        // the distances of the finger tips. It is therefore potentially more accurate than\n        // `zoom_factor_delta` which is based on the `ctrl-scroll` event which, in turn, may be\n        // synthesized from an original touch gesture.\n        self.multi_touch()\n            .map_or(self.zoom_factor_delta, |touch| touch.zoom_delta)\n    }\n\n    /// 2D non-proportional zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).\n    ///\n    /// For multitouch devices the user can do a horizontal or vertical pinch gesture.\n    /// In these cases a non-proportional zoom factor is a available.\n    /// In other cases, this reverts to `Vec2::splat(self.zoom_delta())`.\n    ///\n    /// For horizontal pinches, this will return `[z, 1]`,\n    /// for vertical pinches this will return `[1, z]`,\n    /// and otherwise this will return `[z, z]`,\n    /// where `z` is the zoom factor:\n    /// * `zoom = 1`: no change\n    /// * `zoom < 1`: pinch together\n    /// * `zoom > 1`: pinch spread\n    #[inline(always)]\n    pub fn zoom_delta_2d(&self) -> Vec2 {\n        // If a multi touch gesture is detected, it measures the exact and linear proportions of\n        // the distances of the finger tips.  It is therefore potentially more accurate than\n        // `zoom_factor_delta` which is based on the `ctrl-scroll` event which, in turn, may be\n        // synthesized from an original touch gesture.\n        if let Some(multi_touch) = self.multi_touch() {\n            multi_touch.zoom_delta_2d\n        } else {\n            let mut zoom = Vec2::splat(self.zoom_factor_delta);\n\n            let is_horizontal = self\n                .modifiers\n                .matches_any(self.options.horizontal_scroll_modifier);\n            let is_vertical = self\n                .modifiers\n                .matches_any(self.options.vertical_scroll_modifier);\n\n            if is_horizontal && !is_vertical {\n                // Horizontal-only zooming.\n                zoom.y = 1.0;\n            }\n            if !is_horizontal && is_vertical {\n                // Vertical-only zooming.\n                zoom.x = 1.0;\n            }\n\n            zoom\n        }\n    }\n\n    /// Rotation in radians this frame, measuring clockwise (e.g. from a rotation gesture).\n    #[inline(always)]\n    pub fn rotation_delta(&self) -> f32 {\n        self.multi_touch()\n            .map_or(self.rotation_radians, |touch| touch.rotation_delta)\n    }\n\n    /// Panning translation in pixels this frame (e.g. from scrolling or a pan gesture)\n    ///\n    /// The delta indicates how the **content** should move.\n    ///\n    /// A positive X-value indicates the content is being moved right, as when swiping right on a touch-screen or track-pad with natural scrolling.\n    ///\n    /// A positive Y-value indicates the content is being moved down, as when swiping down on a touch-screen or track-pad with natural scrolling.\n    #[inline(always)]\n    pub fn translation_delta(&self) -> Vec2 {\n        self.multi_touch().map_or_else(\n            || self.smooth_scroll_delta(),\n            |touch| touch.translation_delta,\n        )\n    }\n\n    /// True if there is an active scroll action that might scroll more when using [`Self::smooth_scroll_delta`].\n    pub fn is_scrolling(&self) -> bool {\n        self.wheel.is_scrolling()\n    }\n\n    /// How long has it been (in seconds) since the last scroll event?\n    #[inline(always)]\n    pub fn time_since_last_scroll(&self) -> f32 {\n        (self.time - self.wheel.last_wheel_event) as f32\n    }\n\n    /// The [`crate::Context`] will call this at the beginning of each frame to see if we need a repaint.\n    ///\n    /// Returns how long to wait for a repaint.\n    ///\n    /// NOTE: It's important to call this immediately after [`Self::begin_pass`] since calls to\n    /// [`Self::consume_key`] will remove events from the vec, meaning those key presses wouldn't\n    /// cause a repaint.\n    pub(crate) fn wants_repaint_after(&self) -> Option<Duration> {\n        if self.pointer.wants_repaint()\n            || self.wheel.unprocessed_wheel_delta.abs().max_elem() > 0.2\n            || !self.events.is_empty()\n            || !self.raw.hovered_files.is_empty()\n            || !self.raw.dropped_files.is_empty()\n        {\n            // Immediate repaint\n            return Some(Duration::ZERO);\n        }\n\n        if self.any_touches() && !self.pointer.is_decidedly_dragging() {\n            // We need to wake up and check for press-and-hold for the context menu.\n            if let Some(press_start_time) = self.pointer.press_start_time {\n                let press_duration = self.time - press_start_time;\n                if self.options.max_click_duration.is_finite()\n                    && press_duration < self.options.max_click_duration\n                {\n                    let secs_until_menu = self.options.max_click_duration - press_duration;\n                    return Some(Duration::from_secs_f64(secs_until_menu));\n                }\n            }\n        }\n\n        None\n    }\n\n    /// Count presses of a key. If non-zero, the presses are consumed, so that this will only return non-zero once.\n    ///\n    /// Includes key-repeat events.\n    ///\n    /// This uses [`Modifiers::matches_logically`] to match modifiers,\n    /// meaning extra Shift and Alt modifiers are ignored.\n    /// Therefore, you should match most specific shortcuts first,\n    /// i.e. check for `Cmd-Shift-S` (\"Save as…\") before `Cmd-S` (\"Save\"),\n    /// so that a user pressing `Cmd-Shift-S` won't trigger the wrong command!\n    pub fn count_and_consume_key(&mut self, modifiers: Modifiers, logical_key: Key) -> usize {\n        let mut count = 0usize;\n\n        self.events.retain(|event| {\n            let is_match = matches!(\n                event,\n                Event::Key {\n                    key: ev_key,\n                    modifiers: ev_mods,\n                    pressed: true,\n                    ..\n                } if *ev_key == logical_key && ev_mods.matches_logically(modifiers)\n            );\n\n            count += is_match as usize;\n\n            !is_match\n        });\n\n        count\n    }\n\n    /// Check for a key press. If found, `true` is returned and the key pressed is consumed, so that this will only return `true` once.\n    ///\n    /// Includes key-repeat events.\n    ///\n    /// This uses [`Modifiers::matches_logically`] to match modifiers,\n    /// meaning extra Shift and Alt modifiers are ignored.\n    /// Therefore, you should match most specific shortcuts first,\n    /// i.e. check for `Cmd-Shift-S` (\"Save as…\") before `Cmd-S` (\"Save\"),\n    /// so that a user pressing `Cmd-Shift-S` won't trigger the wrong command!\n    pub fn consume_key(&mut self, modifiers: Modifiers, logical_key: Key) -> bool {\n        self.count_and_consume_key(modifiers, logical_key) > 0\n    }\n\n    /// Check if the given shortcut has been pressed.\n    ///\n    /// If so, `true` is returned and the key pressed is consumed, so that this will only return `true` once.\n    ///\n    /// This uses [`Modifiers::matches_logically`] to match modifiers,\n    /// meaning extra Shift and Alt modifiers are ignored.\n    /// Therefore, you should match most specific shortcuts first,\n    /// i.e. check for `Cmd-Shift-S` (\"Save as…\") before `Cmd-S` (\"Save\"),\n    /// so that a user pressing `Cmd-Shift-S` won't trigger the wrong command!\n    pub fn consume_shortcut(&mut self, shortcut: &KeyboardShortcut) -> bool {\n        let KeyboardShortcut {\n            modifiers,\n            logical_key,\n        } = *shortcut;\n        self.consume_key(modifiers, logical_key)\n    }\n\n    /// Was the given key pressed this frame?\n    ///\n    /// Includes key-repeat events.\n    pub fn key_pressed(&self, desired_key: Key) -> bool {\n        self.num_presses(desired_key) > 0\n    }\n\n    /// How many times was the given key pressed this frame?\n    ///\n    /// Includes key-repeat events.\n    pub fn num_presses(&self, desired_key: Key) -> usize {\n        self.events\n            .iter()\n            .filter(|event| {\n                matches!(\n                    event,\n                    Event::Key { key, pressed: true, .. }\n                    if *key == desired_key\n                )\n            })\n            .count()\n    }\n\n    /// Is the given key currently held down?\n    ///\n    /// Keys released this frame are NOT considered down.\n    pub fn key_down(&self, desired_key: Key) -> bool {\n        self.keys_down.contains(&desired_key)\n    }\n\n    /// Was the given key released this frame?\n    pub fn key_released(&self, desired_key: Key) -> bool {\n        self.events.iter().any(|event| {\n            matches!(\n                event,\n                Event::Key {\n                    key,\n                    pressed: false,\n                    ..\n                } if *key == desired_key\n            )\n        })\n    }\n\n    /// Also known as device pixel ratio, > 1 for high resolution screens.\n    #[inline(always)]\n    pub fn pixels_per_point(&self) -> f32 {\n        self.pixels_per_point\n    }\n\n    /// Size of a physical pixel in logical gui coordinates (points).\n    #[inline(always)]\n    pub fn physical_pixel_size(&self) -> f32 {\n        1.0 / self.pixels_per_point()\n    }\n\n    /// How imprecise do we expect the mouse/touch input to be?\n    /// Returns imprecision in points.\n    #[inline(always)]\n    pub fn aim_radius(&self) -> f32 {\n        // TODO(emilk): multiply by ~3 for touch inputs because fingers are fat\n        self.physical_pixel_size()\n    }\n\n    /// Returns details about the currently ongoing multi-touch gesture, if any. Note that this\n    /// method returns `None` for single-touch gestures (click, drag, …).\n    ///\n    /// ```\n    /// # use egui::emath::Rot2;\n    /// # egui::__run_test_ui(|ui| {\n    /// let mut zoom = 1.0; // no zoom\n    /// let mut rotation = 0.0; // no rotation\n    /// let multi_touch = ui.input(|i| i.multi_touch());\n    /// if let Some(multi_touch) = multi_touch {\n    ///     zoom *= multi_touch.zoom_delta;\n    ///     rotation += multi_touch.rotation_delta;\n    /// }\n    /// let transform = zoom * Rot2::from_angle(rotation);\n    /// # });\n    /// ```\n    ///\n    /// By far not all touch devices are supported, and the details depend on the `egui`\n    /// integration backend you are using. `eframe` web supports multi touch for most mobile\n    /// devices, but not for a `Trackpad` on `MacOS`, for example. The backend has to be able to\n    /// capture native touch events, but many browsers seem to pass such events only for touch\n    /// _screens_, but not touch _pads._\n    ///\n    /// Refer to [`MultiTouchInfo`] for details about the touch information available.\n    ///\n    /// Consider using `zoom_delta()` instead of `MultiTouchInfo::zoom_delta` as the former\n    /// delivers a synthetic zoom factor based on ctrl-scroll events, as a fallback.\n    pub fn multi_touch(&self) -> Option<MultiTouchInfo> {\n        // In case of multiple touch devices simply pick the touch_state of the first active device\n        self.touch_states.values().find_map(|t| t.info())\n    }\n\n    /// True if there currently are any fingers touching egui.\n    pub fn any_touches(&self) -> bool {\n        self.touch_states.values().any(|t| t.any_touches())\n    }\n\n    /// True if we have ever received a touch event.\n    pub fn has_touch_screen(&self) -> bool {\n        !self.touch_states.is_empty()\n    }\n\n    /// Scans `events` for device IDs of touch devices we have not seen before,\n    /// and creates a new [`TouchState`] for each such device.\n    fn create_touch_states_for_new_devices(&mut self, events: &[Event]) {\n        for event in events {\n            if let Event::Touch { device_id, .. } = event {\n                self.touch_states\n                    .entry(*device_id)\n                    .or_insert_with(|| TouchState::new(*device_id));\n            }\n        }\n    }\n\n    pub fn accesskit_action_requests(\n        &self,\n        id: crate::Id,\n        action: accesskit::Action,\n    ) -> impl Iterator<Item = &accesskit::ActionRequest> {\n        let accesskit_id = id.accesskit_id();\n        self.events.iter().filter_map(move |event| {\n            if let Event::AccessKitActionRequest(request) = event\n                && request.target_node == accesskit_id\n                && request.target_tree == accesskit::TreeId::ROOT\n                && request.action == action\n            {\n                return Some(request);\n            }\n            None\n        })\n    }\n\n    pub fn consume_accesskit_action_requests(\n        &mut self,\n        id: crate::Id,\n        mut consume: impl FnMut(&accesskit::ActionRequest) -> bool,\n    ) {\n        let accesskit_id = id.accesskit_id();\n        self.events.retain(|event| {\n            if let Event::AccessKitActionRequest(request) = event\n                && request.target_node == accesskit_id\n                && request.target_tree == accesskit::TreeId::ROOT\n            {\n                return !consume(request);\n            }\n            true\n        });\n    }\n\n    pub fn has_accesskit_action_request(&self, id: crate::Id, action: accesskit::Action) -> bool {\n        self.accesskit_action_requests(id, action).next().is_some()\n    }\n\n    pub fn num_accesskit_action_requests(&self, id: crate::Id, action: accesskit::Action) -> usize {\n        self.accesskit_action_requests(id, action).count()\n    }\n\n    /// Get all events that matches the given filter.\n    pub fn filtered_events(&self, filter: &EventFilter) -> Vec<Event> {\n        self.events\n            .iter()\n            .filter(|event| filter.matches(event))\n            .cloned()\n            .collect()\n    }\n\n    /// A long press is something we detect on touch screens\n    /// to trigger a secondary click (context menu).\n    ///\n    /// Returns `true` only on one frame.\n    pub(crate) fn is_long_touch(&self) -> bool {\n        self.any_touches() && self.pointer.is_long_press()\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A pointer (mouse or touch) click.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub(crate) struct Click {\n    pub pos: Pos2,\n\n    /// 1 or 2 (double-click) or 3 (triple-click)\n    pub count: u32,\n\n    /// Allows you to check for e.g. shift-click\n    pub modifiers: Modifiers,\n}\n\nimpl Click {\n    pub fn is_double(&self) -> bool {\n        self.count == 2\n    }\n\n    pub fn is_triple(&self) -> bool {\n        self.count == 3\n    }\n}\n\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub(crate) enum PointerEvent {\n    Moved(Pos2),\n    Pressed {\n        position: Pos2,\n        button: PointerButton,\n    },\n    Released {\n        click: Option<Click>,\n        button: PointerButton,\n    },\n}\n\nimpl PointerEvent {\n    pub fn is_press(&self) -> bool {\n        matches!(self, Self::Pressed { .. })\n    }\n\n    pub fn is_release(&self) -> bool {\n        matches!(self, Self::Released { .. })\n    }\n\n    pub fn is_click(&self) -> bool {\n        matches!(self, Self::Released { click: Some(_), .. })\n    }\n}\n\n/// Mouse or touch state.\n///\n/// To access the methods of [`PointerState`] you can use the [`crate::Context::input`] function\n///\n/// ```rust\n/// # let ctx = egui::Context::default();\n/// let latest_pos = ctx.input(|i| i.pointer.latest_pos());\n/// let is_pointer_down = ctx.input(|i| i.pointer.any_down());\n/// ```\n///\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct PointerState {\n    /// Latest known time\n    time: f64,\n\n    // Consider a finger tapping a touch screen.\n    // What position should we report?\n    // The location of the touch, or `None`, because the finger is gone?\n    //\n    // For some cases we want the first: e.g. to check for interaction.\n    // For showing tooltips, we want the latter (no tooltips, since there are no fingers).\n    /// Latest reported pointer position.\n    /// When tapping a touch screen, this will be `None`.\n    latest_pos: Option<Pos2>,\n\n    /// Latest position of the mouse, but ignoring any [`Event::PointerGone`]\n    /// if there were interactions this frame.\n    /// When tapping a touch screen, this will be the location of the touch.\n    interact_pos: Option<Pos2>,\n\n    /// How much the pointer moved compared to last frame, in points.\n    delta: Vec2,\n\n    /// How much the mouse moved since the last frame, in unspecified units.\n    /// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.\n    /// May be unavailable on some integrations.\n    motion: Option<Vec2>,\n\n    /// Current velocity of pointer.\n    velocity: Vec2,\n\n    /// Current direction of pointer.\n    direction: Vec2,\n\n    /// Recent movement of the pointer.\n    /// Used for calculating velocity of pointer.\n    pos_history: History<Pos2>,\n\n    /// Buttons currently down, excluding those released this frame.\n    down: [bool; NUM_POINTER_BUTTONS],\n\n    /// Where did the current click/drag originate?\n    /// `None` if no mouse button is down.\n    press_origin: Option<Pos2>,\n\n    /// When did the current click/drag originate?\n    /// `None` if no mouse button is down.\n    press_start_time: Option<f64>,\n\n    /// Set to `true` if the pointer has moved too much (since being pressed)\n    /// for it to be registered as a click.\n    pub(crate) has_moved_too_much_for_a_click: bool,\n\n    /// Did [`Self::is_decidedly_dragging`] go from `false` to `true` this frame?\n    ///\n    /// This could also be the trigger point for a long-touch.\n    pub(crate) started_decidedly_dragging: bool,\n\n    /// Where did the last click originate?\n    /// `None` if no mouse click occurred.\n    last_click_pos: Option<Pos2>,\n\n    /// When did the pointer get click last?\n    /// Used to check for double-clicks.\n    last_click_time: f64,\n\n    /// When did the pointer get click two clicks ago?\n    /// Used to check for triple-clicks.\n    last_last_click_time: f64,\n\n    /// When was the pointer last moved?\n    /// Used for things like showing hover ui/tooltip with a delay.\n    last_move_time: f64,\n\n    /// All button events that occurred this frame\n    pub(crate) pointer_events: Vec<PointerEvent>,\n\n    /// Input state management configuration.\n    ///\n    /// This gets copied from `egui::Options` at the start of each frame for convenience.\n    options: InputOptions,\n}\n\nimpl Default for PointerState {\n    fn default() -> Self {\n        Self {\n            time: -f64::INFINITY,\n            latest_pos: None,\n            interact_pos: None,\n            delta: Vec2::ZERO,\n            motion: None,\n            velocity: Vec2::ZERO,\n            direction: Vec2::ZERO,\n            pos_history: History::new(2..1000, 0.1),\n            down: Default::default(),\n            press_origin: None,\n            press_start_time: None,\n            has_moved_too_much_for_a_click: false,\n            started_decidedly_dragging: false,\n            last_click_pos: None,\n            last_click_time: f64::NEG_INFINITY,\n            last_last_click_time: f64::NEG_INFINITY,\n            last_move_time: f64::NEG_INFINITY,\n            pointer_events: vec![],\n            options: Default::default(),\n        }\n    }\n}\n\nimpl PointerState {\n    #[must_use]\n    pub(crate) fn begin_pass(mut self, time: f64, new: &RawInput, options: InputOptions) -> Self {\n        let was_decidedly_dragging = self.is_decidedly_dragging();\n\n        self.time = time;\n        self.options = options;\n\n        self.pointer_events.clear();\n\n        let old_pos = self.latest_pos;\n        self.interact_pos = self.latest_pos;\n        if self.motion.is_some() {\n            self.motion = Some(Vec2::ZERO);\n        }\n\n        let mut clear_history_after_velocity_calculation = false;\n        for event in &new.events {\n            match event {\n                Event::PointerMoved(pos) => {\n                    let pos = *pos;\n\n                    self.latest_pos = Some(pos);\n                    self.interact_pos = Some(pos);\n\n                    if let Some(press_origin) = self.press_origin {\n                        self.has_moved_too_much_for_a_click |=\n                            press_origin.distance(pos) > self.options.max_click_dist;\n                    }\n\n                    self.last_move_time = time;\n                    self.pointer_events.push(PointerEvent::Moved(pos));\n                }\n                Event::PointerButton {\n                    pos,\n                    button,\n                    pressed,\n                    modifiers,\n                } => {\n                    let pos = *pos;\n                    let button = *button;\n                    let pressed = *pressed;\n                    let modifiers = *modifiers;\n\n                    self.latest_pos = Some(pos);\n                    self.interact_pos = Some(pos);\n\n                    if pressed {\n                        // Start of a drag: we want to track the velocity for during the drag\n                        // and ignore any incoming movement\n                        self.pos_history.clear();\n                    }\n\n                    if pressed {\n                        self.press_origin = Some(pos);\n                        self.press_start_time = Some(time);\n                        self.has_moved_too_much_for_a_click = false;\n                        self.pointer_events.push(PointerEvent::Pressed {\n                            position: pos,\n                            button,\n                        });\n                    } else {\n                        // Released\n                        let clicked = self.could_any_button_be_click();\n\n                        let click = if clicked {\n                            let click_dist_sq = self\n                                .last_click_pos\n                                .map_or(0.0, |last_pos| last_pos.distance_sq(pos));\n\n                            let double_click = (time - self.last_click_time)\n                                < self.options.max_double_click_delay\n                                && click_dist_sq\n                                    < self.options.max_click_dist * self.options.max_click_dist;\n                            let triple_click = (time - self.last_last_click_time)\n                                < (self.options.max_double_click_delay * 2.0)\n                                && click_dist_sq\n                                    < self.options.max_click_dist * self.options.max_click_dist;\n                            let count = if triple_click {\n                                3\n                            } else if double_click {\n                                2\n                            } else {\n                                1\n                            };\n\n                            self.last_last_click_time = self.last_click_time;\n                            self.last_click_time = time;\n                            self.last_click_pos = Some(pos);\n\n                            Some(Click {\n                                pos,\n                                count,\n                                modifiers,\n                            })\n                        } else {\n                            None\n                        };\n\n                        self.pointer_events\n                            .push(PointerEvent::Released { click, button });\n\n                        self.press_origin = None;\n                        self.press_start_time = None;\n                    }\n\n                    self.down[button as usize] = pressed; // must be done after the above call to `could_any_button_be_click`\n                }\n                Event::PointerGone => {\n                    self.latest_pos = None;\n                    // When dragging a slider and the mouse leaves the viewport, we still want the drag to work,\n                    // so we don't treat this as a `PointerEvent::Released`.\n                    // NOTE: we do NOT clear `self.interact_pos` here. It will be cleared next frame.\n\n                    // Delay the clearing until after the final velocity calculation, so we can\n                    // get the final velocity when `drag_stopped` is true.\n                    clear_history_after_velocity_calculation = true;\n                }\n                Event::MouseMoved(delta) => *self.motion.get_or_insert(Vec2::ZERO) += *delta,\n                _ => {}\n            }\n        }\n\n        self.delta = if let (Some(old_pos), Some(new_pos)) = (old_pos, self.latest_pos) {\n            new_pos - old_pos\n        } else {\n            Vec2::ZERO\n        };\n\n        if let Some(pos) = self.latest_pos {\n            self.pos_history.add(time, pos);\n        } else {\n            // we do not clear the `pos_history` here, because it is exactly when a finger has\n            // released from the touch screen that we may want to assign a velocity to whatever\n            // the user tried to throw.\n        }\n\n        self.pos_history.flush(time);\n\n        self.velocity = if self.pos_history.len() >= 3 && self.pos_history.duration() > 0.01 {\n            self.pos_history.velocity().unwrap_or_default()\n        } else {\n            Vec2::default()\n        };\n        if self.velocity != Vec2::ZERO {\n            self.last_move_time = time;\n        }\n        if clear_history_after_velocity_calculation {\n            self.pos_history.clear();\n        }\n\n        self.direction = self.pos_history.velocity().unwrap_or_default().normalized();\n\n        self.started_decidedly_dragging = self.is_decidedly_dragging() && !was_decidedly_dragging;\n\n        self\n    }\n\n    fn wants_repaint(&self) -> bool {\n        !self.pointer_events.is_empty() || self.delta != Vec2::ZERO\n    }\n\n    /// How much the pointer moved compared to last frame, in points.\n    #[inline(always)]\n    pub fn delta(&self) -> Vec2 {\n        self.delta\n    }\n\n    /// How much the mouse moved since the last frame, in unspecified units.\n    /// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.\n    /// May be unavailable on some integrations.\n    #[inline(always)]\n    pub fn motion(&self) -> Option<Vec2> {\n        self.motion\n    }\n\n    /// Current velocity of pointer.\n    ///\n    /// This is smoothed over a few frames,\n    /// but can be ZERO when frame-rate is bad.\n    #[inline(always)]\n    pub fn velocity(&self) -> Vec2 {\n        self.velocity\n    }\n\n    /// Current direction of the pointer.\n    ///\n    /// This is less sensitive to bad framerate than [`Self::velocity`].\n    #[inline(always)]\n    pub fn direction(&self) -> Vec2 {\n        self.direction\n    }\n\n    /// Where did the current click/drag originate?\n    /// `None` if no mouse button is down.\n    #[inline(always)]\n    pub fn press_origin(&self) -> Option<Pos2> {\n        self.press_origin\n    }\n\n    /// How far has the pointer moved since the start of the drag (if any)?\n    pub fn total_drag_delta(&self) -> Option<Vec2> {\n        Some(self.latest_pos? - self.press_origin?)\n    }\n\n    /// When did the current click/drag originate?\n    /// `None` if no mouse button is down.\n    #[inline(always)]\n    pub fn press_start_time(&self) -> Option<f64> {\n        self.press_start_time\n    }\n\n    /// Latest reported pointer position.\n    /// When tapping a touch screen, this will be `None`.\n    #[inline(always)]\n    pub fn latest_pos(&self) -> Option<Pos2> {\n        self.latest_pos\n    }\n\n    /// If it is a good idea to show a tooltip, where is pointer?\n    #[inline(always)]\n    pub fn hover_pos(&self) -> Option<Pos2> {\n        self.latest_pos\n    }\n\n    /// If you detect a click or drag and wants to know where it happened, use this.\n    ///\n    /// Latest position of the mouse, but ignoring any [`Event::PointerGone`]\n    /// if there were interactions this frame.\n    /// When tapping a touch screen, this will be the location of the touch.\n    #[inline(always)]\n    pub fn interact_pos(&self) -> Option<Pos2> {\n        self.interact_pos\n    }\n\n    /// Do we have a pointer?\n    ///\n    /// `false` if the mouse is not over the egui area, or if no touches are down on touch screens.\n    #[inline(always)]\n    pub fn has_pointer(&self) -> bool {\n        self.latest_pos.is_some()\n    }\n\n    /// Is the pointer currently still?\n    /// This is smoothed so a few frames of stillness is required before this returns `true`.\n    #[inline(always)]\n    pub fn is_still(&self) -> bool {\n        self.velocity == Vec2::ZERO\n    }\n\n    /// Is the pointer currently moving?\n    /// This is smoothed so a few frames of stillness is required before this returns `false`.\n    #[inline]\n    pub fn is_moving(&self) -> bool {\n        self.velocity != Vec2::ZERO\n    }\n\n    /// How long has it been (in seconds) since the pointer was last moved?\n    #[inline(always)]\n    pub fn time_since_last_movement(&self) -> f32 {\n        (self.time - self.last_move_time) as f32\n    }\n\n    /// How long has it been (in seconds) since the pointer was clicked?\n    #[inline(always)]\n    pub fn time_since_last_click(&self) -> f32 {\n        (self.time - self.last_click_time) as f32\n    }\n\n    /// Was any pointer button pressed (`!down -> down`) this frame?\n    ///\n    /// This can sometimes return `true` even if `any_down() == false`\n    /// because a press can be shorted than one frame.\n    pub fn any_pressed(&self) -> bool {\n        self.pointer_events.iter().any(|event| event.is_press())\n    }\n\n    /// Was any pointer button released (`down -> !down`) this frame?\n    pub fn any_released(&self) -> bool {\n        self.pointer_events.iter().any(|event| event.is_release())\n    }\n\n    /// Was the button given pressed this frame?\n    pub fn button_pressed(&self, button: PointerButton) -> bool {\n        self.pointer_events\n            .iter()\n            .any(|event| matches!(event, &PointerEvent::Pressed{button: b, ..} if button == b))\n    }\n\n    /// Was the button given released this frame?\n    pub fn button_released(&self, button: PointerButton) -> bool {\n        self.pointer_events\n            .iter()\n            .any(|event| matches!(event, &PointerEvent::Released{button: b, ..} if button == b))\n    }\n\n    /// Was the primary button pressed this frame?\n    pub fn primary_pressed(&self) -> bool {\n        self.button_pressed(PointerButton::Primary)\n    }\n\n    /// Was the secondary button pressed this frame?\n    pub fn secondary_pressed(&self) -> bool {\n        self.button_pressed(PointerButton::Secondary)\n    }\n\n    /// Was the primary button released this frame?\n    pub fn primary_released(&self) -> bool {\n        self.button_released(PointerButton::Primary)\n    }\n\n    /// Was the secondary button released this frame?\n    pub fn secondary_released(&self) -> bool {\n        self.button_released(PointerButton::Secondary)\n    }\n\n    /// Is any pointer button currently down?\n    ///\n    /// Buttons released this frame are NOT considered down.\n    pub fn any_down(&self) -> bool {\n        self.down.iter().any(|&down| down)\n    }\n\n    /// Were there any type of click this frame?\n    pub fn any_click(&self) -> bool {\n        self.pointer_events.iter().any(|event| event.is_click())\n    }\n\n    /// Was the given pointer button given clicked this frame?\n    ///\n    /// Returns true on double- and triple- clicks too.\n    pub fn button_clicked(&self, button: PointerButton) -> bool {\n        self.pointer_events\n            .iter()\n            .any(|event| matches!(event, &PointerEvent::Released { button: b, click: Some(_) } if button == b))\n    }\n\n    /// Was the button given double clicked this frame?\n    pub fn button_double_clicked(&self, button: PointerButton) -> bool {\n        self.pointer_events.iter().any(|event| {\n            matches!(\n                &event,\n                PointerEvent::Released {\n                    click: Some(click),\n                    button: b,\n                } if *b == button && click.is_double()\n            )\n        })\n    }\n\n    /// Was the button given triple clicked this frame?\n    pub fn button_triple_clicked(&self, button: PointerButton) -> bool {\n        self.pointer_events.iter().any(|event| {\n            matches!(\n                &event,\n                PointerEvent::Released {\n                    click: Some(click),\n                    button: b,\n                } if *b == button && click.is_triple()\n            )\n        })\n    }\n\n    /// Was the primary button clicked this frame?\n    pub fn primary_clicked(&self) -> bool {\n        self.button_clicked(PointerButton::Primary)\n    }\n\n    /// Was the secondary button clicked this frame?\n    pub fn secondary_clicked(&self) -> bool {\n        self.button_clicked(PointerButton::Secondary)\n    }\n\n    /// Is this button currently down?\n    ///\n    /// Buttons released this frame are NOT considered down.\n    #[inline(always)]\n    pub fn button_down(&self, button: PointerButton) -> bool {\n        self.down[button as usize]\n    }\n\n    /// If the pointer button is down, will it register as a click when released?\n    ///\n    /// See also [`Self::is_decidedly_dragging`].\n    pub fn could_any_button_be_click(&self) -> bool {\n        if self.any_down() || self.any_released() {\n            if self.has_moved_too_much_for_a_click {\n                return false;\n            }\n\n            if let Some(press_start_time) = self.press_start_time\n                && self.time - press_start_time > self.options.max_click_duration\n            {\n                return false;\n            }\n\n            true\n        } else {\n            false\n        }\n    }\n\n    /// Just because the mouse is down doesn't mean we are dragging.\n    /// We could be at the start of a click.\n    /// But if the mouse is down long enough, or has moved far enough,\n    /// then we consider it a drag.\n    ///\n    /// This function can return true on the same frame the drag is released,\n    /// but NOT on the first frame it was started.\n    ///\n    /// See also [`Self::could_any_button_be_click`].\n    pub fn is_decidedly_dragging(&self) -> bool {\n        (self.any_down() || self.any_released())\n            && !self.any_pressed()\n            && !self.could_any_button_be_click()\n            && !self.any_click()\n    }\n\n    /// A long press is something we detect on touch screens\n    /// to trigger a secondary click (context menu).\n    ///\n    /// Returns `true` only on one frame.\n    pub(crate) fn is_long_press(&self) -> bool {\n        self.started_decidedly_dragging\n            && !self.has_moved_too_much_for_a_click\n            && self.button_down(PointerButton::Primary)\n            && self.press_start_time.is_some_and(|press_start_time| {\n                self.time - press_start_time > self.options.max_click_duration\n            })\n    }\n\n    /// Is the primary button currently down?\n    ///\n    /// Buttons released this frame are NOT considered down.\n    #[inline(always)]\n    pub fn primary_down(&self) -> bool {\n        self.button_down(PointerButton::Primary)\n    }\n\n    /// Is the secondary button currently down?\n    ///\n    /// Buttons released this frame are NOT considered down.\n    #[inline(always)]\n    pub fn secondary_down(&self) -> bool {\n        self.button_down(PointerButton::Secondary)\n    }\n\n    /// Is the middle button currently down?\n    ///\n    /// Buttons released this frame are NOT considered down.\n    #[inline(always)]\n    pub fn middle_down(&self) -> bool {\n        self.button_down(PointerButton::Middle)\n    }\n\n    /// Is the mouse moving in the direction of the given rect?\n    pub fn is_moving_towards_rect(&self, rect: &Rect) -> bool {\n        if self.is_still() {\n            return false;\n        }\n\n        if let Some(pos) = self.hover_pos() {\n            let dir = self.direction();\n            if dir != Vec2::ZERO {\n                return rect.intersects_ray(pos, self.direction());\n            }\n        }\n        false\n    }\n}\n\nimpl InputState {\n    pub fn ui(&self, ui: &mut crate::Ui) {\n        let Self {\n            raw,\n            pointer,\n            touch_states,\n            wheel,\n            smooth_scroll_delta,\n            rotation_radians,\n            zoom_factor_delta,\n            viewport_rect,\n            safe_area_insets,\n            pixels_per_point,\n            max_texture_side,\n            time,\n            unstable_dt,\n            predicted_dt,\n            stable_dt,\n            focused,\n            modifiers,\n            keys_down,\n            events,\n            options: _,\n        } = self;\n\n        if let Some(style) = ui.style_mut().text_styles.get_mut(&crate::TextStyle::Body) {\n            style.family = crate::FontFamily::Monospace;\n        }\n\n        ui.collapsing(\"Raw Input\", |ui| raw.ui(ui));\n\n        crate::containers::CollapsingHeader::new(\"🖱 Pointer\")\n            .default_open(false)\n            .show(ui, |ui| {\n                pointer.ui(ui);\n            });\n\n        for (device_id, touch_state) in touch_states {\n            ui.collapsing(format!(\"Touch State [device {}]\", device_id.0), |ui| {\n                touch_state.ui(ui);\n            });\n        }\n\n        crate::containers::CollapsingHeader::new(\"⬍ Scroll\")\n            .default_open(false)\n            .show(ui, |ui| {\n                wheel.ui(ui);\n            });\n\n        ui.label(format!(\"smooth_scroll_delta: {smooth_scroll_delta:4.1}x\"));\n        ui.label(format!(\"zoom_factor_delta: {zoom_factor_delta:4.2}x\"));\n        ui.label(format!(\"rotation_radians: {rotation_radians:.3} radians\"));\n\n        ui.label(format!(\"viewport_rect: {viewport_rect:?} points\"));\n        ui.label(format!(\"safe_area_insets: {safe_area_insets:?} points\"));\n        ui.label(format!(\n            \"{pixels_per_point} physical pixels for each logical point\"\n        ));\n        ui.label(format!(\n            \"max texture size (on each side): {max_texture_side}\"\n        ));\n        ui.label(format!(\"time: {time:.3} s\"));\n        ui.label(format!(\n            \"time since previous frame: {:.1} ms\",\n            1e3 * unstable_dt\n        ));\n        ui.label(format!(\"predicted_dt: {:.1} ms\", 1e3 * predicted_dt));\n        ui.label(format!(\"stable_dt:    {:.1} ms\", 1e3 * stable_dt));\n        ui.label(format!(\"focused:   {focused}\"));\n        ui.label(format!(\"modifiers: {modifiers:#?}\"));\n        ui.label(format!(\"keys_down: {keys_down:?}\"));\n        ui.scope(|ui| {\n            ui.set_min_height(150.0);\n            ui.label(format!(\"events: {events:#?}\"))\n                .on_hover_text(\"key presses etc\");\n        });\n    }\n}\n\nimpl PointerState {\n    pub fn ui(&self, ui: &mut crate::Ui) {\n        let Self {\n            time: _,\n            latest_pos,\n            interact_pos,\n            delta,\n            motion,\n            velocity,\n            direction,\n            pos_history: _,\n            down,\n            press_origin,\n            press_start_time,\n            has_moved_too_much_for_a_click,\n            started_decidedly_dragging,\n            last_click_pos,\n            last_click_time,\n            last_last_click_time,\n            pointer_events,\n            last_move_time,\n            options: _,\n        } = self;\n\n        ui.label(format!(\"latest_pos: {latest_pos:?}\"));\n        ui.label(format!(\"interact_pos: {interact_pos:?}\"));\n        ui.label(format!(\"delta: {delta:?}\"));\n        ui.label(format!(\"motion: {motion:?}\"));\n        ui.label(format!(\n            \"velocity: [{:3.0} {:3.0}] points/sec\",\n            velocity.x, velocity.y\n        ));\n        ui.label(format!(\"direction: {direction:?}\"));\n        ui.label(format!(\"down: {down:#?}\"));\n        ui.label(format!(\"press_origin: {press_origin:?}\"));\n        ui.label(format!(\"press_start_time: {press_start_time:?} s\"));\n        ui.label(format!(\n            \"has_moved_too_much_for_a_click: {has_moved_too_much_for_a_click}\"\n        ));\n        ui.label(format!(\n            \"started_decidedly_dragging: {started_decidedly_dragging}\"\n        ));\n        ui.label(format!(\"last_click_pos: {last_click_pos:#?}\"));\n        ui.label(format!(\"last_click_time: {last_click_time:#?}\"));\n        ui.label(format!(\"last_last_click_time: {last_last_click_time:#?}\"));\n        ui.label(format!(\"last_move_time: {last_move_time:#?}\"));\n        ui.label(format!(\"pointer_events: {pointer_events:?}\"));\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/input_state/touch_state.rs",
    "content": "use std::{collections::BTreeMap, fmt::Debug};\n\nuse crate::{\n    Event, RawInput, TouchId, TouchPhase,\n    data::input::TouchDeviceId,\n    emath::{Pos2, Vec2, normalized_angle},\n};\n\n/// All you probably need to know about a multi-touch gesture.\n#[derive(Clone, Copy, Debug, PartialEq)]\npub struct MultiTouchInfo {\n    /// Point in time when the gesture started.\n    pub start_time: f64,\n\n    /// Position of the pointer at the time the gesture started.\n    pub start_pos: Pos2,\n\n    /// Center position of the current gesture (average of all touch points).\n    pub center_pos: Pos2,\n\n    /// Number of touches (fingers) on the surface. Value is ≥ 2 since for a single touch no\n    /// [`MultiTouchInfo`] is created.\n    pub num_touches: usize,\n\n    /// Proportional zoom factor (pinch gesture).\n    /// * `zoom = 1`: no change\n    /// * `zoom < 1`: pinch together\n    /// * `zoom > 1`: pinch spread\n    pub zoom_delta: f32,\n\n    /// 2D non-proportional zoom factor (pinch gesture).\n    ///\n    /// For horizontal pinches, this will return `[z, 1]`,\n    /// for vertical pinches this will return `[1, z]`,\n    /// and otherwise this will return `[z, z]`,\n    /// where `z` is the zoom factor:\n    /// * `zoom = 1`: no change\n    /// * `zoom < 1`: pinch together\n    /// * `zoom > 1`: pinch spread\n    pub zoom_delta_2d: Vec2,\n\n    /// Rotation in radians. Moving fingers around each other will change this value. This is a\n    /// relative value, comparing the orientation of fingers in the current frame with the previous\n    /// frame. If all fingers are resting, this value is `0.0`.\n    pub rotation_delta: f32,\n\n    /// Relative movement (comparing previous frame and current frame) of the average position of\n    /// all touch points. Without movement this value is `Vec2::ZERO`.\n    ///\n    /// Note that this may not necessarily be measured in screen points (although it _will_ be for\n    /// most mobile devices). In general (depending on the touch device), touch coordinates cannot\n    /// be directly mapped to the screen. A touch always is considered to start at the position of\n    /// the pointer, but touch movement is always measured in the units delivered by the device,\n    /// and may depend on hardware and system settings.\n    pub translation_delta: Vec2,\n\n    /// Current force of the touch (average of the forces of the individual fingers). This is a\n    /// value in the interval `[0.0 .. =1.0]`.\n    ///\n    /// Note 1: A value of `0.0` either indicates a very light touch, or it means that the device\n    /// is not capable of measuring the touch force at all.\n    ///\n    /// Note 2: Just increasing the physical pressure without actually moving the finger may not\n    /// necessarily lead to a change of this value.\n    pub force: f32,\n}\n\n/// The current state (for a specific touch device) of touch events and gestures.\n#[derive(Clone)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub(crate) struct TouchState {\n    /// Technical identifier of the touch device. This is used to identify relevant touch events\n    /// for this [`TouchState`] instance.\n    device_id: TouchDeviceId,\n\n    /// Active touches, if any.\n    ///\n    /// `TouchId` is the unique identifier of the touch. It is valid as long as the finger/pen touches the surface. The\n    /// next touch will receive a new unique ID.\n    ///\n    /// Refer to [`ActiveTouch`].\n    active_touches: BTreeMap<TouchId, ActiveTouch>,\n\n    /// If a gesture has been recognized (i.e. when exactly two fingers touch the surface), this\n    /// holds state information\n    gesture_state: Option<GestureState>,\n}\n\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nstruct GestureState {\n    start_time: f64,\n    start_pointer_pos: Pos2,\n    pinch_type: PinchType,\n    previous: Option<DynGestureState>,\n    current: DynGestureState,\n}\n\n/// Gesture data that can change over time\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nstruct DynGestureState {\n    /// used for proportional zooming\n    avg_distance: f32,\n\n    /// used for non-proportional zooming\n    avg_abs_distance2: Vec2,\n\n    avg_pos: Pos2,\n\n    avg_force: f32,\n\n    heading: f32,\n}\n\n/// Describes an individual touch (finger or digitizer) on the touch surface. Instances exist as\n/// long as the finger/pen touches the surface.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nstruct ActiveTouch {\n    /// Current position of this touch, in device coordinates (not necessarily screen position)\n    pos: Pos2,\n\n    /// Current force of the touch. A value in the interval [0.0 .. 1.0]\n    ///\n    /// Note that a value of 0.0 either indicates a very light touch, or it means that the device\n    /// is not capable of measuring the touch force.\n    force: Option<f32>,\n}\n\nimpl TouchState {\n    pub fn new(device_id: TouchDeviceId) -> Self {\n        Self {\n            device_id,\n            active_touches: Default::default(),\n            gesture_state: None,\n        }\n    }\n\n    pub fn begin_pass(&mut self, time: f64, new: &RawInput, pointer_pos: Option<Pos2>) {\n        let mut added_or_removed_touches = false;\n        for event in &new.events {\n            match *event {\n                Event::Touch {\n                    device_id,\n                    id,\n                    phase,\n                    pos,\n                    force,\n                } if device_id == self.device_id => match phase {\n                    TouchPhase::Start => {\n                        self.active_touches.insert(id, ActiveTouch { pos, force });\n                        added_or_removed_touches = true;\n                    }\n                    TouchPhase::Move => {\n                        if let Some(touch) = self.active_touches.get_mut(&id) {\n                            touch.pos = pos;\n                            touch.force = force;\n                        }\n                    }\n                    TouchPhase::End | TouchPhase::Cancel => {\n                        self.active_touches.remove(&id);\n                        added_or_removed_touches = true;\n                    }\n                },\n                _ => (),\n            }\n        }\n\n        // This needs to be called each frame, even if there are no new touch events.\n        // Otherwise, we would send the same old delta information multiple times:\n        self.update_gesture(time, pointer_pos);\n\n        if added_or_removed_touches {\n            // Adding or removing fingers makes the average values \"jump\". We better forget\n            // about the previous values, and don't create delta information for this frame:\n            if let Some(state) = &mut self.gesture_state {\n                state.previous = None;\n            }\n        }\n    }\n\n    /// Are there currently any fingers touching the surface?\n    pub fn any_touches(&self) -> bool {\n        !self.active_touches.is_empty()\n    }\n\n    pub fn info(&self) -> Option<MultiTouchInfo> {\n        self.gesture_state.as_ref().map(|state| {\n            // state.previous can be `None` when the number of simultaneous touches has just\n            // changed. In this case, we take `current` as `previous`, pretending that there\n            // was no change for the current frame.\n            let state_previous = state.previous.unwrap_or(state.current);\n\n            let zoom_delta = state.current.avg_distance / state_previous.avg_distance;\n\n            let zoom_delta_2d = match state.pinch_type {\n                PinchType::Horizontal => Vec2::new(\n                    state.current.avg_abs_distance2.x / state_previous.avg_abs_distance2.x,\n                    1.0,\n                ),\n                PinchType::Vertical => Vec2::new(\n                    1.0,\n                    state.current.avg_abs_distance2.y / state_previous.avg_abs_distance2.y,\n                ),\n                PinchType::Proportional => Vec2::splat(zoom_delta),\n            };\n\n            let center_pos = state.current.avg_pos;\n\n            MultiTouchInfo {\n                start_time: state.start_time,\n                start_pos: state.start_pointer_pos,\n                num_touches: self.active_touches.len(),\n                zoom_delta,\n                zoom_delta_2d,\n                rotation_delta: normalized_angle(state.current.heading - state_previous.heading),\n                translation_delta: state.current.avg_pos - state_previous.avg_pos,\n                force: state.current.avg_force,\n                center_pos,\n            }\n        })\n    }\n\n    fn update_gesture(&mut self, time: f64, pointer_pos: Option<Pos2>) {\n        if let Some(dyn_state) = self.calc_dynamic_state() {\n            if let Some(state) = &mut self.gesture_state {\n                // updating an ongoing gesture\n                state.previous = Some(state.current);\n                state.current = dyn_state;\n            } else if let Some(pointer_pos) = pointer_pos {\n                // starting a new gesture\n                self.gesture_state = Some(GestureState {\n                    start_time: time,\n                    start_pointer_pos: pointer_pos,\n                    pinch_type: PinchType::classify(&self.active_touches),\n                    previous: None,\n                    current: dyn_state,\n                });\n            }\n        } else {\n            // the end of a gesture (if there is any)\n            self.gesture_state = None;\n        }\n    }\n\n    /// `None` if less than two fingers\n    fn calc_dynamic_state(&self) -> Option<DynGestureState> {\n        let num_touches = self.active_touches.len();\n        if num_touches < 2 {\n            None\n        } else {\n            let mut state = DynGestureState {\n                avg_distance: 0.0,\n                avg_abs_distance2: Vec2::ZERO,\n                avg_pos: Pos2::ZERO,\n                avg_force: 0.0,\n                heading: 0.0,\n            };\n            let num_touches_recip = 1. / num_touches as f32;\n\n            // first pass: calculate force and center of touch positions:\n            for touch in self.active_touches.values() {\n                state.avg_force += touch.force.unwrap_or(0.0);\n                state.avg_pos.x += touch.pos.x;\n                state.avg_pos.y += touch.pos.y;\n            }\n            state.avg_force *= num_touches_recip;\n            state.avg_pos.x *= num_touches_recip;\n            state.avg_pos.y *= num_touches_recip;\n\n            // second pass: calculate distances from center:\n            for touch in self.active_touches.values() {\n                state.avg_distance += state.avg_pos.distance(touch.pos);\n                state.avg_abs_distance2.x += (state.avg_pos.x - touch.pos.x).abs();\n                state.avg_abs_distance2.y += (state.avg_pos.y - touch.pos.y).abs();\n            }\n            state.avg_distance *= num_touches_recip;\n            state.avg_abs_distance2 *= num_touches_recip;\n\n            // Calculate the direction from the first touch to the center position.\n            // This is not the perfect way of calculating the direction if more than two fingers\n            // are involved, but as long as all fingers rotate more or less at the same angular\n            // velocity, the shortcomings of this method will not be noticed. One can see the\n            // issues though, when touching with three or more fingers, and moving only one of them\n            // (it takes two hands to do this in a controlled manner). A better technique would be\n            // to store the current and previous directions (with reference to the center) for each\n            // touch individually, and then calculate the average of all individual changes in\n            // direction. But this approach cannot be implemented locally in this method, making\n            // everything a bit more complicated.\n            #[expect(clippy::unwrap_used)] // guarded against already\n            let first_touch = self.active_touches.values().next().unwrap();\n            state.heading = (state.avg_pos - first_touch.pos).angle();\n\n            Some(state)\n        }\n    }\n}\n\nimpl TouchState {\n    pub fn ui(&self, ui: &mut crate::Ui) {\n        ui.label(format!(\"{self:?}\"));\n    }\n}\n\nimpl Debug for TouchState {\n    // This outputs less clutter than `#[derive(Debug)]`:\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        for (id, touch) in &self.active_touches {\n            f.write_fmt(format_args!(\"#{id:?}: {touch:#?}\\n\"))?;\n        }\n        f.write_fmt(format_args!(\"gesture: {:#?}\\n\", self.gesture_state))?;\n        Ok(())\n    }\n}\n\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nenum PinchType {\n    Horizontal,\n    Vertical,\n    Proportional,\n}\n\nimpl PinchType {\n    fn classify(touches: &BTreeMap<TouchId, ActiveTouch>) -> Self {\n        #![expect(clippy::unwrap_used)]\n\n        // For non-proportional 2d zooming:\n        // If the user is pinching with two fingers that have roughly the same Y coord,\n        // then the Y zoom is unstable and should be 1.\n        // Similarly, if the fingers are directly above/below each other,\n        // we should only zoom on the Y axis.\n        // If the fingers are roughly on a diagonal, we revert to the proportional zooming.\n        if touches.len() == 2 {\n            let mut touches = touches.values();\n            let t0 = touches.next().unwrap().pos;\n            let t1 = touches.next().unwrap().pos;\n\n            let dx = (t0.x - t1.x).abs();\n            let dy = (t0.y - t1.y).abs();\n\n            if dx > 3.0 * dy {\n                Self::Horizontal\n            } else if dy > 3.0 * dx {\n                Self::Vertical\n            } else {\n                Self::Proportional\n            }\n        } else {\n            Self::Proportional\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/input_state/wheel_state.rs",
    "content": "use emath::{Rect, Vec2, vec2};\n\nuse crate::{InputOptions, Modifiers, MouseWheelUnit, TouchPhase};\n\n/// The current state of scrolling.\n///\n/// There are two important types of scroll input deviced:\n/// * Discreen scroll wheels on a mouse\n/// * Smooth scroll input from a trackpad\n///\n/// Scroll wheels will usually fire one single scroll event,\n/// so it is important that egui smooths it out over time.\n///\n/// On the contrary, trackpads usually provide smooth scroll input,\n/// and with kinetic scrolling (which on Mac is implemented by the OS)\n/// scroll events can arrive _after_ the user lets go of the trackpad.\n///\n/// In either case, we consider use to be scrolling until there is no more\n/// scroll events expected.\n///\n/// This means there are a few different states we can be in:\n/// * Not scrolling\n/// * \"Smooth scrolling\" (low-pass filter of discreet scroll events)\n/// * Trackpad-scrolling (we receive begin/end phases for these)\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum Status {\n    /// Not scrolling,\n    Static,\n\n    /// We're smoothing out previous scroll events\n    Smoothing,\n\n    // We're in-between [`TouchPhase::Start`] and [`TouchPhase::End`] of a trackpad scroll.\n    InTouch,\n}\n\n/// Keeps track of wheel (scroll) input.\n#[derive(Clone, Debug)]\npub struct WheelState {\n    /// Are we currently in a scroll action?\n    ///\n    /// This may be true even if no scroll events came in this frame,\n    /// but we are in a kinetic scroll or in a smoothed scroll.\n    pub status: Status,\n\n    /// The modifiers at the start of the scroll.\n    pub modifiers: Modifiers,\n\n    /// Time of the last scroll event.\n    pub last_wheel_event: f64,\n\n    /// Used for smoothing the scroll delta.\n    pub unprocessed_wheel_delta: Vec2,\n\n    /// How many points the user scrolled, smoothed over a few frames.\n    ///\n    /// The delta dictates how the _content_ should move.\n    ///\n    /// A positive X-value indicates the content is being moved right,\n    /// as when swiping right on a touch-screen or track-pad with natural scrolling.\n    ///\n    /// A positive Y-value indicates the content is being moved down,\n    /// as when swiping down on a touch-screen or track-pad with natural scrolling.\n    ///\n    /// [`crate::ScrollArea`] will both read and write to this field, so that\n    /// at the end of the frame this will be zero if a scroll-area consumed the delta.\n    pub smooth_wheel_delta: Vec2,\n}\n\nimpl Default for WheelState {\n    fn default() -> Self {\n        Self {\n            status: Status::Static,\n            modifiers: Default::default(),\n            last_wheel_event: f64::NEG_INFINITY,\n            unprocessed_wheel_delta: Vec2::ZERO,\n            smooth_wheel_delta: Vec2::ZERO,\n        }\n    }\n}\n\nimpl WheelState {\n    #[expect(clippy::too_many_arguments)]\n    pub fn on_wheel_event(\n        &mut self,\n        viewport_rect: Rect,\n        options: &InputOptions,\n        time: f64,\n        unit: MouseWheelUnit,\n        delta: Vec2,\n        phase: TouchPhase,\n        latest_modifiers: Modifiers,\n    ) {\n        self.last_wheel_event = time;\n        match phase {\n            crate::TouchPhase::Start => {\n                self.status = Status::InTouch;\n                self.modifiers = latest_modifiers;\n            }\n            crate::TouchPhase::Move => {\n                match self.status {\n                    Status::Static | Status::Smoothing => {\n                        self.modifiers = latest_modifiers;\n                        self.status = Status::Smoothing;\n                    }\n                    Status::InTouch => {\n                        // If the user lets go of a modifier - ignore it.\n                        // More kinematic scrolling may arrive.\n                        // But if the users presses down new modifiers - heed it!\n                        self.modifiers |= latest_modifiers;\n                    }\n                }\n\n                let mut delta = match unit {\n                    MouseWheelUnit::Point => delta,\n                    MouseWheelUnit::Line => options.line_scroll_speed * delta,\n                    MouseWheelUnit::Page => viewport_rect.height() * delta,\n                };\n\n                let is_horizontal = self\n                    .modifiers\n                    .matches_any(options.horizontal_scroll_modifier);\n                let is_vertical = self.modifiers.matches_any(options.vertical_scroll_modifier);\n\n                if is_horizontal && !is_vertical {\n                    // Treat all scrolling as horizontal scrolling.\n                    // Note: one Mac we already get horizontal scroll events when shift is down.\n                    delta = vec2(delta.x + delta.y, 0.0);\n                }\n                if !is_horizontal && is_vertical {\n                    // Treat all scrolling as vertical scrolling.\n                    delta = vec2(0.0, delta.x + delta.y);\n                }\n\n                // Mouse wheels often go very large steps.\n                // A single notch on a logitech mouse wheel connected to a Macbook returns 14.0 raw scroll delta.\n                // So we smooth it out over several frames for a nicer user experience when scrolling in egui.\n                // BUT: if the user is using a nice smooth mac trackpad, we don't add smoothing,\n                // because it adds latency.\n                let is_smooth = self.status == Status::InTouch\n                    || match unit {\n                        MouseWheelUnit::Point => delta.length() < 8.0, // a bit arbitrary here\n                        MouseWheelUnit::Line | MouseWheelUnit::Page => false,\n                    };\n\n                if is_smooth {\n                    self.smooth_wheel_delta += delta;\n                } else {\n                    self.unprocessed_wheel_delta += delta;\n                }\n            }\n            crate::TouchPhase::End | crate::TouchPhase::Cancel => {\n                self.status = Status::Static;\n                self.modifiers = Default::default();\n                self.unprocessed_wheel_delta = Default::default();\n                self.smooth_wheel_delta = Default::default();\n            }\n        }\n    }\n\n    pub fn after_events(&mut self, time: f64, dt: f32) {\n        let t = crate::emath::exponential_smooth_factor(0.90, 0.1, dt); // reach _% in _ seconds. TODO(emilk): parameterize\n\n        if self.unprocessed_wheel_delta != Vec2::ZERO {\n            for d in 0..2 {\n                if self.unprocessed_wheel_delta[d].abs() < 1.0 {\n                    self.smooth_wheel_delta[d] += self.unprocessed_wheel_delta[d];\n                    self.unprocessed_wheel_delta[d] = 0.0;\n                } else {\n                    let applied = t * self.unprocessed_wheel_delta[d];\n                    self.smooth_wheel_delta[d] += applied;\n                    self.unprocessed_wheel_delta[d] -= applied;\n                }\n            }\n        }\n\n        let time_since_last_scroll = time - self.last_wheel_event;\n\n        if self.status == Status::Smoothing\n            && self.smooth_wheel_delta == Vec2::ZERO\n            && 0.150 < time_since_last_scroll\n        {\n            // On certain platforms, like web, we don't get the start & stop scrolling events, so\n            // we rely on a timer there.\n            //\n            // Tested on a mac touchpad 2025, where the largest observed gap between scroll events\n            // was 68 ms. But we add some margin to be safe\n            self.status = Status::Static;\n            self.modifiers = Default::default();\n        }\n    }\n\n    /// True if there is an active scroll action that might scroll more when using [`Self::smooth_wheel_delta`].\n    pub fn is_scrolling(&self) -> bool {\n        self.status != Status::Static\n    }\n\n    pub fn ui(&self, ui: &mut crate::Ui) {\n        let Self {\n            status,\n            modifiers,\n            last_wheel_event,\n            unprocessed_wheel_delta,\n            smooth_wheel_delta,\n        } = self;\n\n        let time = ui.input(|i| i.time);\n\n        crate::Grid::new(\"ScrollState\")\n            .num_columns(2)\n            .show(ui, |ui| {\n                ui.label(\"status\");\n                ui.monospace(format!(\"{status:?}\"));\n                ui.end_row();\n\n                ui.label(\"modifiers\");\n                ui.monospace(format!(\"{modifiers:?}\"));\n                ui.end_row();\n\n                ui.label(\"last_wheel_event\");\n                ui.monospace(format!(\"{:.1}s ago\", time - *last_wheel_event));\n                ui.end_row();\n\n                ui.label(\"unprocessed_wheel_delta\");\n                ui.monospace(unprocessed_wheel_delta.to_string());\n                ui.end_row();\n\n                ui.label(\"smooth_wheel_delta\");\n                ui.monospace(smooth_wheel_delta.to_string());\n                ui.end_row();\n            });\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/interaction.rs",
    "content": "//! How mouse and touch interzcts with widgets.\n\nuse crate::{Id, InputState, Key, WidgetRects, hit_test, id, input_state, memory};\n\nuse self::{hit_test::WidgetHits, id::IdSet, input_state::PointerEvent, memory::InteractionState};\n\n/// Calculated at the start of each frame\n/// based on:\n/// * Widget rects from precious frame\n/// * Mouse/touch input\n/// * Current [`InteractionState`].\n#[derive(Clone, Default)]\npub struct InteractionSnapshot {\n    /// The widget that got clicked this frame.\n    pub clicked: Option<Id>,\n\n    /// This widget was long-pressed on a touch screen,\n    /// so trigger a secondary click on it (context menu).\n    pub long_touched: Option<Id>,\n\n    /// Drag started on this widget this frame.\n    ///\n    /// This will also be found in `dragged` this frame.\n    pub drag_started: Option<Id>,\n\n    /// This widget is being dragged this frame.\n    ///\n    /// Set the same frame a drag starts,\n    /// but unset the frame a drag ends.\n    ///\n    /// NOTE: this may not have a corresponding [`crate::WidgetRect`],\n    /// if this for instance is a drag-and-drop widget which\n    /// isn't painted whilst being dragged\n    pub dragged: Option<Id>,\n\n    /// This widget was let go this frame,\n    /// after having been dragged.\n    ///\n    /// The widget will not be found in [`Self::dragged`] this frame.\n    pub drag_stopped: Option<Id>,\n\n    /// A small set of widgets (usually 0-1) that the pointer is hovering over.\n    ///\n    /// Show these widgets as highlighted, if they are interactive.\n    ///\n    /// While dragging or clicking something, nothing else is hovered.\n    ///\n    /// Use [`Self::contains_pointer`] to find a drop-zone for drag-and-drop.\n    pub hovered: IdSet,\n\n    /// All widgets that contain the pointer this frame,\n    /// regardless if the user is currently clicking or dragging.\n    ///\n    /// This is usually a larger set than [`Self::hovered`],\n    /// and can be used for e.g. drag-and-drop zones.\n    pub contains_pointer: IdSet,\n}\n\nimpl InteractionSnapshot {\n    pub fn ui(&self, ui: &mut crate::Ui) {\n        let Self {\n            clicked,\n            long_touched,\n            drag_started,\n            dragged,\n            drag_stopped,\n            hovered,\n            contains_pointer,\n        } = self;\n\n        fn id_ui<'a>(ui: &mut crate::Ui, widgets: impl IntoIterator<Item = &'a Id>) {\n            for id in widgets {\n                ui.label(id.short_debug_format());\n            }\n        }\n\n        crate::Grid::new(\"interaction\").show(ui, |ui| {\n            ui.label(\"clicked\");\n            id_ui(ui, clicked);\n            ui.end_row();\n\n            ui.label(\"long_touched\");\n            id_ui(ui, long_touched);\n            ui.end_row();\n\n            ui.label(\"drag_started\");\n            id_ui(ui, drag_started);\n            ui.end_row();\n\n            ui.label(\"dragged\");\n            id_ui(ui, dragged);\n            ui.end_row();\n\n            ui.label(\"drag_stopped\");\n            id_ui(ui, drag_stopped);\n            ui.end_row();\n\n            ui.label(\"hovered\");\n            id_ui(ui, hovered);\n            ui.end_row();\n\n            ui.label(\"contains_pointer\");\n            id_ui(ui, contains_pointer);\n            ui.end_row();\n        });\n    }\n}\n\npub(crate) fn interact(\n    prev_snapshot: &InteractionSnapshot,\n    widgets: &WidgetRects,\n    hits: &WidgetHits,\n    input: &InputState,\n    interaction: &mut InteractionState,\n) -> InteractionSnapshot {\n    profiling::function_scope!();\n\n    if let Some(id) = interaction.potential_click_id\n        && !widgets.contains(id)\n    {\n        // The widget we were interested in clicking is gone.\n        interaction.potential_click_id = None;\n    }\n    if let Some(id) = interaction.potential_drag_id\n        && !widgets.contains(id)\n    {\n        // The widget we were interested in dragging is gone.\n        // This is fine! This could be drag-and-drop,\n        // and the widget being dragged is now \"in the air\" and thus\n        // not registered in the new frame.\n    }\n\n    let mut clicked = None;\n    let mut dragged = prev_snapshot.dragged;\n    let mut long_touched = None;\n\n    if input.key_pressed(Key::Escape) {\n        // Abort dragging on escape\n        dragged = None;\n        interaction.potential_drag_id = None;\n    }\n\n    if input.is_long_touch() {\n        // We implement \"press-and-hold for context menu\" on touch screens here\n        if let Some(widget) = interaction\n            .potential_click_id\n            .and_then(|id| widgets.get(id))\n        {\n            dragged = None;\n            clicked = Some(widget.id);\n            long_touched = Some(widget.id);\n            interaction.potential_click_id = None;\n            interaction.potential_drag_id = None;\n        }\n    }\n\n    // Note: in the current code a press-release in the same frame is NOT considered a drag.\n    for pointer_event in &input.pointer.pointer_events {\n        match pointer_event {\n            PointerEvent::Moved(_) => {}\n\n            PointerEvent::Pressed { .. } => {\n                // Maybe new click?\n                if interaction.potential_click_id.is_none() {\n                    interaction.potential_click_id = hits.click.map(|w| w.id);\n                }\n\n                // Maybe new drag?\n                if interaction.potential_drag_id.is_none() {\n                    interaction.potential_drag_id = hits.drag.map(|w| w.id);\n                }\n            }\n\n            PointerEvent::Released { click, button: _ } => {\n                if click.is_some()\n                    && !input.pointer.is_decidedly_dragging()\n                    && let Some(widget) = interaction\n                        .potential_click_id\n                        .and_then(|id| widgets.get(id))\n                {\n                    clicked = Some(widget.id);\n                }\n\n                interaction.potential_drag_id = None;\n                interaction.potential_click_id = None;\n                dragged = None;\n            }\n        }\n    }\n\n    if dragged.is_none() {\n        // Check if we started dragging something new:\n        if let Some(widget) = interaction.potential_drag_id.and_then(|id| widgets.get(id))\n            && widget.enabled\n        {\n            let is_dragged = if widget.sense.senses_click() && widget.sense.senses_drag() {\n                // This widget is sensitive to both clicks and drags.\n                // When the mouse first is pressed, it could be either,\n                // so we postpone the decision until we know.\n                input.pointer.is_decidedly_dragging()\n            } else {\n                // This widget is just sensitive to drags, so we can mark it as dragged right away:\n                widget.sense.senses_drag()\n            };\n\n            if is_dragged {\n                dragged = Some(widget.id);\n            }\n        }\n    }\n\n    if !input.pointer.could_any_button_be_click() {\n        interaction.potential_click_id = None;\n    }\n\n    if !input.pointer.any_down() || input.pointer.latest_pos().is_none() {\n        interaction.potential_click_id = None;\n        interaction.potential_drag_id = None;\n    }\n\n    // ------------------------------------------------------------------------\n\n    let drag_changed = dragged != prev_snapshot.dragged;\n    let drag_stopped = drag_changed.then_some(prev_snapshot.dragged).flatten();\n    let drag_started = drag_changed.then_some(dragged).flatten();\n\n    // if let Some(drag_started) = drag_started {\n    //     eprintln!(\n    //         \"Started dragging {} {:?}\",\n    //         drag_started.id.short_debug_format(),\n    //         drag_started.rect\n    //     );\n    // }\n\n    let contains_pointer: IdSet = hits\n        .contains_pointer\n        .iter()\n        .chain(&hits.click)\n        .chain(&hits.drag)\n        .map(|w| w.id)\n        .collect();\n\n    let hovered = if clicked.is_some() || dragged.is_some() || long_touched.is_some() {\n        // If currently clicking or dragging, only that and nothing else is hovered.\n        clicked\n            .iter()\n            .chain(&dragged)\n            .chain(&long_touched)\n            .copied()\n            .collect()\n    } else {\n        // We may be hovering an interactive widget or two.\n        // We must also consider the case where non-interactive widgets\n        // are _on top_ of an interactive widget.\n        // For instance: a label in a draggable window.\n        // In that case we want to hover _both_ widgets,\n        // otherwise we won't see tooltips for the label.\n        //\n        // So: we want to hover _all_ widgets above the interactive widget (if any),\n        // but none below it (an interactive widget stops the hover search).\n        //\n        // To know when to stop we need to first know the order of the widgets,\n        // which luckily we already have in `hits.close`.\n\n        let order = |id| hits.close.iter().position(|w| w.id == id);\n\n        let click_order = hits.click.and_then(|w| order(w.id)).unwrap_or(0);\n        let drag_order = hits.drag.and_then(|w| order(w.id)).unwrap_or(0);\n        let top_interactive_order = click_order.max(drag_order);\n\n        let mut hovered: IdSet = hits.click.iter().chain(&hits.drag).map(|w| w.id).collect();\n\n        for w in &hits.contains_pointer {\n            let is_interactive = w.sense.senses_click() || w.sense.senses_drag();\n            if is_interactive {\n                // The only interactive widgets we mark as hovered are the ones\n                // in `hits.click` and `hits.drag`!\n            } else {\n                let is_on_top_of_the_interactive_widget =\n                    top_interactive_order <= order(w.id).unwrap_or(0);\n                if is_on_top_of_the_interactive_widget {\n                    hovered.insert(w.id);\n                }\n            }\n        }\n\n        hovered\n    };\n\n    InteractionSnapshot {\n        clicked,\n        long_touched,\n        drag_started,\n        dragged,\n        drag_stopped,\n        hovered,\n        contains_pointer,\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/introspection.rs",
    "content": "//! Showing UI:s for egui/epaint types.\nuse crate::{\n    Color32, CursorIcon, FontFamily, FontId, Label, Mesh, NumExt as _, Rect, Response, Sense,\n    Shape, Slider, TextStyle, TextWrapMode, Ui, Widget, epaint, memory, pos2, remap_clamp, vec2,\n};\n\npub fn font_family_ui(ui: &mut Ui, font_family: &mut FontFamily) {\n    let families = ui.fonts(|f| f.families());\n    ui.horizontal(|ui| {\n        for alternative in families {\n            let text = alternative.to_string();\n            ui.radio_value(font_family, alternative, text);\n        }\n    });\n}\n\npub fn font_id_ui(ui: &mut Ui, font_id: &mut FontId) {\n    let families = ui.fonts(|f| f.families());\n    ui.horizontal(|ui| {\n        ui.add(Slider::new(&mut font_id.size, 4.0..=40.0).max_decimals(1));\n        for alternative in families {\n            let text = alternative.to_string();\n            ui.radio_value(&mut font_id.family, alternative, text);\n        }\n    });\n}\n\n// Show font texture in demo Ui\npub(crate) fn font_texture_ui(ui: &mut Ui, [width, height]: [usize; 2]) -> Response {\n    ui.vertical(|ui| {\n        let color = if ui.visuals().dark_mode {\n            Color32::WHITE\n        } else {\n            Color32::BLACK\n        };\n\n        ui.label(format!(\"Texture size: {width} x {height} (hover to zoom)\"));\n        if width <= 1 || height <= 1 {\n            return;\n        }\n        let mut size = vec2(width as f32, height as f32);\n        if size.x > ui.available_width() {\n            size *= ui.available_width() / size.x;\n        }\n        let (rect, response) = ui.allocate_at_least(size, Sense::hover());\n        let mut mesh = Mesh::default();\n        mesh.add_rect_with_uv(rect, [pos2(0.0, 0.0), pos2(1.0, 1.0)].into(), color);\n        ui.painter().add(Shape::mesh(mesh));\n\n        let (tex_w, tex_h) = (width as f32, height as f32);\n\n        response\n            .on_hover_cursor(CursorIcon::ZoomIn)\n            .on_hover_ui_at_pointer(|ui| {\n                if let Some(pos) = ui.ctx().pointer_latest_pos() {\n                    let (_id, zoom_rect) = ui.allocate_space(vec2(128.0, 128.0));\n                    let u = remap_clamp(pos.x, rect.x_range(), 0.0..=tex_w);\n                    let v = remap_clamp(pos.y, rect.y_range(), 0.0..=tex_h);\n\n                    let texel_radius = 32.0;\n                    let u = u.at_least(texel_radius).at_most(tex_w - texel_radius);\n                    let v = v.at_least(texel_radius).at_most(tex_h - texel_radius);\n\n                    let uv_rect = Rect::from_min_max(\n                        pos2((u - texel_radius) / tex_w, (v - texel_radius) / tex_h),\n                        pos2((u + texel_radius) / tex_w, (v + texel_radius) / tex_h),\n                    );\n                    let mut mesh = Mesh::default();\n                    mesh.add_rect_with_uv(zoom_rect, uv_rect, color);\n                    ui.painter().add(Shape::mesh(mesh));\n                }\n            });\n    })\n    .response\n}\n\nimpl Widget for &epaint::stats::PaintStats {\n    fn ui(self, ui: &mut Ui) -> Response {\n        ui.vertical(|ui| {\n            ui.label(\n                \"egui generates intermediate level shapes like circles and text. \\\n                These are later tessellated into triangles.\",\n            );\n            ui.add_space(10.0);\n\n            ui.style_mut().override_text_style = Some(TextStyle::Monospace);\n\n            let epaint::stats::PaintStats {\n                shapes,\n                shape_text,\n                shape_path,\n                shape_mesh,\n                shape_vec,\n                num_callbacks,\n                text_shape_vertices,\n                text_shape_indices,\n                clipped_primitives,\n                vertices,\n                indices,\n            } = self;\n\n            ui.label(\"Intermediate:\");\n            label(ui, shapes, \"shapes\").on_hover_text(\"Boxes, circles, etc\");\n            ui.horizontal(|ui| {\n                label(ui, shape_text, \"text\");\n                ui.small(\"(mostly cached)\");\n            });\n            label(ui, shape_path, \"paths\");\n            label(ui, shape_mesh, \"nested meshes\");\n            label(ui, shape_vec, \"nested shapes\");\n            ui.label(format!(\"{num_callbacks:6} callbacks\"));\n            ui.add_space(10.0);\n\n            ui.label(\"Text shapes:\");\n            label(ui, text_shape_vertices, \"vertices\");\n            label(ui, text_shape_indices, \"indices\")\n                .on_hover_text(\"Three 32-bit indices per triangles\");\n            ui.add_space(10.0);\n\n            ui.label(\"Tessellated (and culled):\");\n            label(ui, clipped_primitives, \"primitives lists\")\n                .on_hover_text(\"Number of separate clip rectangles\");\n            label(ui, vertices, \"vertices\");\n            label(ui, indices, \"indices\").on_hover_text(\"Three 32-bit indices per triangles\");\n            ui.add_space(10.0);\n\n            // ui.label(\"Total:\");\n            // ui.label(self.total().format(\"\"));\n        })\n        .response\n    }\n}\n\nfn label(ui: &mut Ui, alloc_info: &epaint::stats::AllocInfo, what: &str) -> Response {\n    ui.add(Label::new(alloc_info.format(what)).wrap_mode(TextWrapMode::Extend))\n}\n\nimpl Widget for &mut epaint::TessellationOptions {\n    fn ui(self, ui: &mut Ui) -> Response {\n        ui.vertical(|ui| {\n            let epaint::TessellationOptions {\n                feathering,\n                feathering_size_in_pixels,\n                coarse_tessellation_culling,\n                prerasterized_discs,\n                round_text_to_pixels,\n                round_line_segments_to_pixels,\n                round_rects_to_pixels,\n                debug_paint_clip_rects,\n                debug_paint_text_rects,\n                debug_ignore_clip_rects,\n                bezier_tolerance,\n                epsilon: _,\n                parallel_tessellation,\n                validate_meshes,\n            } = self;\n\n            ui.horizontal(|ui| {\n                ui.checkbox(feathering, \"Feathering (antialias)\")\n                    .on_hover_text(\"Apply feathering to smooth out the edges of shapes. Turn off for small performance gain.\");\n\n                if *feathering {\n                    ui.add(crate::DragValue::new(feathering_size_in_pixels).range(0.0..=10.0).speed(0.025).suffix(\" px\"));\n                }\n            });\n\n            ui.checkbox(prerasterized_discs, \"Speed up filled circles with pre-rasterization\");\n\n            ui.horizontal(|ui| {\n                ui.label(\"Spline tolerance\");\n                let speed = 0.01 * *bezier_tolerance;\n                ui.add(\n                    crate::DragValue::new(bezier_tolerance).range(0.0001..=10.0)\n                        .speed(speed)\n                );\n            });\n\n            ui.add_enabled(epaint::HAS_RAYON, crate::Checkbox::new(parallel_tessellation, \"Parallelize tessellation\")\n                ).on_hover_text(\"Only available if epaint was compiled with the rayon feature\")\n                .on_disabled_hover_text(\"epaint was not compiled with the rayon feature\");\n\n            ui.checkbox(validate_meshes, \"Validate meshes\").on_hover_text(\"Check that incoming meshes are valid, i.e. that all indices are in range, etc.\");\n\n            ui.collapsing(\"Align to pixel grid\", |ui| {\n                ui.checkbox(round_text_to_pixels, \"Text\")\n                    .on_hover_text(\"Most text already is, so don't expect to see a large change.\");\n\n                ui.checkbox(round_line_segments_to_pixels, \"Line segments\")\n                    .on_hover_text(\"Makes line segments appear crisp on any display.\");\n\n                ui.checkbox(round_rects_to_pixels, \"Rectangles\")\n                    .on_hover_text(\"Makes line segments appear crisp on any display.\");\n            });\n\n            ui.collapsing(\"Debug\", |ui| {\n                ui.checkbox(\n                    coarse_tessellation_culling,\n                    \"Do coarse culling in the tessellator\",\n                );\n\n                ui.checkbox(debug_ignore_clip_rects, \"Ignore clip rectangles\");\n                ui.checkbox(debug_paint_clip_rects, \"Paint clip rectangles\");\n                ui.checkbox(debug_paint_text_rects, \"Paint text bounds\");\n            });\n        })\n        .response\n    }\n}\n\nimpl Widget for &memory::InteractionState {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let memory::InteractionState {\n            potential_click_id,\n            potential_drag_id,\n        } = self;\n\n        ui.vertical(|ui| {\n            ui.label(format!(\"potential_click_id: {potential_click_id:?}\"));\n            ui.label(format!(\"potential_drag_id: {potential_drag_id:?}\"));\n        })\n        .response\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/layers.rs",
    "content": "//! Handles paint layers, i.e. how things\n//! are sometimes painted behind or in front of other things.\n\nuse crate::{Id, IdMap, Rect, ahash, epaint};\nuse epaint::{ClippedShape, Shape, emath::TSTransform};\n\n/// Different layer categories\n#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum Order {\n    /// Painted behind all floating windows\n    Background,\n\n    /// Normal moveable windows that you reorder by click\n    Middle,\n\n    /// Popups, menus etc that should always be painted on top of windows\n    /// Foreground objects can also have tooltips\n    Foreground,\n\n    /// Things floating on top of everything else, like tooltips.\n    /// You cannot interact with these.\n    Tooltip,\n\n    /// Debug layer, always painted last / on top\n    Debug,\n}\n\nimpl Order {\n    const COUNT: usize = 5;\n    const ALL: [Self; Self::COUNT] = [\n        Self::Background,\n        Self::Middle,\n        Self::Foreground,\n        Self::Tooltip,\n        Self::Debug,\n    ];\n    pub const TOP: Self = Self::Debug;\n\n    #[inline(always)]\n    pub fn allow_interaction(&self) -> bool {\n        match self {\n            Self::Background | Self::Middle | Self::Foreground | Self::Tooltip | Self::Debug => {\n                true\n            }\n        }\n    }\n\n    /// Short and readable summary\n    pub fn short_debug_format(&self) -> &'static str {\n        match self {\n            Self::Background => \"backg\",\n            Self::Middle => \"middl\",\n            Self::Foreground => \"foreg\",\n            Self::Tooltip => \"toolt\",\n            Self::Debug => \"debug\",\n        }\n    }\n}\n\n/// An identifier for a paint layer.\n/// Also acts as an identifier for [`crate::Area`]:s.\n#[derive(Clone, Copy, Hash, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct LayerId {\n    pub order: Order,\n    pub id: Id,\n}\n\nimpl LayerId {\n    pub fn new(order: Order, id: Id) -> Self {\n        Self { order, id }\n    }\n\n    pub fn debug() -> Self {\n        Self {\n            order: Order::Debug,\n            id: Id::new(\"debug\"),\n        }\n    }\n\n    pub fn background() -> Self {\n        Self {\n            order: Order::Background,\n            id: Id::new(\"background\"),\n        }\n    }\n\n    #[inline(always)]\n    #[deprecated = \"Use `Memory::allows_interaction` instead\"]\n    pub fn allow_interaction(&self) -> bool {\n        self.order.allow_interaction()\n    }\n\n    /// Short and readable summary\n    pub fn short_debug_format(&self) -> String {\n        format!(\n            \"{} {}\",\n            self.order.short_debug_format(),\n            self.id.short_debug_format()\n        )\n    }\n}\n\nimpl std::fmt::Debug for LayerId {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let Self { order, id } = self;\n        write!(f, \"LayerId {{ {order:?} {id:?} }}\")\n    }\n}\n\n/// A unique identifier of a specific [`Shape`] in a [`PaintList`].\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub struct ShapeIdx(pub usize);\n\n/// A list of [`Shape`]s paired with a clip rectangle.\n#[derive(Clone, Default)]\npub struct PaintList(Vec<ClippedShape>);\n\nimpl PaintList {\n    #[inline(always)]\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    pub fn next_idx(&self) -> ShapeIdx {\n        ShapeIdx(self.0.len())\n    }\n\n    /// Returns the index of the new [`Shape`] that can be used with `PaintList::set`.\n    #[inline(always)]\n    pub fn add(&mut self, clip_rect: Rect, shape: Shape) -> ShapeIdx {\n        let idx = self.next_idx();\n        self.0.push(ClippedShape { clip_rect, shape });\n        idx\n    }\n\n    pub fn extend<I: IntoIterator<Item = Shape>>(&mut self, clip_rect: Rect, shapes: I) {\n        self.0.extend(\n            shapes\n                .into_iter()\n                .map(|shape| ClippedShape { clip_rect, shape }),\n        );\n    }\n\n    /// Modify an existing [`Shape`].\n    ///\n    /// Sometimes you want to paint a frame behind some contents, but don't know how large the frame needs to be\n    /// until the contents have been added, and therefor also painted to the [`PaintList`].\n    ///\n    /// The solution is to allocate a [`Shape`] using `let idx = paint_list.add(cr, Shape::Noop);`\n    /// and then later setting it using `paint_list.set(idx, cr, frame);`.\n    #[inline(always)]\n    pub fn set(&mut self, idx: ShapeIdx, clip_rect: Rect, shape: Shape) {\n        if self.0.len() <= idx.0 {\n            log::warn!(\"Index {} is out of bounds for PaintList\", idx.0);\n            return;\n        }\n\n        self.0[idx.0] = ClippedShape { clip_rect, shape };\n    }\n\n    /// Set the given shape to be empty (a `Shape::Noop`).\n    #[inline(always)]\n    pub fn reset_shape(&mut self, idx: ShapeIdx) {\n        self.0[idx.0].shape = Shape::Noop;\n    }\n\n    /// Mutate the shape at the given index, if any.\n    pub fn mutate_shape(&mut self, idx: ShapeIdx, f: impl FnOnce(&mut ClippedShape)) {\n        self.0.get_mut(idx.0).map(f);\n    }\n\n    /// Transform each [`Shape`] and clip rectangle by this much, in-place\n    pub fn transform(&mut self, transform: TSTransform) {\n        for ClippedShape { clip_rect, shape } in &mut self.0 {\n            *clip_rect = transform.mul_rect(*clip_rect);\n            shape.transform(transform);\n        }\n    }\n\n    /// Transform each [`Shape`] and clip rectangle in range by this much, in-place\n    pub fn transform_range(&mut self, start: ShapeIdx, end: ShapeIdx, transform: TSTransform) {\n        for ClippedShape { clip_rect, shape } in &mut self.0[start.0..end.0] {\n            *clip_rect = transform.mul_rect(*clip_rect);\n            shape.transform(transform);\n        }\n    }\n\n    /// Read-only access to all held shapes.\n    pub fn all_entries(&self) -> impl ExactSizeIterator<Item = &ClippedShape> {\n        self.0.iter()\n    }\n}\n\n/// This is where painted [`Shape`]s end up during a frame.\n#[derive(Clone, Default)]\npub struct GraphicLayers([IdMap<PaintList>; Order::COUNT]);\n\nimpl GraphicLayers {\n    /// Get or insert the [`PaintList`] for the given [`LayerId`].\n    pub fn entry(&mut self, layer_id: LayerId) -> &mut PaintList {\n        self.0[layer_id.order as usize]\n            .entry(layer_id.id)\n            .or_default()\n    }\n\n    /// Get the [`PaintList`] for the given [`LayerId`].\n    pub fn get(&self, layer_id: LayerId) -> Option<&PaintList> {\n        self.0[layer_id.order as usize].get(&layer_id.id)\n    }\n\n    /// Get the [`PaintList`] for the given [`LayerId`].\n    pub fn get_mut(&mut self, layer_id: LayerId) -> Option<&mut PaintList> {\n        self.0[layer_id.order as usize].get_mut(&layer_id.id)\n    }\n\n    pub fn drain(\n        &mut self,\n        area_order: &[LayerId],\n        to_global: &ahash::HashMap<LayerId, TSTransform>,\n    ) -> Vec<ClippedShape> {\n        profiling::function_scope!();\n\n        let mut all_shapes: Vec<_> = Default::default();\n\n        for &order in &Order::ALL {\n            let order_map = &mut self.0[order as usize];\n\n            // If a layer is empty at the start of the frame\n            // then nobody has added to it, and it is old and defunct.\n            // Free it to save memory:\n            order_map.retain(|_, list| !list.is_empty());\n\n            // First do the layers part of area_order:\n            for layer_id in area_order {\n                if layer_id.order == order\n                    && let Some(list) = order_map.get_mut(&layer_id.id)\n                {\n                    if let Some(to_global) = to_global.get(layer_id) {\n                        for clipped_shape in &mut list.0 {\n                            clipped_shape.transform(*to_global);\n                        }\n                    }\n                    all_shapes.append(&mut list.0);\n                }\n            }\n\n            // Also draw areas that are missing in `area_order`:\n            // NOTE: We don't think we end up here in normal situations.\n            // This is just a safety net in case we have some bug somewhere.\n            #[expect(clippy::iter_over_hash_type)]\n            for (id, list) in order_map {\n                let layer_id = LayerId::new(order, *id);\n\n                if let Some(to_global) = to_global.get(&layer_id) {\n                    for clipped_shape in &mut list.0 {\n                        clipped_shape.transform(*to_global);\n                    }\n                }\n\n                all_shapes.append(&mut list.0);\n            }\n        }\n\n        all_shapes\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/layout.rs",
    "content": "use emath::GuiRounding as _;\n\nuse crate::{\n    Align,\n    emath::{Align2, NumExt as _, Pos2, Rect, Vec2, pos2, vec2},\n};\nconst INFINITY: f32 = f32::INFINITY;\n\n// ----------------------------------------------------------------------------\n\n/// This describes the bounds and existing contents of an [`Ui`][`crate::Ui`].\n/// It is what is used and updated by [`Layout`] when adding new widgets.\n#[derive(Clone, Copy, Debug)]\npub(crate) struct Region {\n    /// This is the minimal size of the [`Ui`](crate::Ui).\n    /// When adding new widgets, this will generally expand.\n    ///\n    /// Always finite.\n    ///\n    /// The bounding box of all child widgets, but not necessarily a tight bounding box\n    /// since [`Ui`](crate::Ui) can start with a non-zero `min_rect` size.\n    pub min_rect: Rect,\n\n    /// The maximum size of this [`Ui`](crate::Ui). This is a *soft max*\n    /// meaning new widgets will *try* not to expand beyond it,\n    /// but if they have to, they will.\n    ///\n    /// Text will wrap at `max_rect.right()`.\n    /// Some widgets (like separator lines) will try to fill the full `max_rect` width of the ui.\n    ///\n    /// `max_rect` will always be at least the size of `min_rect`.\n    ///\n    /// If the `max_rect` size is zero, it is a signal that child widgets should be as small as possible.\n    /// If the `max_rect` size is infinite, it is a signal that child widgets should take up as much room as they want.\n    pub max_rect: Rect,\n\n    /// Where the next widget will be put.\n    ///\n    /// One side of this will always be infinite: the direction in which new widgets will be added.\n    /// The opposing side is what is incremented.\n    /// The crossing sides are initialized to `max_rect`.\n    ///\n    /// So one can think of `cursor` as a constraint on the available region.\n    ///\n    /// If something has already been added, this will point to `style.spacing.item_spacing` beyond the latest child.\n    /// The cursor can thus be `style.spacing.item_spacing` pixels outside of the `min_rect`.\n    pub(crate) cursor: Rect,\n}\n\nimpl Region {\n    /// Expand the `min_rect` and `max_rect` of this ui to include a child at the given rect.\n    pub fn expand_to_include_rect(&mut self, rect: Rect) {\n        self.min_rect |= rect;\n        self.max_rect |= rect;\n    }\n\n    /// Ensure we are big enough to contain the given X-coordinate.\n    /// This is sometimes useful to expand a ui to stretch to a certain place.\n    pub fn expand_to_include_x(&mut self, x: f32) {\n        self.min_rect.extend_with_x(x);\n        self.max_rect.extend_with_x(x);\n        self.cursor.extend_with_x(x);\n    }\n\n    /// Ensure we are big enough to contain the given Y-coordinate.\n    /// This is sometimes useful to expand a ui to stretch to a certain place.\n    pub fn expand_to_include_y(&mut self, y: f32) {\n        self.min_rect.extend_with_y(y);\n        self.max_rect.extend_with_y(y);\n        self.cursor.extend_with_y(y);\n    }\n\n    pub fn sanity_check(&self) {\n        debug_assert!(\n            !self.min_rect.any_nan(),\n            \"min rect has Nan: {:?}\",\n            self.min_rect\n        );\n        debug_assert!(\n            !self.max_rect.any_nan(),\n            \"max rect has Nan: {:?}\",\n            self.max_rect\n        );\n        debug_assert!(!self.cursor.any_nan(), \"cursor has Nan: {:?}\", self.cursor);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Layout direction, one of [`LeftToRight`](Direction::LeftToRight), [`RightToLeft`](Direction::RightToLeft), [`TopDown`](Direction::TopDown), [`BottomUp`](Direction::BottomUp).\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum Direction {\n    LeftToRight,\n    RightToLeft,\n    TopDown,\n    BottomUp,\n}\n\nimpl Direction {\n    #[inline(always)]\n    pub fn is_horizontal(self) -> bool {\n        match self {\n            Self::LeftToRight | Self::RightToLeft => true,\n            Self::TopDown | Self::BottomUp => false,\n        }\n    }\n\n    #[inline(always)]\n    pub fn is_vertical(self) -> bool {\n        match self {\n            Self::LeftToRight | Self::RightToLeft => false,\n            Self::TopDown | Self::BottomUp => true,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// The layout of a [`Ui`][`crate::Ui`], e.g. \"vertical & centered\".\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {\n///     ui.label(\"world!\");\n///     ui.label(\"Hello\");\n/// });\n/// # });\n/// ```\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n// #[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Layout {\n    /// Main axis direction\n    pub main_dir: Direction,\n\n    /// If true, wrap around when reading the end of the main direction.\n    /// For instance, for `main_dir == Direction::LeftToRight` this will\n    /// wrap to a new row when we reach the right side of the `max_rect`.\n    pub main_wrap: bool,\n\n    /// How to align things on the main axis.\n    pub main_align: Align,\n\n    /// Justify the main axis?\n    pub main_justify: bool,\n\n    /// How to align things on the cross axis.\n    /// For vertical layouts: put things to left, center or right?\n    /// For horizontal layouts: put things to top, center or bottom?\n    pub cross_align: Align,\n\n    /// Justify the cross axis?\n    /// For vertical layouts justify mean all widgets get maximum width.\n    /// For horizontal layouts justify mean all widgets get maximum height.\n    pub cross_justify: bool,\n}\n\nimpl Default for Layout {\n    fn default() -> Self {\n        // TODO(emilk): Get from `Style` instead.\n        Self::top_down(Align::LEFT) // This is a very euro-centric default.\n    }\n}\n\n/// ## Constructors\nimpl Layout {\n    /// Place elements horizontally, left to right.\n    ///\n    /// The `valign` parameter controls how to align elements vertically.\n    #[inline(always)]\n    pub fn left_to_right(valign: Align) -> Self {\n        Self {\n            main_dir: Direction::LeftToRight,\n            main_wrap: false,\n            main_align: Align::Center, // looks best to e.g. center text within a button\n            main_justify: false,\n            cross_align: valign,\n            cross_justify: false,\n        }\n    }\n\n    /// Place elements horizontally, right to left.\n    ///\n    /// The `valign` parameter controls how to align elements vertically.\n    #[inline(always)]\n    pub fn right_to_left(valign: Align) -> Self {\n        Self {\n            main_dir: Direction::RightToLeft,\n            main_wrap: false,\n            main_align: Align::Center, // looks best to e.g. center text within a button\n            main_justify: false,\n            cross_align: valign,\n            cross_justify: false,\n        }\n    }\n\n    /// Place elements vertically, top to bottom.\n    ///\n    /// Use the provided horizontal alignment.\n    #[inline(always)]\n    pub fn top_down(halign: Align) -> Self {\n        Self {\n            main_dir: Direction::TopDown,\n            main_wrap: false,\n            main_align: Align::Center, // looks best to e.g. center text within a button\n            main_justify: false,\n            cross_align: halign,\n            cross_justify: false,\n        }\n    }\n\n    /// Top-down layout justified so that buttons etc fill the full available width.\n    #[inline(always)]\n    pub fn top_down_justified(halign: Align) -> Self {\n        Self::top_down(halign).with_cross_justify(true)\n    }\n\n    /// Place elements vertically, bottom up.\n    ///\n    /// Use the provided horizontal alignment.\n    #[inline(always)]\n    pub fn bottom_up(halign: Align) -> Self {\n        Self {\n            main_dir: Direction::BottomUp,\n            main_wrap: false,\n            main_align: Align::Center, // looks best to e.g. center text within a button\n            main_justify: false,\n            cross_align: halign,\n            cross_justify: false,\n        }\n    }\n\n    #[inline(always)]\n    pub fn from_main_dir_and_cross_align(main_dir: Direction, cross_align: Align) -> Self {\n        Self {\n            main_dir,\n            main_wrap: false,\n            main_align: Align::Center, // looks best to e.g. center text within a button\n            main_justify: false,\n            cross_align,\n            cross_justify: false,\n        }\n    }\n\n    /// For when you want to add a single widget to a layout, and that widget\n    /// should use up all available space.\n    ///\n    /// Only one widget may be added to the inner `Ui`!\n    #[inline(always)]\n    pub fn centered_and_justified(main_dir: Direction) -> Self {\n        Self {\n            main_dir,\n            main_wrap: false,\n            main_align: Align::Center,\n            main_justify: true,\n            cross_align: Align::Center,\n            cross_justify: true,\n        }\n    }\n\n    /// Wrap widgets when we overflow the main axis?\n    ///\n    /// For instance, for left-to-right layouts, setting this to `true` will\n    /// put widgets on a new row if we would overflow the right side of [`crate::Ui::max_rect`].\n    #[inline(always)]\n    pub fn with_main_wrap(self, main_wrap: bool) -> Self {\n        Self { main_wrap, ..self }\n    }\n\n    /// The alignment to use on the main axis.\n    #[inline(always)]\n    pub fn with_main_align(self, main_align: Align) -> Self {\n        Self { main_align, ..self }\n    }\n\n    /// The alignment to use on the cross axis.\n    ///\n    /// The \"cross\" axis is the one orthogonal to the main axis.\n    /// For instance: in left-to-right layout, the main axis is horizontal and the cross axis is vertical.\n    #[inline(always)]\n    pub fn with_cross_align(self, cross_align: Align) -> Self {\n        Self {\n            cross_align,\n            ..self\n        }\n    }\n\n    /// Justify widgets on the main axis?\n    ///\n    /// Justify here means \"take up all available space\".\n    #[inline(always)]\n    pub fn with_main_justify(self, main_justify: bool) -> Self {\n        Self {\n            main_justify,\n            ..self\n        }\n    }\n\n    /// Justify widgets along the cross axis?\n    ///\n    /// Justify here means \"take up all available space\".\n    ///\n    /// The \"cross\" axis is the one orthogonal to the main axis.\n    /// For instance: in left-to-right layout, the main axis is horizontal and the cross axis is vertical.\n    #[inline(always)]\n    pub fn with_cross_justify(self, cross_justify: bool) -> Self {\n        Self {\n            cross_justify,\n            ..self\n        }\n    }\n}\n\n/// ## Inspectors\nimpl Layout {\n    #[inline(always)]\n    pub fn main_dir(&self) -> Direction {\n        self.main_dir\n    }\n\n    #[inline(always)]\n    pub fn main_wrap(&self) -> bool {\n        self.main_wrap\n    }\n\n    #[inline(always)]\n    pub fn cross_align(&self) -> Align {\n        self.cross_align\n    }\n\n    #[inline(always)]\n    pub fn cross_justify(&self) -> bool {\n        self.cross_justify\n    }\n\n    #[inline(always)]\n    pub fn is_horizontal(&self) -> bool {\n        self.main_dir().is_horizontal()\n    }\n\n    #[inline(always)]\n    pub fn is_vertical(&self) -> bool {\n        self.main_dir().is_vertical()\n    }\n\n    pub fn prefer_right_to_left(&self) -> bool {\n        self.main_dir == Direction::RightToLeft\n            || self.main_dir.is_vertical() && self.cross_align == Align::Max\n    }\n\n    /// e.g. for adjusting the placement of something.\n    /// * in horizontal layout: left or right?\n    /// * in vertical layout: same as [`Self::horizontal_align`].\n    pub fn horizontal_placement(&self) -> Align {\n        match self.main_dir {\n            Direction::LeftToRight => Align::LEFT,\n            Direction::RightToLeft => Align::RIGHT,\n            Direction::TopDown | Direction::BottomUp => self.cross_align,\n        }\n    }\n\n    /// e.g. for when aligning text within a button.\n    pub fn horizontal_align(&self) -> Align {\n        if self.is_horizontal() {\n            self.main_align\n        } else {\n            self.cross_align\n        }\n    }\n\n    /// e.g. for when aligning text within a button.\n    pub fn vertical_align(&self) -> Align {\n        if self.is_vertical() {\n            self.main_align\n        } else {\n            self.cross_align\n        }\n    }\n\n    /// e.g. for when aligning text within a button.\n    fn align2(&self) -> Align2 {\n        Align2([self.horizontal_align(), self.vertical_align()])\n    }\n\n    pub fn horizontal_justify(&self) -> bool {\n        if self.is_horizontal() {\n            self.main_justify\n        } else {\n            self.cross_justify\n        }\n    }\n\n    pub fn vertical_justify(&self) -> bool {\n        if self.is_vertical() {\n            self.main_justify\n        } else {\n            self.cross_justify\n        }\n    }\n}\n\n/// ## Doing layout\nimpl Layout {\n    pub fn align_size_within_rect(&self, size: Vec2, outer: Rect) -> Rect {\n        debug_assert!(size.x >= 0.0 && size.y >= 0.0, \"Negative size: {size:?}\");\n        debug_assert!(!outer.is_negative(), \"Negative outer: {outer:?}\");\n        self.align2().align_size_within_rect(size, outer).round_ui()\n    }\n\n    fn initial_cursor(&self, max_rect: Rect) -> Rect {\n        let mut cursor = max_rect;\n\n        match self.main_dir {\n            Direction::LeftToRight => {\n                cursor.max.x = INFINITY;\n            }\n            Direction::RightToLeft => {\n                cursor.min.x = -INFINITY;\n            }\n            Direction::TopDown => {\n                cursor.max.y = INFINITY;\n            }\n            Direction::BottomUp => {\n                cursor.min.y = -INFINITY;\n            }\n        }\n\n        cursor\n    }\n\n    pub(crate) fn region_from_max_rect(&self, max_rect: Rect) -> Region {\n        debug_assert!(!max_rect.any_nan(), \"max_rect is not NaN: {max_rect:?}\");\n        let mut region = Region {\n            min_rect: Rect::NOTHING, // temporary\n            max_rect,\n            cursor: self.initial_cursor(max_rect),\n        };\n        let seed = self.next_widget_position(&region);\n        region.min_rect = Rect::from_center_size(seed, Vec2::ZERO);\n        region\n    }\n\n    pub(crate) fn available_rect_before_wrap(&self, region: &Region) -> Rect {\n        self.available_from_cursor_max_rect(region.cursor, region.max_rect)\n    }\n\n    /// Amount of space available for a widget.\n    /// For wrapping layouts, this is the maximum (after wrap).\n    pub(crate) fn available_size(&self, r: &Region) -> Vec2 {\n        if self.main_wrap {\n            if self.main_dir.is_horizontal() {\n                vec2(r.max_rect.width(), r.cursor.height())\n            } else {\n                vec2(r.cursor.width(), r.max_rect.height())\n            }\n        } else {\n            self.available_from_cursor_max_rect(r.cursor, r.max_rect)\n                .size()\n        }\n    }\n\n    /// Given the cursor in the region, how much space is available\n    /// for the next widget?\n    fn available_from_cursor_max_rect(&self, cursor: Rect, max_rect: Rect) -> Rect {\n        debug_assert!(!cursor.any_nan(), \"cursor is NaN: {cursor:?}\");\n        debug_assert!(!max_rect.any_nan(), \"max_rect is NaN: {max_rect:?}\");\n\n        // NOTE: in normal top-down layout the cursor has moved below the current max_rect,\n        // but the available shouldn't be negative.\n\n        // ALSO: with wrapping layouts, cursor jumps to new row before expanding max_rect.\n\n        let mut avail = max_rect;\n\n        match self.main_dir {\n            Direction::LeftToRight => {\n                avail.min.x = cursor.min.x;\n                avail.max.x = avail.max.x.max(cursor.min.x);\n                avail.max.x = avail.max.x.max(avail.min.x);\n                avail.max.y = avail.max.y.max(avail.min.y);\n            }\n            Direction::RightToLeft => {\n                avail.max.x = cursor.max.x;\n                avail.min.x = avail.min.x.min(cursor.max.x);\n                avail.min.x = avail.min.x.min(avail.max.x);\n                avail.max.y = avail.max.y.max(avail.min.y);\n            }\n            Direction::TopDown => {\n                avail.min.y = cursor.min.y;\n                avail.max.y = avail.max.y.max(cursor.min.y);\n                avail.max.x = avail.max.x.max(avail.min.x);\n                avail.max.y = avail.max.y.max(avail.min.y);\n            }\n            Direction::BottomUp => {\n                avail.max.y = cursor.max.y;\n                avail.min.y = avail.min.y.min(cursor.max.y);\n                avail.max.x = avail.max.x.max(avail.min.x);\n                avail.min.y = avail.min.y.min(avail.max.y);\n            }\n        }\n\n        // We can use the cursor to restrict the available region.\n        // For instance, we use this to restrict the available space of a parent Ui\n        // after adding a panel to it.\n        // We also use it for wrapping layouts.\n        avail = avail.intersect(cursor);\n\n        // Make sure it isn't negative:\n        if avail.max.x < avail.min.x {\n            let x = 0.5 * (avail.min.x + avail.max.x);\n            avail.min.x = x;\n            avail.max.x = x;\n        }\n        if avail.max.y < avail.min.y {\n            let y = 0.5 * (avail.min.y + avail.max.y);\n            avail.min.y = y;\n            avail.max.y = y;\n        }\n\n        debug_assert!(!avail.any_nan(), \"avail is NaN: {avail:?}\");\n\n        avail\n    }\n\n    /// Returns where to put the next widget that is of the given size.\n    /// The returned `frame_rect` [`Rect`] will always be justified along the cross axis.\n    /// This is what you then pass to `advance_after_rects`.\n    /// Use `justify_and_align` to get the inner `widget_rect`.\n    pub(crate) fn next_frame(&self, region: &Region, child_size: Vec2, spacing: Vec2) -> Rect {\n        region.sanity_check();\n        debug_assert!(\n            child_size.x >= 0.0 && child_size.y >= 0.0,\n            \"Negative size: {child_size:?}\"\n        );\n\n        if self.main_wrap {\n            let available_size = self.available_rect_before_wrap(region).size();\n\n            let Region {\n                mut cursor,\n                mut max_rect,\n                min_rect,\n            } = *region;\n\n            match self.main_dir {\n                Direction::LeftToRight => {\n                    if available_size.x < child_size.x && max_rect.left() < cursor.left() {\n                        // New row\n                        let new_row_height = cursor.height().max(child_size.y);\n                        // let new_top = cursor.bottom() + spacing.y;\n                        let new_top = min_rect.bottom() + spacing.y; // tighter packing\n                        cursor = Rect::from_min_max(\n                            pos2(max_rect.left(), new_top),\n                            pos2(INFINITY, new_top + new_row_height),\n                        );\n                        max_rect.max.y = max_rect.max.y.max(cursor.max.y);\n                    }\n                }\n                Direction::RightToLeft => {\n                    if available_size.x < child_size.x && cursor.right() < max_rect.right() {\n                        // New row\n                        let new_row_height = cursor.height().max(child_size.y);\n                        // let new_top = cursor.bottom() + spacing.y;\n                        let new_top = min_rect.bottom() + spacing.y; // tighter packing\n                        cursor = Rect::from_min_max(\n                            pos2(-INFINITY, new_top),\n                            pos2(max_rect.right(), new_top + new_row_height),\n                        );\n                        max_rect.max.y = max_rect.max.y.max(cursor.max.y);\n                    }\n                }\n                Direction::TopDown => {\n                    if available_size.y < child_size.y && max_rect.top() < cursor.top() {\n                        // New column\n                        let new_col_width = cursor.width().max(child_size.x);\n                        cursor = Rect::from_min_max(\n                            pos2(cursor.right() + spacing.x, max_rect.top()),\n                            pos2(cursor.right() + spacing.x + new_col_width, INFINITY),\n                        );\n                        max_rect.max.x = max_rect.max.x.max(cursor.max.x);\n                    }\n                }\n                Direction::BottomUp => {\n                    if available_size.y < child_size.y && cursor.bottom() < max_rect.bottom() {\n                        // New column\n                        let new_col_width = cursor.width().max(child_size.x);\n                        cursor = Rect::from_min_max(\n                            pos2(cursor.right() + spacing.x, -INFINITY),\n                            pos2(\n                                cursor.right() + spacing.x + new_col_width,\n                                max_rect.bottom(),\n                            ),\n                        );\n                        max_rect.max.x = max_rect.max.x.max(cursor.max.x);\n                    }\n                }\n            }\n\n            // Use the new cursor:\n            let region = Region {\n                min_rect,\n                max_rect,\n                cursor,\n            };\n\n            self.next_frame_ignore_wrap(&region, child_size)\n        } else {\n            self.next_frame_ignore_wrap(region, child_size)\n        }\n    }\n\n    fn next_frame_ignore_wrap(&self, region: &Region, child_size: Vec2) -> Rect {\n        region.sanity_check();\n        debug_assert!(\n            child_size.x >= 0.0 && child_size.y >= 0.0,\n            \"Negative size: {child_size:?}\"\n        );\n\n        let available_rect = self.available_rect_before_wrap(region);\n\n        let mut frame_size = child_size;\n\n        if (self.is_vertical() && self.horizontal_align() == Align::Center)\n            || self.horizontal_justify()\n        {\n            frame_size.x = frame_size.x.max(available_rect.width()); // fill full width\n        }\n        if (self.is_horizontal() && self.vertical_align() == Align::Center)\n            || self.vertical_justify()\n        {\n            frame_size.y = frame_size.y.max(available_rect.height()); // fill full height\n        }\n\n        let align2 = match self.main_dir {\n            Direction::LeftToRight => Align2([Align::LEFT, self.vertical_align()]),\n            Direction::RightToLeft => Align2([Align::RIGHT, self.vertical_align()]),\n            Direction::TopDown => Align2([self.horizontal_align(), Align::TOP]),\n            Direction::BottomUp => Align2([self.horizontal_align(), Align::BOTTOM]),\n        };\n\n        let mut frame_rect = align2.align_size_within_rect(frame_size, available_rect);\n\n        if self.is_horizontal() && frame_rect.top() < region.cursor.top() {\n            // for horizontal layouts we always want to expand down,\n            // or we will overlap the row above.\n            // This is a bit hacky. Maybe we should do it for vertical layouts too.\n            frame_rect = frame_rect.translate(Vec2::Y * (region.cursor.top() - frame_rect.top()));\n        }\n\n        debug_assert!(!frame_rect.any_nan(), \"frame_rect is NaN: {frame_rect:?}\");\n        debug_assert!(!frame_rect.is_negative(), \"frame_rect is negative\");\n\n        frame_rect.round_ui()\n    }\n\n    /// Apply justify (fill width/height) and/or alignment after calling `next_space`.\n    pub(crate) fn justify_and_align(&self, frame: Rect, mut child_size: Vec2) -> Rect {\n        debug_assert!(\n            child_size.x >= 0.0 && child_size.y >= 0.0,\n            \"Negative size: {child_size:?}\"\n        );\n        debug_assert!(!frame.is_negative(), \"frame is negative\");\n\n        if self.horizontal_justify() {\n            child_size.x = child_size.x.at_least(frame.width()); // fill full width\n        }\n        if self.vertical_justify() {\n            child_size.y = child_size.y.at_least(frame.height()); // fill full height\n        }\n        self.align_size_within_rect(child_size, frame)\n    }\n\n    pub(crate) fn next_widget_space_ignore_wrap_justify(\n        &self,\n        region: &Region,\n        size: Vec2,\n    ) -> Rect {\n        let frame = self.next_frame_ignore_wrap(region, size);\n        let rect = self.align_size_within_rect(size, frame);\n        debug_assert!(!rect.any_nan(), \"rect is NaN: {rect:?}\");\n        debug_assert!(!rect.is_negative(), \"rect is negative: {rect:?}\");\n        rect\n    }\n\n    /// Where would the next tiny widget be centered?\n    pub(crate) fn next_widget_position(&self, region: &Region) -> Pos2 {\n        self.next_widget_space_ignore_wrap_justify(region, Vec2::ZERO)\n            .center()\n    }\n\n    /// Advance the cursor by this many points, and allocate in region.\n    pub(crate) fn advance_cursor(&self, region: &mut Region, amount: f32) {\n        match self.main_dir {\n            Direction::LeftToRight => {\n                region.cursor.min.x += amount;\n                region.expand_to_include_x(region.cursor.min.x);\n            }\n            Direction::RightToLeft => {\n                region.cursor.max.x -= amount;\n                region.expand_to_include_x(region.cursor.max.x);\n            }\n            Direction::TopDown => {\n                region.cursor.min.y += amount;\n                region.expand_to_include_y(region.cursor.min.y);\n            }\n            Direction::BottomUp => {\n                region.cursor.max.y -= amount;\n                region.expand_to_include_y(region.cursor.max.y);\n            }\n        }\n    }\n\n    /// Advance cursor after a widget was added to a specific rectangle.\n    ///\n    /// * `frame_rect`: the frame inside which a widget was e.g. centered\n    /// * `widget_rect`: the actual rect used by the widget\n    pub(crate) fn advance_after_rects(\n        &self,\n        cursor: &mut Rect,\n        frame_rect: Rect,\n        widget_rect: Rect,\n        item_spacing: Vec2,\n    ) {\n        debug_assert!(!cursor.any_nan(), \"cursor is NaN: {cursor:?}\");\n        if self.main_wrap {\n            if cursor.intersects(frame_rect.shrink(1.0)) {\n                // make row/column larger if necessary\n                *cursor |= frame_rect;\n            } else {\n                // this is a new row or column. We temporarily use NAN for what will be filled in later.\n                match self.main_dir {\n                    Direction::LeftToRight => {\n                        *cursor = Rect::from_min_max(\n                            pos2(f32::NAN, frame_rect.min.y),\n                            pos2(INFINITY, frame_rect.max.y),\n                        );\n                    }\n                    Direction::RightToLeft => {\n                        *cursor = Rect::from_min_max(\n                            pos2(-INFINITY, frame_rect.min.y),\n                            pos2(f32::NAN, frame_rect.max.y),\n                        );\n                    }\n                    Direction::TopDown => {\n                        *cursor = Rect::from_min_max(\n                            pos2(frame_rect.min.x, f32::NAN),\n                            pos2(frame_rect.max.x, INFINITY),\n                        );\n                    }\n                    Direction::BottomUp => {\n                        *cursor = Rect::from_min_max(\n                            pos2(frame_rect.min.x, -INFINITY),\n                            pos2(frame_rect.max.x, f32::NAN),\n                        );\n                    }\n                }\n            }\n        } else {\n            // Make sure we also expand where we consider adding things (the cursor):\n            if self.is_horizontal() {\n                cursor.min.y = cursor.min.y.min(frame_rect.min.y);\n                cursor.max.y = cursor.max.y.max(frame_rect.max.y);\n            } else {\n                cursor.min.x = cursor.min.x.min(frame_rect.min.x);\n                cursor.max.x = cursor.max.x.max(frame_rect.max.x);\n            }\n        }\n\n        match self.main_dir {\n            Direction::LeftToRight => {\n                cursor.min.x = widget_rect.max.x + item_spacing.x;\n            }\n            Direction::RightToLeft => {\n                cursor.max.x = widget_rect.min.x - item_spacing.x;\n            }\n            Direction::TopDown => {\n                cursor.min.y = widget_rect.max.y + item_spacing.y;\n            }\n            Direction::BottomUp => {\n                cursor.max.y = widget_rect.min.y - item_spacing.y;\n            }\n        }\n    }\n\n    /// Move to the next row in a wrapping layout.\n    /// Otherwise does nothing.\n    pub(crate) fn end_row(&self, region: &mut Region, spacing: Vec2) {\n        if self.main_wrap {\n            match self.main_dir {\n                Direction::LeftToRight => {\n                    let new_top = region.cursor.bottom() + spacing.y;\n                    region.cursor = Rect::from_min_max(\n                        pos2(region.max_rect.left(), new_top),\n                        pos2(INFINITY, new_top + region.cursor.height()),\n                    );\n                }\n                Direction::RightToLeft => {\n                    let new_top = region.cursor.bottom() + spacing.y;\n                    region.cursor = Rect::from_min_max(\n                        pos2(-INFINITY, new_top),\n                        pos2(region.max_rect.right(), new_top + region.cursor.height()),\n                    );\n                }\n                Direction::TopDown | Direction::BottomUp => {}\n            }\n        }\n    }\n\n    /// Set row height in horizontal wrapping layout.\n    pub(crate) fn set_row_height(&self, region: &mut Region, height: f32) {\n        if self.main_wrap && self.is_horizontal() {\n            region.cursor.max.y = region.cursor.min.y + height;\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// ## Debug stuff\nimpl Layout {\n    /// Shows where the next widget is going to be placed\n    #[cfg(debug_assertions)]\n    pub(crate) fn paint_text_at_cursor(\n        &self,\n        painter: &crate::Painter,\n        region: &Region,\n        stroke: epaint::Stroke,\n        text: impl ToString,\n    ) {\n        let cursor = region.cursor;\n        let next_pos = self.next_widget_position(region);\n\n        let l = 64.0;\n\n        let align = match self.main_dir {\n            Direction::LeftToRight => {\n                painter.line_segment([cursor.left_top(), cursor.left_bottom()], stroke);\n                painter.arrow(next_pos, vec2(l, 0.0), stroke);\n                Align2([Align::LEFT, self.vertical_align()])\n            }\n            Direction::RightToLeft => {\n                painter.line_segment([cursor.right_top(), cursor.right_bottom()], stroke);\n                painter.arrow(next_pos, vec2(-l, 0.0), stroke);\n                Align2([Align::RIGHT, self.vertical_align()])\n            }\n            Direction::TopDown => {\n                painter.line_segment([cursor.left_top(), cursor.right_top()], stroke);\n                painter.arrow(next_pos, vec2(0.0, l), stroke);\n                Align2([self.horizontal_align(), Align::TOP])\n            }\n            Direction::BottomUp => {\n                painter.line_segment([cursor.left_bottom(), cursor.right_bottom()], stroke);\n                painter.arrow(next_pos, vec2(0.0, -l), stroke);\n                Align2([self.horizontal_align(), Align::BOTTOM])\n            }\n        };\n\n        painter.debug_text(next_pos, align, stroke.color, text);\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/lib.rs",
    "content": "//! `egui`:  an easy-to-use GUI in pure Rust!\n//!\n//! Try the live web demo: <https://www.egui.rs/#demo>. Read more about egui at <https://github.com/emilk/egui>.\n//!\n//! `egui` is in heavy development, with each new version having breaking changes.\n//! You need to have rust 1.92.0 or later to use `egui`.\n//!\n//! To quickly get started with egui, you can take a look at [`eframe_template`](https://github.com/emilk/eframe_template)\n//! which uses [`eframe`](https://docs.rs/eframe).\n//!\n//! To create a GUI using egui you first need a [`Context`] (by convention referred to by `ctx`).\n//! Then you add a [`Window`] or a [`Panel`] to get a [`Ui`], which is what you'll be using to add all the buttons and labels that you need.\n//!\n//!\n//! ## Feature flags\n#![cfg_attr(feature = \"document-features\", doc = document_features::document_features!())]\n//!\n//!\n//! # Using egui\n//!\n//! To see what is possible to build with egui you can check out the online demo at <https://www.egui.rs/#demo>.\n//!\n//! If you like the \"learning by doing\" approach, clone <https://github.com/emilk/eframe_template> and get started using egui right away.\n//!\n//! ### A simple example\n//!\n//! Here is a simple counter that can be incremented and decremented using two buttons:\n//! ```\n//! fn ui_counter(ui: &mut egui::Ui, counter: &mut i32) {\n//!     // Put the buttons and label on the same row:\n//!     ui.horizontal(|ui| {\n//!         if ui.button(\"−\").clicked() {\n//!             *counter -= 1;\n//!         }\n//!         ui.label(counter.to_string());\n//!         if ui.button(\"+\").clicked() {\n//!             *counter += 1;\n//!         }\n//!     });\n//! }\n//! ```\n//!\n//! In some GUI frameworks this would require defining multiple types and functions with callbacks or message handlers,\n//! but thanks to `egui` being immediate mode everything is one self-contained function!\n//!\n//! ### Quick start\n//!\n//! ```\n//! # egui::__run_test_ui(|ui| {\n//! # let mut my_string = String::new();\n//! # let mut my_boolean = true;\n//! # let mut my_f32 = 42.0;\n//! ui.label(\"This is a label\");\n//! ui.hyperlink(\"https://github.com/emilk/egui\");\n//! ui.text_edit_singleline(&mut my_string);\n//! if ui.button(\"Click me\").clicked() { }\n//! ui.add(egui::Slider::new(&mut my_f32, 0.0..=100.0));\n//! ui.add(egui::DragValue::new(&mut my_f32));\n//!\n//! ui.checkbox(&mut my_boolean, \"Checkbox\");\n//!\n//! #[derive(PartialEq)]\n//! enum Enum { First, Second, Third }\n//! # let mut my_enum = Enum::First;\n//! ui.horizontal(|ui| {\n//!     ui.radio_value(&mut my_enum, Enum::First, \"First\");\n//!     ui.radio_value(&mut my_enum, Enum::Second, \"Second\");\n//!     ui.radio_value(&mut my_enum, Enum::Third, \"Third\");\n//! });\n//!\n//! ui.separator();\n//!\n//! # let my_image = egui::TextureId::default();\n//! ui.image((my_image, egui::Vec2::new(640.0, 480.0)));\n//!\n//! ui.collapsing(\"Click to see what is hidden!\", |ui| {\n//!     ui.label(\"Not much, as it turns out\");\n//! });\n//! # });\n//! ```\n//!\n//! ## Viewports\n//! Some egui backends support multiple _viewports_, which is what egui calls the native OS windows it resides in.\n//! See [`crate::viewport`] for more information.\n//!\n//! ## Coordinate system\n//! The left-top corner of the screen is `(0.0, 0.0)`,\n//! with X increasing to the right and Y increasing downwards.\n//!\n//! `egui` uses logical _points_ as its coordinate system.\n//! Those related to physical _pixels_ by the `pixels_per_point` scale factor.\n//! For example, a high-dpi screen can have `pixels_per_point = 2.0`,\n//! meaning there are two physical screen pixels for each logical point.\n//!\n//! Angles are in radians, and are measured clockwise from the X-axis, which has angle=0.\n//!\n//! # Integrating with egui\n//!\n//! Most likely you are using an existing `egui` backend/integration such as [`eframe`](https://docs.rs/eframe), [`bevy_egui`](https://docs.rs/bevy_egui),\n//! or [`egui-miniquad`](https://github.com/not-fl3/egui-miniquad),\n//! but if you want to integrate `egui` into a new game engine or graphics backend, this is the section for you.\n//!\n//! You need to collect [`RawInput`] and handle [`FullOutput`]. The basic structure is this:\n//!\n//! ``` no_run\n//! # fn handle_platform_output(_: egui::PlatformOutput) {}\n//! # fn gather_input() -> egui::RawInput { egui::RawInput::default() }\n//! # fn paint(textures_delta: egui::TexturesDelta, _: Vec<egui::ClippedPrimitive>) {}\n//! let mut ctx = egui::Context::default();\n//!\n//! // Game loop:\n//! loop {\n//!     let raw_input: egui::RawInput = gather_input();\n//!\n//!     let full_output = ctx.run(raw_input, |ctx| {\n//!         egui::CentralPanel::default().show(&ctx, |ui| {\n//!             ui.label(\"Hello world!\");\n//!             if ui.button(\"Click me\").clicked() {\n//!                 // take some action here\n//!             }\n//!         });\n//!     });\n//!     handle_platform_output(full_output.platform_output);\n//!     let clipped_primitives = ctx.tessellate(full_output.shapes, full_output.pixels_per_point);\n//!     paint(full_output.textures_delta, clipped_primitives);\n//! }\n//! ```\n//!\n//! For a reference OpenGL renderer, see [the `egui_glow` painter](https://github.com/emilk/egui/blob/main/crates/egui_glow/src/painter.rs).\n//!\n//!\n//! ### Debugging your renderer\n//!\n//! #### Things look jagged\n//!\n//! * Turn off backface culling.\n//!\n//! #### My text is blurry\n//!\n//! * Make sure you set the proper `pixels_per_point` in the input to egui.\n//! * Make sure the texture sampler is not off by half a pixel. Try nearest-neighbor sampler to check.\n//!\n//! #### My windows are too transparent or too dark\n//!\n//! * egui uses premultiplied alpha, so make sure your blending function is `(ONE, ONE_MINUS_SRC_ALPHA)`.\n//! * Make sure your texture sampler is clamped (`GL_CLAMP_TO_EDGE`).\n//! * egui prefers gamma color spaces for all blending so:\n//!   * Do NOT use an sRGBA-aware texture (NOT `GL_SRGB8_ALPHA8`).\n//!   * Multiply texture and vertex colors in gamma space\n//!   * Turn OFF sRGBA/gamma framebuffer (NO `GL_FRAMEBUFFER_SRGB`).\n//!\n//!\n//! # Understanding immediate mode\n//!\n//! `egui` is an immediate mode GUI library.\n//!\n//! Immediate mode has its roots in gaming, where everything on the screen is painted at the\n//! display refresh rate, i.e. at 60+ frames per second.\n//! In immediate mode GUIs, the entire interface is laid out and painted at the same high rate.\n//! This makes immediate mode GUIs especially well suited for highly interactive applications.\n//!\n//! It is useful to fully grok what \"immediate mode\" implies.\n//!\n//! Here is an example to illustrate it:\n//!\n//! ```\n//! # egui::__run_test_ui(|ui| {\n//! if ui.button(\"click me\").clicked() {\n//!     take_action()\n//! }\n//! # });\n//! # fn take_action() {}\n//! ```\n//!\n//! This code is being executed each frame at maybe 60 frames per second.\n//! Each frame egui does these things:\n//!\n//! * lays out the letters `click me` in order to figure out the size of the button\n//! * decides where on screen to place the button\n//! * check if the mouse is hovering or clicking that location\n//! * choose button colors based on if it is being hovered or clicked\n//! * add a [`Shape::Rect`] and [`Shape::Text`] to the list of shapes to be painted later this frame\n//! * return a [`Response`] with the [`clicked`](`Response::clicked`) member so the user can check for interactions\n//!\n//! There is no button being created and stored somewhere.\n//! The only output of this call is some colored shapes, and a [`Response`].\n//!\n//! Similarly, consider this code:\n//!\n//! ```\n//! # egui::__run_test_ui(|ui| {\n//! # let mut value: f32 = 0.0;\n//! ui.add(egui::Slider::new(&mut value, 0.0..=100.0).text(\"My value\"));\n//! # });\n//! ```\n//!\n//! Here egui will read `value` (an `f32`) to display the slider, then look if the mouse is dragging the slider and if so change the `value`.\n//! Note that `egui` does not store the slider value for you - it only displays the current value, and changes it\n//! by how much the slider has been dragged in the previous few milliseconds.\n//! This means it is responsibility of the egui user to store the state (`value`) so that it persists between frames.\n//!\n//! It can be useful to read the code for the toggle switch example widget to get a better understanding\n//! of how egui works: <https://github.com/emilk/egui/blob/main/crates/egui_demo_lib/src/demo/toggle_switch.rs>.\n//!\n//! Read more about the pros and cons of immediate mode at <https://github.com/emilk/egui#why-immediate-mode>.\n//!\n//! ## Multi-pass immediate mode\n//! By default, egui usually only does one pass for each rendered frame.\n//! However, egui supports multi-pass immediate mode.\n//! Another pass can be requested with [`Context::request_discard`].\n//!\n//! This is used by some widgets to cover up \"first-frame jitters\".\n//! For instance, the [`Grid`] needs to know the width of all columns before it can properly place the widgets.\n//! But it cannot know the width of widgets to come.\n//! So it stores the max widths of previous frames and uses that.\n//! This means the first time a `Grid` is shown it will _guess_ the widths of the columns, and will usually guess wrong.\n//! This means the contents of the grid will be wrong for one frame, before settling to the correct places.\n//! Therefore `Grid` calls [`Context::request_discard`] when it is first shown, so the wrong placement is never\n//! visible to the end user.\n//!\n//! This is an example of a form of multi-pass immediate mode, where earlier passes are used for sizing,\n//! and later passes for layout.\n//!\n//! See [`Context::request_discard`] and [`Options::max_passes`] for more.\n//!\n//! # Misc\n//!\n//! ## How widgets works\n//!\n//! ```\n//! # egui::__run_test_ui(|ui| {\n//! if ui.button(\"click me\").clicked() { take_action() }\n//! # });\n//! # fn take_action() {}\n//! ```\n//!\n//! is short for\n//!\n//! ```\n//! # egui::__run_test_ui(|ui| {\n//! let button = egui::Button::new(\"click me\");\n//! if ui.add(button).clicked() { take_action() }\n//! # });\n//! # fn take_action() {}\n//! ```\n//!\n//! which is short for\n//!\n//! ```\n//! # use egui::Widget;\n//! # egui::__run_test_ui(|ui| {\n//! let button = egui::Button::new(\"click me\");\n//! let response = button.ui(ui);\n//! if response.clicked() { take_action() }\n//! # });\n//! # fn take_action() {}\n//! ```\n//!\n//! [`Button`] uses the builder pattern to create the data required to show it. The [`Button`] is then discarded.\n//!\n//! [`Button`] implements `trait` [`Widget`], which looks like this:\n//! ```\n//! # use egui::*;\n//! pub trait Widget {\n//!     /// Allocate space, interact, paint, and return a [`Response`].\n//!     fn ui(self, ui: &mut Ui) -> Response;\n//! }\n//! ```\n//!\n//!\n//! ## Widget interaction\n//! Each widget has a [`Sense`], which defines whether or not the widget\n//! is sensitive to clicking and/or drags.\n//!\n//! For instance, a [`Button`] only has a [`Sense::click`] (by default).\n//! This means if you drag a button it will not respond with [`Response::dragged`].\n//! Instead, the drag will continue through the button to the first\n//! widget behind it that is sensitive to dragging, which for instance could be\n//! a [`ScrollArea`]. This lets you scroll by dragging a scroll area (important\n//! on touch screens), just as long as you don't drag on a widget that is sensitive\n//! to drags (e.g. a [`Slider`]).\n//!\n//! When widgets overlap it is the last added one\n//! that is considered to be on top and which will get input priority.\n//!\n//! The widget interaction logic is run at the _start_ of each frame,\n//! based on the output from the previous frame.\n//! This means that when a new widget shows up you cannot click it in the same\n//! frame (i.e. in the same fraction of a second), but unless the user\n//! is spider-man, they wouldn't be fast enough to do so anyways.\n//!\n//! By running the interaction code early, egui can actually\n//! tell you if a widget is being interacted with _before_ you add it,\n//! as long as you know its [`Id`] before-hand (e.g. using [`Ui::next_auto_id`]),\n//! by calling [`Context::read_response`].\n//! This can be useful in some circumstances in order to style a widget,\n//! or to respond to interactions before adding the widget\n//! (perhaps on top of other widgets).\n//!\n//!\n//! ## Auto-sizing panels and windows\n//! In egui, all panels and windows auto-shrink to fit the content.\n//! If the window or panel is also resizable, this can lead to a weird behavior\n//! where you can drag the edge of the panel/window to make it larger, and\n//! when you release the panel/window shrinks again.\n//! This is an artifact of immediate mode, and here are some alternatives on how to avoid it:\n//!\n//! 1. Turn off resizing with [`Window::resizable`], [`Panel::resizable`].\n//! 2. Wrap your panel contents in a [`ScrollArea`], or use [`Window::vscroll`] and [`Window::hscroll`].\n//! 3. Use a justified layout:\n//!\n//! ```\n//! # egui::__run_test_ui(|ui| {\n//! ui.with_layout(egui::Layout::top_down_justified(egui::Align::Center), |ui| {\n//!     ui.button(\"I am becoming wider as needed\");\n//! });\n//! # });\n//! ```\n//!\n//! 4. Fill in extra space with emptiness:\n//!\n//! ```\n//! # egui::__run_test_ui(|ui| {\n//! ui.allocate_space(ui.available_size()); // put this LAST in your panel/window code\n//! # });\n//! ```\n//!\n//! ## Sizes\n//! You can control the size of widgets using [`Ui::add_sized`].\n//!\n//! ```\n//! # egui::__run_test_ui(|ui| {\n//! # let mut my_value = 0.0_f32;\n//! ui.add_sized([40.0, 20.0], egui::DragValue::new(&mut my_value));\n//! # });\n//! ```\n//!\n//! ## Code snippets\n//!\n//! ```\n//! # use egui::TextWrapMode;\n//! # egui::__run_test_ui(|ui| {\n//! # let mut some_bool = true;\n//! // Miscellaneous tips and tricks\n//!\n//! ui.horizontal_wrapped(|ui| {\n//!     ui.spacing_mut().item_spacing.x = 0.0; // remove spacing between widgets\n//!     // `radio_value` also works for enums, integers, and more.\n//!     ui.radio_value(&mut some_bool, false, \"Off\");\n//!     ui.radio_value(&mut some_bool, true, \"On\");\n//! });\n//!\n//! ui.group(|ui| {\n//!     ui.label(\"Within a frame\");\n//!     ui.set_min_height(200.0);\n//! });\n//!\n//! // A `scope` creates a temporary [`Ui`] in which you can change settings:\n//! ui.scope(|ui| {\n//!     ui.visuals_mut().override_text_color = Some(egui::Color32::RED);\n//!     ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);\n//!     ui.style_mut().wrap_mode = Some(TextWrapMode::Truncate);\n//!\n//!     ui.label(\"This text will be red, monospace, and won't wrap to a new line\");\n//! }); // the temporary settings are reverted here\n//! # });\n//! ```\n//!\n//! ## Installing additional fonts\n//! The default egui fonts only support latin and cryllic characters, and some emojis.\n//! To use egui with e.g. asian characters you need to install your own font (`.ttf` or `.otf`) using [`Context::set_fonts`].\n//!\n//! ## Instrumentation\n//! This crate supports using the [profiling](https://crates.io/crates/profiling) crate for instrumentation.\n//! You can enable features on the profiling crates in your application to add instrumentation for all\n//! crates that support it, including egui. See the profiling crate docs for more information.\n//! ```toml\n//! [dependencies]\n//! profiling = \"1.0\"\n//! [features]\n//! profile-with-puffin = [\"profiling/profile-with-puffin\"]\n//! ```\n//!\n//! ## Custom allocator\n//! egui apps can run significantly (~20%) faster by using a custom allocator, like [mimalloc](https://crates.io/crates/mimalloc) or [talc](https://crates.io/crates/talc).\n//!\n\n#![expect(clippy::float_cmp)]\n#![expect(clippy::manual_range_contains)]\n\nmod animation_manager;\nmod atomics;\npub mod cache;\npub mod containers;\nmod context;\nmod data;\npub mod debug_text;\nmod drag_and_drop;\npub(crate) mod grid;\npub mod gui_zoom;\nmod hit_test;\nmod id;\nmod input_state;\nmod interaction;\npub mod introspection;\npub mod layers;\nmod layout;\npub mod load;\nmod memory;\n#[deprecated = \"Use `egui::containers::menu` instead\"]\npub mod menu;\npub mod os;\nmod painter;\nmod pass_state;\npub(crate) mod placer;\npub mod plugin;\npub mod response;\nmod sense;\npub mod style;\npub mod text_selection;\nmod ui;\nmod ui_builder;\nmod ui_stack;\npub mod util;\npub mod viewport;\nmod widget_rect;\npub mod widget_style;\npub mod widget_text;\npub mod widgets;\n\n#[cfg(feature = \"callstack\")]\n#[cfg(debug_assertions)]\nmod callstack;\n\npub use accesskit;\n\n#[deprecated = \"Use the ahash crate directly.\"]\npub use ahash;\n\npub use epaint;\npub use epaint::ecolor;\npub use epaint::emath;\n\n#[cfg(feature = \"color-hex\")]\npub use ecolor::hex_color;\npub use ecolor::{Color32, Rgba};\npub use emath::{\n    Align, Align2, NumExt, Pos2, Rangef, Rect, RectAlign, Vec2, Vec2b, lerp, pos2, remap,\n    remap_clamp, vec2,\n};\npub use epaint::{\n    ClippedPrimitive, ColorImage, CornerRadius, ImageData, Margin, Mesh, PaintCallback,\n    PaintCallbackInfo, Shadow, Shape, Stroke, StrokeKind, TextureHandle, TextureId, mutex,\n    text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},\n    textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta},\n};\n\npub mod text {\n    pub use crate::text_selection::CCursorRange;\n    pub use epaint::text::{\n        FontData, FontDefinitions, FontFamily, Fonts, Galley, LayoutJob, LayoutSection, TAB_SIZE,\n        TextFormat, TextWrapping, cursor::CCursor,\n    };\n}\n\npub use self::{\n    atomics::*,\n    containers::{menu::MenuBar, *},\n    context::{Context, RepaintCause, RequestRepaintInfo},\n    data::{\n        Key, UserData,\n        input::*,\n        output::{\n            self, CursorIcon, FullOutput, OpenUrl, OutputCommand, PlatformOutput,\n            UserAttentionType, WidgetInfo,\n        },\n    },\n    drag_and_drop::DragAndDrop,\n    epaint::text::TextWrapMode,\n    grid::Grid,\n    id::{Id, IdMap},\n    input_state::{InputOptions, InputState, MultiTouchInfo, PointerState, SurrenderFocusOn},\n    layers::{LayerId, Order},\n    layout::*,\n    load::SizeHint,\n    memory::{FocusDirection, Memory, Options, Theme, ThemePreference},\n    painter::Painter,\n    plugin::Plugin,\n    response::{InnerResponse, Response},\n    sense::Sense,\n    style::{FontSelection, Spacing, Style, TextStyle, Visuals},\n    text::{Galley, TextFormat},\n    ui::Ui,\n    ui_builder::UiBuilder,\n    ui_stack::*,\n    viewport::*,\n    widget_rect::{InteractOptions, WidgetRect, WidgetRects},\n    widget_text::{RichText, WidgetText},\n    widgets::*,\n};\n\n#[deprecated = \"Renamed to CornerRadius\"]\npub type Rounding = CornerRadius;\n\n// ----------------------------------------------------------------------------\n\n/// Helper function that adds a label when compiling with debug assertions enabled.\npub fn warn_if_debug_build(ui: &mut crate::Ui) {\n    if cfg!(debug_assertions) {\n        ui.label(\n            RichText::new(\"⚠ Debug build ⚠\")\n                .small()\n                .color(ui.visuals().warn_fg_color),\n        )\n        .on_hover_text(\"egui was compiled with debug assertions enabled.\");\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Include an image in the binary.\n///\n/// This is a wrapper over `include_bytes!`, and behaves in the same way.\n///\n/// It produces an [`ImageSource`] which can be used directly in [`Ui::image`] or [`Image::new`]:\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// ui.image(egui::include_image!(\"../assets/ferris.png\"));\n/// ui.add(\n///     egui::Image::new(egui::include_image!(\"../assets/ferris.png\"))\n///         .max_width(200.0)\n///         .corner_radius(10),\n/// );\n///\n/// let image_source: egui::ImageSource = egui::include_image!(\"../assets/ferris.png\");\n/// assert_eq!(image_source.uri(), Some(\"bytes://../assets/ferris.png\"));\n/// # });\n/// ```\n#[macro_export]\nmacro_rules! include_image {\n    ($path:expr $(,)?) => {\n        $crate::ImageSource::Bytes {\n            uri: ::std::borrow::Cow::Borrowed(concat!(\"bytes://\", $path)),\n            bytes: $crate::load::Bytes::Static(include_bytes!($path)),\n        }\n    };\n}\n\n/// Create a [`Hyperlink`] to the current [`file!()`] (and line) on Github\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// ui.add(egui::github_link_file_line!(\"https://github.com/YOUR/PROJECT/blob/main/\", \"(source code)\"));\n/// # });\n/// ```\n#[macro_export]\nmacro_rules! github_link_file_line {\n    ($github_url: expr, $label: expr) => {{\n        let url = format!(\"{}{}#L{}\", $github_url, file!(), line!());\n        $crate::Hyperlink::from_label_and_url($label, url)\n    }};\n}\n\n/// Create a [`Hyperlink`] to the current [`file!()`] on github.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// ui.add(egui::github_link_file!(\"https://github.com/YOUR/PROJECT/blob/main/\", \"(source code)\"));\n/// # });\n/// ```\n#[macro_export]\nmacro_rules! github_link_file {\n    ($github_url: expr, $label: expr) => {{\n        let url = format!(\"{}{}\", $github_url, file!());\n        $crate::Hyperlink::from_label_and_url($label, url)\n    }};\n}\n\n// ----------------------------------------------------------------------------\n\n/// The minus character: <https://www.compart.com/en/unicode/U+2212>\npub(crate) const MINUS_CHAR_STR: &str = \"−\";\n\n/// The default egui fonts supports around 1216 emojis in total.\n/// Here are some of the most useful:\n/// ∞⊗⎗⎘⎙⏏⏴⏵⏶⏷\n/// ⏩⏪⏭⏮⏸⏹⏺■▶📾🔀🔁🔃\n/// ☀☁★☆☐☑☜☝☞☟⛃⛶✔\n/// ↺↻⟲⟳⬅➡⬆⬇⬈⬉⬊⬋⬌⬍⮨⮩⮪⮫\n/// ♡\n/// 📅📆\n/// 📈📉📊\n/// 📋📌📎📤📥🔆\n/// 🔈🔉🔊🔍🔎🔗🔘\n/// 🕓🖧🖩🖮🖱🖴🖵🖼🗀🗁🗋🗐🗑🗙🚫❓\n///\n/// NOTE: In egui all emojis are monochrome!\n///\n/// You can explore them all in the Font Book in [the online demo](https://www.egui.rs/#demo).\n///\n/// In addition, egui supports a few special emojis that are not part of the unicode standard.\n/// This module contains some of them:\npub mod special_emojis {\n    /// Tux, the Linux penguin.\n    pub const OS_LINUX: char = '🐧';\n\n    /// The Windows logo.\n    pub const OS_WINDOWS: char = '';\n\n    /// The Android logo.\n    pub const OS_ANDROID: char = '';\n\n    /// The Apple logo.\n    pub const OS_APPLE: char = '';\n\n    /// The Github logo.\n    pub const GITHUB: char = '';\n\n    /// The word `git`.\n    pub const GIT: char = '';\n\n    // I really would like to have ferris here.\n}\n\n/// The different types of built-in widgets in egui\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum WidgetType {\n    Label, // TODO(emilk): emit Label events\n\n    /// e.g. a hyperlink\n    Link,\n\n    TextEdit,\n\n    Button,\n\n    Checkbox,\n\n    RadioButton,\n\n    /// A group of radio buttons.\n    RadioGroup,\n\n    SelectableLabel,\n\n    ComboBox,\n\n    Slider,\n\n    DragValue,\n\n    ColorButton,\n\n    Image,\n\n    CollapsingHeader,\n\n    Panel,\n\n    ProgressIndicator,\n\n    Window,\n\n    ResizeHandle,\n\n    ScrollBar,\n\n    /// If you cannot fit any of the above slots.\n    ///\n    /// If this is something you think should be added, file an issue.\n    Other,\n}\n\n// ----------------------------------------------------------------------------\n\n/// For use in tests; especially doctests.\npub fn __run_test_ctx(mut run_ui: impl FnMut(&Context)) {\n    let ctx = Context::default();\n    ctx.set_fonts(FontDefinitions::empty()); // prevent fonts from being loaded (save CPU time)\n    let _ = ctx.run_ui(Default::default(), |ui| {\n        run_ui(ui.ctx());\n    });\n}\n\n/// For use in tests; especially doctests.\npub fn __run_test_ui(mut add_contents: impl FnMut(&mut Ui)) {\n    let ctx = Context::default();\n    ctx.set_fonts(FontDefinitions::empty()); // prevent fonts from being loaded (save CPU time)\n    let _ = ctx.run_ui(Default::default(), |ui| {\n        add_contents(ui);\n    });\n}\n\npub fn accesskit_root_id() -> Id {\n    Id::new(\"accesskit_root\")\n}\n"
  },
  {
    "path": "crates/egui/src/load/bytes_loader.rs",
    "content": "use super::{\n    Bytes, BytesLoadResult, BytesLoader, BytesPoll, Context, Cow, HashMap, LoadError, Mutex,\n    generate_loader_id,\n};\n\n/// Maps URI:s to [`Bytes`], e.g. found with `include_bytes!`.\n///\n/// By convention, the URI:s should be prefixed with `bytes://`.\n#[derive(Default)]\npub struct DefaultBytesLoader {\n    cache: Mutex<HashMap<Cow<'static, str>, Bytes>>,\n}\n\nimpl DefaultBytesLoader {\n    pub fn insert(&self, uri: impl Into<Cow<'static, str>>, bytes: impl Into<Bytes>) {\n        self.cache\n            .lock()\n            .entry(uri.into())\n            .or_insert_with_key(|_uri| {\n                let bytes: Bytes = bytes.into();\n\n                log::trace!(\"loaded {} bytes for uri {_uri:?}\", bytes.len());\n\n                bytes\n            });\n    }\n}\n\nimpl BytesLoader for DefaultBytesLoader {\n    fn id(&self) -> &'static str {\n        generate_loader_id!(DefaultBytesLoader)\n    }\n\n    fn load(&self, _: &Context, uri: &str) -> BytesLoadResult {\n        // We accept uri:s that don't start with `bytes://` too… for now.\n        match self.cache.lock().get(uri).cloned() {\n            Some(bytes) => Ok(BytesPoll::Ready {\n                size: None,\n                bytes,\n                mime: None,\n            }),\n            None => {\n                if uri.starts_with(\"bytes://\") {\n                    Err(LoadError::Loading(\n                        \"Bytes not found. Did you forget to call Context::include_bytes?\".into(),\n                    ))\n                } else {\n                    Err(LoadError::NotSupported)\n                }\n            }\n        }\n    }\n\n    fn forget(&self, uri: &str) {\n        log::trace!(\"forget {uri:?}\");\n\n        self.cache.lock().remove(uri);\n    }\n\n    fn forget_all(&self) {\n        log::trace!(\"forget all\");\n\n        self.cache.lock().clear();\n    }\n\n    fn byte_size(&self) -> usize {\n        self.cache.lock().values().map(|bytes| bytes.len()).sum()\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/load/texture_loader.rs",
    "content": "use std::sync::atomic::{AtomicU64, Ordering::Relaxed};\n\nuse emath::Vec2;\n\nuse super::{\n    BytesLoader as _, Context, HashMap, ImagePoll, Mutex, SizeHint, SizedTexture, TextureHandle,\n    TextureLoadResult, TextureLoader, TextureOptions, TexturePoll,\n};\n\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\nstruct PrimaryKey {\n    uri: String,\n    texture_options: TextureOptions,\n}\n\n/// SVG:s might have several different sizes loaded\ntype Bucket = HashMap<Option<SizeHint>, Entry>;\n\nstruct Entry {\n    last_used: AtomicU64,\n\n    /// Size of the original SVG, if any, or the texel size of the image if not an SVG.\n    source_size: Vec2,\n\n    handle: TextureHandle,\n}\n\n#[derive(Default)]\npub struct DefaultTextureLoader {\n    pass_index: AtomicU64,\n    cache: Mutex<HashMap<PrimaryKey, Bucket>>,\n}\n\nimpl TextureLoader for DefaultTextureLoader {\n    fn id(&self) -> &'static str {\n        crate::generate_loader_id!(DefaultTextureLoader)\n    }\n\n    fn load(\n        &self,\n        ctx: &Context,\n        uri: &str,\n        texture_options: TextureOptions,\n        size_hint: SizeHint,\n    ) -> TextureLoadResult {\n        let svg_size_hint = if is_svg(uri) {\n            // For SVGs it's important that we render at the desired size,\n            // or we might get a blurry image when we scale it up.\n            // So we make the size hint a part of the cache key.\n            // This might lead to a lot of extra entries for the same SVG file,\n            // which is potentially wasteful of RAM, but better that than blurry images.\n            Some(size_hint)\n        } else {\n            // For other images we just use one cache value, no matter what the size we render at.\n            None\n        };\n\n        let mut cache = self.cache.lock();\n        let bucket = cache\n            .entry(PrimaryKey {\n                uri: uri.to_owned(),\n                texture_options,\n            })\n            .or_default();\n\n        if let Some(texture) = bucket.get(&svg_size_hint) {\n            texture\n                .last_used\n                .store(self.pass_index.load(Relaxed), Relaxed);\n            let texture = SizedTexture::new(texture.handle.id(), texture.source_size);\n            Ok(TexturePoll::Ready { texture })\n        } else {\n            match ctx.try_load_image(uri, size_hint)? {\n                ImagePoll::Pending { size } => Ok(TexturePoll::Pending { size }),\n                ImagePoll::Ready { image } => {\n                    let source_size = image.source_size;\n                    let handle = ctx.load_texture(uri, image, texture_options);\n                    let texture = SizedTexture::new(handle.id(), source_size);\n                    bucket.insert(\n                        svg_size_hint,\n                        Entry {\n                            last_used: AtomicU64::new(self.pass_index.load(Relaxed)),\n                            source_size,\n                            handle,\n                        },\n                    );\n                    let reduce_texture_memory = ctx.options(|o| o.reduce_texture_memory);\n                    if reduce_texture_memory {\n                        let loaders = ctx.loaders();\n                        loaders.include.forget(uri);\n                        for loader in loaders.bytes.lock().iter().rev() {\n                            loader.forget(uri);\n                        }\n                        for loader in loaders.image.lock().iter().rev() {\n                            loader.forget(uri);\n                        }\n                    }\n                    Ok(TexturePoll::Ready { texture })\n                }\n            }\n        }\n    }\n\n    fn forget(&self, uri: &str) {\n        log::trace!(\"forget {uri:?}\");\n\n        self.cache.lock().retain(|key, _value| key.uri != uri);\n    }\n\n    fn forget_all(&self) {\n        log::trace!(\"forget all\");\n\n        self.cache.lock().clear();\n    }\n\n    fn end_pass(&self, pass_index: u64) {\n        self.pass_index.store(pass_index, Relaxed);\n        let mut cache = self.cache.lock();\n        cache.retain(|_key, bucket| {\n            if 2 <= bucket.len() {\n                // There are multiple textures of the same URI (e.g. SVGs of different scales).\n                // This could be because someone has an SVG in a resizable container,\n                // and so we get a lot of different sizes of it.\n                // This could wast VRAM, so we remove the ones that are not used in this frame.\n                bucket.retain(|_, texture| pass_index <= texture.last_used.load(Relaxed) + 1);\n            }\n            !bucket.is_empty()\n        });\n    }\n\n    fn byte_size(&self) -> usize {\n        self.cache\n            .lock()\n            .values()\n            .map(|bucket| {\n                bucket\n                    .values()\n                    .map(|texture| texture.handle.byte_size())\n                    .sum::<usize>()\n            })\n            .sum()\n    }\n}\n\nfn is_svg(uri: &str) -> bool {\n    uri.ends_with(\".svg\")\n}\n"
  },
  {
    "path": "crates/egui/src/load.rs",
    "content": "//! # Image loading\n//!\n//! If you just want to display some images, [`egui_extras`](https://crates.io/crates/egui_extras/)\n//! will get you up and running quickly with its reasonable default implementations of the traits described below.\n//!\n//! 1. Add [`egui_extras`](https://crates.io/crates/egui_extras/) as a dependency with the `all_loaders` feature (`cargo add egui_extras -F all_loaders`).\n//! 2. Add a call to [`egui_extras::install_image_loaders`](https://docs.rs/egui_extras/latest/egui_extras/fn.install_image_loaders.html)\n//!    in your app's setup code.\n//! 3. Use [`Ui::image`][`crate::ui::Ui::image`] with some [`ImageSource`][`crate::ImageSource`].\n//!\n//! ## Loading process\n//!\n//! There are three kinds of loaders:\n//! - [`BytesLoader`]: load the raw bytes of an image\n//! - [`ImageLoader`]: decode the bytes into an array of colors\n//! - [`TextureLoader`]: ask the backend to put an image onto the GPU\n//!\n//! The different kinds of loaders represent different layers in the loading process:\n//!\n//! ```text,ignore\n//! ui.image(\"file://image.png\")\n//! └► Context::try_load_texture\n//! └► TextureLoader::load\n//!    └► Context::try_load_image\n//!    └► ImageLoader::load\n//!       └► Context::try_load_bytes\n//!       └► BytesLoader::load\n//! ```\n//!\n//! As each layer attempts to load the URI, it first asks the layer below it\n//! for the data it needs to do its job. But this is not a strict requirement,\n//! an implementation could instead generate the data it needs!\n//!\n//! Loader trait implementations may be registered on a context with:\n//! - [`Context::add_bytes_loader`]\n//! - [`Context::add_image_loader`]\n//! - [`Context::add_texture_loader`]\n//!\n//! There may be multiple loaders of the same kind registered at the same time.\n//! The `try_load` methods on [`Context`] will attempt to call each loader one by one,\n//! until one of them returns something other than [`LoadError::NotSupported`].\n//!\n//! The loaders are stored in the context. This means they may hold state across frames,\n//! which they can (and _should_) use to cache the results of the operations they perform.\n//!\n//! For example, a [`BytesLoader`] that loads file URIs (`file://image.png`)\n//! would cache each file read. A [`TextureLoader`] would cache each combination\n//! of `(URI, TextureOptions)`, and so on.\n//!\n//! Each URI will be passed through the loaders as a plain `&str`.\n//! The loaders are free to derive as much meaning from the URI as they wish to.\n//! For example, a loader may determine that it doesn't support loading a specific URI\n//! if the protocol does not match what it expects.\n\nmod bytes_loader;\nmod texture_loader;\n\nuse std::{\n    borrow::Cow,\n    fmt::{Debug, Display},\n    ops::Deref,\n    sync::Arc,\n};\n\nuse ahash::HashMap;\n\nuse emath::{Float as _, OrderedFloat};\nuse epaint::{ColorImage, TextureHandle, TextureId, Vec2, mutex::Mutex, textures::TextureOptions};\n\nuse crate::Context;\n\npub use self::{bytes_loader::DefaultBytesLoader, texture_loader::DefaultTextureLoader};\n\n/// Represents a failed attempt at loading an image.\n#[derive(Clone, Debug)]\npub enum LoadError {\n    /// Programmer error: There are no image loaders installed.\n    NoImageLoaders,\n\n    /// A specific loader does not support this scheme or protocol.\n    NotSupported,\n\n    /// A specific loader does not support the format of the image.\n    FormatNotSupported { detected_format: Option<String> },\n\n    /// Programmer error: Failed to find the bytes for this image because\n    /// there was no [`BytesLoader`] supporting the scheme.\n    NoMatchingBytesLoader,\n\n    /// Programmer error: Failed to parse the bytes as an image because\n    /// there was no [`ImageLoader`] supporting the format.\n    NoMatchingImageLoader { detected_format: Option<String> },\n\n    /// Programmer error: no matching [`TextureLoader`].\n    /// Because of the [`DefaultTextureLoader`], this error should never happen.\n    NoMatchingTextureLoader,\n\n    /// Runtime error: Loading was attempted, but failed (e.g. \"File not found\").\n    Loading(String),\n}\n\nimpl LoadError {\n    /// Returns the (approximate) size of the error message in bytes.\n    pub fn byte_size(&self) -> usize {\n        match self {\n            Self::FormatNotSupported { detected_format }\n            | Self::NoMatchingImageLoader { detected_format } => {\n                detected_format.as_ref().map_or(0, |s| s.len())\n            }\n            Self::Loading(message) => message.len(),\n            _ => std::mem::size_of::<Self>(),\n        }\n    }\n}\n\nimpl Display for LoadError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::NoImageLoaders => f.write_str(\n                \"No image loaders are installed. If you're trying to load some images \\\n                for the first time, follow the steps outlined in https://docs.rs/egui/latest/egui/load/index.html\"),\n\n            Self::NoMatchingBytesLoader => f.write_str(\"No matching BytesLoader. Either you need to call Context::include_bytes, or install some more bytes loaders, e.g. using egui_extras.\"),\n\n            Self::NoMatchingImageLoader { detected_format: None } => f.write_str(\"No matching ImageLoader. Either no ImageLoader is installed or the image is corrupted / has an unsupported format.\"),\n            Self::NoMatchingImageLoader { detected_format: Some(detected_format) } => write!(f, \"No matching ImageLoader for format: {detected_format:?}. Make sure you enabled the necessary features on the image crate.\"),\n\n            Self::NoMatchingTextureLoader => f.write_str(\"No matching TextureLoader. Did you remove the default one?\"),\n\n            Self::NotSupported => f.write_str(\"Image scheme or URI not supported by this loader\"),\n\n            Self::FormatNotSupported { detected_format } => write!(f, \"Image format not supported by this loader: {detected_format:?}\"),\n\n            Self::Loading(message) => f.write_str(message),\n        }\n    }\n}\n\nimpl std::error::Error for LoadError {}\n\npub type Result<T, E = LoadError> = std::result::Result<T, E>;\n\n/// Given as a hint for image loading requests.\n///\n/// Used mostly for rendering SVG:s to a good size.\n/// The [`SizeHint`] determines at what resolution the image should be rasterized.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub enum SizeHint {\n    /// Scale original size by some factor, keeping the original aspect ratio.\n    ///\n    /// The original size of the image is usually its texel resolution,\n    /// but for an SVG it's the point size of the SVG.\n    ///\n    /// For instance, setting `Scale(2.0)` will rasterize SVG:s to twice their original size,\n    /// which is useful for high-DPI displays.\n    Scale(OrderedFloat<f32>),\n\n    /// Scale to exactly this pixel width, keeping the original aspect ratio.\n    Width(u32),\n\n    /// Scale to exactly this pixel height, keeping the original aspect ratio.\n    Height(u32),\n\n    /// Scale to this pixel size.\n    Size {\n        width: u32,\n        height: u32,\n\n        /// If true, the image will be as large as possible\n        /// while still fitting within the given width/height.\n        maintain_aspect_ratio: bool,\n    },\n}\n\nimpl SizeHint {\n    /// Multiply size hint by a factor.\n    pub fn scale_by(self, factor: f32) -> Self {\n        match self {\n            Self::Scale(scale) => Self::Scale(OrderedFloat(factor * scale.0)),\n            Self::Width(width) => Self::Width((factor * width as f32).round() as _),\n            Self::Height(height) => Self::Height((factor * height as f32).round() as _),\n            Self::Size {\n                width,\n                height,\n                maintain_aspect_ratio,\n            } => Self::Size {\n                width: (factor * width as f32).round() as _,\n                height: (factor * height as f32).round() as _,\n                maintain_aspect_ratio,\n            },\n        }\n    }\n}\n\nimpl Default for SizeHint {\n    #[inline]\n    fn default() -> Self {\n        Self::Scale(1.0.ord())\n    }\n}\n\n/// Represents a byte buffer.\n///\n/// This is essentially `Cow<'static, [u8]>` but with the `Owned` variant being an `Arc`.\n#[derive(Clone)]\npub enum Bytes {\n    Static(&'static [u8]),\n    Shared(Arc<[u8]>),\n}\n\nimpl Debug for Bytes {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Static(arg0) => f.debug_tuple(\"Static\").field(&arg0.len()).finish(),\n            Self::Shared(arg0) => f.debug_tuple(\"Shared\").field(&arg0.len()).finish(),\n        }\n    }\n}\n\nimpl From<&'static [u8]> for Bytes {\n    #[inline]\n    fn from(value: &'static [u8]) -> Self {\n        Self::Static(value)\n    }\n}\n\nimpl<const N: usize> From<&'static [u8; N]> for Bytes {\n    #[inline]\n    fn from(value: &'static [u8; N]) -> Self {\n        Self::Static(value)\n    }\n}\n\nimpl From<Arc<[u8]>> for Bytes {\n    #[inline]\n    fn from(value: Arc<[u8]>) -> Self {\n        Self::Shared(value)\n    }\n}\n\nimpl From<Vec<u8>> for Bytes {\n    #[inline]\n    fn from(value: Vec<u8>) -> Self {\n        Self::Shared(value.into())\n    }\n}\n\nimpl AsRef<[u8]> for Bytes {\n    #[inline]\n    fn as_ref(&self) -> &[u8] {\n        match self {\n            Self::Static(bytes) => bytes,\n            Self::Shared(bytes) => bytes,\n        }\n    }\n}\n\nimpl Deref for Bytes {\n    type Target = [u8];\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        self.as_ref()\n    }\n}\n\n/// Represents bytes which are currently being loaded.\n///\n/// This is similar to [`std::task::Poll`], but the `Pending` variant\n/// contains an optional `size`, which may be used during layout to\n/// pre-allocate space the image.\n#[derive(Clone)]\npub enum BytesPoll {\n    /// Bytes are being loaded.\n    Pending {\n        /// Point size of the image.\n        ///\n        /// Set if known (e.g. from a HTTP header, or by parsing the image file header).\n        size: Option<Vec2>,\n    },\n\n    /// Bytes are loaded.\n    Ready {\n        /// Point size of the image.\n        ///\n        /// Set if known (e.g. from a HTTP header, or by parsing the image file header).\n        size: Option<Vec2>,\n\n        /// File contents, e.g. the contents of a `.png`.\n        bytes: Bytes,\n\n        /// Mime type of the content, e.g. `image/png`.\n        ///\n        /// Set if known (e.g. from `Content-Type` HTTP header).\n        mime: Option<String>,\n    },\n}\n\n/// Used to get a unique ID when implementing one of the loader traits: [`BytesLoader::id`], [`ImageLoader::id`], and [`TextureLoader::id`].\n///\n/// This just expands to `module_path!()` concatenated with the given type name.\n#[macro_export]\nmacro_rules! generate_loader_id {\n    ($ty:ident) => {\n        concat!(module_path!(), \"::\", stringify!($ty))\n    };\n}\npub use crate::generate_loader_id;\n\npub type BytesLoadResult = Result<BytesPoll>;\n\n/// Represents a loader capable of loading raw unstructured bytes from somewhere,\n/// e.g. from disk or network.\n///\n/// It should also provide any subsequent loaders a hint for what the bytes may\n/// represent using [`BytesPoll::Ready::mime`], if it can be inferred.\n///\n/// Implementations are expected to cache at least each `URI`.\npub trait BytesLoader {\n    /// Unique ID of this loader.\n    ///\n    /// To reduce the chance of collisions, use [`generate_loader_id`] for this.\n    fn id(&self) -> &str;\n\n    /// Try loading the bytes from the given uri.\n    ///\n    /// Implementations should call `ctx.request_repaint` to wake up the ui\n    /// once the data is ready.\n    ///\n    /// The implementation should cache any result, so that calling this\n    /// is immediate-mode safe.\n    ///\n    /// # Errors\n    /// This may fail with:\n    /// - [`LoadError::NotSupported`] if the loader does not support loading `uri`.\n    /// - [`LoadError::Loading`] if the loading process failed.\n    fn load(&self, ctx: &Context, uri: &str) -> BytesLoadResult;\n\n    /// Forget the given `uri`.\n    ///\n    /// If `uri` is cached, it should be evicted from cache,\n    /// so that it may be fully reloaded.\n    fn forget(&self, uri: &str);\n\n    /// Forget all URIs ever given to this loader.\n    ///\n    /// If the loader caches any URIs, the entire cache should be cleared,\n    /// so that all of them may be fully reloaded.\n    fn forget_all(&self);\n\n    /// Implementations may use this to perform work at the end of a frame,\n    /// such as evicting unused entries from a cache.\n    fn end_pass(&self, pass_index: u64) {\n        let _ = pass_index;\n    }\n\n    /// If the loader caches any data, this should return the size of that cache.\n    fn byte_size(&self) -> usize;\n\n    /// Returns `true` if some data is currently being loaded.\n    fn has_pending(&self) -> bool {\n        false\n    }\n}\n\n/// Represents an image which is currently being loaded.\n///\n/// This is similar to [`std::task::Poll`], but the `Pending` variant\n/// contains an optional `size`, which may be used during layout to\n/// pre-allocate space the image.\n#[derive(Clone)]\npub enum ImagePoll {\n    /// Image is loading.\n    Pending {\n        /// Point size of the image.\n        ///\n        /// Set if known (e.g. from a HTTP header, or by parsing the image file header).\n        size: Option<Vec2>,\n    },\n\n    /// Image is loaded.\n    Ready { image: Arc<ColorImage> },\n}\n\npub type ImageLoadResult = Result<ImagePoll>;\n\n/// An `ImageLoader` decodes raw bytes into a [`ColorImage`].\n///\n/// Implementations are expected to cache at least each `URI`.\npub trait ImageLoader: std::any::Any {\n    /// Unique ID of this loader.\n    ///\n    /// To reduce the chance of collisions, include `module_path!()` as part of this ID.\n    ///\n    /// For example: `concat!(module_path!(), \"::MyLoader\")`\n    /// for `my_crate::my_loader::MyLoader`.\n    fn id(&self) -> &str;\n\n    /// Try loading the image from the given uri.\n    ///\n    /// Implementations should call `ctx.request_repaint` to wake up the ui\n    /// once the image is ready.\n    ///\n    /// The implementation should cache any result, so that calling this\n    /// is immediate-mode safe.\n    ///\n    /// # Errors\n    /// This may fail with:\n    /// - [`LoadError::NotSupported`] if the loader does not support loading `uri`.\n    /// - [`LoadError::Loading`] if the loading process failed.\n    fn load(&self, ctx: &Context, uri: &str, size_hint: SizeHint) -> ImageLoadResult;\n\n    /// Forget the given `uri`.\n    ///\n    /// If `uri` is cached, it should be evicted from cache,\n    /// so that it may be fully reloaded.\n    fn forget(&self, uri: &str);\n\n    /// Forget all URIs ever given to this loader.\n    ///\n    /// If the loader caches any URIs, the entire cache should be cleared,\n    /// so that all of them may be fully reloaded.\n    fn forget_all(&self);\n\n    /// Implementations may use this to perform work at the end of a pass,\n    /// such as evicting unused entries from a cache.\n    fn end_pass(&self, pass_index: u64) {\n        let _ = pass_index;\n    }\n\n    /// If the loader caches any data, this should return the size of that cache.\n    fn byte_size(&self) -> usize;\n\n    /// Returns `true` if some image is currently being loaded.\n    ///\n    /// NOTE: You probably also want to check [`BytesLoader::has_pending`].\n    fn has_pending(&self) -> bool {\n        false\n    }\n}\n\n/// A texture with a known size.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub struct SizedTexture {\n    pub id: TextureId,\n\n    /// Point size of the original SVG, or the size of the image in texels.\n    pub size: Vec2,\n}\n\nimpl SizedTexture {\n    /// Create a [`SizedTexture`] from a texture `id` with a specific `size`.\n    pub fn new(id: impl Into<TextureId>, size: impl Into<Vec2>) -> Self {\n        Self {\n            id: id.into(),\n            size: size.into(),\n        }\n    }\n\n    /// Fetch the [id][`SizedTexture::id`] and [size][`SizedTexture::size`] from a [`TextureHandle`].\n    pub fn from_handle(handle: &TextureHandle) -> Self {\n        let size = handle.size();\n        Self {\n            id: handle.id(),\n            size: Vec2::new(size[0] as f32, size[1] as f32),\n        }\n    }\n}\n\nimpl From<(TextureId, Vec2)> for SizedTexture {\n    #[inline]\n    fn from((id, size): (TextureId, Vec2)) -> Self {\n        Self { id, size }\n    }\n}\n\nimpl<'a> From<&'a TextureHandle> for SizedTexture {\n    #[inline]\n    fn from(handle: &'a TextureHandle) -> Self {\n        Self::from_handle(handle)\n    }\n}\n\n/// Represents a texture is currently being loaded.\n///\n/// This is similar to [`std::task::Poll`], but the `Pending` variant\n/// contains an optional `size`, which may be used during layout to\n/// pre-allocate space the image.\n#[derive(Clone, Copy)]\npub enum TexturePoll {\n    /// Texture is loading.\n    Pending {\n        /// Point size of the image.\n        ///\n        /// Set if known (e.g. from a HTTP header, or by parsing the image file header).\n        size: Option<Vec2>,\n    },\n\n    /// Texture is loaded.\n    Ready { texture: SizedTexture },\n}\n\nimpl TexturePoll {\n    /// Point size of the original SVG, or the size of the image in texels.\n    #[inline]\n    pub fn size(&self) -> Option<Vec2> {\n        match self {\n            Self::Pending { size } => *size,\n            Self::Ready { texture } => Some(texture.size),\n        }\n    }\n\n    #[inline]\n    pub fn texture_id(&self) -> Option<TextureId> {\n        match self {\n            Self::Pending { .. } => None,\n            Self::Ready { texture } => Some(texture.id),\n        }\n    }\n\n    #[inline]\n    pub fn is_pending(&self) -> bool {\n        matches!(self, Self::Pending { .. })\n    }\n\n    #[inline]\n    pub fn is_ready(&self) -> bool {\n        matches!(self, Self::Ready { .. })\n    }\n}\n\npub type TextureLoadResult = Result<TexturePoll>;\n\n/// A `TextureLoader` uploads a [`ColorImage`] to the GPU, returning a [`SizedTexture`].\n///\n/// `egui` comes with an implementation that uses [`Context::load_texture`],\n/// which just asks the egui backend to upload the image to the GPU.\n///\n/// You can implement this trait if you do your own uploading of images to the GPU.\n/// For instance, you can use this to refer to textures in a game engine that egui\n/// doesn't otherwise know about.\n///\n/// Implementations are expected to cache each combination of `(URI, TextureOptions)`.\npub trait TextureLoader {\n    /// Unique ID of this loader.\n    ///\n    /// To reduce the chance of collisions, include `module_path!()` as part of this ID.\n    ///\n    /// For example: `concat!(module_path!(), \"::MyLoader\")`\n    /// for `my_crate::my_loader::MyLoader`.\n    fn id(&self) -> &str;\n\n    /// Try loading the texture from the given uri.\n    ///\n    /// Implementations should call `ctx.request_repaint` to wake up the ui\n    /// once the texture is ready.\n    ///\n    /// The implementation should cache any result, so that calling this\n    /// is immediate-mode safe.\n    ///\n    /// # Errors\n    /// This may fail with:\n    /// - [`LoadError::NotSupported`] if the loader does not support loading `uri`.\n    /// - [`LoadError::Loading`] if the loading process failed.\n    fn load(\n        &self,\n        ctx: &Context,\n        uri: &str,\n        texture_options: TextureOptions,\n        size_hint: SizeHint,\n    ) -> TextureLoadResult;\n\n    /// Forget the given `uri`.\n    ///\n    /// If `uri` is cached, it should be evicted from cache,\n    /// so that it may be fully reloaded.\n    fn forget(&self, uri: &str);\n\n    /// Forget all URIs ever given to this loader.\n    ///\n    /// If the loader caches any URIs, the entire cache should be cleared,\n    /// so that all of them may be fully reloaded.\n    fn forget_all(&self);\n\n    /// Implementations may use this to perform work at the end of a pass,\n    /// such as evicting unused entries from a cache.\n    fn end_pass(&self, pass_index: u64) {\n        let _ = pass_index;\n    }\n\n    /// If the loader caches any data, this should return the size of that cache.\n    fn byte_size(&self) -> usize;\n}\n\ntype BytesLoaderImpl = Arc<dyn BytesLoader + Send + Sync + 'static>;\ntype ImageLoaderImpl = Arc<dyn ImageLoader + Send + Sync + 'static>;\ntype TextureLoaderImpl = Arc<dyn TextureLoader + Send + Sync + 'static>;\n\n#[derive(Clone)]\n/// The loaders of bytes, images, and textures.\npub struct Loaders {\n    pub include: Arc<DefaultBytesLoader>,\n    pub bytes: Mutex<Vec<BytesLoaderImpl>>,\n    pub image: Mutex<Vec<ImageLoaderImpl>>,\n    pub texture: Mutex<Vec<TextureLoaderImpl>>,\n}\n\nimpl Default for Loaders {\n    fn default() -> Self {\n        let include = Arc::new(DefaultBytesLoader::default());\n        Self {\n            bytes: Mutex::new(vec![Arc::clone(&include) as _]),\n            image: Mutex::new(Vec::new()),\n            // By default we only include `DefaultTextureLoader`.\n            texture: Mutex::new(vec![Arc::new(DefaultTextureLoader::default())]),\n            include,\n        }\n    }\n}\n\nimpl Loaders {\n    /// The given pass has just ended.\n    pub fn end_pass(&self, pass_index: u64) {\n        let Self {\n            include,\n            bytes,\n            image,\n            texture,\n        } = self;\n\n        include.end_pass(pass_index);\n        for loader in bytes.lock().iter() {\n            loader.end_pass(pass_index);\n        }\n        for loader in image.lock().iter() {\n            loader.end_pass(pass_index);\n        }\n        for loader in texture.lock().iter() {\n            loader.end_pass(pass_index);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/memory/mod.rs",
    "content": "#![warn(missing_docs)] // Let's keep this file well-documented.` to memory.rs\n\nuse std::num::NonZeroUsize;\n\nuse ahash::{HashMap, HashSet};\nuse epaint::emath::TSTransform;\n\nuse crate::{\n    EventFilter, Id, IdMap, LayerId, Order, Pos2, Rangef, RawInput, Rect, Style, Vec2, ViewportId,\n    ViewportIdMap, ViewportIdSet, area, vec2,\n};\n\nmod theme;\npub use theme::{Theme, ThemePreference};\n\n// ----------------------------------------------------------------------------\n\n/// The data that egui persists between frames.\n///\n/// This includes window positions and sizes,\n/// how far the user has scrolled in a [`ScrollArea`](crate::ScrollArea) etc.\n///\n/// If you want this to persist when closing your app, you should serialize [`Memory`] and store it.\n/// For this you need to enable the `persistence` feature.\n///\n/// If you want to store data for your widgets, you should look at [`Memory::data`]\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"persistence\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"persistence\", serde(default))]\npub struct Memory {\n    /// Global egui options.\n    pub options: Options,\n\n    /// This map stores some superficial state for all widgets with custom [`Id`]s.\n    ///\n    /// This includes storing whether a [`crate::CollapsingHeader`] is open, how far scrolled a\n    /// [`crate::ScrollArea`] is, where the cursor in a [`crate::TextEdit`] is, etc.\n    ///\n    /// This is NOT meant to store any important data. Store that in your own structures!\n    ///\n    /// Each read clones the data, so keep your values cheap to clone.\n    /// If you want to store a lot of data, you should wrap it in `Arc<Mutex<…>>` so it is cheap to clone.\n    ///\n    /// This will be saved between different program runs if you use the `persistence` feature.\n    ///\n    /// To store a state common for all your widgets (a singleton), use [`Id::NULL`] as the key.\n    pub data: crate::util::IdTypeMap,\n\n    // ------------------------------------------\n    /// Can be used to cache computations from one frame to another.\n    ///\n    /// This is for saving CPU time when you have something that may take 1-100ms to compute.\n    /// Very slow operations (>100ms) should instead be done async (i.e. in another thread)\n    /// so as not to lock the UI thread.\n    ///\n    /// ```\n    /// use egui::cache::{ComputerMut, FrameCache};\n    ///\n    /// #[derive(Default)]\n    /// struct CharCounter {}\n    /// impl ComputerMut<&str, usize> for CharCounter {\n    ///     fn compute(&mut self, s: &str) -> usize {\n    ///         s.chars().count() // you probably want to cache something more expensive than this\n    ///     }\n    /// }\n    /// type CharCountCache<'a> = FrameCache<usize, CharCounter>;\n    ///\n    /// # let mut ctx = egui::Context::default();\n    /// ctx.memory_mut(|mem| {\n    ///     let cache = mem.caches.cache::<CharCountCache<'_>>();\n    ///     assert_eq!(*cache.get(\"hello\"), 5);\n    /// });\n    /// ```\n    #[cfg_attr(feature = \"persistence\", serde(skip))]\n    pub caches: crate::cache::CacheStorage,\n\n    // ------------------------------------------\n    /// new fonts that will be applied at the start of the next frame\n    #[cfg_attr(feature = \"persistence\", serde(skip))]\n    pub(crate) new_font_definitions: Option<epaint::text::FontDefinitions>,\n\n    /// add new font that will be applied at the start of the next frame\n    #[cfg_attr(feature = \"persistence\", serde(skip))]\n    pub(crate) add_fonts: Vec<epaint::text::FontInsert>,\n\n    // Current active viewport\n    #[cfg_attr(feature = \"persistence\", serde(skip))]\n    pub(crate) viewport_id: ViewportId,\n\n    #[cfg_attr(feature = \"persistence\", serde(skip))]\n    everything_is_visible: bool,\n\n    /// Transforms per layer.\n    ///\n    /// Instead of using this directly, use:\n    /// * [`crate::Context::set_transform_layer`]\n    /// * [`crate::Context::layer_transform_to_global`]\n    /// * [`crate::Context::layer_transform_from_global`]\n    pub to_global: HashMap<LayerId, TSTransform>,\n\n    // -------------------------------------------------\n    // Per-viewport:\n    areas: ViewportIdMap<Areas>,\n\n    #[cfg_attr(feature = \"persistence\", serde(skip))]\n    pub(crate) interactions: ViewportIdMap<InteractionState>,\n\n    #[cfg_attr(feature = \"persistence\", serde(skip))]\n    pub(crate) focus: ViewportIdMap<Focus>,\n\n    /// Which popup-window is open on a viewport (if any)?\n    /// Could be a combo box, color picker, menu, etc.\n    /// Optionally stores the position of the popup (usually this would be the position where\n    /// the user clicked).\n    /// If position is [`None`], the popup position will be calculated based on some configuration\n    /// (e.g. relative to some other widget).\n    #[cfg_attr(feature = \"persistence\", serde(skip))]\n    popups: ViewportIdMap<OpenPopup>,\n}\n\nimpl Default for Memory {\n    fn default() -> Self {\n        let mut slf = Self {\n            options: Default::default(),\n            data: Default::default(),\n            caches: Default::default(),\n            new_font_definitions: Default::default(),\n            interactions: Default::default(),\n            focus: Default::default(),\n            viewport_id: Default::default(),\n            areas: Default::default(),\n            to_global: Default::default(),\n            popups: Default::default(),\n            everything_is_visible: Default::default(),\n            add_fonts: Default::default(),\n        };\n        slf.interactions.entry(slf.viewport_id).or_default();\n        slf.areas.entry(slf.viewport_id).or_default();\n        slf\n    }\n}\n\n/// A direction in which to move the keyboard focus.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\npub enum FocusDirection {\n    /// Select the widget closest above the current focused widget.\n    Up,\n\n    /// Select the widget to the right of the current focused widget.\n    Right,\n\n    /// Select the widget below the current focused widget.\n    Down,\n\n    /// Select the widget to the left of the current focused widget.\n    Left,\n\n    /// Select the previous widget that had focus.\n    Previous,\n\n    /// Select the next widget that wants focus.\n    Next,\n\n    /// Don't change focus.\n    #[default]\n    None,\n}\n\nimpl FocusDirection {\n    fn is_cardinal(&self) -> bool {\n        match self {\n            Self::Up | Self::Right | Self::Down | Self::Left => true,\n\n            Self::Previous | Self::Next | Self::None => false,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Some global options that you can read and write.\n///\n/// See also [`crate::style::DebugOptions`].\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Options {\n    /// The default style for new [`Ui`](crate::Ui):s in dark mode.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub dark_style: std::sync::Arc<Style>,\n\n    /// The default style for new [`Ui`](crate::Ui):s in light mode.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub light_style: std::sync::Arc<Style>,\n\n    /// Preference for selection between dark and light [`crate::Context::style`]\n    /// as the active style used by all subsequent windows, panels, etc.\n    ///\n    /// Default: `ThemePreference::System`.\n    pub theme_preference: ThemePreference,\n\n    /// Which theme to use in case [`Self::theme_preference`] is [`ThemePreference::System`]\n    /// and egui fails to detect the system theme.\n    ///\n    /// Default: [`crate::Theme::Dark`].\n    pub fallback_theme: Theme,\n\n    /// The current system theme, used to choose between\n    /// dark and light style in case [`Self::theme_preference`] is [`ThemePreference::System`].\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) system_theme: Option<Theme>,\n\n    /// Global zoom factor of the UI.\n    ///\n    /// This is used to calculate the `pixels_per_point`\n    /// for the UI as `pixels_per_point = zoom_fator * native_pixels_per_point`.\n    ///\n    /// The default is 1.0. Increase it to make all UI elements larger.\n    ///\n    /// You should call [`crate::Context::set_zoom_factor`]\n    /// instead of modifying this directly!\n    pub zoom_factor: f32,\n\n    /// If `true`, egui will change the scale of the ui ([`crate::Context::zoom_factor`]) when the user\n    /// presses Cmd+Plus, Cmd+Minus or Cmd+0, just like in a browser.\n    ///\n    /// This is `true` by default.\n    ///\n    /// On the web-backend of `eframe` this is set to false by default,\n    /// so that the zoom shortcuts are handled exclusively by the browser,\n    /// which will change the `native_pixels_per_point` (`devicePixelRatio`).\n    /// You can still zoom egui independently by calling [`crate::Context::set_zoom_factor`],\n    /// which will be applied on top of the browsers global zoom.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub zoom_with_keyboard: bool,\n\n    /// Keyboard shortcuts to close the application.\n    ///\n    /// Pressing any of these will send [`crate::ViewportCommand::Close`]\n    /// to the root viewport.\n    ///\n    /// Defaults to `Cmd-Q` (which is Ctrl-Q on Linux/Windows, Cmd-Q on Mac).\n    /// Set to empty to disable.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub quit_shortcuts: Vec<crate::KeyboardShortcut>,\n\n    /// Controls the tessellator.\n    pub tessellation_options: epaint::TessellationOptions,\n\n    /// If any widget moves or changes id, repaint everything.\n    ///\n    /// It is recommended you keep this OFF, as it may\n    /// lead to endless repaints for an unknown reason. See\n    /// (<https://github.com/rerun-io/rerun/issues/5018>).\n    pub repaint_on_widget_change: bool,\n\n    /// Maximum number of passes to run in one frame.\n    ///\n    /// Set to `1` for pure single-pass immediate mode.\n    /// Set to something larger than `1` to allow multi-pass when needed.\n    ///\n    /// Default is `2`. This means sometimes a frame will cost twice as much,\n    /// but usually only rarely (e.g. when showing a new panel for the first time).\n    ///\n    /// egui will usually only ever run one pass, even if `max_passes` is large.\n    ///\n    /// If this is `1`, [`crate::Context::request_discard`] will be ignored.\n    ///\n    /// Multi-pass is supported by [`crate::Context::run`].\n    ///\n    /// See [`crate::Context::request_discard`] for more.\n    pub max_passes: NonZeroUsize,\n\n    /// This is a signal to any backend that we want the [`crate::PlatformOutput::events`] read out loud.\n    ///\n    /// The only change to egui is that labels can be focused by pressing tab.\n    ///\n    /// Screen readers are an experimental feature of egui, and not supported on all platforms.\n    /// `eframe` only supports it on web.\n    ///\n    /// Consider using [AccessKit](https://github.com/AccessKit/accesskit) instead,\n    /// which is supported by `eframe`.\n    pub screen_reader: bool,\n\n    /// Check reusing of [`Id`]s, and show a visual warning on screen when one is found.\n    ///\n    /// By default this is `true` in debug builds.\n    pub warn_on_id_clash: bool,\n\n    /// Options related to input state handling.\n    pub input_options: crate::input_state::InputOptions,\n\n    /// If `true`, `egui` will discard the loaded image data after\n    /// the texture is loaded onto the GPU to reduce memory usage.\n    ///\n    /// In modern GPU rendering, the texture data is not required after the texture is loaded.\n    ///\n    /// This is beneficial when using a large number or resolution of images and there is no need to\n    /// retain the image data, potentially saving a significant amount of memory.\n    ///\n    /// The drawback is that it becomes impossible to serialize the loaded images or render in non-GPU systems.\n    ///\n    /// Default is `false`.\n    pub reduce_texture_memory: bool,\n}\n\nimpl Default for Options {\n    fn default() -> Self {\n        Self {\n            dark_style: std::sync::Arc::new(Theme::Dark.default_style()),\n            light_style: std::sync::Arc::new(Theme::Light.default_style()),\n            theme_preference: Default::default(),\n            fallback_theme: Theme::Dark,\n            system_theme: None,\n            zoom_factor: 1.0,\n            zoom_with_keyboard: true,\n            quit_shortcuts: vec![crate::KeyboardShortcut::new(\n                crate::Modifiers::COMMAND,\n                crate::Key::Q,\n            )],\n            tessellation_options: Default::default(),\n            repaint_on_widget_change: false,\n\n            #[expect(clippy::unwrap_used)]\n            max_passes: NonZeroUsize::new(2).unwrap(),\n            screen_reader: false,\n            warn_on_id_clash: cfg!(debug_assertions),\n\n            // Input:\n            input_options: Default::default(),\n            reduce_texture_memory: false,\n        }\n    }\n}\n\nimpl Options {\n    // Needs to be pub because we need to set the system_theme early in the eframe glow renderer.\n    #[doc(hidden)]\n    pub fn begin_pass(&mut self, new_raw_input: &RawInput) {\n        self.system_theme = new_raw_input.system_theme;\n    }\n\n    /// The currently active theme (may depend on the system theme).\n    pub(crate) fn theme(&self) -> Theme {\n        match self.theme_preference {\n            ThemePreference::Dark => Theme::Dark,\n            ThemePreference::Light => Theme::Light,\n            ThemePreference::System => self.system_theme.unwrap_or(self.fallback_theme),\n        }\n    }\n\n    pub(crate) fn style(&self) -> &std::sync::Arc<Style> {\n        match self.theme() {\n            Theme::Dark => &self.dark_style,\n            Theme::Light => &self.light_style,\n        }\n    }\n\n    pub(crate) fn style_mut(&mut self) -> &mut std::sync::Arc<Style> {\n        match self.theme() {\n            Theme::Dark => &mut self.dark_style,\n            Theme::Light => &mut self.light_style,\n        }\n    }\n}\n\nimpl Options {\n    /// Show the options in the ui.\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        let theme = self.theme();\n\n        let Self {\n            dark_style, // covered above\n            light_style,\n            theme_preference,\n            fallback_theme: _,\n            system_theme: _,\n            zoom_factor,\n            zoom_with_keyboard,\n            quit_shortcuts: _, // not shown in ui\n            tessellation_options,\n            repaint_on_widget_change,\n            max_passes,\n            screen_reader: _, // needs to come from the integration\n            warn_on_id_clash,\n            input_options,\n            reduce_texture_memory,\n        } = self;\n\n        use crate::Widget as _;\n        use crate::containers::CollapsingHeader;\n\n        CollapsingHeader::new(\"⚙ Options\")\n            .default_open(false)\n            .show(ui, |ui| {\n                ui.horizontal(|ui| {\n                    ui.label(\"Max passes:\");\n                    ui.add(crate::DragValue::new(max_passes).range(0..=10));\n                });\n\n                ui.checkbox(\n                    repaint_on_widget_change,\n                    \"Repaint if any widget moves or changes id\",\n                );\n\n                ui.horizontal(|ui| {\n                    ui.label(\"Zoom factor:\");\n                    ui.add(crate::DragValue::new(zoom_factor).range(0.10..=10.0));\n                });\n\n                ui.checkbox(\n                    zoom_with_keyboard,\n                    \"Zoom with keyboard (Cmd +, Cmd -, Cmd 0)\",\n                );\n\n                ui.checkbox(warn_on_id_clash, \"Warn if two widgets have the same Id\");\n\n                ui.checkbox(reduce_texture_memory, \"Reduce texture memory\");\n            });\n\n        CollapsingHeader::new(\"🎑 Style\")\n            .default_open(true)\n            .show(ui, |ui| {\n                theme_preference.radio_buttons(ui);\n\n                let style = std::sync::Arc::make_mut(match theme {\n                    Theme::Dark => dark_style,\n                    Theme::Light => light_style,\n                });\n                style.ui(ui);\n            });\n\n        CollapsingHeader::new(\"✒ Painting\")\n            .default_open(false)\n            .show(ui, |ui| {\n                tessellation_options.ui(ui);\n                ui.vertical_centered(|ui| {\n                    crate::reset_button(ui, tessellation_options, \"Reset paint settings\");\n                });\n            });\n\n        CollapsingHeader::new(\"🖱 Input\")\n            .default_open(false)\n            .show(ui, |ui| {\n                input_options.ui(ui);\n            });\n\n        ui.vertical_centered(|ui| crate::reset_button(ui, self, \"Reset all\"));\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// The state of the interaction in egui,\n/// i.e. what is being dragged.\n///\n/// Say there is a button in a scroll area.\n/// If the user clicks the button, the button should click.\n/// If the user drags the button we should scroll the scroll area.\n/// Therefore, when the mouse is pressed, we register both the button\n/// and the scroll area (as `click_id`/`drag_id`).\n/// If the user releases the button without moving the mouse, we register it as a click on `click_id`.\n/// If the cursor moves too much, we clear the `click_id` and start passing move events to `drag_id`.\n#[derive(Clone, Debug, Default)]\npub(crate) struct InteractionState {\n    /// A widget interested in clicks that has a mouse press on it.\n    pub potential_click_id: Option<Id>,\n\n    /// A widget interested in drags that has a mouse press on it.\n    ///\n    /// Note that this is set as soon as the mouse is pressed,\n    /// so the widget may not yet be marked as \"dragged\"\n    /// as that can only happen after the mouse has moved a bit\n    /// (at least if the widget is interesated in both clicks and drags).\n    pub potential_drag_id: Option<Id>,\n}\n\n/// Keeps tracks of what widget has keyboard focus\n#[derive(Clone, Debug, Default)]\npub(crate) struct Focus {\n    /// The widget with keyboard focus (i.e. a text input field).\n    focused_widget: Option<FocusWidget>,\n\n    /// The ID of a widget that had keyboard focus during the previous frame.\n    id_previous_frame: Option<Id>,\n\n    /// The ID of a widget to give the focus to in the next frame.\n    id_next_frame: Option<Id>,\n\n    id_requested_by_accesskit: Option<accesskit::NodeId>,\n\n    /// If set, the next widget that is interested in focus will automatically get it.\n    /// Probably because the user pressed Tab.\n    give_to_next: bool,\n\n    /// The last widget interested in focus.\n    last_interested: Option<Id>,\n\n    /// Set when looking for widget with navigational keys like arrows, tab, shift+tab.\n    focus_direction: FocusDirection,\n\n    /// The top-most modal layer from the previous frame.\n    top_modal_layer: Option<LayerId>,\n\n    /// The top-most modal layer from the current frame.\n    top_modal_layer_current_frame: Option<LayerId>,\n\n    /// A cache of widget IDs that are interested in focus with their corresponding rectangles.\n    focus_widgets_cache: IdMap<Rect>,\n}\n\n/// The widget with focus.\n#[derive(Clone, Copy, Debug)]\nstruct FocusWidget {\n    pub id: Id,\n    pub filter: EventFilter,\n}\n\nimpl FocusWidget {\n    pub fn new(id: Id) -> Self {\n        Self {\n            id,\n            filter: Default::default(),\n        }\n    }\n}\n\nimpl InteractionState {\n    /// Are we currently clicking or dragging an egui widget?\n    pub fn is_using_pointer(&self) -> bool {\n        self.potential_click_id.is_some() || self.potential_drag_id.is_some()\n    }\n}\n\nimpl Focus {\n    /// Which widget currently has keyboard focus?\n    pub fn focused(&self) -> Option<Id> {\n        self.focused_widget.as_ref().map(|w| w.id)\n    }\n\n    fn begin_pass(&mut self, new_input: &crate::data::input::RawInput) {\n        self.id_previous_frame = self.focused();\n        if let Some(id) = self.id_next_frame.take() {\n            self.focused_widget = Some(FocusWidget::new(id));\n        }\n        let event_filter = self.focused_widget.map(|w| w.filter).unwrap_or_default();\n\n        self.id_requested_by_accesskit = None;\n\n        self.focus_direction = FocusDirection::None;\n\n        for event in &new_input.events {\n            if !event_filter.matches(event)\n                && let crate::Event::Key {\n                    key,\n                    pressed: true,\n                    modifiers,\n                    ..\n                } = event\n                && let Some(cardinality) = match key {\n                    crate::Key::ArrowUp if !modifiers.any() => Some(FocusDirection::Up),\n                    crate::Key::ArrowRight if !modifiers.any() => Some(FocusDirection::Right),\n                    crate::Key::ArrowDown if !modifiers.any() => Some(FocusDirection::Down),\n                    crate::Key::ArrowLeft if !modifiers.any() => Some(FocusDirection::Left),\n\n                    crate::Key::Tab if !modifiers.any() => Some(FocusDirection::Next),\n                    crate::Key::Tab if modifiers.shift_only() => Some(FocusDirection::Previous),\n\n                    crate::Key::Escape if !modifiers.any() => {\n                        self.focused_widget = None;\n                        Some(FocusDirection::None)\n                    }\n\n                    _ => None,\n                }\n            {\n                self.focus_direction = cardinality;\n            }\n\n            if let crate::Event::AccessKitActionRequest(accesskit::ActionRequest {\n                action: accesskit::Action::Focus,\n                target_node,\n                target_tree,\n                data: None,\n            }) = event\n                && *target_tree == accesskit::TreeId::ROOT\n            {\n                self.id_requested_by_accesskit = Some(*target_node);\n            }\n        }\n    }\n\n    pub(crate) fn end_pass(&mut self, used_ids: &IdMap<Rect>) {\n        if self.focus_direction.is_cardinal()\n            && let Some(found_widget) = self.find_widget_in_direction(used_ids)\n        {\n            self.focused_widget = Some(FocusWidget::new(found_widget));\n        }\n\n        if let Some(focused_widget) = self.focused_widget {\n            // Allow calling `request_focus` one frame and not using it until next frame\n            let recently_gained_focus = self.id_previous_frame != Some(focused_widget.id);\n\n            if !recently_gained_focus && !used_ids.contains_key(&focused_widget.id) {\n                // Dead-mans-switch: the widget with focus has disappeared!\n                self.focused_widget = None;\n            }\n        }\n\n        self.top_modal_layer = self.top_modal_layer_current_frame.take();\n    }\n\n    pub(crate) fn had_focus_last_frame(&self, id: Id) -> bool {\n        self.id_previous_frame == Some(id)\n    }\n\n    fn interested_in_focus(&mut self, id: Id) {\n        if self.id_requested_by_accesskit == Some(id.accesskit_id()) {\n            self.focused_widget = Some(FocusWidget::new(id));\n            self.id_requested_by_accesskit = None;\n            self.give_to_next = false;\n            self.reset_focus();\n        }\n\n        // The rect is updated at the end of the frame.\n        self.focus_widgets_cache\n            .entry(id)\n            .or_insert(Rect::EVERYTHING);\n\n        if self.give_to_next && !self.had_focus_last_frame(id) {\n            self.focused_widget = Some(FocusWidget::new(id));\n            self.give_to_next = false;\n        } else if self.focused() == Some(id) {\n            if self.focus_direction == FocusDirection::Next {\n                self.focused_widget = None;\n                self.give_to_next = true;\n                self.reset_focus();\n            } else if self.focus_direction == FocusDirection::Previous {\n                self.id_next_frame = self.last_interested; // frame-delay so gained_focus works\n                self.reset_focus();\n            }\n        } else if self.focus_direction == FocusDirection::Next\n            && self.focused_widget.is_none()\n            && !self.give_to_next\n        {\n            // nothing has focus and the user pressed tab - give focus to the first widgets that wants it:\n            self.focused_widget = Some(FocusWidget::new(id));\n            self.reset_focus();\n        } else if self.focus_direction == FocusDirection::Previous\n            && self.focused_widget.is_none()\n            && !self.give_to_next\n        {\n            // nothing has focus and the user pressed Shift+Tab - give focus to the last widgets that wants it:\n            self.focused_widget = self.last_interested.map(FocusWidget::new);\n            self.reset_focus();\n        }\n\n        self.last_interested = Some(id);\n    }\n\n    fn set_modal_layer(&mut self, layer_id: LayerId) {\n        self.top_modal_layer_current_frame = Some(layer_id);\n    }\n\n    pub(crate) fn top_modal_layer(&self) -> Option<LayerId> {\n        self.top_modal_layer\n    }\n\n    fn reset_focus(&mut self) {\n        self.focus_direction = FocusDirection::None;\n    }\n\n    fn find_widget_in_direction(&mut self, new_rects: &IdMap<Rect>) -> Option<Id> {\n        // NOTE: `new_rects` here include some widgets _not_ interested in focus.\n\n        /// * negative if `a` is left of `b`\n        /// * positive if `a` is right of `b`\n        /// * zero if the ranges overlap significantly\n        fn range_diff(a: Rangef, b: Rangef) -> f32 {\n            let has_significant_overlap = a.intersection(b).span() >= 0.5 * b.span().min(a.span());\n            if has_significant_overlap {\n                0.0\n            } else {\n                a.center() - b.center()\n            }\n        }\n\n        let current_focused = self.focused_widget?;\n\n        // In what direction we are looking for the next widget.\n        let search_direction = match self.focus_direction {\n            FocusDirection::Up => Vec2::UP,\n            FocusDirection::Right => Vec2::RIGHT,\n            FocusDirection::Down => Vec2::DOWN,\n            FocusDirection::Left => Vec2::LEFT,\n            _ => {\n                return None;\n            }\n        };\n\n        // Update cache with new rects\n        self.focus_widgets_cache.retain(|id, old_rect| {\n            if let Some(new_rect) = new_rects.get(id) {\n                *old_rect = *new_rect;\n                true // Keep the item\n            } else {\n                false // Remove the item\n            }\n        });\n\n        let current_rect = self.focus_widgets_cache.get(&current_focused.id)?;\n\n        let mut best_score = f32::INFINITY;\n        let mut best_id = None;\n\n        // iteration order should only matter in case of a tie, and that should be very rare\n        #[expect(clippy::iter_over_hash_type)]\n        for (candidate_id, candidate_rect) in &self.focus_widgets_cache {\n            if *candidate_id == current_focused.id {\n                continue;\n            }\n\n            // There is a lot of room for improvement here.\n            let to_candidate = vec2(\n                range_diff(candidate_rect.x_range(), current_rect.x_range()),\n                range_diff(candidate_rect.y_range(), current_rect.y_range()),\n            );\n\n            let acos_angle = to_candidate.normalized().dot(search_direction);\n\n            // Only interested in widgets that fall in a 90° cone (±45°)\n            // of the search direction.\n            let is_in_search_cone = 0.5_f32.sqrt() <= acos_angle;\n            if is_in_search_cone {\n                let distance = to_candidate.length();\n\n                // There is a lot of room for improvement here.\n                let score = distance / (acos_angle * acos_angle);\n\n                if score < best_score {\n                    best_score = score;\n                    best_id = Some(*candidate_id);\n                }\n            }\n        }\n\n        best_id\n    }\n}\n\nimpl Memory {\n    pub(crate) fn begin_pass(&mut self, new_raw_input: &RawInput, viewports: &ViewportIdSet) {\n        profiling::function_scope!();\n\n        self.viewport_id = new_raw_input.viewport_id;\n\n        // Cleanup\n        self.interactions.retain(|id, _| viewports.contains(id));\n        self.areas.retain(|id, _| viewports.contains(id));\n        self.popups.retain(|id, _| viewports.contains(id));\n\n        self.areas.entry(self.viewport_id).or_default();\n\n        // self.interactions  is handled elsewhere\n\n        self.options.begin_pass(new_raw_input);\n\n        self.focus\n            .entry(self.viewport_id)\n            .or_default()\n            .begin_pass(new_raw_input);\n    }\n\n    pub(crate) fn end_pass(&mut self, used_ids: &IdMap<Rect>) {\n        self.caches.update();\n        self.areas_mut().end_pass();\n        self.focus_mut().end_pass(used_ids);\n\n        // Clean up abandoned popups.\n        if let Some(popup) = self.popups.get_mut(&self.viewport_id) {\n            if popup.open_this_frame {\n                popup.open_this_frame = false;\n            } else {\n                self.popups.remove(&self.viewport_id);\n            }\n        }\n    }\n\n    pub(crate) fn set_viewport_id(&mut self, viewport_id: ViewportId) {\n        self.viewport_id = viewport_id;\n    }\n\n    /// Access memory of the [`Area`](crate::containers::area::Area)s, such as `Window`s.\n    pub fn areas(&self) -> &Areas {\n        self.areas\n            .get(&self.viewport_id)\n            .expect(\"Memory broken: no area for the current viewport\")\n    }\n\n    /// Access memory of the [`Area`](crate::containers::area::Area)s, such as `Window`s.\n    pub fn areas_mut(&mut self) -> &mut Areas {\n        self.areas.entry(self.viewport_id).or_default()\n    }\n\n    /// Top-most layer at the given position.\n    pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {\n        let layer_id = self.areas().layer_id_at(pos, &self.to_global)?;\n        if self.is_above_modal_layer(layer_id) {\n            Some(layer_id)\n        } else {\n            self.top_modal_layer()\n        }\n    }\n\n    /// The currently set transform of a layer.\n    #[deprecated = \"Use `Context::layer_transform_to_global` instead\"]\n    pub fn layer_transforms(&self, layer_id: LayerId) -> Option<TSTransform> {\n        self.to_global.get(&layer_id).copied()\n    }\n\n    /// An iterator over all layers. Back-to-front, top is last.\n    pub fn layer_ids(&self) -> impl ExactSizeIterator<Item = LayerId> + '_ {\n        self.areas().order().iter().copied()\n    }\n\n    /// Check if the layer had focus last frame.\n    /// returns `true` if the layer had focus last frame, but not this one.\n    pub fn had_focus_last_frame(&self, id: Id) -> bool {\n        self.focus().and_then(|f| f.id_previous_frame) == Some(id)\n    }\n\n    /// Check if the layer lost focus last frame.\n    /// returns `true` if the layer lost focus last frame, but not this one.\n    pub(crate) fn lost_focus(&self, id: Id) -> bool {\n        self.had_focus_last_frame(id) && !self.has_focus(id)\n    }\n\n    /// Check if the layer gained focus this frame.\n    /// returns `true` if the layer gained focus this frame, but not last one.\n    pub(crate) fn gained_focus(&self, id: Id) -> bool {\n        !self.had_focus_last_frame(id) && self.has_focus(id)\n    }\n\n    /// Does this widget have keyboard focus?\n    ///\n    /// This function does not consider whether the UI as a whole (e.g. window)\n    /// has the keyboard focus. That makes this function suitable for deciding\n    /// widget state that should not be disrupted if the user moves away from\n    /// the window and back.\n    #[inline(always)]\n    pub fn has_focus(&self, id: Id) -> bool {\n        self.focused() == Some(id)\n    }\n\n    /// Which widget has keyboard focus?\n    pub fn focused(&self) -> Option<Id> {\n        self.focus()?.focused()\n    }\n\n    /// Set an event filter for a widget.\n    ///\n    /// This allows you to control whether the widget will loose focus\n    /// when the user presses tab, arrow keys, or escape.\n    ///\n    /// You must first give focus to the widget before calling this.\n    pub fn set_focus_lock_filter(&mut self, id: Id, event_filter: EventFilter) {\n        if self.had_focus_last_frame(id)\n            && self.has_focus(id)\n            && let Some(focused) = &mut self.focus_mut().focused_widget\n            && focused.id == id\n        {\n            focused.filter = event_filter;\n        }\n    }\n\n    /// Give keyboard focus to a specific widget.\n    /// See also [`crate::Response::request_focus`].\n    #[inline(always)]\n    pub fn request_focus(&mut self, id: Id) {\n        self.focus_mut().focused_widget = Some(FocusWidget::new(id));\n    }\n\n    /// Surrender keyboard focus for a specific widget.\n    /// See also [`crate::Response::surrender_focus`].\n    #[inline(always)]\n    pub fn surrender_focus(&mut self, id: Id) {\n        let focus = self.focus_mut();\n        if focus.focused() == Some(id) {\n            focus.focused_widget = None;\n        }\n    }\n\n    /// Move keyboard focus in a specific direction.\n    pub fn move_focus(&mut self, direction: FocusDirection) {\n        self.focus_mut().focus_direction = direction;\n    }\n\n    /// Returns true if\n    /// - this layer is the top-most modal layer or above it\n    /// - there is no modal layer\n    pub fn is_above_modal_layer(&self, layer_id: LayerId) -> bool {\n        if let Some(modal_layer) = self.focus().and_then(|f| f.top_modal_layer) {\n            matches!(\n                self.areas().compare_order(layer_id, modal_layer),\n                std::cmp::Ordering::Equal | std::cmp::Ordering::Greater\n            )\n        } else {\n            true\n        }\n    }\n\n    /// Does this layer allow interaction?\n    /// Returns true if\n    ///  - the layer is not behind a modal layer\n    ///  - the [`Order`] allows interaction\n    pub fn allows_interaction(&self, layer_id: LayerId) -> bool {\n        let is_above_modal_layer = self.is_above_modal_layer(layer_id);\n        let ordering_allows_interaction = layer_id.order.allow_interaction();\n        is_above_modal_layer && ordering_allows_interaction\n    }\n\n    /// Register this widget as being interested in getting keyboard focus.\n    /// This will allow the user to select it with tab and shift-tab.\n    /// This is normally done automatically when handling interactions,\n    /// but it is sometimes useful to pre-register interest in focus,\n    /// e.g. before deciding which type of underlying widget to use,\n    /// as in the [`crate::DragValue`] widget, so a widget can be focused\n    /// and rendered correctly in a single frame.\n    ///\n    /// Pass in the `layer_id` of the layer that the widget is in.\n    #[inline(always)]\n    pub fn interested_in_focus(&mut self, id: Id, layer_id: LayerId) {\n        if !self.allows_interaction(layer_id) {\n            return;\n        }\n        self.focus_mut().interested_in_focus(id);\n    }\n\n    /// Limit focus to widgets on the given layer and above.\n    /// If this is called multiple times per frame, the top layer wins.\n    pub fn set_modal_layer(&mut self, layer_id: LayerId) {\n        if let Some(current) = self.focus().and_then(|f| f.top_modal_layer_current_frame)\n            && matches!(\n                self.areas().compare_order(layer_id, current),\n                std::cmp::Ordering::Less\n            )\n        {\n            return;\n        }\n\n        self.focus_mut().set_modal_layer(layer_id);\n    }\n\n    /// Get the top modal layer (from the previous frame).\n    pub fn top_modal_layer(&self) -> Option<LayerId> {\n        self.focus()?.top_modal_layer()\n    }\n\n    /// Stop editing the active [`TextEdit`](crate::TextEdit) (if any).\n    #[inline(always)]\n    pub fn stop_text_input(&mut self) {\n        self.focus_mut().focused_widget = None;\n    }\n\n    /// Forget window positions, sizes etc.\n    /// Can be used to auto-layout windows.\n    pub fn reset_areas(&mut self) {\n        #[expect(clippy::iter_over_hash_type)]\n        for area in self.areas.values_mut() {\n            *area = Default::default();\n        }\n    }\n\n    /// Obtain the previous rectangle of an area.\n    pub fn area_rect(&self, id: impl Into<Id>) -> Option<Rect> {\n        self.areas().get(id.into()).map(|state| state.rect())\n    }\n\n    pub(crate) fn interaction(&self) -> &InteractionState {\n        self.interactions\n            .get(&self.viewport_id)\n            .expect(\"Failed to get interaction\")\n    }\n\n    pub(crate) fn interaction_mut(&mut self) -> &mut InteractionState {\n        self.interactions.entry(self.viewport_id).or_default()\n    }\n\n    pub(crate) fn focus(&self) -> Option<&Focus> {\n        self.focus.get(&self.viewport_id)\n    }\n\n    pub(crate) fn focus_mut(&mut self) -> &mut Focus {\n        self.focus.entry(self.viewport_id).or_default()\n    }\n}\n\n/// State of an open popup.\n#[derive(Clone, Copy, Debug)]\nstruct OpenPopup {\n    /// Id of the popup.\n    id: Id,\n\n    /// Optional position of the popup.\n    pos: Option<Pos2>,\n\n    /// Whether this popup was still open this frame. Otherwise it's considered abandoned and `Memory::popup` will be cleared.\n    open_this_frame: bool,\n}\n\nimpl OpenPopup {\n    /// Create a new `OpenPopup`.\n    fn new(id: Id, pos: Option<Pos2>) -> Self {\n        Self {\n            id,\n            pos,\n            open_this_frame: true,\n        }\n    }\n}\n\n/// ## Deprecated popup API\n/// Use [`crate::Popup`] instead.\nimpl Memory {\n    /// Is the given popup open?\n    #[deprecated = \"Use Popup::is_id_open instead\"]\n    pub fn is_popup_open(&self, popup_id: Id) -> bool {\n        self.popups\n            .get(&self.viewport_id)\n            .is_some_and(|state| state.id == popup_id)\n            || self.everything_is_visible()\n    }\n\n    /// Is any popup open?\n    #[deprecated = \"Use Popup::is_any_open instead\"]\n    pub fn any_popup_open(&self) -> bool {\n        self.popups.contains_key(&self.viewport_id) || self.everything_is_visible()\n    }\n\n    /// Open the given popup and close all others.\n    ///\n    /// Note that you must call `keep_popup_open` on subsequent frames as long as the popup is open.\n    #[deprecated = \"Use Popup::open_id instead\"]\n    pub fn open_popup(&mut self, popup_id: Id) {\n        self.popups\n            .insert(self.viewport_id, OpenPopup::new(popup_id, None));\n    }\n\n    /// Popups must call this every frame while open.\n    ///\n    /// This is needed because in some cases popups can go away without `close_popup` being\n    /// called. For example, when a context menu is open and the underlying widget stops\n    /// being rendered.\n    #[deprecated = \"Use Popup::show instead\"]\n    pub fn keep_popup_open(&mut self, popup_id: Id) {\n        if let Some(state) = self.popups.get_mut(&self.viewport_id)\n            && state.id == popup_id\n        {\n            state.open_this_frame = true;\n        }\n    }\n\n    /// Open the popup and remember its position.\n    #[deprecated = \"Use Popup with PopupAnchor::Position instead\"]\n    pub fn open_popup_at(&mut self, popup_id: Id, pos: impl Into<Option<Pos2>>) {\n        self.popups\n            .insert(self.viewport_id, OpenPopup::new(popup_id, pos.into()));\n    }\n\n    /// Get the position for this popup.\n    #[deprecated = \"Use Popup::position_of_id instead\"]\n    pub fn popup_position(&self, id: Id) -> Option<Pos2> {\n        let state = self.popups.get(&self.viewport_id)?;\n        if state.id == id { state.pos } else { None }\n    }\n\n    /// Close any currently open popup.\n    #[deprecated = \"Use Popup::close_all instead\"]\n    pub fn close_all_popups(&mut self) {\n        self.popups.clear();\n    }\n\n    /// Close the given popup, if it is open.\n    ///\n    /// See also [`Self::close_all_popups`] if you want to close any / all currently open popups.\n    #[deprecated = \"Use Popup::close_id instead\"]\n    pub fn close_popup(&mut self, popup_id: Id) {\n        #[expect(deprecated)]\n        if self.is_popup_open(popup_id) {\n            self.popups.remove(&self.viewport_id);\n        }\n    }\n\n    /// Toggle the given popup between closed and open.\n    ///\n    /// Note: At most, only one popup can be open at a time.\n    #[deprecated = \"Use Popup::toggle_id instead\"]\n    pub fn toggle_popup(&mut self, popup_id: Id) {\n        #[expect(deprecated)]\n        if self.is_popup_open(popup_id) {\n            self.close_popup(popup_id);\n        } else {\n            self.open_popup(popup_id);\n        }\n    }\n}\n\nimpl Memory {\n    /// If true, all windows, menus, tooltips, etc., will be visible at once.\n    ///\n    /// This is useful for testing, benchmarking, pre-caching, etc.\n    ///\n    /// Experimental feature!\n    #[inline(always)]\n    pub fn everything_is_visible(&self) -> bool {\n        self.everything_is_visible\n    }\n\n    /// If true, all windows, menus, tooltips etc are to be visible at once.\n    ///\n    /// This is useful for testing, benchmarking, pre-caching, etc.\n    ///\n    /// Experimental feature!\n    pub fn set_everything_is_visible(&mut self, value: bool) {\n        self.everything_is_visible = value;\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Map containing the index of each layer in the order list, for quick lookups.\ntype OrderMap = HashMap<LayerId, usize>;\n\n/// Keeps track of [`Area`](crate::containers::area::Area)s, which are free-floating [`Ui`](crate::Ui)s.\n/// These [`Area`](crate::containers::area::Area)s can be in any [`Order`].\n#[derive(Clone, Debug, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Areas {\n    areas: IdMap<area::AreaState>,\n\n    visible_areas_last_frame: ahash::HashSet<LayerId>,\n    visible_areas_current_frame: ahash::HashSet<LayerId>,\n\n    // ----------------------------\n    // Everything below this is general to all layers, not just areas.\n    // TODO(emilk): move this to a separate struct.\n    /// Back-to-front,  top is last.\n    order: Vec<LayerId>,\n\n    /// Inverse of [`Self::order`], calculated at the end of the frame.\n    order_map: OrderMap,\n\n    /// When an area wants to be on top, it is assigned here.\n    /// This is used to reorder the layers at the end of the frame.\n    /// If several layers want to be on top, they will keep their relative order.\n    /// This means closing three windows and then reopening them all in one frame\n    /// results in them being sent to the top and keeping their previous internal order.\n    wants_to_be_on_top: ahash::HashSet<LayerId>,\n\n    /// The sublayers that each layer has.\n    ///\n    /// The parent sublayer is moved directly above the child sublayers in the ordering.\n    sublayers: ahash::HashMap<LayerId, HashSet<LayerId>>,\n}\n\nimpl Areas {\n    pub(crate) fn count(&self) -> usize {\n        self.areas.len()\n    }\n\n    pub(crate) fn get(&self, id: Id) -> Option<&area::AreaState> {\n        self.areas.get(&id)\n    }\n\n    /// All layers back-to-front, top is last.\n    pub(crate) fn order(&self) -> &[LayerId] {\n        &self.order\n    }\n\n    /// Compare the order of two layers, based on the order list from last frame.\n    ///\n    /// May return [`std::cmp::Ordering::Equal`] if the layers are not in the order list.\n    pub(crate) fn compare_order(&self, a: LayerId, b: LayerId) -> std::cmp::Ordering {\n        // Sort by layer `order` first and use `order_map` to resolve disputes.\n        // If `order_map` only contains one layer ID, then the other one will be\n        // lower because `None < Some(x)`.\n        match a.order.cmp(&b.order) {\n            std::cmp::Ordering::Equal => self.order_map.get(&a).cmp(&self.order_map.get(&b)),\n            cmp => cmp,\n        }\n    }\n\n    pub(crate) fn set_state(&mut self, layer_id: LayerId, state: area::AreaState) {\n        self.visible_areas_current_frame.insert(layer_id);\n        self.areas.insert(layer_id.id, state);\n        if !self.order.contains(&layer_id) {\n            self.order.push(layer_id);\n        }\n    }\n\n    /// Top-most layer at the given position.\n    pub fn layer_id_at(\n        &self,\n        pos: Pos2,\n        layer_to_global: &HashMap<LayerId, TSTransform>,\n    ) -> Option<LayerId> {\n        for layer in self.order.iter().rev() {\n            if self.is_visible(layer)\n                && let Some(state) = self.areas.get(&layer.id)\n            {\n                let mut rect = state.rect();\n                if state.interactable {\n                    if let Some(to_global) = layer_to_global.get(layer) {\n                        rect = *to_global * rect;\n                    }\n\n                    if rect.contains(pos) {\n                        return Some(*layer);\n                    }\n                }\n            }\n        }\n        None\n    }\n\n    pub fn visible_last_frame(&self, layer_id: &LayerId) -> bool {\n        self.visible_areas_last_frame.contains(layer_id)\n    }\n\n    pub fn is_visible(&self, layer_id: &LayerId) -> bool {\n        self.visible_areas_last_frame.contains(layer_id)\n            || self.visible_areas_current_frame.contains(layer_id)\n    }\n\n    pub fn visible_layer_ids(&self) -> ahash::HashSet<LayerId> {\n        self.visible_areas_last_frame\n            .iter()\n            .copied()\n            .chain(self.visible_areas_current_frame.iter().copied())\n            .collect()\n    }\n\n    pub(crate) fn visible_windows(&self) -> impl Iterator<Item = (LayerId, &area::AreaState)> {\n        self.visible_layer_ids()\n            .into_iter()\n            .filter(|layer| layer.order == crate::Order::Middle)\n            .filter(|&layer| !self.is_sublayer(&layer))\n            .filter_map(|layer| Some((layer, self.get(layer.id)?)))\n    }\n\n    pub fn move_to_top(&mut self, layer_id: LayerId) {\n        self.visible_areas_current_frame.insert(layer_id);\n        self.wants_to_be_on_top.insert(layer_id);\n\n        if !self.order.contains(&layer_id) {\n            self.order.push(layer_id);\n        }\n    }\n\n    /// Mark the `child` layer as a sublayer of `parent`.\n    ///\n    /// Sublayers are moved directly above the parent layer at the end of the frame. This is mainly\n    /// intended for adding a new [Area](crate::Area) inside a [Window](crate::Window).\n    ///\n    /// This currently only supports one level of nesting. If `parent` is a sublayer of another\n    /// layer, the behavior is unspecified.\n    ///\n    /// The two layers must have the same [`LayerId::order`].\n    pub fn set_sublayer(&mut self, parent: LayerId, child: LayerId) {\n        debug_assert_eq!(\n            parent.order, child.order,\n            \"DEBUG ASSERT: Trying to set sublayers across layers of different order ({:?}, {:?}), which is currently undefined behavior in egui\",\n            parent.order, child.order\n        );\n\n        self.sublayers.entry(parent).or_default().insert(child);\n\n        // Make sure the layers are in the order list:\n        if !self.order.contains(&parent) {\n            self.order.push(parent);\n        }\n        if !self.order.contains(&child) {\n            self.order.push(child);\n        }\n    }\n\n    pub fn top_layer_id(&self, order: Order) -> Option<LayerId> {\n        self.order\n            .iter()\n            .rfind(|layer| layer.order == order && !self.is_sublayer(layer))\n            .copied()\n    }\n\n    /// If this layer is the sublayer of another layer, return the parent.\n    pub fn parent_layer(&self, layer_id: LayerId) -> Option<LayerId> {\n        self.sublayers.iter().find_map(|(parent, children)| {\n            if children.contains(&layer_id) {\n                Some(*parent)\n            } else {\n                None\n            }\n        })\n    }\n\n    /// All the child layers of this layer.\n    pub fn child_layers(&self, layer_id: LayerId) -> impl Iterator<Item = LayerId> + '_ {\n        self.sublayers.get(&layer_id).into_iter().flatten().copied()\n    }\n\n    pub(crate) fn is_sublayer(&self, layer: &LayerId) -> bool {\n        self.parent_layer(*layer).is_some()\n    }\n\n    pub(crate) fn end_pass(&mut self) {\n        let Self {\n            visible_areas_last_frame,\n            visible_areas_current_frame,\n            order,\n            wants_to_be_on_top,\n            sublayers,\n            ..\n        } = self;\n\n        std::mem::swap(visible_areas_last_frame, visible_areas_current_frame);\n        visible_areas_current_frame.clear();\n\n        order.sort_by_key(|layer| (layer.order, wants_to_be_on_top.contains(layer)));\n        wants_to_be_on_top.clear();\n\n        // For all layers with sublayers, put the sublayers directly after the parent layer:\n        // (it doesn't matter in which order we replace parents with their children)\n        #[expect(clippy::iter_over_hash_type)]\n        for (parent, children) in std::mem::take(sublayers) {\n            let mut moved_layers = vec![parent]; // parent first…\n\n            order.retain(|l| {\n                if children.contains(l) {\n                    moved_layers.push(*l); // …followed by children\n                    false\n                } else {\n                    true\n                }\n            });\n            let Some(parent_pos) = order.iter().position(|l| l == &parent) else {\n                continue;\n            };\n            order.splice(parent_pos..=parent_pos, moved_layers); // replace the parent with itself and its children\n        }\n\n        self.order_map = self\n            .order\n            .iter()\n            .enumerate()\n            .map(|(i, id)| (*id, i))\n            .collect();\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[test]\nfn memory_impl_send_sync() {\n    fn assert_send_sync<T: Send + Sync>() {}\n    assert_send_sync::<Memory>();\n}\n\n#[test]\nfn order_map_total_ordering() {\n    let mut layers = [\n        LayerId::new(Order::Tooltip, Id::new(\"a\")),\n        LayerId::new(Order::Background, Id::new(\"b\")),\n        LayerId::new(Order::Background, Id::new(\"c\")),\n        LayerId::new(Order::Tooltip, Id::new(\"d\")),\n        LayerId::new(Order::Background, Id::new(\"e\")),\n        LayerId::new(Order::Background, Id::new(\"f\")),\n        LayerId::new(Order::Tooltip, Id::new(\"g\")),\n    ];\n    let mut areas = Areas::default();\n\n    // skip some of the layers\n    for &layer in &layers[3..] {\n        areas.set_state(layer, crate::AreaState::default());\n    }\n    areas.end_pass(); // sort layers\n\n    // Sort layers\n    layers.sort_by(|&a, &b| areas.compare_order(a, b));\n\n    // Assert that `areas.compare_order()` forms a total ordering\n    let mut equivalence_classes = vec![0];\n    let mut i = 0;\n    for l in layers.windows(2) {\n        assert!(l[0].order <= l[1].order, \"does not follow LayerId.order\");\n        if areas.compare_order(l[0], l[1]) != std::cmp::Ordering::Equal {\n            i += 1;\n        }\n        equivalence_classes.push(i);\n    }\n    assert_eq!(layers.len(), equivalence_classes.len());\n    for (&l1, c1) in std::iter::zip(&layers, &equivalence_classes) {\n        for (&l2, c2) in std::iter::zip(&layers, &equivalence_classes) {\n            assert_eq!(\n                c1.cmp(c2),\n                areas.compare_order(l1, l2),\n                \"not a total ordering\",\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/memory/theme.rs",
    "content": "use crate::Button;\n\n/// Dark or Light theme.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum Theme {\n    /// Dark mode: light text on a dark background.\n    Dark,\n\n    /// Light mode: dark text on a light background.\n    Light,\n}\n\nimpl Theme {\n    /// Default visuals for this theme.\n    pub fn default_visuals(self) -> crate::Visuals {\n        match self {\n            Self::Dark => crate::Visuals::dark(),\n            Self::Light => crate::Visuals::light(),\n        }\n    }\n\n    /// Default style for this theme.\n    pub fn default_style(self) -> crate::Style {\n        crate::Style {\n            visuals: self.default_visuals(),\n            ..Default::default()\n        }\n    }\n\n    /// Chooses between [`Self::Dark`] or [`Self::Light`] based on a boolean value.\n    pub fn from_dark_mode(dark_mode: bool) -> Self {\n        if dark_mode { Self::Dark } else { Self::Light }\n    }\n}\n\nimpl Theme {\n    /// Show small toggle-button for light and dark mode.\n    /// This is not the best design as it doesn't allow switching back to \"follow system\".\n    #[must_use]\n    pub(crate) fn small_toggle_button(self, ui: &mut crate::Ui) -> Option<Self> {\n        #![expect(clippy::collapsible_else_if)]\n        if self == Self::Dark {\n            if ui\n                .add(Button::new(\"☀\").frame(false))\n                .on_hover_text(\"Switch to light mode\")\n                .clicked()\n            {\n                return Some(Self::Light);\n            }\n        } else {\n            if ui\n                .add(Button::new(\"🌙\").frame(false))\n                .on_hover_text(\"Switch to dark mode\")\n                .clicked()\n            {\n                return Some(Self::Dark);\n            }\n        }\n        None\n    }\n}\n\n/// The user's theme preference.\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum ThemePreference {\n    /// Dark mode: light text on a dark background.\n    Dark,\n\n    /// Light mode: dark text on a light background.\n    Light,\n\n    /// Follow the system's theme preference.\n    #[default]\n    System,\n}\n\nimpl From<Theme> for ThemePreference {\n    fn from(value: Theme) -> Self {\n        match value {\n            Theme::Dark => Self::Dark,\n            Theme::Light => Self::Light,\n        }\n    }\n}\n\nimpl ThemePreference {\n    /// Show radio-buttons to switch between light mode, dark mode and following the system theme.\n    pub fn radio_buttons(&mut self, ui: &mut crate::Ui) {\n        ui.horizontal(|ui| {\n            let system_theme = ui.input(|i| i.raw.system_theme);\n\n            ui.selectable_value(self, Self::System, \"💻 System\")\n                .on_hover_ui(|ui| {\n                    ui.label(\"Follow the system theme preference.\");\n\n                    ui.add_space(4.0);\n\n                    if let Some(system_theme) = system_theme {\n                        ui.label(format!(\n                            \"The current system theme is: {}\",\n                            match system_theme {\n                                Theme::Dark => \"dark\",\n                                Theme::Light => \"light\",\n                            }\n                        ));\n                    } else {\n                        ui.label(\"The system theme is unknown.\");\n                    }\n                });\n\n            ui.selectable_value(self, Self::Dark, \"🌙 Dark\")\n                .on_hover_text(\"Use the dark mode theme\");\n\n            ui.selectable_value(self, Self::Light, \"☀ Light\")\n                .on_hover_text(\"Use the light mode theme\");\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/menu.rs",
    "content": "#![expect(deprecated)]\n//! Deprecated menu API - Use [`crate::containers::menu`] instead.\n//!\n//! Usage:\n//! ```\n//! fn show_menu(ui: &mut egui::Ui) {\n//!     use egui::{menu, Button};\n//!\n//!     menu::bar(ui, |ui| {\n//!         ui.menu_button(\"File\", |ui| {\n//!             if ui.button(\"Open\").clicked() {\n//!                 // …\n//!             }\n//!         });\n//!     });\n//! }\n//! ```\n\nuse super::{\n    Align, Context, Id, InnerResponse, PointerState, Pos2, Rect, Response, Sense, TextStyle, Ui,\n    Vec2, style::WidgetVisuals,\n};\nuse crate::{\n    Align2, Area, Color32, Frame, Key, LayerId, Layout, NumExt as _, Order, Stroke, Style,\n    TextWrapMode, UiKind, WidgetText, epaint, vec2,\n    widgets::{Button, ImageButton},\n};\nuse epaint::mutex::RwLock;\nuse std::sync::Arc;\n\n/// What is saved between frames.\n#[derive(Clone, Default)]\npub struct BarState {\n    open_menu: MenuRootManager,\n}\n\nimpl BarState {\n    pub fn load(ctx: &Context, bar_id: Id) -> Self {\n        ctx.data_mut(|d| d.get_temp::<Self>(bar_id).unwrap_or_default())\n    }\n\n    pub fn store(self, ctx: &Context, bar_id: Id) {\n        ctx.data_mut(|d| d.insert_temp(bar_id, self));\n    }\n\n    /// Show a menu at pointer if primary-clicked response.\n    ///\n    /// Should be called from [`Context`] on a [`Response`]\n    pub fn bar_menu<R>(\n        &mut self,\n        button: &Response,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> Option<InnerResponse<R>> {\n        MenuRoot::stationary_click_interaction(button, &mut self.open_menu);\n        self.open_menu.show(button, add_contents)\n    }\n\n    pub(crate) fn has_root(&self) -> bool {\n        self.open_menu.inner.is_some()\n    }\n}\n\nimpl std::ops::Deref for BarState {\n    type Target = MenuRootManager;\n\n    fn deref(&self) -> &Self::Target {\n        &self.open_menu\n    }\n}\n\nimpl std::ops::DerefMut for BarState {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.open_menu\n    }\n}\n\nfn set_menu_style(style: &mut Style) {\n    if style.compact_menu_style {\n        style.spacing.button_padding = vec2(2.0, 0.0);\n        style.visuals.widgets.active.bg_stroke = Stroke::NONE;\n        style.visuals.widgets.hovered.bg_stroke = Stroke::NONE;\n        style.visuals.widgets.inactive.weak_bg_fill = Color32::TRANSPARENT;\n        style.visuals.widgets.inactive.bg_stroke = Stroke::NONE;\n    }\n}\n\n/// The menu bar goes well in a [`crate::Panel::top`],\n/// but can also be placed in a [`crate::Window`].\n/// In the latter case you may want to wrap it in [`Frame`].\n#[deprecated = \"Use `egui::MenuBar::new().ui(` instead\"]\npub fn bar<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {\n    ui.horizontal(|ui| {\n        set_menu_style(ui.style_mut());\n\n        // Take full width and fixed height:\n        let height = ui.spacing().interact_size.y;\n        ui.set_min_size(vec2(ui.available_width(), height));\n\n        add_contents(ui)\n    })\n}\n\n/// Construct a top level menu in a menu bar. This would be e.g. \"File\", \"Edit\" etc.\n///\n/// Responds to primary clicks.\n///\n/// Returns `None` if the menu is not open.\npub fn menu_button<R>(\n    ui: &mut Ui,\n    title: impl Into<WidgetText>,\n    add_contents: impl FnOnce(&mut Ui) -> R,\n) -> InnerResponse<Option<R>> {\n    stationary_menu_impl(ui, title, Box::new(add_contents))\n}\n\n/// Construct a top level menu with a custom button in a menu bar.\n///\n/// Responds to primary clicks.\n///\n/// Returns `None` if the menu is not open.\npub fn menu_custom_button<R>(\n    ui: &mut Ui,\n    button: Button<'_>,\n    add_contents: impl FnOnce(&mut Ui) -> R,\n) -> InnerResponse<Option<R>> {\n    stationary_menu_button_impl(ui, button, Box::new(add_contents))\n}\n\n/// Construct a top level menu with an image in a menu bar. This would be e.g. \"File\", \"Edit\" etc.\n///\n/// Responds to primary clicks.\n///\n/// Returns `None` if the menu is not open.\n#[deprecated = \"Use `menu_custom_button` instead\"]\npub fn menu_image_button<R>(\n    ui: &mut Ui,\n    image_button: ImageButton<'_>,\n    add_contents: impl FnOnce(&mut Ui) -> R,\n) -> InnerResponse<Option<R>> {\n    stationary_menu_button_impl(\n        ui,\n        Button::image(image_button.image),\n        Box::new(add_contents),\n    )\n}\n\n/// Construct a nested sub menu in another menu.\n///\n/// Opens on hover.\n///\n/// Returns `None` if the menu is not open.\npub fn submenu_button<R>(\n    ui: &mut Ui,\n    parent_state: Arc<RwLock<MenuState>>,\n    title: impl Into<WidgetText>,\n    add_contents: impl FnOnce(&mut Ui) -> R,\n) -> InnerResponse<Option<R>> {\n    SubMenu::new(parent_state, title).show(ui, add_contents)\n}\n\n/// wrapper for the contents of every menu.\nfn menu_popup<'c, R>(\n    ctx: &Context,\n    parent_layer: LayerId,\n    menu_state_arc: &Arc<RwLock<MenuState>>,\n    menu_id: Id,\n    add_contents: impl FnOnce(&mut Ui) -> R + 'c,\n) -> InnerResponse<R> {\n    let pos = {\n        let mut menu_state = menu_state_arc.write();\n        menu_state.entry_count = 0;\n        menu_state.rect.min\n    };\n\n    let area_id = menu_id.with(\"__menu\");\n\n    ctx.pass_state_mut(|fs| {\n        fs.layers\n            .entry(parent_layer)\n            .or_default()\n            .open_popups\n            .insert(area_id)\n    });\n\n    let area = Area::new(area_id)\n        .kind(UiKind::Menu)\n        .order(Order::Foreground)\n        .fixed_pos(pos)\n        .default_width(ctx.global_style().spacing.menu_width)\n        .sense(Sense::hover());\n\n    let mut sizing_pass = false;\n\n    let area_response = area.show(ctx, |ui| {\n        sizing_pass = ui.is_sizing_pass();\n\n        set_menu_style(ui.style_mut());\n\n        Frame::menu(ui.style())\n            .show(ui, |ui| {\n                ui.set_menu_state(Some(Arc::clone(menu_state_arc)));\n                ui.with_layout(Layout::top_down_justified(Align::LEFT), add_contents)\n                    .inner\n            })\n            .inner\n    });\n\n    let area_rect = area_response.response.rect;\n\n    menu_state_arc.write().rect = if sizing_pass {\n        // During the sizing pass we didn't know the size yet,\n        // so we might have just constrained the position unnecessarily.\n        // Therefore keep the original=desired position until the next frame.\n        Rect::from_min_size(pos, area_rect.size())\n    } else {\n        // We knew the size, and this is where it ended up (potentially constrained to screen).\n        // Remember it for the future:\n        area_rect\n    };\n\n    area_response\n}\n\n/// Build a top level menu with a button.\n///\n/// Responds to primary clicks.\nfn stationary_menu_impl<'c, R>(\n    ui: &mut Ui,\n    title: impl Into<WidgetText>,\n    add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n) -> InnerResponse<Option<R>> {\n    let title = title.into();\n    let bar_id = ui.id();\n    let menu_id = bar_id.with(title.text());\n\n    let mut bar_state = BarState::load(ui.ctx(), bar_id);\n\n    let mut button = Button::new(title);\n\n    if bar_state.open_menu.is_menu_open(menu_id) {\n        button = button.fill(ui.visuals().widgets.open.weak_bg_fill);\n        button = button.stroke(ui.visuals().widgets.open.bg_stroke);\n    }\n\n    let button_response = ui.add(button);\n    let inner = bar_state.bar_menu(&button_response, add_contents);\n\n    bar_state.store(ui.ctx(), bar_id);\n    InnerResponse::new(inner.map(|r| r.inner), button_response)\n}\n\n/// Build a top level menu with an image button.\n///\n/// Responds to primary clicks.\nfn stationary_menu_button_impl<'c, R>(\n    ui: &mut Ui,\n    button: Button<'_>,\n    add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n) -> InnerResponse<Option<R>> {\n    let bar_id = ui.id();\n\n    let mut bar_state = BarState::load(ui.ctx(), bar_id);\n    let button_response = ui.add(button);\n    let inner = bar_state.bar_menu(&button_response, add_contents);\n\n    bar_state.store(ui.ctx(), bar_id);\n    InnerResponse::new(inner.map(|r| r.inner), button_response)\n}\n\npub(crate) const CONTEXT_MENU_ID_STR: &str = \"__egui::context_menu\";\n\n/// Response to secondary clicks (right-clicks) by showing the given menu.\npub fn context_menu(\n    response: &Response,\n    add_contents: impl FnOnce(&mut Ui),\n) -> Option<InnerResponse<()>> {\n    let menu_id = Id::new(CONTEXT_MENU_ID_STR);\n    let mut bar_state = BarState::load(&response.ctx, menu_id);\n\n    MenuRoot::context_click_interaction(response, &mut bar_state);\n    let inner_response = bar_state.show(response, add_contents);\n\n    bar_state.store(&response.ctx, menu_id);\n    inner_response\n}\n\n/// Returns `true` if the context menu is opened for this widget.\npub fn context_menu_opened(response: &Response) -> bool {\n    let menu_id = Id::new(CONTEXT_MENU_ID_STR);\n    let bar_state = BarState::load(&response.ctx, menu_id);\n    bar_state.is_menu_open(response.id)\n}\n\n/// Stores the state for the context menu.\n#[derive(Clone, Default)]\npub struct MenuRootManager {\n    inner: Option<MenuRoot>,\n}\n\nimpl MenuRootManager {\n    /// Show a menu at pointer if right-clicked response.\n    ///\n    /// Should be called from [`Context`] on a [`Response`]\n    pub fn show<R>(\n        &mut self,\n        button: &Response,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> Option<InnerResponse<R>> {\n        if let Some(root) = self.inner.as_mut() {\n            let (menu_response, inner_response) = root.show(button, add_contents);\n            if menu_response.is_close() {\n                self.inner = None;\n            }\n            inner_response\n        } else {\n            None\n        }\n    }\n\n    fn is_menu_open(&self, id: Id) -> bool {\n        self.inner.as_ref().map(|m| m.id) == Some(id)\n    }\n}\n\nimpl std::ops::Deref for MenuRootManager {\n    type Target = Option<MenuRoot>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl std::ops::DerefMut for MenuRootManager {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.inner\n    }\n}\n\n/// Menu root associated with an Id from a Response\n#[derive(Clone)]\npub struct MenuRoot {\n    pub menu_state: Arc<RwLock<MenuState>>,\n    pub id: Id,\n}\n\nimpl MenuRoot {\n    pub fn new(position: Pos2, id: Id) -> Self {\n        Self {\n            menu_state: Arc::new(RwLock::new(MenuState::new(position))),\n            id,\n        }\n    }\n\n    pub fn show<R>(\n        &self,\n        button: &Response,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> (MenuResponse, Option<InnerResponse<R>>) {\n        if self.id == button.id {\n            let inner_response = menu_popup(\n                &button.ctx,\n                button.layer_id,\n                &self.menu_state,\n                self.id,\n                add_contents,\n            );\n            let menu_state = self.menu_state.read();\n\n            let escape_pressed = button.ctx.input(|i| i.key_pressed(Key::Escape));\n            if menu_state.response.is_close()\n                || escape_pressed\n                || inner_response.response.should_close()\n            {\n                return (MenuResponse::Close, Some(inner_response));\n            }\n        }\n        (MenuResponse::Stay, None)\n    }\n\n    /// Interaction with a stationary menu, i.e. fixed in another Ui.\n    ///\n    /// Responds to primary clicks.\n    fn stationary_interaction(button: &Response, root: &mut MenuRootManager) -> MenuResponse {\n        let id = button.id;\n\n        if (button.clicked() && root.is_menu_open(id))\n            || button.ctx.input(|i| i.key_pressed(Key::Escape))\n        {\n            // menu open and button clicked or esc pressed\n            return MenuResponse::Close;\n        } else if (button.clicked() && !root.is_menu_open(id))\n            || (button.hovered() && root.is_some())\n        {\n            // menu not open and button clicked\n            // or button hovered while other menu is open\n            let mut pos = button.rect.left_bottom();\n\n            let menu_frame = Frame::menu(&button.ctx.global_style());\n            pos.x -= menu_frame.total_margin().left; // Make fist button in menu align with the parent button\n            pos.y += button.ctx.global_style().spacing.menu_spacing;\n\n            if let Some(root) = root.inner.as_mut() {\n                let menu_rect = root.menu_state.read().rect;\n                let content_rect = button.ctx.input(|i| i.content_rect());\n\n                if pos.y + menu_rect.height() > content_rect.max.y {\n                    pos.y = content_rect.max.y - menu_rect.height() - button.rect.height();\n                }\n\n                if pos.x + menu_rect.width() > content_rect.max.x {\n                    pos.x = content_rect.max.x - menu_rect.width();\n                }\n            }\n\n            if let Some(to_global) = button.ctx.layer_transform_to_global(button.layer_id) {\n                pos = to_global * pos;\n            }\n\n            return MenuResponse::Create(pos, id);\n        } else if button\n            .ctx\n            .input(|i| i.pointer.any_pressed() && i.pointer.primary_down())\n            && let Some(pos) = button.ctx.input(|i| i.pointer.interact_pos())\n            && let Some(root) = root.inner.as_mut()\n            && root.id == id\n        {\n            // pressed somewhere while this menu is open\n            let in_menu = root.menu_state.read().area_contains(pos);\n            if !in_menu {\n                return MenuResponse::Close;\n            }\n        }\n        MenuResponse::Stay\n    }\n\n    /// Interaction with a context menu (secondary click).\n    pub fn context_interaction(response: &Response, root: &mut Option<Self>) -> MenuResponse {\n        let response = response.interact(Sense::click());\n        let hovered = response.hovered();\n        let secondary_clicked = response.secondary_clicked();\n\n        response.ctx.input(|input| {\n            let pointer = &input.pointer;\n            if let Some(pos) = pointer.interact_pos() {\n                let (in_old_menu, destroy) = if let Some(root) = root {\n                    let in_old_menu = root.menu_state.read().area_contains(pos);\n                    let destroy = !in_old_menu && pointer.any_pressed() && root.id == response.id;\n                    (in_old_menu, destroy)\n                } else {\n                    (false, false)\n                };\n                if !in_old_menu {\n                    if hovered && secondary_clicked {\n                        return MenuResponse::Create(pos, response.id);\n                    } else if destroy || hovered && pointer.primary_down() {\n                        return MenuResponse::Close;\n                    }\n                }\n            }\n            MenuResponse::Stay\n        })\n    }\n\n    pub fn handle_menu_response(root: &mut MenuRootManager, menu_response: MenuResponse) {\n        match menu_response {\n            MenuResponse::Create(pos, id) => {\n                root.inner = Some(Self::new(pos, id));\n            }\n            MenuResponse::Close => root.inner = None,\n            MenuResponse::Stay => {}\n        }\n    }\n\n    /// Respond to secondary (right) clicks.\n    pub fn context_click_interaction(response: &Response, root: &mut MenuRootManager) {\n        let menu_response = Self::context_interaction(response, root);\n        Self::handle_menu_response(root, menu_response);\n    }\n\n    // Responds to primary clicks.\n    pub fn stationary_click_interaction(button: &Response, root: &mut MenuRootManager) {\n        let menu_response = Self::stationary_interaction(button, root);\n        Self::handle_menu_response(root, menu_response);\n    }\n}\n\n#[derive(Copy, Clone, PartialEq, Eq)]\npub enum MenuResponse {\n    Close,\n    Stay,\n    Create(Pos2, Id),\n}\n\nimpl MenuResponse {\n    pub fn is_close(&self) -> bool {\n        *self == Self::Close\n    }\n}\n\npub struct SubMenuButton {\n    text: WidgetText,\n    icon: WidgetText,\n    index: usize,\n}\n\nimpl SubMenuButton {\n    /// The `icon` can be an emoji (e.g. `⏵` right arrow), shown right of the label\n    fn new(text: impl Into<WidgetText>, icon: impl Into<WidgetText>, index: usize) -> Self {\n        Self {\n            text: text.into(),\n            icon: icon.into(),\n            index,\n        }\n    }\n\n    fn visuals<'a>(\n        ui: &'a Ui,\n        response: &Response,\n        menu_state: &MenuState,\n        sub_id: Id,\n    ) -> &'a WidgetVisuals {\n        if menu_state.is_open(sub_id) && !response.hovered() {\n            &ui.style().visuals.widgets.open\n        } else {\n            ui.style().interact(response)\n        }\n    }\n\n    #[inline]\n    pub fn icon(mut self, icon: impl Into<WidgetText>) -> Self {\n        self.icon = icon.into();\n        self\n    }\n\n    pub(crate) fn show(self, ui: &mut Ui, menu_state: &MenuState, sub_id: Id) -> Response {\n        let Self { text, icon, .. } = self;\n\n        let text_style = TextStyle::Button;\n        let sense = Sense::click();\n\n        let text_icon_gap = ui.spacing().item_spacing.x;\n        let button_padding = ui.spacing().button_padding;\n        let total_extra = button_padding + button_padding;\n        let text_available_width = ui.available_width() - total_extra.x;\n        let text_galley = text.into_galley(\n            ui,\n            Some(TextWrapMode::Wrap),\n            text_available_width,\n            text_style.clone(),\n        );\n\n        let icon_available_width = text_available_width - text_galley.size().x;\n        let icon_galley = icon.into_galley(\n            ui,\n            Some(TextWrapMode::Wrap),\n            icon_available_width,\n            text_style,\n        );\n        let text_and_icon_size = Vec2::new(\n            text_galley.size().x + text_icon_gap + icon_galley.size().x,\n            text_galley.size().y.max(icon_galley.size().y),\n        );\n        let mut desired_size = text_and_icon_size + 2.0 * button_padding;\n        desired_size.y = desired_size.y.at_least(ui.spacing().interact_size.y);\n\n        let (rect, response) = ui.allocate_at_least(desired_size, sense);\n        response.widget_info(|| {\n            crate::WidgetInfo::labeled(\n                crate::WidgetType::Button,\n                ui.is_enabled(),\n                text_galley.text(),\n            )\n        });\n\n        if ui.is_rect_visible(rect) {\n            let visuals = Self::visuals(ui, &response, menu_state, sub_id);\n            let text_pos = Align2::LEFT_CENTER\n                .align_size_within_rect(text_galley.size(), rect.shrink2(button_padding))\n                .min;\n            let icon_pos = Align2::RIGHT_CENTER\n                .align_size_within_rect(icon_galley.size(), rect.shrink2(button_padding))\n                .min;\n\n            if ui.visuals().button_frame {\n                ui.painter().rect_filled(\n                    rect.expand(visuals.expansion),\n                    visuals.corner_radius,\n                    visuals.weak_bg_fill,\n                );\n            }\n\n            let text_color = visuals.text_color();\n            ui.painter().galley(text_pos, text_galley, text_color);\n            ui.painter().galley(icon_pos, icon_galley, text_color);\n        }\n        response\n    }\n}\n\npub struct SubMenu {\n    button: SubMenuButton,\n    parent_state: Arc<RwLock<MenuState>>,\n}\n\nimpl SubMenu {\n    fn new(parent_state: Arc<RwLock<MenuState>>, text: impl Into<WidgetText>) -> Self {\n        let index = parent_state.write().next_entry_index();\n        Self {\n            button: SubMenuButton::new(text, \"⏵\", index),\n            parent_state,\n        }\n    }\n\n    pub fn show<R>(\n        self,\n        ui: &mut Ui,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<Option<R>> {\n        let sub_id = ui.id().with(self.button.index);\n        let response = self.button.show(ui, &self.parent_state.read(), sub_id);\n        self.parent_state\n            .write()\n            .submenu_button_interaction(ui, sub_id, &response);\n        let inner =\n            self.parent_state\n                .write()\n                .show_submenu(ui.ctx(), ui.layer_id(), sub_id, add_contents);\n        InnerResponse::new(inner, response)\n    }\n}\n\n/// Components of menu state, public for advanced usage.\n///\n/// Usually you don't need to use it directly.\npub struct MenuState {\n    /// The opened sub-menu and its [`Id`]\n    sub_menu: Option<(Id, Arc<RwLock<Self>>)>,\n\n    /// Bounding box of this menu (without the sub-menu),\n    /// including the frame and everything.\n    pub rect: Rect,\n\n    /// Used to check if any menu in the tree wants to close\n    pub response: MenuResponse,\n\n    /// Used to hash different [`Id`]s for sub-menus\n    entry_count: usize,\n}\n\nimpl MenuState {\n    pub fn new(position: Pos2) -> Self {\n        Self {\n            rect: Rect::from_min_size(position, Vec2::ZERO),\n            sub_menu: None,\n            response: MenuResponse::Stay,\n            entry_count: 0,\n        }\n    }\n\n    /// Close menu hierarchy.\n    pub fn close(&mut self) {\n        self.response = MenuResponse::Close;\n    }\n\n    fn show_submenu<R>(\n        &mut self,\n        ctx: &Context,\n        parent_layer: LayerId,\n        id: Id,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> Option<R> {\n        let (sub_response, response) = self.submenu(id).map(|sub| {\n            let inner_response = menu_popup(ctx, parent_layer, sub, id, add_contents);\n            if inner_response.response.should_close() {\n                sub.write().close();\n            }\n            (sub.read().response, inner_response.inner)\n        })?;\n        self.cascade_close_response(sub_response);\n        Some(response)\n    }\n\n    /// Check if position is in the menu hierarchy's area.\n    pub fn area_contains(&self, pos: Pos2) -> bool {\n        self.rect.contains(pos)\n            || self\n                .sub_menu\n                .as_ref()\n                .is_some_and(|(_, sub)| sub.read().area_contains(pos))\n    }\n\n    fn next_entry_index(&mut self) -> usize {\n        self.entry_count += 1;\n        self.entry_count - 1\n    }\n\n    /// Sense button interaction opening and closing submenu.\n    fn submenu_button_interaction(&mut self, ui: &Ui, sub_id: Id, button: &Response) {\n        let pointer = ui.input(|i| i.pointer.clone());\n        let open = self.is_open(sub_id);\n        if self.moving_towards_current_submenu(&pointer) {\n            // We don't close the submenu if the pointer is on its way to hover it.\n            // ensure to repaint once even when pointer is not moving\n            ui.request_repaint();\n        } else if !open && button.hovered() {\n            // TODO(emilk): open menu to the left if there isn't enough space to the right\n            let mut pos = button.rect.right_top();\n            pos.x = self.rect.right() + ui.spacing().menu_spacing;\n            pos.y -= Frame::menu(ui.style()).total_margin().top; // align the first button in the submenu with the parent button\n\n            self.open_submenu(sub_id, pos);\n        } else if open\n            && ui.response().contains_pointer()\n            && !button.hovered()\n            && !self.hovering_current_submenu(&pointer)\n        {\n            // We are hovering something else in the menu, so close the submenu.\n            self.close_submenu();\n        }\n    }\n\n    /// Check if pointer is moving towards current submenu.\n    fn moving_towards_current_submenu(&self, pointer: &PointerState) -> bool {\n        if pointer.is_still() {\n            return false;\n        }\n\n        if let Some(sub_menu) = self.current_submenu()\n            && let Some(pos) = pointer.hover_pos()\n        {\n            let rect = sub_menu.read().rect;\n            return rect.intersects_ray(pos, pointer.direction().normalized());\n        }\n        false\n    }\n\n    /// Check if pointer is hovering current submenu.\n    fn hovering_current_submenu(&self, pointer: &PointerState) -> bool {\n        if let Some(sub_menu) = self.current_submenu()\n            && let Some(pos) = pointer.hover_pos()\n        {\n            return sub_menu.read().area_contains(pos);\n        }\n        false\n    }\n\n    /// Cascade close response to menu root.\n    fn cascade_close_response(&mut self, response: MenuResponse) {\n        if response.is_close() {\n            self.response = response;\n        }\n    }\n\n    fn is_open(&self, id: Id) -> bool {\n        self.sub_id() == Some(id)\n    }\n\n    fn sub_id(&self) -> Option<Id> {\n        self.sub_menu.as_ref().map(|(id, _)| *id)\n    }\n\n    fn current_submenu(&self) -> Option<&Arc<RwLock<Self>>> {\n        self.sub_menu.as_ref().map(|(_, sub)| sub)\n    }\n\n    fn submenu(&self, id: Id) -> Option<&Arc<RwLock<Self>>> {\n        let (k, sub) = self.sub_menu.as_ref()?;\n        if id == *k { Some(sub) } else { None }\n    }\n\n    /// Open submenu at position, if not already open.\n    fn open_submenu(&mut self, id: Id, pos: Pos2) {\n        if !self.is_open(id) {\n            self.sub_menu = Some((id, Arc::new(RwLock::new(Self::new(pos)))));\n        }\n    }\n\n    fn close_submenu(&mut self) {\n        self.sub_menu = None;\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/os.rs",
    "content": "/// An `enum` of common operating systems.\n#[expect(clippy::upper_case_acronyms)] // `Ios` looks too ugly\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\npub enum OperatingSystem {\n    /// Unknown OS - could be wasm\n    Unknown,\n\n    /// Android OS\n    Android,\n\n    /// Apple iPhone OS\n    IOS,\n\n    /// Linux or Unix other than Android\n    Nix,\n\n    /// macOS\n    Mac,\n\n    /// Windows\n    Windows,\n}\n\nimpl Default for OperatingSystem {\n    fn default() -> Self {\n        Self::from_target_os()\n    }\n}\n\nimpl OperatingSystem {\n    /// Uses the compile-time `target_arch` to identify the OS.\n    pub const fn from_target_os() -> Self {\n        if cfg!(target_arch = \"wasm32\") {\n            Self::Unknown\n        } else if cfg!(target_os = \"android\") {\n            Self::Android\n        } else if cfg!(target_os = \"ios\") {\n            Self::IOS\n        } else if cfg!(target_os = \"macos\") {\n            Self::Mac\n        } else if cfg!(target_os = \"windows\") {\n            Self::Windows\n        } else if cfg!(target_os = \"linux\")\n            || cfg!(target_os = \"dragonfly\")\n            || cfg!(target_os = \"freebsd\")\n            || cfg!(target_os = \"netbsd\")\n            || cfg!(target_os = \"openbsd\")\n        {\n            Self::Nix\n        } else {\n            Self::Unknown\n        }\n    }\n\n    /// Helper: try to guess from the user-agent of a browser.\n    pub fn from_user_agent(user_agent: &str) -> Self {\n        if user_agent.contains(\"Android\") {\n            Self::Android\n        } else if user_agent.contains(\"like Mac\") {\n            Self::IOS\n        } else if user_agent.contains(\"Win\") {\n            Self::Windows\n        } else if user_agent.contains(\"Mac\") {\n            Self::Mac\n        } else if user_agent.contains(\"Linux\")\n            || user_agent.contains(\"X11\")\n            || user_agent.contains(\"Unix\")\n        {\n            Self::Nix\n        } else {\n            log::warn!(\n                \"egui: Failed to guess operating system from User-Agent {user_agent:?}. Please file an issue at https://github.com/emilk/egui/issues\"\n            );\n\n            Self::Unknown\n        }\n    }\n\n    /// Are we either macOS or iOS?\n    pub fn is_mac(&self) -> bool {\n        matches!(self, Self::Mac | Self::IOS)\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/painter.rs",
    "content": "use std::sync::Arc;\n\nuse emath::GuiRounding as _;\nuse epaint::{\n    CircleShape, ClippedShape, CornerRadius, PathStroke, RectShape, Shape, Stroke, StrokeKind,\n    text::{FontsView, Galley, LayoutJob},\n};\n\nuse crate::{\n    Color32, Context, FontId,\n    emath::{Align2, Pos2, Rangef, Rect, Vec2},\n    layers::{LayerId, PaintList, ShapeIdx},\n};\n\n/// Helper to paint shapes and text to a specific region on a specific layer.\n///\n/// All coordinates are screen coordinates in the unit points (one point can consist of many physical pixels).\n///\n/// A [`Painter`] never outlive a single frame/pass.\n#[derive(Clone)]\npub struct Painter {\n    /// Source of fonts and destination of shapes\n    ctx: Context,\n\n    /// For quick access, without having to go via [`Context`].\n    pixels_per_point: f32,\n\n    /// Where we paint\n    layer_id: LayerId,\n\n    /// Everything painted in this [`Painter`] will be clipped against this.\n    /// This means nothing outside of this rectangle will be visible on screen.\n    clip_rect: Rect,\n\n    /// If set, all shapes will have their colors modified to be closer to this.\n    /// This is used to implement grayed out interfaces.\n    fade_to_color: Option<Color32>,\n\n    /// If set, all shapes will have their colors modified with [`Color32::gamma_multiply`] with\n    /// this value as the factor.\n    /// This is used to make interfaces semi-transparent.\n    opacity_factor: f32,\n}\n\nimpl Painter {\n    /// Create a painter to a specific layer within a certain clip rectangle.\n    pub fn new(ctx: Context, layer_id: LayerId, clip_rect: Rect) -> Self {\n        let pixels_per_point = ctx.pixels_per_point();\n        Self {\n            ctx,\n            pixels_per_point,\n            layer_id,\n            clip_rect,\n            fade_to_color: None,\n            opacity_factor: 1.0,\n        }\n    }\n\n    /// Redirect where you are painting.\n    #[must_use]\n    #[inline]\n    pub fn with_layer_id(mut self, layer_id: LayerId) -> Self {\n        self.layer_id = layer_id;\n        self\n    }\n\n    /// Create a painter for a sub-region of this [`Painter`].\n    ///\n    /// The clip-rect of the returned [`Painter`] will be the intersection\n    /// of the given rectangle and the `clip_rect()` of the parent [`Painter`].\n    pub fn with_clip_rect(&self, rect: Rect) -> Self {\n        let mut new_self = self.clone();\n        new_self.clip_rect = rect.intersect(self.clip_rect);\n        new_self\n    }\n\n    /// Redirect where you are painting.\n    ///\n    /// It is undefined behavior to change the [`LayerId`]\n    /// of [`crate::Ui::painter`].\n    pub fn set_layer_id(&mut self, layer_id: LayerId) {\n        self.layer_id = layer_id;\n    }\n\n    /// If set, colors will be modified to look like this\n    #[deprecated = \"Use `multiply_opacity` instead\"]\n    pub fn set_fade_to_color(&mut self, fade_to_color: Option<Color32>) {\n        self.fade_to_color = fade_to_color;\n    }\n\n    /// Set the opacity (alpha multiplier) of everything painted by this painter from this point forward.\n    ///\n    /// `opacity` must be between 0.0 and 1.0, where 0.0 means fully transparent (i.e., invisible)\n    /// and 1.0 means fully opaque.\n    ///\n    /// See also: [`Self::opacity`] and [`Self::multiply_opacity`].\n    pub fn set_opacity(&mut self, opacity: f32) {\n        if opacity.is_finite() {\n            self.opacity_factor = opacity.clamp(0.0, 1.0);\n        }\n    }\n\n    /// Like [`Self::set_opacity`], but multiplies the given value with the current opacity.\n    ///\n    /// See also: [`Self::set_opacity`] and [`Self::opacity`].\n    pub fn multiply_opacity(&mut self, opacity: f32) {\n        if opacity.is_finite() {\n            self.opacity_factor *= opacity.clamp(0.0, 1.0);\n        }\n    }\n\n    /// Read the current opacity of the underlying painter.\n    ///\n    /// See also: [`Self::set_opacity`] and [`Self::multiply_opacity`].\n    #[inline]\n    pub fn opacity(&self) -> f32 {\n        self.opacity_factor\n    }\n\n    /// If `false`, nothing you paint will show up.\n    ///\n    /// Also checks [`Context::will_discard`].\n    pub fn is_visible(&self) -> bool {\n        self.fade_to_color != Some(Color32::TRANSPARENT) && !self.ctx.will_discard()\n    }\n\n    /// If `false`, nothing added to the painter will be visible\n    pub fn set_invisible(&mut self) {\n        self.fade_to_color = Some(Color32::TRANSPARENT);\n    }\n\n    /// Get a reference to the parent [`Context`].\n    #[inline]\n    pub fn ctx(&self) -> &Context {\n        &self.ctx\n    }\n\n    /// Number of physical pixels for each logical UI point.\n    #[inline]\n    pub fn pixels_per_point(&self) -> f32 {\n        self.pixels_per_point\n    }\n\n    /// Read-only access to the shared [`FontsView`].\n    ///\n    /// See [`Context`] documentation for how locks work.\n    #[inline]\n    pub fn fonts<R>(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R {\n        self.ctx.fonts(reader)\n    }\n\n    /// Read-write access to the shared [`FontsView`].\n    ///\n    /// See [`Context`] documentation for how locks work.\n    #[inline]\n    pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R {\n        self.ctx.fonts_mut(reader)\n    }\n\n    /// Where we paint\n    #[inline]\n    pub fn layer_id(&self) -> LayerId {\n        self.layer_id\n    }\n\n    /// Everything painted in this [`Painter`] will be clipped against this.\n    /// This means nothing outside of this rectangle will be visible on screen.\n    #[inline]\n    pub fn clip_rect(&self) -> Rect {\n        self.clip_rect\n    }\n\n    /// Constrain the rectangle in which we can paint.\n    ///\n    /// Short for `painter.set_clip_rect(painter.clip_rect().intersect(new_clip_rect))`.\n    ///\n    /// See also: [`Self::clip_rect`] and [`Self::set_clip_rect`].\n    #[inline]\n    pub fn shrink_clip_rect(&mut self, new_clip_rect: Rect) {\n        self.clip_rect = self.clip_rect.intersect(new_clip_rect);\n    }\n\n    /// Everything painted in this [`Painter`] will be clipped against this.\n    /// This means nothing outside of this rectangle will be visible on screen.\n    ///\n    /// Warning: growing the clip rect might cause unexpected results!\n    /// When in doubt, use [`Self::shrink_clip_rect`] instead.\n    #[inline]\n    pub fn set_clip_rect(&mut self, clip_rect: Rect) {\n        self.clip_rect = clip_rect;\n    }\n\n    /// Useful for pixel-perfect rendering of lines that are one pixel wide (or any odd number of pixels).\n    #[inline]\n    pub fn round_to_pixel_center(&self, point: f32) -> f32 {\n        point.round_to_pixel_center(self.pixels_per_point())\n    }\n\n    /// Useful for pixel-perfect rendering of lines that are one pixel wide (or any odd number of pixels).\n    #[deprecated = \"Use `emath::GuiRounding` with `painter.pixels_per_point()` instead\"]\n    #[inline]\n    pub fn round_pos_to_pixel_center(&self, pos: Pos2) -> Pos2 {\n        pos.round_to_pixel_center(self.pixels_per_point())\n    }\n\n    /// Useful for pixel-perfect rendering of filled shapes.\n    #[deprecated = \"Use `emath::GuiRounding` with `painter.pixels_per_point()` instead\"]\n    #[inline]\n    pub fn round_to_pixel(&self, point: f32) -> f32 {\n        point.round_to_pixels(self.pixels_per_point())\n    }\n\n    /// Useful for pixel-perfect rendering.\n    #[deprecated = \"Use `emath::GuiRounding` with `painter.pixels_per_point()` instead\"]\n    #[inline]\n    pub fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 {\n        vec.round_to_pixels(self.pixels_per_point())\n    }\n\n    /// Useful for pixel-perfect rendering.\n    #[deprecated = \"Use `emath::GuiRounding` with `painter.pixels_per_point()` instead\"]\n    #[inline]\n    pub fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 {\n        pos.round_to_pixels(self.pixels_per_point())\n    }\n\n    /// Useful for pixel-perfect rendering.\n    #[deprecated = \"Use `emath::GuiRounding` with `painter.pixels_per_point()` instead\"]\n    #[inline]\n    pub fn round_rect_to_pixels(&self, rect: Rect) -> Rect {\n        rect.round_to_pixels(self.pixels_per_point())\n    }\n}\n\n/// ## Low level\nimpl Painter {\n    #[inline]\n    fn paint_list<R>(&self, writer: impl FnOnce(&mut PaintList) -> R) -> R {\n        self.ctx.graphics_mut(|g| writer(g.entry(self.layer_id)))\n    }\n\n    fn transform_shape(&self, shape: &mut Shape) {\n        if let Some(fade_to_color) = self.fade_to_color {\n            tint_shape_towards(shape, fade_to_color);\n        }\n        if self.opacity_factor < 1.0 {\n            multiply_opacity(shape, self.opacity_factor);\n        }\n    }\n\n    /// It is up to the caller to make sure there is room for this.\n    /// Can be used for free painting.\n    /// NOTE: all coordinates are screen coordinates!\n    pub fn add(&self, shape: impl Into<Shape>) -> ShapeIdx {\n        if self.fade_to_color == Some(Color32::TRANSPARENT) || self.opacity_factor == 0.0 {\n            self.paint_list(|l| l.add(self.clip_rect, Shape::Noop))\n        } else {\n            let mut shape = shape.into();\n            self.transform_shape(&mut shape);\n            self.paint_list(|l| l.add(self.clip_rect, shape))\n        }\n    }\n\n    /// Add many shapes at once.\n    ///\n    /// Calling this once is generally faster than calling [`Self::add`] multiple times.\n    pub fn extend<I: IntoIterator<Item = Shape>>(&self, shapes: I) {\n        if self.fade_to_color == Some(Color32::TRANSPARENT) || self.opacity_factor == 0.0 {\n            return;\n        }\n        if self.fade_to_color.is_some() || self.opacity_factor < 1.0 {\n            let shapes = shapes.into_iter().map(|mut shape| {\n                self.transform_shape(&mut shape);\n                shape\n            });\n            self.paint_list(|l| l.extend(self.clip_rect, shapes));\n        } else {\n            self.paint_list(|l| l.extend(self.clip_rect, shapes));\n        }\n    }\n\n    /// Modify an existing [`Shape`].\n    pub fn set(&self, idx: ShapeIdx, shape: impl Into<Shape>) {\n        if self.fade_to_color == Some(Color32::TRANSPARENT) {\n            return;\n        }\n        let mut shape = shape.into();\n        self.transform_shape(&mut shape);\n        self.paint_list(|l| l.set(idx, self.clip_rect, shape));\n    }\n\n    /// Access all shapes added this frame.\n    pub fn for_each_shape(&self, mut reader: impl FnMut(&ClippedShape)) {\n        self.ctx.graphics(|g| {\n            if let Some(list) = g.get(self.layer_id) {\n                for c in list.all_entries() {\n                    reader(c);\n                }\n            }\n        });\n    }\n}\n\n/// ## Debug painting\nimpl Painter {\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn debug_rect(&self, rect: Rect, color: Color32, text: impl ToString) {\n        self.rect(\n            rect,\n            0.0,\n            color.additive().linear_multiply(0.015),\n            (1.0, color),\n            StrokeKind::Outside,\n        );\n        self.text(\n            rect.min,\n            Align2::LEFT_TOP,\n            text.to_string(),\n            FontId::monospace(12.0),\n            color,\n        );\n    }\n\n    pub fn error(&self, pos: Pos2, text: impl std::fmt::Display) -> Rect {\n        let color = self.ctx.global_style().visuals.error_fg_color;\n        self.debug_text(pos, Align2::LEFT_TOP, color, format!(\"🔥 {text}\"))\n    }\n\n    /// Text with a background.\n    ///\n    /// See also [`Context::debug_text`].\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn debug_text(\n        &self,\n        pos: Pos2,\n        anchor: Align2,\n        color: Color32,\n        text: impl ToString,\n    ) -> Rect {\n        let galley = self.layout_no_wrap(text.to_string(), FontId::monospace(12.0), color);\n        let rect = anchor.anchor_size(pos, galley.size());\n        let frame_rect = rect.expand(2.0);\n\n        let is_text_bright = color.is_additive() || epaint::Rgba::from(color).intensity() > 0.5;\n        let bg_color = if is_text_bright {\n            Color32::from_black_alpha(150)\n        } else {\n            Color32::from_white_alpha(150)\n        };\n        self.add(Shape::rect_filled(frame_rect, 0.0, bg_color));\n        self.galley(rect.min, galley, color);\n        frame_rect\n    }\n}\n\n/// # Paint different primitives\nimpl Painter {\n    /// Paints a line from the first point to the second.\n    pub fn line_segment(&self, points: [Pos2; 2], stroke: impl Into<Stroke>) -> ShapeIdx {\n        self.add(Shape::LineSegment {\n            points,\n            stroke: stroke.into(),\n        })\n    }\n\n    /// Paints a line connecting the points.\n    /// NOTE: all coordinates are screen coordinates!\n    pub fn line(&self, points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> ShapeIdx {\n        self.add(Shape::line(points, stroke))\n    }\n\n    /// Paints a horizontal line.\n    pub fn hline(&self, x: impl Into<Rangef>, y: f32, stroke: impl Into<Stroke>) -> ShapeIdx {\n        self.add(Shape::hline(x, y, stroke))\n    }\n\n    /// Paints a vertical line.\n    pub fn vline(&self, x: f32, y: impl Into<Rangef>, stroke: impl Into<Stroke>) -> ShapeIdx {\n        self.add(Shape::vline(x, y, stroke))\n    }\n\n    pub fn circle(\n        &self,\n        center: Pos2,\n        radius: f32,\n        fill_color: impl Into<Color32>,\n        stroke: impl Into<Stroke>,\n    ) -> ShapeIdx {\n        self.add(CircleShape {\n            center,\n            radius,\n            fill: fill_color.into(),\n            stroke: stroke.into(),\n        })\n    }\n\n    pub fn circle_filled(\n        &self,\n        center: Pos2,\n        radius: f32,\n        fill_color: impl Into<Color32>,\n    ) -> ShapeIdx {\n        self.add(CircleShape {\n            center,\n            radius,\n            fill: fill_color.into(),\n            stroke: Default::default(),\n        })\n    }\n\n    pub fn circle_stroke(&self, center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> ShapeIdx {\n        self.add(CircleShape {\n            center,\n            radius,\n            fill: Default::default(),\n            stroke: stroke.into(),\n        })\n    }\n\n    /// See also [`Self::rect_filled`] and [`Self::rect_stroke`].\n    pub fn rect(\n        &self,\n        rect: Rect,\n        corner_radius: impl Into<CornerRadius>,\n        fill_color: impl Into<Color32>,\n        stroke: impl Into<Stroke>,\n        stroke_kind: StrokeKind,\n    ) -> ShapeIdx {\n        self.add(RectShape::new(\n            rect,\n            corner_radius,\n            fill_color,\n            stroke,\n            stroke_kind,\n        ))\n    }\n\n    pub fn rect_filled(\n        &self,\n        rect: Rect,\n        corner_radius: impl Into<CornerRadius>,\n        fill_color: impl Into<Color32>,\n    ) -> ShapeIdx {\n        self.add(RectShape::filled(rect, corner_radius, fill_color))\n    }\n\n    pub fn rect_stroke(\n        &self,\n        rect: Rect,\n        corner_radius: impl Into<CornerRadius>,\n        stroke: impl Into<Stroke>,\n        stroke_kind: StrokeKind,\n    ) -> ShapeIdx {\n        self.add(RectShape::stroke(rect, corner_radius, stroke, stroke_kind))\n    }\n\n    /// Show an arrow starting at `origin` and going in the direction of `vec`, with the length `vec.length()`.\n    pub fn arrow(&self, origin: Pos2, vec: Vec2, stroke: impl Into<Stroke>) {\n        use crate::emath::Rot2;\n        let rot = Rot2::from_angle(std::f32::consts::TAU / 10.0);\n        let tip_length = vec.length() / 4.0;\n        let tip = origin + vec;\n        let dir = vec.normalized();\n        let stroke = stroke.into();\n        self.line_segment([origin, tip], stroke);\n        self.line_segment([tip, tip - tip_length * (rot * dir)], stroke);\n        self.line_segment([tip, tip - tip_length * (rot.inverse() * dir)], stroke);\n    }\n\n    /// An image at the given position.\n    ///\n    /// `uv` should normally be `Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0))`\n    /// unless you want to crop or flip the image.\n    ///\n    /// `tint` is a color multiplier. Use [`Color32::WHITE`] if you don't want to tint the image.\n    ///\n    /// Usually it is easier to use [`crate::Image::paint_at`] instead:\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let rect = egui::Rect::from_min_size(Default::default(), egui::Vec2::splat(100.0));\n    /// egui::Image::new(egui::include_image!(\"../assets/ferris.png\"))\n    ///     .corner_radius(5)\n    ///     .tint(egui::Color32::LIGHT_BLUE)\n    ///     .paint_at(ui, rect);\n    /// # });\n    /// ```\n    pub fn image(\n        &self,\n        texture_id: epaint::TextureId,\n        rect: Rect,\n        uv: Rect,\n        tint: Color32,\n    ) -> ShapeIdx {\n        self.add(Shape::image(texture_id, rect, uv, tint))\n    }\n}\n\n/// ## Text\nimpl Painter {\n    /// Lay out and paint some text.\n    ///\n    /// To center the text at the given position, use `Align2::CENTER_CENTER`.\n    ///\n    /// To find out the size of text before painting it, use\n    /// [`Self::layout`] or [`Self::layout_no_wrap`].\n    ///\n    /// Returns where the text ended up.\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn text(\n        &self,\n        pos: Pos2,\n        anchor: Align2,\n        text: impl ToString,\n        font_id: FontId,\n        text_color: Color32,\n    ) -> Rect {\n        let galley = self.layout_no_wrap(text.to_string(), font_id, text_color);\n        let rect = anchor.anchor_size(pos, galley.size());\n        self.galley(rect.min, galley, text_color);\n        rect\n    }\n\n    /// Will wrap text at the given width and line break at `\\n`.\n    ///\n    /// Paint the results with [`Self::galley`].\n    #[inline]\n    #[must_use]\n    pub fn layout(\n        &self,\n        text: String,\n        font_id: FontId,\n        color: crate::Color32,\n        wrap_width: f32,\n    ) -> Arc<Galley> {\n        self.fonts_mut(|f| f.layout(text, font_id, color, wrap_width))\n    }\n\n    /// Will line break at `\\n`.\n    ///\n    /// Paint the results with [`Self::galley`].\n    #[inline]\n    #[must_use]\n    pub fn layout_no_wrap(\n        &self,\n        text: String,\n        font_id: FontId,\n        color: crate::Color32,\n    ) -> Arc<Galley> {\n        self.fonts_mut(|f| f.layout(text, font_id, color, f32::INFINITY))\n    }\n\n    /// Lay out this text layut job in a galley.\n    ///\n    /// Paint the results with [`Self::galley`].\n    #[inline]\n    #[must_use]\n    pub fn layout_job(&self, layout_job: LayoutJob) -> Arc<Galley> {\n        self.fonts_mut(|f| f.layout_job(layout_job))\n    }\n\n    /// Paint text that has already been laid out in a [`Galley`].\n    ///\n    /// You can create the [`Galley`] with [`Self::layout`] or [`Self::layout_job`].\n    ///\n    /// Any uncolored parts of the [`Galley`] (using [`Color32::PLACEHOLDER`]) will be replaced with the given color.\n    ///\n    /// Any non-placeholder color in the galley takes precedence over this fallback color.\n    #[inline]\n    pub fn galley(&self, pos: Pos2, galley: Arc<Galley>, fallback_color: Color32) {\n        if !galley.is_empty() {\n            self.add(Shape::galley(pos, galley, fallback_color));\n        }\n    }\n\n    /// Paint text that has already been laid out in a [`Galley`].\n    ///\n    /// You can create the [`Galley`] with [`Self::layout`].\n    ///\n    /// All text color in the [`Galley`] will be replaced with the given color.\n    #[inline]\n    pub fn galley_with_override_text_color(\n        &self,\n        pos: Pos2,\n        galley: Arc<Galley>,\n        text_color: Color32,\n    ) {\n        if !galley.is_empty() {\n            self.add(Shape::galley_with_override_text_color(\n                pos, galley, text_color,\n            ));\n        }\n    }\n}\n\nfn tint_shape_towards(shape: &mut Shape, target: Color32) {\n    epaint::shape_transform::adjust_colors(shape, move |color| {\n        if *color != Color32::PLACEHOLDER {\n            *color = crate::ecolor::tint_color_towards(*color, target);\n        }\n    });\n}\n\nfn multiply_opacity(shape: &mut Shape, opacity: f32) {\n    epaint::shape_transform::adjust_colors(shape, move |color| {\n        if *color != Color32::PLACEHOLDER {\n            *color = color.gamma_multiply(opacity);\n        }\n    });\n}\n"
  },
  {
    "path": "crates/egui/src/pass_state.rs",
    "content": "use ahash::HashMap;\n\nuse crate::{Align, Id, IdMap, LayerId, Rangef, Rect, Vec2, WidgetRects, id::IdSet, style};\n\n#[cfg(debug_assertions)]\nuse crate::{Align2, Color32, FontId, NumExt as _, Painter, pos2};\n\n/// Reset at the start of each frame.\n#[derive(Clone, Debug, Default)]\npub struct TooltipPassState {\n    /// If a tooltip has been shown this frame, where was it?\n    /// This is used to prevent multiple tooltips to cover each other.\n    pub widget_tooltips: IdMap<PerWidgetTooltipState>,\n}\n\nimpl TooltipPassState {\n    pub fn clear(&mut self) {\n        let Self { widget_tooltips } = self;\n        widget_tooltips.clear();\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct PerWidgetTooltipState {\n    /// Bounding rectangle for all widget and all previous tooltips.\n    pub bounding_rect: Rect,\n\n    /// How many tooltips have been shown for this widget this frame?\n    pub tooltip_count: usize,\n}\n\n#[derive(Clone, Debug, Default)]\npub struct PerLayerState {\n    /// Is there any open popup (menus, combo-boxes, etc)?\n    ///\n    /// Does NOT include tooltips.\n    pub open_popups: IdSet,\n\n    /// Which widget is showing a tooltip (if any)?\n    ///\n    /// Only one widget per layer may show a tooltip.\n    /// But if a tooltip contains a tooltip, you can show a tooltip on top of a tooltip.\n    pub widget_with_tooltip: Option<Id>,\n}\n\n#[derive(Clone, Debug)]\npub struct ScrollTarget {\n    // The range that the scroll area should scroll to.\n    pub range: Rangef,\n\n    /// How should we align the rect within the visible area?\n    /// If `align` is [`Align::TOP`] it means \"put the top of the rect at the top of the scroll area\", etc.\n    /// If `align` is `None`, it'll scroll enough to bring the UI into view.\n    pub align: Option<Align>,\n\n    /// How should the scroll be animated?\n    pub animation: style::ScrollAnimation,\n}\n\nimpl ScrollTarget {\n    pub fn new(range: Rangef, align: Option<Align>, animation: style::ScrollAnimation) -> Self {\n        Self {\n            range,\n            align,\n            animation,\n        }\n    }\n}\n\n#[derive(Clone)]\npub struct AccessKitPassState {\n    pub nodes: IdMap<accesskit::Node>,\n    pub parent_map: IdMap<Id>,\n}\n\n#[cfg(debug_assertions)]\n#[derive(Clone)]\npub struct DebugRect {\n    pub rect: Rect,\n    pub callstack: String,\n    pub is_clicking: bool,\n}\n\n#[cfg(debug_assertions)]\nimpl DebugRect {\n    pub fn paint(self, painter: &Painter) {\n        let Self {\n            rect,\n            callstack,\n            is_clicking,\n        } = self;\n\n        let ctx = painter.ctx();\n\n        // Paint rectangle around widget:\n        {\n            // Print width and height:\n            let text_color = if ctx.global_style().visuals.dark_mode {\n                Color32::WHITE\n            } else {\n                Color32::BLACK\n            };\n            painter.debug_text(\n                rect.left_center() + 2.0 * Vec2::LEFT,\n                Align2::RIGHT_CENTER,\n                text_color,\n                format!(\"H: {:.1}\", rect.height()),\n            );\n            painter.debug_text(\n                rect.center_top(),\n                Align2::CENTER_BOTTOM,\n                text_color,\n                format!(\"W: {:.1}\", rect.width()),\n            );\n\n            // Paint rect:\n            let rect_fg_color = if is_clicking {\n                Color32::WHITE\n            } else {\n                Color32::LIGHT_BLUE\n            };\n            let rect_bg_color = Color32::BLUE.gamma_multiply(0.5);\n            painter.rect(\n                rect,\n                0.0,\n                rect_bg_color,\n                (1.0, rect_fg_color),\n                crate::StrokeKind::Outside,\n            );\n        }\n\n        if !callstack.is_empty() {\n            let font_id = FontId::monospace(12.0);\n            let text = format!(\"{callstack}\\n\\n(click to copy)\");\n            let text_color = Color32::WHITE;\n            let galley = painter.layout_no_wrap(text, font_id, text_color);\n\n            // Position the text either under or above:\n            let content_rect = ctx.content_rect();\n            let y = if galley.size().y <= rect.top() {\n                // Above\n                rect.top() - galley.size().y - 16.0\n            } else {\n                // Below\n                rect.bottom()\n            };\n\n            let y = y\n                .at_most(content_rect.bottom() - galley.size().y)\n                .at_least(0.0);\n\n            let x = rect\n                .left()\n                .at_most(content_rect.right() - galley.size().x)\n                .at_least(0.0);\n            let text_pos = pos2(x, y);\n\n            let text_bg_color = Color32::from_black_alpha(180);\n            let text_rect_stroke_color = if is_clicking {\n                Color32::WHITE\n            } else {\n                text_bg_color\n            };\n            let text_rect = Rect::from_min_size(text_pos, galley.size());\n            painter.rect(\n                text_rect,\n                0.0,\n                text_bg_color,\n                (1.0, text_rect_stroke_color),\n                crate::StrokeKind::Middle,\n            );\n            painter.galley(text_pos, galley, text_color);\n\n            if is_clicking {\n                ctx.copy_text(callstack);\n            }\n        }\n    }\n}\n\n/// State that is collected during a pass, then saved for the next pass,\n/// and then cleared.\n///\n/// (NOTE: we usually run only one pass per frame).\n///\n/// One per viewport.\n#[derive(Clone)]\npub struct PassState {\n    /// All [`Id`]s that were used this pass.\n    pub used_ids: IdMap<Rect>,\n\n    /// All widgets produced this pass.\n    pub widgets: WidgetRects,\n\n    /// Per-layer state.\n    ///\n    /// Not all layers registers themselves there though.\n    pub layers: HashMap<LayerId, PerLayerState>,\n\n    pub tooltips: TooltipPassState,\n\n    /// Starts off as the `screen_rect`, shrinks as panels are added.\n    /// The [`crate::CentralPanel`] does not change this.\n    pub available_rect: Rect,\n\n    /// Starts off as the `screen_rect`, shrinks as panels are added.\n    /// The [`crate::CentralPanel`] retracts from this.\n    pub unused_rect: Rect,\n\n    /// How much space is used by panels.\n    pub used_by_panels: Rect,\n\n    /// The current scroll area should scroll to this range (horizontal, vertical).\n    pub scroll_target: [Option<ScrollTarget>; 2],\n\n    /// The current scroll area should scroll by this much.\n    ///\n    /// The delta dictates how the _content_ should move.\n    ///\n    /// A positive X-value indicates the content is being moved right,\n    /// as when swiping right on a touch-screen or track-pad with natural scrolling.\n    ///\n    /// A positive Y-value indicates the content is being moved down,\n    /// as when swiping down on a touch-screen or track-pad with natural scrolling.\n    pub scroll_delta: (Vec2, style::ScrollAnimation),\n\n    pub accesskit_state: Option<AccessKitPassState>,\n\n    /// Highlight these widgets the next pass.\n    pub highlight_next_pass: IdSet,\n\n    #[cfg(debug_assertions)]\n    pub debug_rect: Option<DebugRect>,\n}\n\nimpl Default for PassState {\n    fn default() -> Self {\n        Self {\n            used_ids: Default::default(),\n            widgets: Default::default(),\n            layers: Default::default(),\n            tooltips: Default::default(),\n            available_rect: Rect::NAN,\n            unused_rect: Rect::NAN,\n            used_by_panels: Rect::NAN,\n            scroll_target: [None, None],\n            scroll_delta: (Vec2::default(), style::ScrollAnimation::none()),\n            accesskit_state: None,\n            highlight_next_pass: Default::default(),\n\n            #[cfg(debug_assertions)]\n            debug_rect: None,\n        }\n    }\n}\n\nimpl PassState {\n    pub(crate) fn begin_pass(&mut self, content_rect: Rect) {\n        profiling::function_scope!();\n        let Self {\n            used_ids,\n            widgets,\n            tooltips,\n            layers,\n            available_rect,\n            unused_rect,\n            used_by_panels,\n            scroll_target,\n            scroll_delta,\n            accesskit_state,\n            highlight_next_pass,\n\n            #[cfg(debug_assertions)]\n            debug_rect,\n        } = self;\n\n        used_ids.clear();\n        widgets.clear();\n        tooltips.clear();\n        layers.clear();\n        *available_rect = content_rect;\n        *unused_rect = content_rect;\n        *used_by_panels = Rect::NOTHING;\n        *scroll_target = [None, None];\n        *scroll_delta = Default::default();\n\n        #[cfg(debug_assertions)]\n        {\n            *debug_rect = None;\n        }\n\n        *accesskit_state = None;\n\n        highlight_next_pass.clear();\n    }\n\n    /// How much space is still available after panels has been added.\n    pub(crate) fn available_rect(&self) -> Rect {\n        debug_assert!(\n            self.available_rect.is_finite(),\n            \"Called `available_rect()` before `Context::run()`\"\n        );\n        self.available_rect\n    }\n\n    /// Shrink `available_rect`.\n    pub(crate) fn allocate_left_panel(&mut self, panel_rect: Rect) {\n        debug_assert!(\n            panel_rect.min.distance(self.available_rect.min) < 0.1,\n            \"Mismatching left panel. You must not create a panel from within another panel.\"\n        );\n        self.available_rect.min.x = panel_rect.max.x;\n        self.unused_rect.min.x = panel_rect.max.x;\n        self.used_by_panels |= panel_rect;\n    }\n\n    /// Shrink `available_rect`.\n    pub(crate) fn allocate_right_panel(&mut self, panel_rect: Rect) {\n        debug_assert!(\n            panel_rect.max.distance(self.available_rect.max) < 0.1,\n            \"Mismatching right panel. You must not create a panel from within another panel.\"\n        );\n        self.available_rect.max.x = panel_rect.min.x;\n        self.unused_rect.max.x = panel_rect.min.x;\n        self.used_by_panels |= panel_rect;\n    }\n\n    /// Shrink `available_rect`.\n    pub(crate) fn allocate_top_panel(&mut self, panel_rect: Rect) {\n        debug_assert!(\n            panel_rect.min.distance(self.available_rect.min) < 0.1,\n            \"Mismatching top panel. You must not create a panel from within another panel.\"\n        );\n        self.available_rect.min.y = panel_rect.max.y;\n        self.unused_rect.min.y = panel_rect.max.y;\n        self.used_by_panels |= panel_rect;\n    }\n\n    /// Shrink `available_rect`.\n    pub(crate) fn allocate_bottom_panel(&mut self, panel_rect: Rect) {\n        debug_assert!(\n            panel_rect.max.distance(self.available_rect.max) < 0.1,\n            \"Mismatching bottom panel. You must not create a panel from within another panel.\"\n        );\n        self.available_rect.max.y = panel_rect.min.y;\n        self.unused_rect.max.y = panel_rect.min.y;\n        self.used_by_panels |= panel_rect;\n    }\n\n    pub(crate) fn allocate_central_panel(&mut self, panel_rect: Rect) {\n        // Note: we do not shrink `available_rect`, because\n        // we allow windows to cover the CentralPanel.\n        self.unused_rect = Rect::NOTHING; // Nothing left unused after this\n        self.used_by_panels |= panel_rect;\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/placer.rs",
    "content": "use crate::{Layout, Painter, Pos2, Rect, Region, Vec2, grid, vec2};\nuse emath::GuiRounding as _;\n\n#[cfg(debug_assertions)]\nuse crate::{Align2, Color32, Stroke};\n\npub(crate) struct Placer {\n    /// If set this will take precedence over [`crate::layout`].\n    grid: Option<grid::GridLayout>,\n    layout: Layout,\n    region: Region,\n}\n\nimpl Placer {\n    pub(crate) fn new(max_rect: Rect, layout: Layout) -> Self {\n        let region = layout.region_from_max_rect(max_rect);\n        Self {\n            grid: None,\n            layout,\n            region,\n        }\n    }\n\n    #[inline(always)]\n    pub(crate) fn set_grid(&mut self, grid: grid::GridLayout) {\n        self.grid = Some(grid);\n    }\n\n    pub(crate) fn save_grid(&mut self) {\n        if let Some(grid) = &mut self.grid {\n            grid.save();\n        }\n    }\n\n    #[inline(always)]\n    pub(crate) fn grid(&self) -> Option<&grid::GridLayout> {\n        self.grid.as_ref()\n    }\n\n    #[inline(always)]\n    pub(crate) fn is_grid(&self) -> bool {\n        self.grid.is_some()\n    }\n\n    #[inline(always)]\n    pub(crate) fn layout(&self) -> &Layout {\n        &self.layout\n    }\n\n    #[inline(always)]\n    pub(crate) fn prefer_right_to_left(&self) -> bool {\n        self.layout.prefer_right_to_left()\n    }\n\n    #[inline(always)]\n    pub(crate) fn min_rect(&self) -> Rect {\n        self.region.min_rect\n    }\n\n    #[inline(always)]\n    pub(crate) fn max_rect(&self) -> Rect {\n        self.region.max_rect\n    }\n\n    #[inline(always)]\n    pub(crate) fn force_set_min_rect(&mut self, min_rect: Rect) {\n        self.region.min_rect = min_rect;\n    }\n\n    #[inline(always)]\n    pub(crate) fn cursor(&self) -> Rect {\n        self.region.cursor\n    }\n\n    #[inline(always)]\n    pub(crate) fn set_cursor(&mut self, cursor: Rect) {\n        self.region.cursor = cursor;\n    }\n}\n\nimpl Placer {\n    pub(crate) fn align_size_within_rect(&self, size: Vec2, outer: Rect) -> Rect {\n        if let Some(grid) = &self.grid {\n            grid.align_size_within_rect(size, outer)\n        } else {\n            self.layout.align_size_within_rect(size, outer)\n        }\n    }\n\n    pub(crate) fn available_rect_before_wrap(&self) -> Rect {\n        if let Some(grid) = &self.grid {\n            grid.available_rect(&self.region)\n        } else {\n            self.layout.available_rect_before_wrap(&self.region)\n        }\n        .round_ui()\n    }\n\n    /// Amount of space available for a widget.\n    /// For wrapping layouts, this is the maximum (after wrap).\n    pub(crate) fn available_size(&self) -> Vec2 {\n        if let Some(grid) = &self.grid {\n            grid.available_rect(&self.region).size()\n        } else {\n            self.layout.available_size(&self.region)\n        }\n    }\n\n    /// Returns where to put the next widget that is of the given size.\n    /// The returned `frame_rect` will always be justified along the cross axis.\n    /// This is what you then pass to `advance_after_rects`.\n    /// Use `justify_and_align` to get the inner `widget_rect`.\n    pub(crate) fn next_space(&self, child_size: Vec2, item_spacing: Vec2) -> Rect {\n        debug_assert!(\n            0.0 <= child_size.x && 0.0 <= child_size.y,\n            \"Negative child size: {child_size:?}\"\n        );\n        self.region.sanity_check();\n        if let Some(grid) = &self.grid {\n            grid.next_cell(self.region.cursor, child_size)\n        } else {\n            self.layout\n                .next_frame(&self.region, child_size, item_spacing)\n        }\n    }\n\n    /// Where do we expect a zero-sized widget to be placed?\n    pub(crate) fn next_widget_position(&self) -> Pos2 {\n        if let Some(grid) = &self.grid {\n            grid.next_cell(self.region.cursor, Vec2::ZERO).center()\n        } else {\n            self.layout.next_widget_position(&self.region)\n        }\n    }\n\n    /// Apply justify or alignment after calling `next_space`.\n    pub(crate) fn justify_and_align(&self, rect: Rect, child_size: Vec2) -> Rect {\n        debug_assert!(!rect.any_nan(), \"rect: {rect:?}\");\n        debug_assert!(!child_size.any_nan(), \"child_size is NaN: {child_size:?}\");\n\n        if let Some(grid) = &self.grid {\n            grid.justify_and_align(rect, child_size)\n        } else {\n            self.layout.justify_and_align(rect, child_size)\n        }\n    }\n\n    /// Advance the cursor by this many points.\n    /// [`Self::min_rect`] will expand to contain the cursor.\n    ///\n    /// Note that `advance_cursor` isn't supported when in a grid layout.\n    pub(crate) fn advance_cursor(&mut self, amount: f32) {\n        debug_assert!(\n            self.grid.is_none(),\n            \"You cannot advance the cursor when in a grid layout\"\n        );\n        self.layout.advance_cursor(&mut self.region, amount);\n    }\n\n    /// Advance cursor after a widget was added to a specific rectangle\n    /// and expand the region `min_rect`.\n    ///\n    /// * `frame_rect`: the frame inside which a widget was e.g. centered\n    /// * `widget_rect`: the actual rect used by the widget\n    pub(crate) fn advance_after_rects(\n        &mut self,\n        frame_rect: Rect,\n        widget_rect: Rect,\n        item_spacing: Vec2,\n    ) {\n        debug_assert!(!frame_rect.any_nan(), \"frame_rect: {frame_rect:?}\");\n        debug_assert!(\n            !widget_rect.any_nan(),\n            \"widget_rect is NaN: {widget_rect:?}\"\n        );\n        self.region.sanity_check();\n\n        if let Some(grid) = &mut self.grid {\n            grid.advance(&mut self.region.cursor, frame_rect, widget_rect);\n        } else {\n            self.layout.advance_after_rects(\n                &mut self.region.cursor,\n                frame_rect,\n                widget_rect,\n                item_spacing,\n            );\n        }\n\n        self.expand_to_include_rect(frame_rect); // e.g. for centered layouts: pretend we used whole frame\n\n        self.region.sanity_check();\n    }\n\n    /// Move to the next row in a grid layout or wrapping layout.\n    /// Otherwise does nothing.\n    pub(crate) fn end_row(&mut self, item_spacing: Vec2, painter: &Painter) {\n        if let Some(grid) = &mut self.grid {\n            grid.end_row(&mut self.region.cursor, painter);\n        } else {\n            self.layout.end_row(&mut self.region, item_spacing);\n        }\n    }\n\n    /// Set row height in horizontal wrapping layout.\n    pub(crate) fn set_row_height(&mut self, height: f32) {\n        self.layout.set_row_height(&mut self.region, height);\n    }\n}\n\nimpl Placer {\n    /// Expand the `min_rect` and `max_rect` of this ui to include a child at the given rect.\n    pub(crate) fn expand_to_include_rect(&mut self, rect: Rect) {\n        self.region.expand_to_include_rect(rect);\n    }\n\n    /// Expand the `min_rect` and `max_rect` of this ui to include a child at the given x-coordinate.\n    pub(crate) fn expand_to_include_x(&mut self, x: f32) {\n        self.region.expand_to_include_x(x);\n    }\n\n    /// Expand the `min_rect` and `max_rect` of this ui to include a child at the given y-coordinate.\n    pub(crate) fn expand_to_include_y(&mut self, y: f32) {\n        self.region.expand_to_include_y(y);\n    }\n\n    fn next_widget_space_ignore_wrap_justify(&self, size: Vec2) -> Rect {\n        self.layout\n            .next_widget_space_ignore_wrap_justify(&self.region, size)\n    }\n\n    /// Set the maximum width of the ui.\n    /// You won't be able to shrink it below the current minimum size.\n    pub(crate) fn set_max_width(&mut self, width: f32) {\n        let rect = self.next_widget_space_ignore_wrap_justify(vec2(width, 0.0));\n        let region = &mut self.region;\n        region.max_rect.min.x = rect.min.x;\n        region.max_rect.max.x = rect.max.x;\n        region.max_rect |= region.min_rect; // make sure we didn't shrink too much\n\n        region.cursor.min.x = region.max_rect.min.x;\n        region.cursor.max.x = region.max_rect.max.x;\n\n        region.sanity_check();\n    }\n\n    /// Set the maximum height of the ui.\n    /// You won't be able to shrink it below the current minimum size.\n    pub(crate) fn set_max_height(&mut self, height: f32) {\n        let rect = self.next_widget_space_ignore_wrap_justify(vec2(0.0, height));\n        let region = &mut self.region;\n        region.max_rect.min.y = rect.min.y;\n        region.max_rect.max.y = rect.max.y;\n        region.max_rect |= region.min_rect; // make sure we didn't shrink too much\n\n        region.cursor.min.y = region.max_rect.min.y;\n        region.cursor.max.y = region.max_rect.max.y;\n\n        region.sanity_check();\n    }\n\n    /// Set the minimum width of the ui.\n    /// This can't shrink the ui, only make it larger.\n    pub(crate) fn set_min_width(&mut self, width: f32) {\n        if width <= 0.0 {\n            return;\n        }\n        let rect = self.next_widget_space_ignore_wrap_justify(vec2(width, 0.0));\n        self.region.expand_to_include_x(rect.min.x);\n        self.region.expand_to_include_x(rect.max.x);\n    }\n\n    /// Set the minimum height of the ui.\n    /// This can't shrink the ui, only make it larger.\n    pub(crate) fn set_min_height(&mut self, height: f32) {\n        if height <= 0.0 {\n            return;\n        }\n        let rect = self.next_widget_space_ignore_wrap_justify(vec2(0.0, height));\n        self.region.expand_to_include_y(rect.min.y);\n        self.region.expand_to_include_y(rect.max.y);\n    }\n}\n\nimpl Placer {\n    #[cfg(debug_assertions)]\n    pub(crate) fn debug_paint_cursor(&self, painter: &crate::Painter, text: impl ToString) {\n        let stroke = Stroke::new(1.0, Color32::DEBUG_COLOR);\n\n        if let Some(grid) = &self.grid {\n            let rect = grid.next_cell(self.cursor(), Vec2::splat(0.0));\n            painter.rect_stroke(rect, 1.0, stroke, epaint::StrokeKind::Inside);\n            let align = Align2::CENTER_CENTER;\n            painter.debug_text(align.pos_in_rect(&rect), align, stroke.color, text);\n        } else {\n            self.layout\n                .paint_text_at_cursor(painter, &self.region, stroke, text);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/plugin.rs",
    "content": "use crate::{Context, FullOutput, RawInput, Ui};\nuse ahash::HashMap;\nuse epaint::mutex::{Mutex, MutexGuard};\nuse std::sync::Arc;\n\n/// A plugin to extend egui.\n///\n/// Add plugins via [`Context::add_plugin`].\n///\n/// Plugins should not hold a reference to the [`Context`], since this would create a cycle\n/// (which would prevent the [`Context`] from being dropped).\n#[expect(unused_variables)]\npub trait Plugin: Send + Sync + std::any::Any + 'static {\n    /// Plugin name.\n    ///\n    /// Used when profiling.\n    fn debug_name(&self) -> &'static str;\n\n    /// Called once, when the plugin is registered.\n    ///\n    /// Useful to e.g. register image loaders.\n    fn setup(&mut self, ctx: &Context) {}\n\n    /// Called at the start of each pass.\n    ///\n    /// Can be used to show ui, e.g. a [`crate::Window`] or [`crate::Panel`].\n    fn on_begin_pass(&mut self, ui: &mut Ui) {}\n\n    /// Called at the end of each pass.\n    ///\n    /// Can be used to show ui, e.g. a [`crate::Window`].\n    fn on_end_pass(&mut self, ui: &mut Ui) {}\n\n    /// Called just before the input is processed.\n    ///\n    /// Useful to inspect or modify the input.\n    /// Since this is called outside a pass, don't show ui here. Using `Context::debug_painter` is fine though.\n    fn input_hook(&mut self, input: &mut RawInput) {}\n\n    /// Called just before the output is passed to the backend.\n    ///\n    /// Useful to inspect or modify the output.\n    /// Since this is called outside a pass, don't show ui here. Using `Context::debug_painter` is fine though.\n    fn output_hook(&mut self, output: &mut FullOutput) {}\n\n    /// Called when a widget is created and is under the pointer.\n    ///\n    /// Useful for capturing a stack trace so that widgets can be mapped back to their source code.\n    /// Since this is called outside a pass, don't show ui here. Using `Context::debug_painter` is fine though.\n    #[cfg(debug_assertions)]\n    fn on_widget_under_pointer(&mut self, ctx: &Context, widget: &crate::WidgetRect) {}\n}\n\npub(crate) struct PluginHandle {\n    plugin: Box<dyn Plugin>,\n}\n\n/// A typed handle to a registered [`Plugin`].\n///\n/// Use [`Self::lock`] to access the plugin.\npub struct TypedPluginHandle<P: Plugin> {\n    handle: Arc<Mutex<PluginHandle>>,\n    _type: std::marker::PhantomData<P>,\n}\n\nimpl<P: Plugin> TypedPluginHandle<P> {\n    pub(crate) fn new(handle: Arc<Mutex<PluginHandle>>) -> Self {\n        Self {\n            handle,\n            _type: std::marker::PhantomData,\n        }\n    }\n\n    /// Lock the plugin for access.\n    ///\n    /// Returns a guard that dereferences to the plugin.\n    pub fn lock(&self) -> TypedPluginGuard<'_, P> {\n        TypedPluginGuard {\n            guard: self.handle.lock(),\n            _type: std::marker::PhantomData,\n        }\n    }\n}\n\n/// A guard that provides access to a [`Plugin`].\npub struct TypedPluginGuard<'a, P: Plugin> {\n    guard: MutexGuard<'a, PluginHandle>,\n    _type: std::marker::PhantomData<P>,\n}\n\nimpl<P: Plugin> TypedPluginGuard<'_, P> {}\n\nimpl<P: Plugin> std::ops::Deref for TypedPluginGuard<'_, P> {\n    type Target = P;\n\n    fn deref(&self) -> &Self::Target {\n        self.guard.typed_plugin()\n    }\n}\n\nimpl<P: Plugin> std::ops::DerefMut for TypedPluginGuard<'_, P> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.guard.typed_plugin_mut()\n    }\n}\n\nimpl PluginHandle {\n    pub fn new<P: Plugin>(plugin: P) -> Arc<Mutex<Self>> {\n        Arc::new(Mutex::new(Self {\n            plugin: Box::new(plugin),\n        }))\n    }\n\n    fn plugin_type_id(&self) -> std::any::TypeId {\n        (*self.plugin).type_id()\n    }\n\n    pub fn dyn_plugin_mut(&mut self) -> &mut dyn Plugin {\n        &mut *self.plugin\n    }\n\n    fn typed_plugin<P: Plugin + 'static>(&self) -> &P {\n        (self.plugin.as_ref() as &dyn std::any::Any)\n            .downcast_ref::<P>()\n            .expect(\"PluginHandle: plugin is not of the expected type\")\n    }\n\n    pub fn typed_plugin_mut<P: Plugin + 'static>(&mut self) -> &mut P {\n        (self.plugin.as_mut() as &mut dyn std::any::Any)\n            .downcast_mut::<P>()\n            .expect(\"PluginHandle: plugin is not of the expected type\")\n    }\n}\n\n/// User-registered plugins.\n#[derive(Clone, Default)]\npub(crate) struct Plugins {\n    plugins: HashMap<std::any::TypeId, Arc<Mutex<PluginHandle>>>,\n    plugins_ordered: PluginsOrdered,\n}\n\n#[derive(Clone, Default)]\npub(crate) struct PluginsOrdered(Vec<Arc<Mutex<PluginHandle>>>);\n\nimpl PluginsOrdered {\n    fn for_each_dyn<F>(&self, mut f: F)\n    where\n        F: FnMut(&mut dyn Plugin),\n    {\n        for plugin in &self.0 {\n            let mut plugin = plugin.lock();\n            profiling::scope!(\"plugin\", plugin.dyn_plugin_mut().debug_name());\n            f(plugin.dyn_plugin_mut());\n        }\n    }\n\n    pub fn on_begin_pass(&self, ui: &mut Ui) {\n        profiling::scope!(\"plugins\", \"on_begin_pass\");\n        self.for_each_dyn(|p| {\n            p.on_begin_pass(ui);\n        });\n    }\n\n    pub fn on_end_pass(&self, ui: &mut Ui) {\n        profiling::scope!(\"plugins\", \"on_end_pass\");\n        self.for_each_dyn(|p| {\n            p.on_end_pass(ui);\n        });\n    }\n\n    pub fn on_input(&self, input: &mut RawInput) {\n        profiling::scope!(\"plugins\", \"on_input\");\n        self.for_each_dyn(|plugin| {\n            plugin.input_hook(input);\n        });\n    }\n\n    pub fn on_output(&self, output: &mut FullOutput) {\n        profiling::scope!(\"plugins\", \"on_output\");\n        self.for_each_dyn(|plugin| {\n            plugin.output_hook(output);\n        });\n    }\n\n    #[cfg(debug_assertions)]\n    pub fn on_widget_under_pointer(&self, ctx: &Context, widget: &crate::WidgetRect) {\n        profiling::scope!(\"plugins\", \"on_widget_under_pointer\");\n        self.for_each_dyn(|plugin| {\n            plugin.on_widget_under_pointer(ctx, widget);\n        });\n    }\n}\n\nimpl Plugins {\n    pub fn ordered_plugins(&self) -> PluginsOrdered {\n        self.plugins_ordered.clone()\n    }\n\n    /// Remember to call [`Plugin::setup`] on the plugin after adding it.\n    ///\n    /// Will not add the plugin if a plugin of the same type already exists.\n    /// Returns `false` if the plugin was not added, `true` if it was added.\n    pub fn add(&mut self, handle: Arc<Mutex<PluginHandle>>) -> bool {\n        profiling::scope!(\"plugins\", \"add\");\n\n        let type_id = handle.lock().plugin_type_id();\n\n        if self.plugins.contains_key(&type_id) {\n            return false;\n        }\n\n        self.plugins.insert(type_id, Arc::clone(&handle));\n        self.plugins_ordered.0.push(handle);\n\n        true\n    }\n\n    pub fn get(&self, type_id: std::any::TypeId) -> Option<Arc<Mutex<PluginHandle>>> {\n        self.plugins.get(&type_id).cloned()\n    }\n}\n\n/// Generic event callback.\npub type ContextCallback = Arc<dyn Fn(&mut Ui) + Send + Sync>;\n\n#[derive(Default)]\npub(crate) struct CallbackPlugin {\n    pub on_begin_plugins: Vec<(&'static str, ContextCallback)>,\n    pub on_end_plugins: Vec<(&'static str, ContextCallback)>,\n}\n\nimpl Plugin for CallbackPlugin {\n    fn debug_name(&self) -> &'static str {\n        \"CallbackPlugins\"\n    }\n\n    fn on_begin_pass(&mut self, ui: &mut Ui) {\n        profiling::function_scope!();\n\n        for (_debug_name, cb) in &self.on_begin_plugins {\n            profiling::scope!(\"on_begin_pass\", *_debug_name);\n            (cb)(ui);\n        }\n    }\n\n    fn on_end_pass(&mut self, ui: &mut Ui) {\n        profiling::function_scope!();\n\n        for (_debug_name, cb) in &self.on_end_plugins {\n            profiling::scope!(\"on_end_pass\", *_debug_name);\n            (cb)(ui);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/response.rs",
    "content": "use std::{any::Any, sync::Arc};\n\nuse crate::{\n    Context, CursorIcon, Id, LayerId, PointerButton, Popup, PopupKind, Sense, Tooltip, Ui,\n    WidgetRect, WidgetText,\n    emath::{Align, Pos2, Rect, Vec2},\n    pass_state,\n};\n// ----------------------------------------------------------------------------\n\n/// The result of adding a widget to a [`Ui`].\n///\n/// A [`Response`] lets you know whether a widget is being hovered, clicked or dragged.\n/// It also lets you easily show a tooltip on hover.\n///\n/// Whenever something gets added to a [`Ui`], a [`Response`] object is returned.\n/// [`Ui::add`] returns a [`Response`], as does [`Ui::button`], and all similar shortcuts.\n///\n/// ⚠️ The `Response` contains a clone of [`Context`], and many methods lock the `Context`.\n/// It can therefore be a deadlock to use `Context` from within a context-locking closures,\n/// such as [`Context::input`].\n#[derive(Clone, Debug)]\npub struct Response {\n    // CONTEXT:\n    /// Used for optionally showing a tooltip and checking for more interactions.\n    pub ctx: Context,\n\n    // IN:\n    /// Which layer the widget is part of.\n    pub layer_id: LayerId,\n\n    /// The [`Id`] of the widget/area this response pertains.\n    pub id: Id,\n\n    /// The area of the screen we are talking about.\n    pub rect: Rect,\n\n    /// The rectangle sensing interaction.\n    ///\n    /// This is sometimes smaller than [`Self::rect`] because of clipping\n    /// (e.g. when inside a scroll area).\n    pub interact_rect: Rect,\n\n    /// The senses (click and/or drag) that the widget was interested in (if any).\n    ///\n    /// Note: if [`Self::enabled`] is `false`, then\n    /// the widget _effectively_ doesn't sense anything,\n    /// but can still have the same `Sense`.\n    /// This is because the sense informs the styling of the widget,\n    /// but we don't want to change the style when a widget is disabled\n    /// (that is handled by the `Painter` directly).\n    pub sense: Sense,\n\n    // OUT:\n    /// Where the pointer (mouse/touch) were when this widget was clicked or dragged.\n    /// `None` if the widget is not being interacted with.\n    #[doc(hidden)]\n    pub interact_pointer_pos: Option<Pos2>,\n\n    /// The intrinsic / desired size of the widget.\n    ///\n    /// This is the size that a non-wrapped, non-truncated, non-justified version of the widget\n    /// would have.\n    ///\n    /// If this is `None`, use [`Self::rect`] instead.\n    ///\n    /// At the time of writing, this is only used by external crates\n    /// for improved layouting.\n    /// See for instance [`egui_flex`](https://github.com/lucasmerlin/hello_egui/tree/main/crates/egui_flex).\n    pub intrinsic_size: Option<Vec2>,\n\n    #[doc(hidden)]\n    pub flags: Flags,\n}\n\n/// A bit set for various boolean properties of `Response`.\n#[doc(hidden)]\n#[derive(Copy, Clone, Debug)]\npub struct Flags(u16);\n\nbitflags::bitflags! {\n    impl Flags: u16 {\n        /// Was the widget enabled?\n        /// If `false`, there was no interaction attempted (not even hover).\n        const ENABLED = 1<<0;\n\n        /// The pointer is above this widget with no other blocking it.\n        const CONTAINS_POINTER = 1<<1;\n\n        /// The pointer is hovering above this widget or the widget was clicked/tapped this frame.\n        const HOVERED = 1<<2;\n\n        /// The widget is highlighted via a call to [`Response::highlight`] or\n        /// [`Context::highlight_widget`].\n        const HIGHLIGHTED = 1<<3;\n\n        /// This widget was clicked this frame.\n        ///\n        /// Which pointer and how many times we don't know,\n        /// and ask [`crate::InputState`] about at runtime.\n        ///\n        /// This is only set to true if the widget was clicked\n        /// by an actual mouse.\n        const CLICKED = 1<<4;\n\n        /// This widget should act as if clicked due\n        /// to something else than a click.\n        ///\n        /// This is set to true if the widget has keyboard focus and\n        /// the user hit the Space or Enter key.\n        const FAKE_PRIMARY_CLICKED = 1<<5;\n\n        /// This widget was long-pressed on a touch screen to simulate a secondary click.\n        const LONG_TOUCHED = 1<<6;\n\n        /// The widget started being dragged this frame.\n        const DRAG_STARTED = 1<<7;\n\n        /// The widget is being dragged.\n        const DRAGGED = 1<<8;\n\n        /// The widget was being dragged, but now it has been released.\n        const DRAG_STOPPED = 1<<9;\n\n        /// Is the pointer button currently down on this widget?\n        /// This is true if the pointer is pressing down or dragging a widget\n        const IS_POINTER_BUTTON_DOWN_ON = 1<<10;\n\n        /// Was the underlying data changed?\n        ///\n        /// e.g. the slider was dragged, text was entered in a [`TextEdit`](crate::TextEdit) etc.\n        /// Always `false` for something like a [`Button`](crate::Button).\n        ///\n        /// Note that this can be `true` even if the user did not interact with the widget,\n        /// for instance if an existing slider value was clamped to the given range.\n        const CHANGED = 1<<11;\n\n        /// Should this container be closed?\n        const CLOSE = 1<<12;\n    }\n}\n\nimpl Response {\n    /// Returns true if this widget was clicked this frame by the primary button.\n    ///\n    /// A click is registered when the mouse or touch is released within\n    /// a certain amount of time and distance from when and where it was pressed.\n    ///\n    /// This will also return true if the widget was clicked via accessibility integration,\n    /// or if the widget had keyboard focus and the use pressed Space/Enter.\n    ///\n    /// Note that the widget must be sensing clicks with [`Sense::click`].\n    /// [`crate::Button`] senses clicks; [`crate::Label`] does not (unless you call [`crate::Label::sense`]).\n    ///\n    /// You can use [`Self::interact`] to sense more things *after* adding a widget.\n    #[inline(always)]\n    pub fn clicked(&self) -> bool {\n        self.flags.contains(Flags::FAKE_PRIMARY_CLICKED) || self.clicked_by(PointerButton::Primary)\n    }\n\n    /// Returns true if this widget was clicked this frame by the given mouse button.\n    ///\n    /// This will NOT return true if the widget was \"clicked\" via\n    /// some accessibility integration, or if the widget had keyboard focus and the\n    /// user pressed Space/Enter. For that, use [`Self::clicked`] instead.\n    ///\n    /// This will likewise ignore the press-and-hold action on touch screens.\n    /// Use [`Self::secondary_clicked`] instead to also detect that.\n    #[inline]\n    pub fn clicked_by(&self, button: PointerButton) -> bool {\n        self.flags.contains(Flags::CLICKED) && self.ctx.input(|i| i.pointer.button_clicked(button))\n    }\n\n    /// Returns true if this widget was clicked this frame by the secondary mouse button (e.g. the right mouse button).\n    ///\n    /// This also returns true if the widget was pressed-and-held on a touch screen.\n    #[inline]\n    pub fn secondary_clicked(&self) -> bool {\n        self.flags.contains(Flags::LONG_TOUCHED) || self.clicked_by(PointerButton::Secondary)\n    }\n\n    /// Was this long-pressed on a touch screen?\n    ///\n    /// Usually you want to check [`Self::secondary_clicked`] instead.\n    #[inline]\n    pub fn long_touched(&self) -> bool {\n        self.flags.contains(Flags::LONG_TOUCHED)\n    }\n\n    /// Returns true if this widget was clicked this frame by the middle mouse button.\n    #[inline]\n    pub fn middle_clicked(&self) -> bool {\n        self.clicked_by(PointerButton::Middle)\n    }\n\n    /// Returns true if this widget was double-clicked this frame by the primary button.\n    #[inline]\n    pub fn double_clicked(&self) -> bool {\n        self.double_clicked_by(PointerButton::Primary)\n    }\n\n    /// Returns true if this widget was triple-clicked this frame by the primary button.\n    #[inline]\n    pub fn triple_clicked(&self) -> bool {\n        self.triple_clicked_by(PointerButton::Primary)\n    }\n\n    /// Returns true if this widget was double-clicked this frame by the given button.\n    #[inline]\n    pub fn double_clicked_by(&self, button: PointerButton) -> bool {\n        self.flags.contains(Flags::CLICKED)\n            && self.ctx.input(|i| i.pointer.button_double_clicked(button))\n    }\n\n    /// Returns true if this widget was triple-clicked this frame by the given button.\n    #[inline]\n    pub fn triple_clicked_by(&self, button: PointerButton) -> bool {\n        self.flags.contains(Flags::CLICKED)\n            && self.ctx.input(|i| i.pointer.button_triple_clicked(button))\n    }\n\n    /// Was this widget middle-clicked or clicked while holding down a modifier key?\n    ///\n    /// This is used by [`crate::Hyperlink`] to check if a URL should be opened\n    /// in a new tab, using [`crate::OpenUrl::new_tab`].\n    pub fn clicked_with_open_in_background(&self) -> bool {\n        self.middle_clicked() || self.clicked() && self.ctx.input(|i| i.modifiers.any())\n    }\n\n    /// `true` if there was a click *outside* the rect of this widget.\n    ///\n    /// Clicks on widgets contained in this one counts as clicks inside this widget,\n    /// so that clicking a button in an area will not be considered as clicking \"elsewhere\" from the area.\n    ///\n    /// Clicks on other layers above this widget *will* be considered as clicking elsewhere.\n    pub fn clicked_elsewhere(&self) -> bool {\n        let (pointer_interact_pos, any_click) = self\n            .ctx\n            .input(|i| (i.pointer.interact_pos(), i.pointer.any_click()));\n\n        // We do not use self.clicked(), because we want to catch all clicks within our frame,\n        // even if we aren't clickable (or even enabled).\n        // This is important for windows and such that should close then the user clicks elsewhere.\n        if any_click {\n            if self.contains_pointer() || self.hovered() {\n                false\n            } else if let Some(pos) = pointer_interact_pos {\n                let layer_under_pointer = self.ctx.layer_id_at(pos);\n                if layer_under_pointer != Some(self.layer_id) {\n                    true\n                } else {\n                    !self.interact_rect.contains(pos)\n                }\n            } else {\n                false // clicked without a pointer, weird\n            }\n        } else {\n            false\n        }\n    }\n\n    /// Was the widget enabled?\n    /// If false, there was no interaction attempted\n    /// and the widget should be drawn in a gray disabled look.\n    #[inline(always)]\n    pub fn enabled(&self) -> bool {\n        self.flags.contains(Flags::ENABLED)\n    }\n\n    /// The pointer is hovering above this widget or the widget was clicked/tapped this frame.\n    ///\n    /// In contrast to [`Self::contains_pointer`], this will be `false` whenever some other widget is being dragged.\n    /// `hovered` is always `false` for disabled widgets.\n    #[inline(always)]\n    pub fn hovered(&self) -> bool {\n        self.flags.contains(Flags::HOVERED)\n    }\n\n    /// Returns true if the pointer is contained by the response rect, and no other widget is covering it.\n    ///\n    /// In contrast to [`Self::hovered`], this can be `true` even if some other widget is being dragged.\n    /// This means it is useful for styling things like drag-and-drop targets.\n    /// `contains_pointer` can also be `true` for disabled widgets.\n    ///\n    /// This is slightly different from [`Ui::rect_contains_pointer`] and [`Context::rect_contains_pointer`], in that\n    /// [`Self::contains_pointer`] also checks that no other widget is covering this response rectangle.\n    #[inline(always)]\n    pub fn contains_pointer(&self) -> bool {\n        self.flags.contains(Flags::CONTAINS_POINTER)\n    }\n\n    /// The widget is highlighted via a call to [`Self::highlight`] or [`Context::highlight_widget`].\n    #[doc(hidden)]\n    #[inline(always)]\n    pub fn highlighted(&self) -> bool {\n        self.flags.contains(Flags::HIGHLIGHTED)\n    }\n\n    /// This widget has the keyboard focus (i.e. is receiving key presses).\n    ///\n    /// This function only returns true if the UI as a whole (e.g. window)\n    /// also has the keyboard focus. That makes this function suitable\n    /// for style choices, e.g. a thicker border around focused widgets.\n    pub fn has_focus(&self) -> bool {\n        self.ctx.input(|i| i.focused) && self.ctx.memory(|mem| mem.has_focus(self.id))\n    }\n\n    /// True if this widget has keyboard focus this frame, but didn't last frame.\n    pub fn gained_focus(&self) -> bool {\n        self.ctx.memory(|mem| mem.gained_focus(self.id))\n    }\n\n    /// The widget had keyboard focus and lost it,\n    /// either because the user pressed tab or clicked somewhere else,\n    /// or (in case of a [`crate::TextEdit`]) because the user pressed enter.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_text = String::new();\n    /// # fn do_request(_: &str) {}\n    /// let response = ui.text_edit_singleline(&mut my_text);\n    /// if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {\n    ///     do_request(&my_text);\n    /// }\n    /// # });\n    /// ```\n    pub fn lost_focus(&self) -> bool {\n        self.ctx.memory(|mem| mem.lost_focus(self.id))\n    }\n\n    /// Request that this widget get keyboard focus.\n    pub fn request_focus(&self) {\n        self.ctx.memory_mut(|mem| mem.request_focus(self.id));\n    }\n\n    /// Surrender keyboard focus for this widget.\n    pub fn surrender_focus(&self) {\n        self.ctx.memory_mut(|mem| mem.surrender_focus(self.id));\n    }\n\n    /// Did a drag on this widget begin this frame?\n    ///\n    /// This is only true if the widget sense drags.\n    /// If the widget also senses clicks, this will only become true if the pointer has moved a bit.\n    ///\n    /// This will only be true for a single frame.\n    #[inline]\n    pub fn drag_started(&self) -> bool {\n        self.flags.contains(Flags::DRAG_STARTED)\n    }\n\n    /// Did a drag on this widget by the button begin this frame?\n    ///\n    /// This is only true if the widget sense drags.\n    /// If the widget also senses clicks, this will only become true if the pointer has moved a bit.\n    ///\n    /// This will only be true for a single frame.\n    #[inline]\n    pub fn drag_started_by(&self, button: PointerButton) -> bool {\n        self.drag_started() && self.ctx.input(|i| i.pointer.button_down(button))\n    }\n\n    /// The widget is being dragged.\n    ///\n    /// To find out which button(s), use [`Self::dragged_by`].\n    ///\n    /// If the widget is only sensitive to drags, this is `true` as soon as the pointer presses down on it.\n    /// If the widget also senses clicks, this won't be true until the pointer has moved a bit,\n    /// or the user has pressed down for long enough.\n    /// See [`crate::input_state::PointerState::is_decidedly_dragging`] for details.\n    ///\n    /// If you want to avoid the delay, use [`Self::is_pointer_button_down_on`] instead.\n    ///\n    /// If the widget is NOT sensitive to drags, this will always be `false`.\n    /// [`crate::DragValue`] senses drags; [`crate::Label`] does not (unless you call [`crate::Label::sense`]).\n    /// You can use [`Self::interact`] to sense more things *after* adding a widget.\n    #[inline(always)]\n    pub fn dragged(&self) -> bool {\n        self.flags.contains(Flags::DRAGGED)\n    }\n\n    /// See [`Self::dragged`].\n    #[inline]\n    pub fn dragged_by(&self, button: PointerButton) -> bool {\n        self.dragged() && self.ctx.input(|i| i.pointer.button_down(button))\n    }\n\n    /// The widget was being dragged, but now it has been released.\n    #[inline]\n    pub fn drag_stopped(&self) -> bool {\n        self.flags.contains(Flags::DRAG_STOPPED)\n    }\n\n    /// The widget was being dragged by the button, but now it has been released.\n    pub fn drag_stopped_by(&self, button: PointerButton) -> bool {\n        self.drag_stopped() && self.ctx.input(|i| i.pointer.button_released(button))\n    }\n\n    /// If dragged, how many points were we dragged in since last frame?\n    #[inline]\n    pub fn drag_delta(&self) -> Vec2 {\n        if self.dragged() {\n            let mut delta = self.ctx.input(|i| i.pointer.delta());\n            if let Some(from_global) = self.ctx.layer_transform_from_global(self.layer_id) {\n                delta *= from_global.scaling;\n            }\n            delta\n        } else {\n            Vec2::ZERO\n        }\n    }\n\n    /// If dragged, how many points have we been dragged since the start of the drag?\n    #[inline]\n    pub fn total_drag_delta(&self) -> Option<Vec2> {\n        if self.dragged() {\n            let mut delta = self.ctx.input(|i| i.pointer.total_drag_delta())?;\n            if let Some(from_global) = self.ctx.layer_transform_from_global(self.layer_id) {\n                delta *= from_global.scaling;\n            }\n            Some(delta)\n        } else {\n            None\n        }\n    }\n\n    /// If dragged, how far did the mouse move since last frame?\n    ///\n    /// This will use raw mouse movement if provided by the integration, otherwise will fall back to [`Response::drag_delta`]\n    /// Raw mouse movement is unaccelerated and unclamped by screen boundaries, and does not relate to any position on the screen.\n    /// This may be useful in certain situations such as draggable values and 3D cameras, where screen position does not matter.\n    #[inline]\n    pub fn drag_motion(&self) -> Vec2 {\n        if self.dragged() {\n            self.ctx\n                .input(|i| i.pointer.motion().unwrap_or_else(|| i.pointer.delta()))\n        } else {\n            Vec2::ZERO\n        }\n    }\n\n    /// If the user started dragging this widget this frame, store the payload for drag-and-drop.\n    #[doc(alias = \"drag and drop\")]\n    pub fn dnd_set_drag_payload<Payload: Any + Send + Sync>(&self, payload: Payload) {\n        if self.drag_started() {\n            crate::DragAndDrop::set_payload(&self.ctx, payload);\n        }\n\n        if self.hovered() && !self.sense.senses_click() {\n            // Things that can be drag-dropped should use the Grab cursor icon,\n            // but if the thing is _also_ clickable, that can be annoying.\n            self.ctx.set_cursor_icon(CursorIcon::Grab);\n        }\n    }\n\n    /// Drag-and-Drop: Return what is being held over this widget, if any.\n    ///\n    /// Only returns something if [`Self::contains_pointer`] is true,\n    /// and the user is drag-dropping something of this type.\n    #[doc(alias = \"drag and drop\")]\n    pub fn dnd_hover_payload<Payload: Any + Send + Sync>(&self) -> Option<Arc<Payload>> {\n        // NOTE: we use `response.contains_pointer` here instead of `hovered`, because\n        // `hovered` is always false when another widget is being dragged.\n        if self.contains_pointer() {\n            crate::DragAndDrop::payload::<Payload>(&self.ctx)\n        } else {\n            None\n        }\n    }\n\n    /// Drag-and-Drop: Return what is being dropped onto this widget, if any.\n    ///\n    /// Only returns something if [`Self::contains_pointer`] is true,\n    /// the user is drag-dropping something of this type,\n    /// and they released it this frame.\n    #[doc(alias = \"drag and drop\")]\n    pub fn dnd_release_payload<Payload: Any + Send + Sync>(&self) -> Option<Arc<Payload>> {\n        // NOTE: we use `response.contains_pointer` here instead of `hovered`, because\n        // `hovered` is always false when another widget is being dragged.\n        if self.contains_pointer() && self.ctx.input(|i| i.pointer.any_released()) {\n            crate::DragAndDrop::take_payload::<Payload>(&self.ctx)\n        } else {\n            None\n        }\n    }\n\n    /// Where the pointer (mouse/touch) were when this widget was clicked or dragged.\n    ///\n    /// `None` if the widget is not being interacted with.\n    #[inline]\n    pub fn interact_pointer_pos(&self) -> Option<Pos2> {\n        self.interact_pointer_pos\n    }\n\n    /// If it is a good idea to show a tooltip, where is pointer?\n    ///\n    /// None if the pointer is outside the response area.\n    #[inline]\n    pub fn hover_pos(&self) -> Option<Pos2> {\n        if self.hovered() {\n            let mut pos = self.ctx.input(|i| i.pointer.hover_pos())?;\n            if let Some(from_global) = self.ctx.layer_transform_from_global(self.layer_id) {\n                pos = from_global * pos;\n            }\n            Some(pos)\n        } else {\n            None\n        }\n    }\n\n    /// Is the pointer button currently down on this widget?\n    ///\n    /// This is true if the pointer is pressing down or dragging a widget,\n    /// even when dragging outside the widget.\n    ///\n    /// This could also be thought of as \"is this widget being interacted with?\".\n    #[inline(always)]\n    pub fn is_pointer_button_down_on(&self) -> bool {\n        self.flags.contains(Flags::IS_POINTER_BUTTON_DOWN_ON)\n    }\n\n    /// Was the underlying data changed?\n    ///\n    /// e.g. the slider was dragged, text was entered in a [`TextEdit`](crate::TextEdit) etc.\n    /// Always `false` for something like a [`Button`](crate::Button).\n    ///\n    /// Can sometimes be `true` even though the data didn't changed\n    /// (e.g. if the user entered a character and erased it the same frame).\n    ///\n    /// This is not set if the *view* of the data was changed.\n    /// For instance, moving the cursor in a [`TextEdit`](crate::TextEdit) does not set this to `true`.\n    ///\n    /// Note that this can be `true` even if the user did not interact with the widget,\n    /// for instance if an existing slider value was clamped to the given range.\n    #[inline(always)]\n    pub fn changed(&self) -> bool {\n        self.flags.contains(Flags::CHANGED)\n    }\n\n    /// Report the data shown by this widget changed.\n    ///\n    /// This must be called by widgets that represent some mutable data,\n    /// e.g. checkboxes, sliders etc.\n    ///\n    /// This should be called when the *content* changes, but not when the view does.\n    /// So we call this when the text of a [`crate::TextEdit`], but not when the cursor changes.\n    #[inline(always)]\n    pub fn mark_changed(&mut self) {\n        self.flags.set(Flags::CHANGED, true);\n    }\n\n    /// Should the container be closed?\n    ///\n    /// Will e.g. be set by calling [`Ui::close`] in a child [`Ui`] or by calling\n    /// [`Self::set_close`].\n    pub fn should_close(&self) -> bool {\n        self.flags.contains(Flags::CLOSE)\n    }\n\n    /// Set the [`Flags::CLOSE`] flag.\n    ///\n    /// Can be used to e.g. signal that a container should be closed.\n    pub fn set_close(&mut self) {\n        self.flags.set(Flags::CLOSE, true);\n    }\n\n    /// Show this UI if the widget was hovered (i.e. a tooltip).\n    ///\n    /// The text will not be visible if the widget is not enabled.\n    /// For that, use [`Self::on_disabled_hover_ui`] instead.\n    ///\n    /// If you call this multiple times the tooltips will stack underneath the previous ones.\n    ///\n    /// The widget can contain interactive widgets, such as buttons and links.\n    /// If so, it will stay open as the user moves their pointer over it.\n    /// By default, the text of a tooltip is NOT selectable (i.e. interactive),\n    /// but you can change this by setting [`style::Interaction::selectable_labels` from within the tooltip:\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.label(\"Hover me\").on_hover_ui(|ui| {\n    ///     ui.style_mut().interaction.selectable_labels = true;\n    ///     ui.label(\"This text can be selected\");\n    /// });\n    /// # });\n    /// ```\n    #[doc(alias = \"tooltip\")]\n    pub fn on_hover_ui(self, add_contents: impl FnOnce(&mut Ui)) -> Self {\n        Tooltip::for_enabled(&self).show(add_contents);\n        self\n    }\n\n    /// Show this UI when hovering if the widget is disabled.\n    pub fn on_disabled_hover_ui(self, add_contents: impl FnOnce(&mut Ui)) -> Self {\n        Tooltip::for_disabled(&self).show(add_contents);\n        self\n    }\n\n    /// Like `on_hover_ui`, but show the ui next to cursor.\n    pub fn on_hover_ui_at_pointer(self, add_contents: impl FnOnce(&mut Ui)) -> Self {\n        Tooltip::for_enabled(&self)\n            .at_pointer()\n            .gap(12.0)\n            .show(add_contents);\n        self\n    }\n\n    /// Always show this tooltip, even if disabled and the user isn't hovering it.\n    ///\n    /// This can be used to give attention to a widget during a tutorial.\n    pub fn show_tooltip_ui(&self, add_contents: impl FnOnce(&mut Ui)) {\n        Popup::from_response(self)\n            .kind(PopupKind::Tooltip)\n            .show(add_contents);\n    }\n\n    /// Always show this tooltip, even if disabled and the user isn't hovering it.\n    ///\n    /// This can be used to give attention to a widget during a tutorial.\n    pub fn show_tooltip_text(&self, text: impl Into<WidgetText>) {\n        self.show_tooltip_ui(|ui| {\n            ui.label(text);\n        });\n    }\n\n    /// Was the tooltip open last frame?\n    pub fn is_tooltip_open(&self) -> bool {\n        Tooltip::was_tooltip_open_last_frame(&self.ctx, self.id)\n    }\n\n    /// Like `on_hover_text`, but show the text next to cursor.\n    #[doc(alias = \"tooltip\")]\n    pub fn on_hover_text_at_pointer(self, text: impl Into<WidgetText>) -> Self {\n        self.on_hover_ui_at_pointer(|ui| {\n            // Prevent `Area` auto-sizing from shrinking tooltips with dynamic content.\n            // See https://github.com/emilk/egui/issues/5167\n            ui.set_max_width(ui.spacing().tooltip_width);\n\n            ui.add(crate::widgets::Label::new(text));\n        })\n    }\n\n    /// Show this text if the widget was hovered (i.e. a tooltip).\n    ///\n    /// The text will not be visible if the widget is not enabled.\n    /// For that, use [`Self::on_disabled_hover_text`] instead.\n    ///\n    /// If you call this multiple times the tooltips will stack underneath the previous ones.\n    #[doc(alias = \"tooltip\")]\n    pub fn on_hover_text(self, text: impl Into<WidgetText>) -> Self {\n        self.on_hover_ui(|ui| {\n            // Prevent `Area` auto-sizing from shrinking tooltips with dynamic content.\n            // See https://github.com/emilk/egui/issues/5167\n            ui.set_max_width(ui.spacing().tooltip_width);\n\n            ui.add(crate::widgets::Label::new(text));\n        })\n    }\n\n    /// Highlight this widget, to make it look like it is hovered, even if it isn't.\n    ///\n    /// The highlight takes one frame to take effect if you call this after the widget has been fully rendered.\n    ///\n    /// See also [`Context::highlight_widget`].\n    #[inline]\n    pub fn highlight(mut self) -> Self {\n        self.ctx.highlight_widget(self.id);\n        self.flags.set(Flags::HIGHLIGHTED, true);\n        self\n    }\n\n    /// Show this text when hovering if the widget is disabled.\n    pub fn on_disabled_hover_text(self, text: impl Into<WidgetText>) -> Self {\n        self.on_disabled_hover_ui(|ui| {\n            // Prevent `Area` auto-sizing from shrinking tooltips with dynamic content.\n            // See https://github.com/emilk/egui/issues/5167\n            ui.set_max_width(ui.spacing().tooltip_width);\n\n            ui.add(crate::widgets::Label::new(text));\n        })\n    }\n\n    /// When hovered, use this icon for the mouse cursor.\n    #[inline]\n    pub fn on_hover_cursor(self, cursor: CursorIcon) -> Self {\n        if self.hovered() {\n            self.ctx.set_cursor_icon(cursor);\n        }\n        self\n    }\n\n    /// When hovered or dragged, use this icon for the mouse cursor.\n    #[inline]\n    pub fn on_hover_and_drag_cursor(self, cursor: CursorIcon) -> Self {\n        if self.hovered() || self.dragged() {\n            self.ctx.set_cursor_icon(cursor);\n        }\n        self\n    }\n\n    /// Sense more interactions (e.g. sense clicks on a [`Response`] returned from a label).\n    ///\n    /// The interaction will occur on the same plane as the original widget,\n    /// i.e. if the response was from a widget behind button, the interaction will also be behind that button.\n    /// egui gives priority to the _last_ added widget (the one on top gets clicked first).\n    ///\n    /// Note that this call will not add any hover-effects to the widget, so when possible\n    /// it is better to give the widget a [`Sense`] instead, e.g. using [`crate::Label::sense`].\n    ///\n    /// Using this method on a `Response` that is the result of calling `union` on multiple `Response`s\n    /// is undefined behavior.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let horiz_response = ui.horizontal(|ui| {\n    ///     ui.label(\"hello\");\n    /// }).response;\n    /// assert!(!horiz_response.clicked()); // ui's don't sense clicks by default\n    /// let horiz_response = horiz_response.interact(egui::Sense::click());\n    /// if horiz_response.clicked() {\n    ///     // The background behind the label was clicked\n    /// }\n    /// # });\n    /// ```\n    #[must_use]\n    pub fn interact(&self, sense: Sense) -> Self {\n        // We could check here if the new Sense equals the old one to avoid the extra create_widget\n        // call. But that would break calling `interact` on a response from `Context::read_response`\n        // or `Ui::response`. (See https://github.com/emilk/egui/pull/7713 for more details.)\n\n        self.ctx.create_widget(\n            WidgetRect {\n                layer_id: self.layer_id,\n                id: self.id,\n                rect: self.rect,\n                interact_rect: self.interact_rect,\n                sense: self.sense | sense,\n                enabled: self.enabled(),\n            },\n            true,\n            Default::default(),\n        )\n    }\n\n    /// Adjust the scroll position until this UI becomes visible.\n    ///\n    /// If `align` is [`Align::TOP`] it means \"put the top of the rect at the top of the scroll area\", etc.\n    /// If `align` is `None`, it'll scroll enough to bring the UI into view.\n    ///\n    /// See also: [`Ui::scroll_to_cursor`], [`Ui::scroll_to_rect`]. [`Ui::scroll_with_delta`].\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// egui::ScrollArea::vertical().show(ui, |ui| {\n    ///     for i in 0..1000 {\n    ///         let response = ui.button(\"Scroll to me\");\n    ///         if response.clicked() {\n    ///             response.scroll_to_me(Some(egui::Align::Center));\n    ///         }\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    pub fn scroll_to_me(&self, align: Option<Align>) {\n        self.scroll_to_me_animation(align, self.ctx.global_style().scroll_animation);\n    }\n\n    /// Like [`Self::scroll_to_me`], but allows you to specify the [`crate::style::ScrollAnimation`].\n    pub fn scroll_to_me_animation(\n        &self,\n        align: Option<Align>,\n        animation: crate::style::ScrollAnimation,\n    ) {\n        self.ctx.pass_state_mut(|state| {\n            state.scroll_target[0] = Some(pass_state::ScrollTarget::new(\n                self.rect.x_range(),\n                align,\n                animation,\n            ));\n            state.scroll_target[1] = Some(pass_state::ScrollTarget::new(\n                self.rect.y_range(),\n                align,\n                animation,\n            ));\n        });\n    }\n\n    /// For accessibility.\n    ///\n    /// Call after interacting and potential calls to [`Self::mark_changed`].\n    pub fn widget_info(&self, make_info: impl Fn() -> crate::WidgetInfo) {\n        use crate::output::OutputEvent;\n\n        let event = if self.clicked() {\n            Some(OutputEvent::Clicked(make_info()))\n        } else if self.double_clicked() {\n            Some(OutputEvent::DoubleClicked(make_info()))\n        } else if self.triple_clicked() {\n            Some(OutputEvent::TripleClicked(make_info()))\n        } else if self.gained_focus() {\n            Some(OutputEvent::FocusGained(make_info()))\n        } else if self.changed() {\n            Some(OutputEvent::ValueChanged(make_info()))\n        } else {\n            None\n        };\n\n        if let Some(event) = event {\n            self.output_event(event);\n        } else {\n            self.ctx.accesskit_node_builder(self.id, |builder| {\n                self.fill_accesskit_node_from_widget_info(builder, make_info());\n            });\n\n            self.ctx.register_widget_info(self.id, make_info);\n        }\n    }\n\n    pub fn output_event(&self, event: crate::output::OutputEvent) {\n        self.ctx.accesskit_node_builder(self.id, |builder| {\n            self.fill_accesskit_node_from_widget_info(builder, event.widget_info().clone());\n        });\n\n        self.ctx\n            .register_widget_info(self.id, || event.widget_info().clone());\n\n        self.ctx.output_mut(|o| o.events.push(event));\n    }\n\n    pub(crate) fn fill_accesskit_node_common(&self, builder: &mut accesskit::Node) {\n        if !self.enabled() {\n            builder.set_disabled();\n        }\n        builder.set_bounds(accesskit::Rect {\n            x0: self.rect.min.x.into(),\n            y0: self.rect.min.y.into(),\n            x1: self.rect.max.x.into(),\n            y1: self.rect.max.y.into(),\n        });\n        if self.sense.is_focusable() {\n            builder.add_action(accesskit::Action::Focus);\n        }\n        if self.sense.senses_click() {\n            builder.add_action(accesskit::Action::Click);\n        }\n    }\n\n    fn fill_accesskit_node_from_widget_info(\n        &self,\n        builder: &mut accesskit::Node,\n        info: crate::WidgetInfo,\n    ) {\n        use crate::WidgetType;\n        use accesskit::{Role, Toggled};\n\n        self.fill_accesskit_node_common(builder);\n        builder.set_role(match info.typ {\n            WidgetType::Label => Role::Label,\n            WidgetType::Link => Role::Link,\n            WidgetType::TextEdit => Role::TextInput,\n            WidgetType::Button | WidgetType::CollapsingHeader | WidgetType::SelectableLabel => {\n                Role::Button\n            }\n            WidgetType::Image => Role::Image,\n            WidgetType::Checkbox => Role::CheckBox,\n            WidgetType::RadioButton => Role::RadioButton,\n            WidgetType::RadioGroup => Role::RadioGroup,\n            WidgetType::ComboBox => Role::ComboBox,\n            WidgetType::Slider => Role::Slider,\n            WidgetType::DragValue => Role::SpinButton,\n            WidgetType::ColorButton => Role::ColorWell,\n            WidgetType::Panel => Role::Pane,\n            WidgetType::ProgressIndicator => Role::ProgressIndicator,\n            WidgetType::Window => Role::Window,\n\n            WidgetType::ResizeHandle => Role::Splitter,\n            WidgetType::ScrollBar => Role::ScrollBar,\n\n            WidgetType::Other => Role::Unknown,\n        });\n        if !info.enabled {\n            builder.set_disabled();\n        }\n        if let Some(label) = info.label {\n            if matches!(builder.role(), Role::Label) {\n                builder.set_value(label);\n            } else {\n                builder.set_label(label);\n            }\n        }\n        if let Some(value) = info.current_text_value {\n            builder.set_value(value);\n        }\n        if let Some(value) = info.value {\n            builder.set_numeric_value(value);\n        }\n        if let Some(selected) = info.selected {\n            builder.set_toggled(if selected {\n                Toggled::True\n            } else {\n                Toggled::False\n            });\n        } else if matches!(info.typ, WidgetType::Checkbox) {\n            // Indeterminate state\n            builder.set_toggled(Toggled::Mixed);\n        }\n        if let Some(hint_text) = info.hint_text {\n            builder.set_placeholder(hint_text);\n        }\n    }\n\n    /// Associate a label with a control for accessibility.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut text = \"Arthur\".to_string();\n    /// ui.horizontal(|ui| {\n    ///     let label = ui.label(\"Your name: \");\n    ///     ui.text_edit_singleline(&mut text).labelled_by(label.id);\n    /// });\n    /// # });\n    /// ```\n    pub fn labelled_by(self, id: Id) -> Self {\n        self.ctx.accesskit_node_builder(self.id, |builder| {\n            builder.push_labelled_by(id.accesskit_id());\n        });\n\n        self\n    }\n\n    /// Response to secondary clicks (right-clicks) by showing the given menu.\n    ///\n    /// Make sure the widget senses clicks (e.g. [`crate::Button`] does, [`crate::Label`] does not).\n    ///\n    /// ```\n    /// # use egui::{Label, Sense};\n    /// # egui::__run_test_ui(|ui| {\n    /// let response = ui.add(Label::new(\"Right-click me!\").sense(Sense::click()));\n    /// response.context_menu(|ui| {\n    ///     if ui.button(\"Close the menu\").clicked() {\n    ///         ui.close();\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    ///\n    /// See also: [`Ui::menu_button`] and [`Ui::close`].\n    pub fn context_menu(&self, add_contents: impl FnOnce(&mut Ui)) -> Option<InnerResponse<()>> {\n        Popup::context_menu(self).show(add_contents)\n    }\n\n    /// Returns whether a context menu is currently open for this widget.\n    ///\n    /// See [`Self::context_menu`].\n    pub fn context_menu_opened(&self) -> bool {\n        Popup::context_menu(self).is_open()\n    }\n\n    /// Draw a debug rectangle over the response displaying the response's id and whether it is\n    /// enabled and/or hovered.\n    ///\n    /// This function is intended for debugging purpose and can be useful, for example, in case of\n    /// widget id instability.\n    ///\n    /// Color code:\n    /// - Blue: Enabled but not hovered\n    /// - Green: Enabled and hovered\n    /// - Red: Disabled\n    pub fn paint_debug_info(&self) {\n        self.ctx.debug_painter().debug_rect(\n            self.rect,\n            if self.hovered() {\n                crate::Color32::DARK_GREEN\n            } else if self.enabled() {\n                crate::Color32::BLUE\n            } else {\n                crate::Color32::RED\n            },\n            format!(\"{:?}\", self.id),\n        );\n    }\n}\n\nimpl Response {\n    /// A logical \"or\" operation.\n    /// For instance `a.union(b).hovered` means \"was either a or b hovered?\".\n    ///\n    /// The resulting [`Self::id`] will come from the first (`self`) argument.\n    ///\n    /// You may not call [`Self::interact`] on the resulting `Response`.\n    pub fn union(&self, other: Self) -> Self {\n        assert!(\n            self.ctx == other.ctx,\n            \"Responses must be from the same `Context`\"\n        );\n        debug_assert!(\n            self.layer_id == other.layer_id,\n            \"It makes no sense to combine Responses from two different layers\"\n        );\n        Self {\n            ctx: other.ctx,\n            layer_id: self.layer_id,\n            id: self.id,\n            rect: self.rect.union(other.rect),\n            interact_rect: self.interact_rect.union(other.interact_rect),\n            sense: self.sense.union(other.sense),\n            flags: self.flags | other.flags,\n            interact_pointer_pos: self.interact_pointer_pos.or(other.interact_pointer_pos),\n            intrinsic_size: None,\n        }\n    }\n}\n\nimpl Response {\n    /// Returns a response with a modified [`Self::rect`].\n    #[inline]\n    pub fn with_new_rect(self, rect: Rect) -> Self {\n        Self { rect, ..self }\n    }\n}\n\n/// See [`Response::union`].\n///\n/// To summarize the response from many widgets you can use this pattern:\n///\n/// ```\n/// use egui::*;\n/// fn draw_vec2(ui: &mut Ui, v: &mut Vec2) -> Response {\n///     ui.add(DragValue::new(&mut v.x)) | ui.add(DragValue::new(&mut v.y))\n/// }\n/// ```\n///\n/// Now `draw_vec2(ui, foo).hovered` is true if either [`DragValue`](crate::DragValue) were hovered.\nimpl std::ops::BitOr for Response {\n    type Output = Self;\n\n    fn bitor(self, rhs: Self) -> Self {\n        self.union(rhs)\n    }\n}\n\n/// See [`Response::union`].\n///\n/// To summarize the response from many widgets you can use this pattern:\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # let (widget_a, widget_b, widget_c) = (egui::Label::new(\"a\"), egui::Label::new(\"b\"), egui::Label::new(\"c\"));\n/// let mut response = ui.add(widget_a);\n/// response |= ui.add(widget_b);\n/// response |= ui.add(widget_c);\n/// if response.hovered() { ui.label(\"You hovered at least one of the widgets\"); }\n/// # });\n/// ```\nimpl std::ops::BitOrAssign for Response {\n    fn bitor_assign(&mut self, rhs: Self) {\n        *self = self.union(rhs);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Returned when we wrap some ui-code and want to return both\n/// the results of the inner function and the ui as a whole, e.g.:\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// let inner_resp = ui.horizontal(|ui| {\n///     ui.label(\"Blah blah\");\n///     42\n/// });\n/// inner_resp.response.on_hover_text(\"You hovered the horizontal layout\");\n/// assert_eq!(inner_resp.inner, 42);\n/// # });\n/// ```\n#[derive(Debug)]\npub struct InnerResponse<R> {\n    /// What the user closure returned.\n    pub inner: R,\n\n    /// The response of the area.\n    pub response: Response,\n}\n\nimpl<R> InnerResponse<R> {\n    #[inline]\n    pub fn new(inner: R, response: Response) -> Self {\n        Self { inner, response }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/sense.rs",
    "content": "/// What sort of interaction is a widget sensitive to?\n#[derive(Clone, Copy, Eq, PartialEq)]\n// #[cfg_attr(feature = \"serde\", derive(serde::Serialize))]\npub struct Sense(u8);\n\nbitflags::bitflags! {\n    impl Sense: u8 {\n\n        const HOVER = 0;\n\n        /// Buttons, sliders, windows, …\n        const CLICK = 1<<0;\n\n        /// Sliders, windows, scroll bars, scroll areas, …\n        const DRAG = 1<<1;\n\n        /// This widget wants focus.\n        ///\n        /// Anything interactive + labels that can be focused\n        /// for the benefit of screen readers.\n        const FOCUSABLE = 1<<2;\n    }\n}\n\nimpl std::fmt::Debug for Sense {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"Sense {{\")?;\n        if self.senses_click() {\n            write!(f, \" click\")?;\n        }\n        if self.senses_drag() {\n            write!(f, \" drag\")?;\n        }\n        if self.is_focusable() {\n            write!(f, \" focusable\")?;\n        }\n        write!(f, \" }}\")\n    }\n}\n\nimpl Sense {\n    /// Senses no clicks or drags. Only senses mouse hover.\n    #[doc(alias = \"none\")]\n    #[inline]\n    pub fn hover() -> Self {\n        Self::empty()\n    }\n\n    /// Senses no clicks or drags, but can be focused with the keyboard.\n    /// Used for labels that can be focused for the benefit of screen readers.\n    #[inline]\n    pub fn focusable_noninteractive() -> Self {\n        Self::FOCUSABLE\n    }\n\n    /// Sense clicks and hover, but not drags, and make the widget focusable.\n    ///\n    /// Use [`Sense::CLICK`] if you don't want the widget to be focusable.\n    #[inline]\n    pub fn click() -> Self {\n        Self::CLICK | Self::FOCUSABLE\n    }\n\n    /// Sense drags and hover, but not clicks. Make the widget focusable.\n    ///\n    /// Use [`Sense::DRAG`] if you don't want the widget to be focusable\n    #[inline]\n    pub fn drag() -> Self {\n        Self::DRAG | Self::FOCUSABLE\n    }\n\n    /// Sense both clicks, drags and hover (e.g. a slider or window), and make the widget focusable.\n    ///\n    /// Note that this will introduce a latency when dragging,\n    /// because when the user starts a press egui can't know if this is the start\n    /// of a click or a drag, and it won't know until the cursor has\n    /// either moved a certain distance, or the user has released the mouse button.\n    ///\n    /// See [`crate::PointerState::is_decidedly_dragging`] for details.\n    #[inline]\n    pub fn click_and_drag() -> Self {\n        Self::CLICK | Self::FOCUSABLE | Self::DRAG\n    }\n\n    /// Returns true if we sense either clicks or drags.\n    #[inline]\n    pub fn interactive(&self) -> bool {\n        self.intersects(Self::CLICK | Self::DRAG)\n    }\n\n    #[inline]\n    pub fn senses_click(&self) -> bool {\n        self.contains(Self::CLICK)\n    }\n\n    #[inline]\n    pub fn senses_drag(&self) -> bool {\n        self.contains(Self::DRAG)\n    }\n\n    #[inline]\n    pub fn is_focusable(&self) -> bool {\n        self.contains(Self::FOCUSABLE)\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/style.rs",
    "content": "//! egui theme (spacing, colors, etc).\n\nuse emath::Align;\nuse epaint::{\n    AlphaFromCoverage, CornerRadius, Shadow, Stroke, TextOptions,\n    mutex::Mutex,\n    text::{FontTweak, Tag},\n};\nuse std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc};\n\nuse crate::{\n    ComboBox, CursorIcon, FontFamily, FontId, Grid, Margin, Response, RichText, TextWrapMode,\n    WidgetText,\n    ecolor::Color32,\n    emath::{Rangef, Rect, Vec2, pos2, vec2},\n    reset_button_with,\n};\n\n/// How to format numbers in e.g. a [`crate::DragValue`].\n#[derive(Clone)]\npub struct NumberFormatter(\n    Arc<dyn 'static + Sync + Send + Fn(f64, RangeInclusive<usize>) -> String>,\n);\n\nimpl NumberFormatter {\n    /// The first argument is the number to be formatted.\n    /// The second argument is the range of the number of decimals to show.\n    ///\n    /// See [`Self::format`] for the meaning of the `decimals` argument.\n    #[inline]\n    pub fn new(\n        formatter: impl 'static + Sync + Send + Fn(f64, RangeInclusive<usize>) -> String,\n    ) -> Self {\n        Self(Arc::new(formatter))\n    }\n\n    /// Format the given number with the given number of decimals.\n    ///\n    /// Decimals are counted after the decimal point.\n    ///\n    /// The minimum number of decimals is usually automatically calculated\n    /// from the sensitivity of the [`crate::DragValue`] and will usually be respected (e.g. include trailing zeroes),\n    /// but if the given value requires more decimals to represent accurately,\n    /// more decimals will be shown, up to the given max.\n    #[inline]\n    pub fn format(&self, value: f64, decimals: RangeInclusive<usize>) -> String {\n        (self.0)(value, decimals)\n    }\n}\n\nimpl std::fmt::Debug for NumberFormatter {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"NumberFormatter\")\n    }\n}\n\nimpl PartialEq for NumberFormatter {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.0, &other.0)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Alias for a [`FontId`] (font of a certain size).\n///\n/// The font is found via look-up in [`Style::text_styles`].\n/// You can use [`TextStyle::resolve`] to do this lookup.\n#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum TextStyle {\n    /// Used when small text is needed.\n    Small,\n\n    /// Normal labels. Easily readable, doesn't take up too much space.\n    Body,\n\n    /// Same size as [`Self::Body`], but used when monospace is important (for code snippets, aligning numbers, etc).\n    Monospace,\n\n    /// Buttons. Maybe slightly bigger than [`Self::Body`].\n    ///\n    /// Signifies that he item can be interacted with.\n    Button,\n\n    /// Heading. Probably larger than [`Self::Body`].\n    Heading,\n\n    /// A user-chosen style, found in [`Style::text_styles`].\n    /// ```\n    /// egui::TextStyle::Name(\"footing\".into());\n    /// ````\n    Name(std::sync::Arc<str>),\n}\n\nimpl std::fmt::Display for TextStyle {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Small => \"Small\".fmt(f),\n            Self::Body => \"Body\".fmt(f),\n            Self::Monospace => \"Monospace\".fmt(f),\n            Self::Button => \"Button\".fmt(f),\n            Self::Heading => \"Heading\".fmt(f),\n            Self::Name(name) => (*name).fmt(f),\n        }\n    }\n}\n\nimpl TextStyle {\n    /// Look up this [`TextStyle`] in [`Style::text_styles`].\n    pub fn resolve(&self, style: &Style) -> FontId {\n        style.text_styles.get(self).cloned().unwrap_or_else(|| {\n            panic!(\n                \"Failed to find {:?} in Style::text_styles. Available styles:\\n{:#?}\",\n                self,\n                style.text_styles()\n            )\n        })\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A way to select [`FontId`], either by picking one directly or by using a [`TextStyle`].\n#[derive(Debug, Clone)]\npub enum FontSelection {\n    /// Default text style - will use [`TextStyle::Body`], unless\n    /// [`Style::override_font_id`] or [`Style::override_text_style`] is set.\n    Default,\n\n    /// Directly select size and font family\n    FontId(FontId),\n\n    /// Use a [`TextStyle`] to look up the [`FontId`] in [`Style::text_styles`].\n    Style(TextStyle),\n}\n\nimpl Default for FontSelection {\n    #[inline]\n    fn default() -> Self {\n        Self::Default\n    }\n}\n\nimpl FontSelection {\n    /// Resolve to a [`FontId`].\n    ///\n    /// On [`Self::Default`] and no override in the style, this will\n    /// resolve to [`TextStyle::Body`].\n    pub fn resolve(self, style: &Style) -> FontId {\n        self.resolve_with_fallback(style, TextStyle::Body.into())\n    }\n\n    /// Resolve with a final fallback.\n    ///\n    /// Fallback is resolved on [`Self::Default`] and no override in the style.\n    pub fn resolve_with_fallback(self, style: &Style, fallback: Self) -> FontId {\n        match self {\n            Self::Default => {\n                if let Some(override_font_id) = &style.override_font_id {\n                    override_font_id.clone()\n                } else if let Some(text_style) = &style.override_text_style {\n                    text_style.resolve(style)\n                } else {\n                    fallback.resolve(style)\n                }\n            }\n            Self::FontId(font_id) => font_id,\n            Self::Style(text_style) => text_style.resolve(style),\n        }\n    }\n}\n\nimpl From<FontId> for FontSelection {\n    #[inline(always)]\n    fn from(font_id: FontId) -> Self {\n        Self::FontId(font_id)\n    }\n}\n\nimpl From<TextStyle> for FontSelection {\n    #[inline(always)]\n    fn from(text_style: TextStyle) -> Self {\n        Self::Style(text_style)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Utility to modify a [`Style`] in some way.\n/// Constructed via [`StyleModifier::from`] from a `Fn(&mut Style)` or a [`Style`].\n#[derive(Clone, Default)]\npub struct StyleModifier(Option<Arc<dyn Fn(&mut Style) + Send + Sync>>);\n\nimpl std::fmt::Debug for StyleModifier {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"StyleModifier\")\n    }\n}\n\nimpl<T> From<T> for StyleModifier\nwhere\n    T: Fn(&mut Style) + Send + Sync + 'static,\n{\n    fn from(f: T) -> Self {\n        Self(Some(Arc::new(f)))\n    }\n}\n\nimpl From<Style> for StyleModifier {\n    fn from(style: Style) -> Self {\n        Self(Some(Arc::new(move |s| *s = style.clone())))\n    }\n}\n\nimpl StyleModifier {\n    /// Create a new [`StyleModifier`] from a function.\n    pub fn new(f: impl Fn(&mut Style) + Send + Sync + 'static) -> Self {\n        Self::from(f)\n    }\n\n    /// Apply the modification to the given [`Style`].\n    /// Usually used with [`Ui::style_mut`].\n    pub fn apply(&self, style: &mut Style) {\n        if let Some(f) = &self.0 {\n            f(style);\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Specifies the look and feel of egui.\n///\n/// You can change the visuals of a [`Ui`] with [`Ui::style_mut`]\n/// and of everything with [`crate::Context::set_style_of`].\n/// To choose between dark and light style, use [`crate::Context::set_theme`].\n///\n/// If you want to change fonts, use [`crate::Context::set_fonts`] instead.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Style {\n    /// If set this will change the default [`TextStyle`] for all widgets.\n    ///\n    /// On most widgets you can also set an explicit text style,\n    /// which will take precedence over this.\n    pub override_text_style: Option<TextStyle>,\n\n    /// If set this will change the font family and size for all widgets.\n    ///\n    /// On most widgets you can also set an explicit text style,\n    /// which will take precedence over this.\n    pub override_font_id: Option<FontId>,\n\n    /// How to vertically align text.\n    ///\n    /// Set to `None` to use align that depends on the current layout.\n    pub override_text_valign: Option<Align>,\n\n    /// The [`FontFamily`] and size you want to use for a specific [`TextStyle`].\n    ///\n    /// The most convenient way to look something up in this is to use [`TextStyle::resolve`].\n    ///\n    /// If you would like to overwrite app `text_styles`\n    ///\n    /// ```\n    /// # let mut ctx = egui::Context::default();\n    /// use egui::FontFamily::Proportional;\n    /// use egui::FontId;\n    /// use egui::TextStyle::*;\n    /// use std::collections::BTreeMap;\n    ///\n    /// // Redefine text_styles\n    /// let text_styles: BTreeMap<_, _> = [\n    ///   (Heading, FontId::new(30.0, Proportional)),\n    ///   (Name(\"Heading2\".into()), FontId::new(25.0, Proportional)),\n    ///   (Name(\"Context\".into()), FontId::new(23.0, Proportional)),\n    ///   (Body, FontId::new(18.0, Proportional)),\n    ///   (Monospace, FontId::new(14.0, Proportional)),\n    ///   (Button, FontId::new(14.0, Proportional)),\n    ///   (Small, FontId::new(10.0, Proportional)),\n    /// ].into();\n    ///\n    /// // Mutate global styles with new text styles\n    /// ctx.all_styles_mut(move |style| style.text_styles = text_styles.clone());\n    /// ```\n    pub text_styles: BTreeMap<TextStyle, FontId>,\n\n    /// The style to use for [`DragValue`] text.\n    pub drag_value_text_style: TextStyle,\n\n    /// How to format numbers as strings, e.g. in a [`crate::DragValue`].\n    ///\n    /// You can override this to e.g. add thousands separators.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub number_formatter: NumberFormatter,\n\n    /// If set, labels, buttons, etc. will use this to determine whether to wrap the text at the\n    /// right edge of the [`Ui`] they are in. By default, this is `None`.\n    ///\n    /// **Note**: this API is deprecated, use `wrap_mode` instead.\n    ///\n    /// * `None`: use `wrap_mode` instead\n    /// * `Some(true)`: wrap mode defaults to [`crate::TextWrapMode::Wrap`]\n    /// * `Some(false)`: wrap mode defaults to [`crate::TextWrapMode::Extend`]\n    #[deprecated = \"Use wrap_mode instead\"]\n    pub wrap: Option<bool>,\n\n    /// If set, labels, buttons, etc. will use this to determine whether to wrap or truncate the\n    /// text at the right edge of the [`Ui`] they are in, or to extend it. By default, this is\n    /// `None`.\n    ///\n    /// * `None`: follow layout (with may wrap)\n    /// * `Some(mode)`: use the specified mode as default\n    pub wrap_mode: Option<crate::TextWrapMode>,\n\n    /// Sizes and distances between widgets\n    pub spacing: Spacing,\n\n    /// How and when interaction happens.\n    pub interaction: Interaction,\n\n    /// Colors etc.\n    pub visuals: Visuals,\n\n    /// How many seconds a typical animation should last.\n    pub animation_time: f32,\n\n    /// Options to help debug why egui behaves strangely.\n    ///\n    /// Only available in debug builds.\n    #[cfg(debug_assertions)]\n    pub debug: DebugOptions,\n\n    /// Show tooltips explaining [`DragValue`]:s etc when hovered.\n    ///\n    /// This only affects a few egui widgets.\n    pub explanation_tooltips: bool,\n\n    /// Show the URL of hyperlinks in a tooltip when hovered.\n    pub url_in_tooltip: bool,\n\n    /// If true and scrolling is enabled for only one direction, allow horizontal scrolling without pressing shift\n    pub always_scroll_the_only_direction: bool,\n\n    /// The animation that should be used when scrolling a [`crate::ScrollArea`] using e.g. [`Ui::scroll_to_rect`].\n    pub scroll_animation: ScrollAnimation,\n\n    /// Use a more compact style for menus.\n    pub compact_menu_style: bool,\n}\n\n#[test]\nfn style_impl_send_sync() {\n    fn assert_send_sync<T: Send + Sync>() {}\n    assert_send_sync::<Style>();\n}\n\nimpl Style {\n    // TODO(emilk): rename style.interact() to maybe… `style.interactive` ?\n    /// Use this style for interactive things.\n    /// Note that you must already have a response,\n    /// i.e. you must allocate space and interact BEFORE painting the widget!\n    pub fn interact(&self, response: &Response) -> &WidgetVisuals {\n        self.visuals.widgets.style(response)\n    }\n\n    pub fn interact_selectable(&self, response: &Response, selected: bool) -> WidgetVisuals {\n        let mut visuals = *self.visuals.widgets.style(response);\n        if selected {\n            visuals.weak_bg_fill = self.visuals.selection.bg_fill;\n            visuals.bg_fill = self.visuals.selection.bg_fill;\n            // visuals.bg_stroke = self.visuals.selection.stroke;\n            visuals.fg_stroke = self.visuals.selection.stroke;\n        }\n        visuals\n    }\n\n    /// Style to use for non-interactive widgets.\n    pub fn noninteractive(&self) -> &WidgetVisuals {\n        &self.visuals.widgets.noninteractive\n    }\n\n    /// All known text styles.\n    pub fn text_styles(&self) -> Vec<TextStyle> {\n        self.text_styles.keys().cloned().collect()\n    }\n}\n\n/// Controls the sizes and distances between widgets.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Spacing {\n    /// Horizontal and vertical spacing between widgets.\n    ///\n    /// To add extra space between widgets, use [`Ui::add_space`].\n    ///\n    /// `item_spacing` is inserted _after_ adding a widget, so to increase the spacing between\n    /// widgets `A` and `B` you need to change `item_spacing` before adding `A`.\n    pub item_spacing: Vec2,\n\n    /// Horizontal and vertical margins within a window frame.\n    pub window_margin: Margin,\n\n    /// Button size is text size plus this on each side\n    pub button_padding: Vec2,\n\n    /// Horizontal and vertical margins within a menu frame.\n    pub menu_margin: Margin,\n\n    /// Indent collapsing regions etc by this much.\n    pub indent: f32,\n\n    /// Minimum size of a [`DragValue`], color picker button, and other small widgets.\n    /// `interact_size.y` is the default height of button, slider, etc.\n    /// Anything clickable should be (at least) this size.\n    pub interact_size: Vec2, // TODO(emilk): rename min_interact_size ?\n\n    /// Default width of a [`Slider`].\n    pub slider_width: f32,\n\n    /// Default rail height of a [`Slider`].\n    pub slider_rail_height: f32,\n\n    /// Default (minimum) width of a [`ComboBox`].\n    pub combo_width: f32,\n\n    /// Default width of a [`crate::TextEdit`].\n    pub text_edit_width: f32,\n\n    /// Checkboxes, radio button and collapsing headers have an icon at the start.\n    /// This is the width/height of the outer part of this icon (e.g. the BOX of the checkbox).\n    pub icon_width: f32,\n\n    /// Checkboxes, radio button and collapsing headers have an icon at the start.\n    /// This is the width/height of the inner part of this icon (e.g. the check of the checkbox).\n    pub icon_width_inner: f32,\n\n    /// Checkboxes, radio button and collapsing headers have an icon at the start.\n    /// This is the spacing between the icon and the text\n    pub icon_spacing: f32,\n\n    /// The size used for the [`Ui::max_rect`] the first frame.\n    ///\n    /// Text will wrap at this width, and images that expand to fill the available space\n    /// will expand to this size.\n    ///\n    /// If the contents are smaller than this size, the area will shrink to fit the contents.\n    /// If the contents overflow, the area will grow.\n    pub default_area_size: Vec2,\n\n    /// Width of a tooltip (`on_hover_ui`, `on_hover_text` etc).\n    pub tooltip_width: f32,\n\n    /// The default wrapping width of a menu.\n    ///\n    /// Items longer than this will wrap to a new line.\n    pub menu_width: f32,\n\n    /// Horizontal distance between a menu and a submenu.\n    pub menu_spacing: f32,\n\n    /// End indented regions with a horizontal line\n    pub indent_ends_with_horizontal_line: bool,\n\n    /// Height of a combo-box before showing scroll bars.\n    pub combo_height: f32,\n\n    /// Controls the spacing of a [`crate::ScrollArea`].\n    pub scroll: ScrollStyle,\n}\n\nimpl Spacing {\n    /// Returns small icon rectangle and big icon rectangle\n    pub fn icon_rectangles(&self, rect: Rect) -> (Rect, Rect) {\n        let icon_width = self.icon_width;\n        let big_icon_rect = Rect::from_center_size(\n            pos2(rect.left() + icon_width / 2.0, rect.center().y),\n            vec2(icon_width, icon_width),\n        );\n\n        let small_icon_rect =\n            Rect::from_center_size(big_icon_rect.center(), Vec2::splat(self.icon_width_inner));\n\n        (small_icon_rect, big_icon_rect)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Controls the spacing and visuals of a [`crate::ScrollArea`].\n///\n/// There are three presets to chose from:\n/// * [`Self::solid`]\n/// * [`Self::thin`]\n/// * [`Self::floating`]\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct ScrollStyle {\n    /// If `true`, scroll bars float above the content, partially covering it.\n    ///\n    /// If `false`, the scroll bars allocate space, shrinking the area\n    /// available to the contents.\n    ///\n    /// This also changes the colors of the scroll-handle to make\n    /// it more promiment.\n    pub floating: bool,\n\n    /// Extra margin added around the contents of a [`crate::ScrollArea`].\n    ///\n    /// The scroll bars will be either on top of this margin, or outside of it,\n    /// depending on the value of [`Self::floating`].\n    pub content_margin: Margin,\n\n    /// The width of the scroll bars at it largest.\n    pub bar_width: f32,\n\n    /// Make sure the scroll handle is at least this big\n    pub handle_min_length: f32,\n\n    /// Margin between contents and scroll bar.\n    pub bar_inner_margin: f32,\n\n    /// Margin between scroll bar and the outer container (e.g. right of a vertical scroll bar).\n    /// Only makes sense for non-floating scroll bars.\n    pub bar_outer_margin: f32,\n\n    /// The thin width of floating scroll bars that the user is NOT hovering.\n    ///\n    /// When the user hovers the scroll bars they expand to [`Self::bar_width`].\n    pub floating_width: f32,\n\n    /// How much space is allocated for a floating scroll bar?\n    ///\n    /// Normally this is zero, but you could set this to something small\n    /// like 4.0 and set [`Self::dormant_handle_opacity`] and\n    /// [`Self::dormant_background_opacity`] to e.g. 0.5\n    /// so as to always show a thin scroll bar.\n    pub floating_allocated_width: f32,\n\n    /// If true, use colors with more contrast. Good for floating scroll bars.\n    pub foreground_color: bool,\n\n    /// The opaqueness of the background when the user is neither scrolling\n    /// nor hovering the scroll area.\n    ///\n    /// This is only for floating scroll bars.\n    /// Solid scroll bars are always opaque.\n    pub dormant_background_opacity: f32,\n\n    /// The opaqueness of the background when the user is hovering\n    /// the scroll area, but not the scroll bar.\n    ///\n    /// This is only for floating scroll bars.\n    /// Solid scroll bars are always opaque.\n    pub active_background_opacity: f32,\n\n    /// The opaqueness of the background when the user is hovering\n    /// over the scroll bars.\n    ///\n    /// This is only for floating scroll bars.\n    /// Solid scroll bars are always opaque.\n    pub interact_background_opacity: f32,\n\n    /// The opaqueness of the handle when the user is neither scrolling\n    /// nor hovering the scroll area.\n    ///\n    /// This is only for floating scroll bars.\n    /// Solid scroll bars are always opaque.\n    pub dormant_handle_opacity: f32,\n\n    /// The opaqueness of the handle when the user is hovering\n    /// the scroll area, but not the scroll bar.\n    ///\n    /// This is only for floating scroll bars.\n    /// Solid scroll bars are always opaque.\n    pub active_handle_opacity: f32,\n\n    /// The opaqueness of the handle when the user is hovering\n    /// over the scroll bars.\n    ///\n    /// This is only for floating scroll bars.\n    /// Solid scroll bars are always opaque.\n    pub interact_handle_opacity: f32,\n}\n\nimpl Default for ScrollStyle {\n    fn default() -> Self {\n        Self::floating()\n    }\n}\n\nimpl ScrollStyle {\n    /// Solid scroll bars that always use up space\n    pub fn solid() -> Self {\n        Self {\n            floating: false,\n            content_margin: Margin::ZERO,\n            bar_width: 6.0,\n            handle_min_length: 12.0,\n            bar_inner_margin: 4.0,\n            bar_outer_margin: 0.0,\n            floating_width: 2.0,\n            floating_allocated_width: 0.0,\n\n            foreground_color: false,\n\n            dormant_background_opacity: 0.0,\n            active_background_opacity: 0.4,\n            interact_background_opacity: 0.7,\n\n            dormant_handle_opacity: 0.0,\n            active_handle_opacity: 0.6,\n            interact_handle_opacity: 1.0,\n        }\n    }\n\n    /// Thin scroll bars that expand on hover\n    pub fn thin() -> Self {\n        Self {\n            floating: true,\n            bar_width: 10.0,\n            floating_allocated_width: 6.0,\n            foreground_color: false,\n\n            dormant_background_opacity: 1.0,\n            dormant_handle_opacity: 1.0,\n\n            active_background_opacity: 1.0,\n            active_handle_opacity: 1.0,\n\n            // Be translucent when expanded so we can see the content\n            interact_background_opacity: 0.6,\n            interact_handle_opacity: 0.6,\n\n            ..Self::solid()\n        }\n    }\n\n    /// No scroll bars until you hover the scroll area,\n    /// at which time they appear faintly, and then expand\n    /// when you hover the scroll bars.\n    pub fn floating() -> Self {\n        Self {\n            floating: true,\n            bar_width: 10.0,\n            foreground_color: true,\n            floating_allocated_width: 0.0,\n            dormant_background_opacity: 0.0,\n            dormant_handle_opacity: 0.0,\n            ..Self::solid()\n        }\n    }\n\n    /// Width of a solid vertical scrollbar, or height of a horizontal scroll bar, when it is at its widest.\n    pub fn allocated_width(&self) -> f32 {\n        if self.floating {\n            self.floating_allocated_width\n        } else {\n            self.bar_inner_margin + self.bar_width + self.bar_outer_margin\n        }\n    }\n\n    pub fn ui(&mut self, ui: &mut Ui) {\n        ui.horizontal(|ui| {\n            ui.label(\"Presets:\");\n            ui.selectable_value(self, Self::solid(), \"Solid\");\n            ui.selectable_value(self, Self::thin(), \"Thin\");\n            ui.selectable_value(self, Self::floating(), \"Floating\");\n        });\n\n        ui.collapsing(\"Details\", |ui| {\n            self.details_ui(ui);\n        });\n    }\n\n    pub fn details_ui(&mut self, ui: &mut Ui) {\n        let Self {\n            floating,\n\n            content_margin,\n\n            bar_width,\n            handle_min_length,\n            bar_inner_margin,\n            bar_outer_margin,\n            floating_width,\n            floating_allocated_width,\n\n            foreground_color,\n\n            dormant_background_opacity,\n            active_background_opacity,\n            interact_background_opacity,\n            dormant_handle_opacity,\n            active_handle_opacity,\n            interact_handle_opacity,\n        } = self;\n\n        ui.horizontal(|ui| {\n            ui.label(\"Type:\");\n            ui.selectable_value(floating, false, \"Solid\");\n            ui.selectable_value(floating, true, \"Floating\");\n        });\n\n        ui.horizontal(|ui| {\n            ui.label(\"Content margin:\");\n            content_margin.ui(ui);\n        });\n\n        ui.horizontal(|ui| {\n            ui.add(DragValue::new(bar_width).range(0.0..=32.0));\n            ui.label(\"Full bar width\");\n        });\n        if *floating {\n            ui.horizontal(|ui| {\n                ui.add(DragValue::new(floating_width).range(0.0..=32.0));\n                ui.label(\"Thin bar width\");\n            });\n            ui.horizontal(|ui| {\n                ui.add(DragValue::new(floating_allocated_width).range(0.0..=32.0));\n                ui.label(\"Allocated width\");\n            });\n        }\n\n        ui.horizontal(|ui| {\n            ui.add(DragValue::new(handle_min_length).range(0.0..=32.0));\n            ui.label(\"Minimum handle length\");\n        });\n        ui.horizontal(|ui| {\n            ui.add(DragValue::new(bar_outer_margin).range(0.0..=32.0));\n            ui.label(\"Outer margin\");\n        });\n\n        ui.horizontal(|ui| {\n            ui.label(\"Color:\");\n            ui.selectable_value(foreground_color, false, \"Background\");\n            ui.selectable_value(foreground_color, true, \"Foreground\");\n        });\n\n        if *floating {\n            crate::Grid::new(\"opacity\").show(ui, |ui| {\n                fn opacity_ui(ui: &mut Ui, opacity: &mut f32) {\n                    ui.add(DragValue::new(opacity).speed(0.01).range(0.0..=1.0));\n                }\n\n                ui.label(\"Opacity\");\n                ui.label(\"Dormant\");\n                ui.label(\"Active\");\n                ui.label(\"Interacting\");\n                ui.end_row();\n\n                ui.label(\"Background:\");\n                opacity_ui(ui, dormant_background_opacity);\n                opacity_ui(ui, active_background_opacity);\n                opacity_ui(ui, interact_background_opacity);\n                ui.end_row();\n\n                ui.label(\"Handle:\");\n                opacity_ui(ui, dormant_handle_opacity);\n                opacity_ui(ui, active_handle_opacity);\n                opacity_ui(ui, interact_handle_opacity);\n                ui.end_row();\n            });\n        } else {\n            ui.horizontal(|ui| {\n                ui.add(DragValue::new(bar_inner_margin).range(0.0..=32.0));\n                ui.label(\"Inner margin\");\n            });\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Scroll animation configuration, used when programmatically scrolling somewhere (e.g. with `[crate::Ui::scroll_to_cursor]`).\n///\n/// The animation duration is calculated based on the distance to be scrolled via `[ScrollAnimation::points_per_second]`\n/// and can be clamped to a min / max duration via `[ScrollAnimation::duration]`.\n#[derive(Copy, Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct ScrollAnimation {\n    /// With what speed should we scroll? (Default: 1000.0)\n    pub points_per_second: f32,\n\n    /// The min / max scroll duration.\n    pub duration: Rangef,\n}\n\nimpl Default for ScrollAnimation {\n    fn default() -> Self {\n        Self {\n            points_per_second: 1000.0,\n            duration: Rangef::new(0.1, 0.3),\n        }\n    }\n}\n\nimpl ScrollAnimation {\n    /// New scroll animation\n    pub fn new(points_per_second: f32, duration: Rangef) -> Self {\n        Self {\n            points_per_second,\n            duration,\n        }\n    }\n\n    /// No animation, scroll instantly.\n    pub fn none() -> Self {\n        Self {\n            points_per_second: f32::INFINITY,\n            duration: Rangef::new(0.0, 0.0),\n        }\n    }\n\n    /// Scroll with a fixed duration, regardless of distance.\n    pub fn duration(t: f32) -> Self {\n        Self {\n            points_per_second: f32::INFINITY,\n            duration: Rangef::new(t, t),\n        }\n    }\n\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        crate::Grid::new(\"scroll_animation\").show(ui, |ui| {\n            ui.label(\"Scroll animation:\");\n            ui.add(\n                DragValue::new(&mut self.points_per_second)\n                    .speed(100.0)\n                    .range(0.0..=5000.0),\n            );\n            ui.label(\"points/second\");\n            ui.end_row();\n\n            ui.label(\"Min duration:\");\n            ui.add(\n                DragValue::new(&mut self.duration.min)\n                    .speed(0.01)\n                    .range(0.0..=self.duration.max),\n            );\n            ui.label(\"seconds\");\n            ui.end_row();\n\n            ui.label(\"Max duration:\");\n            ui.add(\n                DragValue::new(&mut self.duration.max)\n                    .speed(0.01)\n                    .range(0.0..=1.0),\n            );\n            ui.label(\"seconds\");\n            ui.end_row();\n        });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// How and when interaction happens.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Interaction {\n    /// How close a widget must be to the mouse to have a chance to register as a click or drag.\n    ///\n    /// If this is larger than zero, it gets easier to hit widgets,\n    /// which is important for e.g. touch screens.\n    pub interact_radius: f32,\n\n    /// Radius of the interactive area of the side of a window during drag-to-resize.\n    pub resize_grab_radius_side: f32,\n\n    /// Radius of the interactive area of the corner of a window during drag-to-resize.\n    pub resize_grab_radius_corner: f32,\n\n    /// If `false`, tooltips will show up anytime you hover anything, even if mouse is still moving\n    pub show_tooltips_only_when_still: bool,\n\n    /// Delay in seconds before showing tooltips after the mouse stops moving\n    pub tooltip_delay: f32,\n\n    /// If you have waited for a tooltip and then hover some other widget within\n    /// this many seconds, then show the new tooltip right away,\n    /// skipping [`Self::tooltip_delay`].\n    ///\n    /// This lets the user quickly move over some dead space to hover the next thing.\n    pub tooltip_grace_time: f32,\n\n    /// Can you select the text on a [`crate::Label`] by default?\n    pub selectable_labels: bool,\n\n    /// Can the user select text that span multiple labels?\n    ///\n    /// The default is `true`, but text selection can be slightly glitchy,\n    /// so you may want to disable it.\n    pub multi_widget_text_select: bool,\n}\n\n/// Look and feel of the text cursor.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct TextCursorStyle {\n    /// The color and width of the text cursor\n    pub stroke: Stroke,\n\n    /// Show where the text cursor would be if you clicked?\n    pub preview: bool,\n\n    /// Should the cursor blink?\n    pub blink: bool,\n\n    /// When blinking, this is how long the cursor is visible.\n    pub on_duration: f32,\n\n    /// When blinking, this is how long the cursor is invisible.\n    pub off_duration: f32,\n}\n\nimpl Default for TextCursorStyle {\n    fn default() -> Self {\n        Self {\n            stroke: Stroke::new(2.0, Color32::from_rgb(192, 222, 255)), // Dark mode\n            preview: false,\n            blink: true,\n            on_duration: 0.5,\n            off_duration: 0.5,\n        }\n    }\n}\n\n/// Controls the visual style (colors etc) of egui.\n///\n/// You can change the visuals of a [`Ui`] with [`Ui::visuals_mut`]\n/// and of everything with [`crate::Context::set_visuals_of`].\n///\n/// If you want to change fonts, use [`crate::Context::set_fonts`] instead.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Visuals {\n    /// If true, the visuals are overall dark with light text.\n    /// If false, the visuals are overall light with dark text.\n    ///\n    /// NOTE: setting this does very little by itself,\n    /// this is more to provide a convenient summary of the rest of the settings.\n    pub dark_mode: bool,\n\n    /// Controls how we render text.\n    ///\n    /// The [`TextOptions::max_texture_side`] is ignored and overruled by\n    /// [`crate::RawInput::max_texture_side`].\n    pub text_options: TextOptions,\n\n    /// Override default text color for all text.\n    ///\n    /// This is great for setting the color of text for any widget.\n    ///\n    /// If `text_color` is `None` (default), then the text color will be the same as the\n    /// foreground stroke color (`WidgetVisuals::fg_stroke`)\n    /// and will depend on whether or not the widget is being interacted with.\n    ///\n    /// In the future we may instead modulate\n    /// the `text_color` based on whether or not it is interacted with\n    /// so that `visuals.text_color` is always used,\n    /// but its alpha may be different based on whether or not\n    /// it is disabled, non-interactive, hovered etc.\n    pub override_text_color: Option<Color32>,\n\n    /// How strong \"weak\" text is.\n    ///\n    /// Ignored if [`Self::weak_text_color`] is set.\n    pub weak_text_alpha: f32,\n\n    /// Color of \"weak\" text.\n    ///\n    /// If `None`, the color is [`Self::text_color`]\n    /// multiplied by [`Self::weak_text_alpha`].\n    pub weak_text_color: Option<Color32>,\n\n    /// Visual styles of widgets\n    pub widgets: Widgets,\n\n    pub selection: Selection,\n\n    /// The color used for [`crate::Hyperlink`],\n    pub hyperlink_color: Color32,\n\n    /// Something just barely different from the background color.\n    /// Used for [`crate::Grid::striped`].\n    pub faint_bg_color: Color32,\n\n    /// Very dark or light color (for corresponding theme).\n    /// Used as the background of text edits, scroll bars and others things\n    /// that needs to look different from other interactive stuff.\n    pub extreme_bg_color: Color32,\n\n    /// The background color of [`crate::TextEdit`].\n    ///\n    /// Defaults to [`Self::extreme_bg_color`].\n    pub text_edit_bg_color: Option<Color32>,\n\n    /// Background color behind code-styled monospaced labels.\n    pub code_bg_color: Color32,\n\n    /// A good color for warning text (e.g. orange).\n    pub warn_fg_color: Color32,\n\n    /// A good color for error text (e.g. red).\n    pub error_fg_color: Color32,\n\n    pub window_corner_radius: CornerRadius,\n    pub window_shadow: Shadow,\n    pub window_fill: Color32,\n    pub window_stroke: Stroke,\n\n    /// Highlight the topmost window.\n    pub window_highlight_topmost: bool,\n\n    pub menu_corner_radius: CornerRadius,\n\n    /// Panel background color\n    pub panel_fill: Color32,\n\n    pub popup_shadow: Shadow,\n\n    pub resize_corner_size: f32,\n\n    /// How the text cursor acts.\n    pub text_cursor: TextCursorStyle,\n\n    /// Allow child widgets to be just on the border and still have a stroke with some thickness\n    pub clip_rect_margin: f32,\n\n    /// Show a background behind buttons.\n    pub button_frame: bool,\n\n    /// Show a background behind collapsing headers.\n    pub collapsing_header_frame: bool,\n\n    /// Draw a vertical line left of indented region, in e.g. [`crate::CollapsingHeader`].\n    pub indent_has_left_vline: bool,\n\n    /// Whether or not Grids and Tables should be striped by default\n    /// (have alternating rows differently colored).\n    pub striped: bool,\n\n    /// Show trailing color behind the circle of a [`Slider`]. Default is OFF.\n    ///\n    /// Enabling this will affect ALL sliders, and can be enabled/disabled per slider with [`Slider::trailing_fill`].\n    pub slider_trailing_fill: bool,\n\n    /// Shape of the handle for sliders and similar widgets.\n    ///\n    /// Changing this will affect ALL sliders, and can be enabled/disabled per slider with [`Slider::handle_shape`].\n    pub handle_shape: HandleShape,\n\n    /// Should the cursor change when the user hovers over an interactive/clickable item?\n    ///\n    /// This is consistent with a lot of browser-based applications (vscode, github\n    /// all turn your cursor into [`CursorIcon::PointingHand`] when a button is\n    /// hovered) but it is inconsistent with native UI toolkits.\n    pub interact_cursor: Option<CursorIcon>,\n\n    /// Show a spinner when loading an image.\n    pub image_loading_spinners: bool,\n\n    /// How to display numeric color values.\n    pub numeric_color_space: NumericColorSpace,\n\n    /// How much to modify the alpha of a disabled widget.\n    pub disabled_alpha: f32,\n}\n\nimpl Visuals {\n    #[inline(always)]\n    pub fn noninteractive(&self) -> &WidgetVisuals {\n        &self.widgets.noninteractive\n    }\n\n    // Non-interactive text color.\n    pub fn text_color(&self) -> Color32 {\n        self.override_text_color\n            .unwrap_or_else(|| self.widgets.noninteractive.text_color())\n    }\n\n    pub fn weak_text_color(&self) -> Color32 {\n        self.weak_text_color\n            .unwrap_or_else(|| self.text_color().gamma_multiply(self.weak_text_alpha))\n    }\n\n    #[inline(always)]\n    pub fn strong_text_color(&self) -> Color32 {\n        self.widgets.active.text_color()\n    }\n\n    /// The background color of [`crate::TextEdit`].\n    pub fn text_edit_bg_color(&self) -> Color32 {\n        self.text_edit_bg_color.unwrap_or(self.extreme_bg_color)\n    }\n\n    /// Window background color.\n    #[inline(always)]\n    pub fn window_fill(&self) -> Color32 {\n        self.window_fill\n    }\n\n    #[inline(always)]\n    pub fn window_stroke(&self) -> Stroke {\n        self.window_stroke\n    }\n\n    /// When fading out things, we fade the colors towards this.\n    #[inline(always)]\n    #[deprecated = \"Use disabled_alpha(). Fading is now handled by modifying the alpha channel.\"]\n    pub fn fade_out_to_color(&self) -> Color32 {\n        self.widgets.noninteractive.weak_bg_fill\n    }\n\n    /// Disabled widgets have their alpha modified by this.\n    #[inline(always)]\n    pub fn disabled_alpha(&self) -> f32 {\n        self.disabled_alpha\n    }\n\n    /// Returns a \"disabled\" version of the given color.\n    ///\n    /// This function modifies the opcacity of the given color.\n    /// If this is undesirable use [`gray_out`](Self::gray_out).\n    #[inline(always)]\n    pub fn disable(&self, color: Color32) -> Color32 {\n        color.gamma_multiply(self.disabled_alpha())\n    }\n\n    /// Returns a \"grayed out\" version of the given color.\n    #[doc(alias = \"grey_out\")]\n    #[inline(always)]\n    pub fn gray_out(&self, color: Color32) -> Color32 {\n        crate::ecolor::tint_color_towards(color, self.widgets.noninteractive.weak_bg_fill)\n    }\n}\n\n/// Selected text, selected elements etc\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Selection {\n    /// Background color behind selected text and other selectable buttons.\n    pub bg_fill: Color32,\n\n    /// Color of selected text.\n    pub stroke: Stroke,\n}\n\n/// Shape of the handle for sliders and similar widgets.\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum HandleShape {\n    /// Circular handle\n    Circle,\n\n    /// Rectangular handle\n    Rect {\n        /// Aspect ratio of the rectangle. Set to < 1.0 to make it narrower.\n        aspect_ratio: f32,\n    },\n}\n\n/// The visuals of widgets for different states of interaction.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Widgets {\n    /// The style of a widget that you cannot interact with.\n    /// * `noninteractive.bg_stroke` is the outline of windows.\n    /// * `noninteractive.bg_fill` is the background color of windows.\n    /// * `noninteractive.fg_stroke` is the normal text color.\n    pub noninteractive: WidgetVisuals,\n\n    /// The style of an interactive widget, such as a button, at rest.\n    pub inactive: WidgetVisuals,\n\n    /// The style of an interactive widget while you hover it, or when it is highlighted.\n    ///\n    /// See [`Response::hovered`], [`Response::highlighted`] and [`Response::highlight`].\n    pub hovered: WidgetVisuals,\n\n    /// The style of an interactive widget as you are clicking or dragging it.\n    pub active: WidgetVisuals,\n\n    /// The style of a button that has an open menu beneath it (e.g. a combo-box)\n    pub open: WidgetVisuals,\n}\n\nimpl Widgets {\n    pub fn style(&self, response: &Response) -> &WidgetVisuals {\n        if !response.sense.interactive() {\n            &self.noninteractive\n        } else if response.is_pointer_button_down_on() || response.has_focus() || response.clicked()\n        {\n            &self.active\n        } else if response.hovered() || response.highlighted() {\n            &self.hovered\n        } else {\n            &self.inactive\n        }\n    }\n}\n\n/// bg = background, fg = foreground.\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct WidgetVisuals {\n    /// Background color of widgets that must have a background fill,\n    /// such as the slider background, a checkbox background, or a radio button background.\n    ///\n    /// Must never be [`Color32::TRANSPARENT`].\n    pub bg_fill: Color32,\n\n    /// Background color of widgets that can _optionally_ have a background fill, such as buttons.\n    ///\n    /// May be [`Color32::TRANSPARENT`].\n    pub weak_bg_fill: Color32,\n\n    /// For surrounding rectangle of things that need it,\n    /// like buttons, the box of the checkbox, etc.\n    /// Should maybe be called `frame_stroke`.\n    pub bg_stroke: Stroke,\n\n    /// Button frames etc.\n    pub corner_radius: CornerRadius,\n\n    /// Stroke and text color of the interactive part of a component (button text, slider grab, check-mark, …).\n    pub fg_stroke: Stroke,\n\n    /// Make the frame this much larger.\n    ///\n    /// The problem with \"expanding\" widgets is that they now want to paint outside their own bounds,\n    /// which then requires all parent UIs to have proper margins.\n    ///\n    /// It also means hovered things are no longer properly aligned with every other widget.\n    pub expansion: f32,\n}\n\nimpl WidgetVisuals {\n    #[inline(always)]\n    pub fn text_color(&self) -> Color32 {\n        self.fg_stroke.color\n    }\n\n    #[deprecated = \"Renamed to corner_radius\"]\n    pub fn rounding(&self) -> CornerRadius {\n        self.corner_radius\n    }\n}\n\n/// Options for help debug egui by adding extra visualization\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg(debug_assertions)]\npub struct DebugOptions {\n    /// Always show callstack to ui on hover.\n    ///\n    /// Useful for figuring out where in the code some UI is being created.\n    ///\n    /// Only works in debug builds.\n    /// Requires the `callstack` feature.\n    /// Does not work on web.\n    #[cfg(debug_assertions)]\n    pub debug_on_hover: bool,\n\n    /// Show callstack for the current widget on hover if all modifier keys are pressed down.\n    ///\n    /// Useful for figuring out where in the code some UI is being created.\n    ///\n    /// Only works in debug builds.\n    /// Requires the `callstack` feature.\n    /// Does not work on web.\n    ///\n    /// Default is `true` in debug builds, on native, if the `callstack` feature is enabled.\n    #[cfg(debug_assertions)]\n    pub debug_on_hover_with_all_modifiers: bool,\n\n    /// If we show the hover ui, include where the next widget is placed.\n    #[cfg(debug_assertions)]\n    pub hover_shows_next: bool,\n\n    /// Show which widgets make their parent wider\n    pub show_expand_width: bool,\n\n    /// Show which widgets make their parent higher\n    pub show_expand_height: bool,\n\n    pub show_resize: bool,\n\n    /// Show an overlay on all interactive widgets.\n    pub show_interactive_widgets: bool,\n\n    /// Show interesting widgets under the mouse cursor.\n    pub show_widget_hits: bool,\n\n    /// Show a warning if the same `Rect` had different `Id` on the previous frame.\n    pub warn_if_rect_changes_id: bool,\n\n    /// If true, highlight widgets that are not aligned to [`emath::GUI_ROUNDING`].\n    ///\n    /// See [`emath::GuiRounding`] for more.\n    pub show_unaligned: bool,\n\n    /// Highlight the currently focused widget.\n    ///\n    /// This is useful when some widget has a invisible focus (e.g. when a widget is using\n    /// `Sense::click()` when it should be using `Sense::CLICK`) and you need to find which one it\n    /// is.\n    pub show_focused_widget: bool,\n}\n\n#[cfg(debug_assertions)]\nimpl Default for DebugOptions {\n    fn default() -> Self {\n        Self {\n            debug_on_hover: false,\n            debug_on_hover_with_all_modifiers: cfg!(feature = \"callstack\")\n                && !cfg!(target_arch = \"wasm32\"),\n            hover_shows_next: false,\n            show_expand_width: false,\n            show_expand_height: false,\n            show_resize: false,\n            show_interactive_widgets: false,\n            show_widget_hits: false,\n            warn_if_rect_changes_id: cfg!(debug_assertions),\n            show_unaligned: cfg!(debug_assertions),\n            show_focused_widget: false,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// The default text styles of the default egui theme.\npub fn default_text_styles() -> BTreeMap<TextStyle, FontId> {\n    use FontFamily::{Monospace, Proportional};\n\n    [\n        (TextStyle::Small, FontId::new(9.0, Proportional)),\n        (TextStyle::Body, FontId::new(13.0, Proportional)),\n        (TextStyle::Button, FontId::new(13.0, Proportional)),\n        (TextStyle::Heading, FontId::new(18.0, Proportional)),\n        (TextStyle::Monospace, FontId::new(13.0, Monospace)),\n    ]\n    .into()\n}\n\nimpl Default for Style {\n    fn default() -> Self {\n        #[expect(deprecated)]\n        Self {\n            override_font_id: None,\n            override_text_style: None,\n            override_text_valign: Some(Align::Center),\n            text_styles: default_text_styles(),\n            drag_value_text_style: TextStyle::Button,\n            number_formatter: NumberFormatter(Arc::new(emath::format_with_decimals_in_range)),\n            wrap: None,\n            wrap_mode: None,\n            spacing: Spacing::default(),\n            interaction: Interaction::default(),\n            visuals: Visuals::default(),\n            animation_time: 6.0 / 60.0, // If we make this too slow, it will be too obvious that our panel animations look like shit :(\n            #[cfg(debug_assertions)]\n            debug: Default::default(),\n            explanation_tooltips: false,\n            url_in_tooltip: false,\n            always_scroll_the_only_direction: false,\n            scroll_animation: ScrollAnimation::default(),\n            compact_menu_style: true,\n        }\n    }\n}\n\nimpl Default for Spacing {\n    fn default() -> Self {\n        Self {\n            item_spacing: vec2(8.0, 3.0),\n            window_margin: Margin::same(6),\n            menu_margin: Margin::same(6),\n            button_padding: vec2(4.0, 1.0),\n            indent: 18.0, // match checkbox/radio-button with `button_padding.x + icon_width + icon_spacing`\n            interact_size: vec2(40.0, 18.0),\n            slider_width: 100.0,\n            slider_rail_height: 8.0,\n            combo_width: 100.0,\n            text_edit_width: 280.0,\n            icon_width: 14.0,\n            icon_width_inner: 8.0,\n            icon_spacing: 4.0,\n            default_area_size: vec2(600.0, 400.0),\n            tooltip_width: 500.0,\n            menu_width: 400.0,\n            menu_spacing: 2.0,\n            combo_height: 200.0,\n            scroll: Default::default(),\n            indent_ends_with_horizontal_line: false,\n        }\n    }\n}\n\nimpl Default for Interaction {\n    fn default() -> Self {\n        Self {\n            interact_radius: 5.0,\n            resize_grab_radius_side: 3.0,\n            resize_grab_radius_corner: 10.0,\n            show_tooltips_only_when_still: true,\n            tooltip_delay: 0.5,\n            tooltip_grace_time: 0.2,\n            selectable_labels: true,\n            multi_widget_text_select: true,\n        }\n    }\n}\n\nimpl Visuals {\n    /// Default dark theme.\n    pub fn dark() -> Self {\n        Self {\n            dark_mode: true,\n            text_options: TextOptions {\n                alpha_from_coverage: AlphaFromCoverage::DARK_MODE_DEFAULT,\n                ..Default::default()\n            },\n            override_text_color: None,\n            weak_text_alpha: 0.6,\n            weak_text_color: None,\n            widgets: Widgets::default(),\n            selection: Selection::default(),\n            hyperlink_color: Color32::from_rgb(90, 170, 255),\n            faint_bg_color: Color32::from_additive_luminance(5), // visible, but barely so\n            extreme_bg_color: Color32::from_gray(10),            // e.g. TextEdit background\n            text_edit_bg_color: None, // use `extreme_bg_color` by default\n            code_bg_color: Color32::from_gray(64),\n            warn_fg_color: Color32::from_rgb(255, 143, 0), // orange\n            error_fg_color: Color32::from_rgb(255, 0, 0),  // red\n\n            window_corner_radius: CornerRadius::same(6),\n            window_shadow: Shadow {\n                offset: [10, 20],\n                blur: 15,\n                spread: 0,\n                color: Color32::from_black_alpha(96),\n            },\n            window_fill: Color32::from_gray(27),\n            window_stroke: Stroke::new(1.0, Color32::from_gray(60)),\n            window_highlight_topmost: true,\n\n            menu_corner_radius: CornerRadius::same(6),\n\n            panel_fill: Color32::from_gray(27),\n\n            popup_shadow: Shadow {\n                offset: [6, 10],\n                blur: 8,\n                spread: 0,\n                color: Color32::from_black_alpha(96),\n            },\n\n            resize_corner_size: 12.0,\n\n            text_cursor: Default::default(),\n\n            clip_rect_margin: 3.0, // should be at least half the size of the widest frame stroke + max WidgetVisuals::expansion\n            button_frame: true,\n            collapsing_header_frame: false,\n            indent_has_left_vline: true,\n\n            striped: false,\n\n            slider_trailing_fill: false,\n            handle_shape: HandleShape::Rect { aspect_ratio: 0.75 },\n\n            interact_cursor: None,\n\n            image_loading_spinners: true,\n\n            numeric_color_space: NumericColorSpace::GammaByte,\n            disabled_alpha: 0.5,\n        }\n    }\n\n    /// Default light theme.\n    pub fn light() -> Self {\n        Self {\n            dark_mode: false,\n            text_options: TextOptions {\n                alpha_from_coverage: AlphaFromCoverage::LIGHT_MODE_DEFAULT,\n                ..Default::default()\n            },\n            widgets: Widgets::light(),\n            selection: Selection::light(),\n            hyperlink_color: Color32::from_rgb(0, 155, 255),\n            faint_bg_color: Color32::from_additive_luminance(5), // visible, but barely so\n            extreme_bg_color: Color32::from_gray(255),           // e.g. TextEdit background\n            code_bg_color: Color32::from_gray(230),\n            warn_fg_color: Color32::from_rgb(255, 100, 0), // slightly orange red. it's difficult to find a warning color that pops on bright background.\n            error_fg_color: Color32::from_rgb(255, 0, 0),  // red\n\n            window_shadow: Shadow {\n                offset: [10, 20],\n                blur: 15,\n                spread: 0,\n                color: Color32::from_black_alpha(25),\n            },\n            window_fill: Color32::from_gray(248),\n            window_stroke: Stroke::new(1.0, Color32::from_gray(190)),\n\n            panel_fill: Color32::from_gray(248),\n\n            popup_shadow: Shadow {\n                offset: [6, 10],\n                blur: 8,\n                spread: 0,\n                color: Color32::from_black_alpha(25),\n            },\n\n            text_cursor: TextCursorStyle {\n                stroke: Stroke::new(2.0, Color32::from_rgb(0, 83, 125)),\n                ..Default::default()\n            },\n\n            ..Self::dark()\n        }\n    }\n}\n\nimpl Default for Visuals {\n    fn default() -> Self {\n        Self::dark()\n    }\n}\n\nimpl Selection {\n    fn dark() -> Self {\n        Self {\n            bg_fill: Color32::from_rgb(0, 92, 128),\n            stroke: Stroke::new(1.0, Color32::from_rgb(192, 222, 255)),\n        }\n    }\n\n    fn light() -> Self {\n        Self {\n            bg_fill: Color32::from_rgb(144, 209, 255),\n            stroke: Stroke::new(1.0, Color32::from_rgb(0, 83, 125)),\n        }\n    }\n}\n\nimpl Default for Selection {\n    fn default() -> Self {\n        Self::dark()\n    }\n}\n\nimpl Widgets {\n    pub fn dark() -> Self {\n        Self {\n            noninteractive: WidgetVisuals {\n                weak_bg_fill: Color32::from_gray(27),\n                bg_fill: Color32::from_gray(27),\n                bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), // separators, indentation lines\n                fg_stroke: Stroke::new(1.0, Color32::from_gray(140)), // normal text color\n                corner_radius: CornerRadius::same(2),\n                expansion: 0.0,\n            },\n            inactive: WidgetVisuals {\n                weak_bg_fill: Color32::from_gray(60), // button background\n                bg_fill: Color32::from_gray(60),      // checkbox background\n                bg_stroke: Default::default(),\n                fg_stroke: Stroke::new(1.0, Color32::from_gray(180)), // button text\n                corner_radius: CornerRadius::same(2),\n                expansion: 0.0,\n            },\n            hovered: WidgetVisuals {\n                weak_bg_fill: Color32::from_gray(70),\n                bg_fill: Color32::from_gray(70),\n                bg_stroke: Stroke::new(1.0, Color32::from_gray(150)), // e.g. hover over window edge or button\n                fg_stroke: Stroke::new(1.5, Color32::from_gray(240)),\n                corner_radius: CornerRadius::same(3),\n                expansion: 0.0,\n            },\n            active: WidgetVisuals {\n                weak_bg_fill: Color32::from_gray(55),\n                bg_fill: Color32::from_gray(55),\n                bg_stroke: Stroke::new(1.0, Color32::WHITE),\n                fg_stroke: Stroke::new(2.0, Color32::WHITE),\n                corner_radius: CornerRadius::same(2),\n                expansion: 0.0,\n            },\n            open: WidgetVisuals {\n                weak_bg_fill: Color32::from_gray(45),\n                bg_fill: Color32::from_gray(27),\n                bg_stroke: Stroke::new(1.0, Color32::from_gray(60)),\n                fg_stroke: Stroke::new(1.0, Color32::from_gray(210)),\n                corner_radius: CornerRadius::same(2),\n                expansion: 0.0,\n            },\n        }\n    }\n\n    pub fn light() -> Self {\n        Self {\n            noninteractive: WidgetVisuals {\n                weak_bg_fill: Color32::from_gray(248),\n                bg_fill: Color32::from_gray(248),\n                bg_stroke: Stroke::new(1.0, Color32::from_gray(190)), // separators, indentation lines\n                fg_stroke: Stroke::new(1.0, Color32::from_gray(80)),  // normal text color\n                corner_radius: CornerRadius::same(2),\n                expansion: 0.0,\n            },\n            inactive: WidgetVisuals {\n                weak_bg_fill: Color32::from_gray(230), // button background\n                bg_fill: Color32::from_gray(230),      // checkbox background\n                bg_stroke: Default::default(),\n                fg_stroke: Stroke::new(1.0, Color32::from_gray(60)), // button text\n                corner_radius: CornerRadius::same(2),\n                expansion: 0.0,\n            },\n            hovered: WidgetVisuals {\n                weak_bg_fill: Color32::from_gray(220),\n                bg_fill: Color32::from_gray(220),\n                bg_stroke: Stroke::new(1.0, Color32::from_gray(105)), // e.g. hover over window edge or button\n                fg_stroke: Stroke::new(1.5, Color32::BLACK),\n                corner_radius: CornerRadius::same(3),\n                expansion: 0.0,\n            },\n            active: WidgetVisuals {\n                weak_bg_fill: Color32::from_gray(165),\n                bg_fill: Color32::from_gray(165),\n                bg_stroke: Stroke::new(1.0, Color32::BLACK),\n                fg_stroke: Stroke::new(2.0, Color32::BLACK),\n                corner_radius: CornerRadius::same(2),\n                expansion: 0.0,\n            },\n            open: WidgetVisuals {\n                weak_bg_fill: Color32::from_gray(220),\n                bg_fill: Color32::from_gray(220),\n                bg_stroke: Stroke::new(1.0, Color32::from_gray(160)),\n                fg_stroke: Stroke::new(1.0, Color32::BLACK),\n                corner_radius: CornerRadius::same(2),\n                expansion: 0.0,\n            },\n        }\n    }\n}\n\nimpl Default for Widgets {\n    fn default() -> Self {\n        Self::dark()\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nuse crate::{\n    Ui,\n    widgets::{DragValue, Slider, Widget, reset_button},\n};\n\nimpl Style {\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        #[expect(deprecated)]\n        let Self {\n            override_font_id,\n            override_text_style,\n            override_text_valign,\n            text_styles,\n            drag_value_text_style,\n            number_formatter: _, // can't change callbacks in the UI\n            wrap: _,\n            wrap_mode,\n            spacing,\n            interaction,\n            visuals,\n            animation_time,\n            #[cfg(debug_assertions)]\n            debug,\n            explanation_tooltips,\n            url_in_tooltip,\n            always_scroll_the_only_direction,\n            scroll_animation,\n            compact_menu_style,\n        } = self;\n\n        crate::Grid::new(\"_options\").show(ui, |ui| {\n            ui.label(\"Override font id\");\n            ui.vertical(|ui| {\n                ui.horizontal(|ui| {\n                    ui.radio_value(override_font_id, None, \"None\");\n                    if ui.radio(override_font_id.is_some(), \"override\").clicked() {\n                        *override_font_id = Some(FontId::default());\n                    }\n                });\n                if let Some(override_font_id) = override_font_id {\n                    crate::introspection::font_id_ui(ui, override_font_id);\n                }\n            });\n            ui.end_row();\n\n            ui.label(\"Override text style\");\n            crate::ComboBox::from_id_salt(\"override_text_style\")\n                .selected_text(match override_text_style {\n                    None => \"None\".to_owned(),\n                    Some(override_text_style) => override_text_style.to_string(),\n                })\n                .show_ui(ui, |ui| {\n                    ui.selectable_value(override_text_style, None, \"None\");\n                    let all_text_styles = ui.style().text_styles();\n                    for style in all_text_styles {\n                        let text =\n                            crate::RichText::new(style.to_string()).text_style(style.clone());\n                        ui.selectable_value(override_text_style, Some(style), text);\n                    }\n                });\n            ui.end_row();\n\n            fn valign_name(valign: Align) -> &'static str {\n                match valign {\n                    Align::TOP => \"Top\",\n                    Align::Center => \"Center\",\n                    Align::BOTTOM => \"Bottom\",\n                }\n            }\n\n            ui.label(\"Override text valign\");\n            crate::ComboBox::from_id_salt(\"override_text_valign\")\n                .selected_text(match override_text_valign {\n                    None => \"None\",\n                    Some(override_text_valign) => valign_name(*override_text_valign),\n                })\n                .show_ui(ui, |ui| {\n                    ui.selectable_value(override_text_valign, None, \"None\");\n                    for align in [Align::TOP, Align::Center, Align::BOTTOM] {\n                        ui.selectable_value(override_text_valign, Some(align), valign_name(align));\n                    }\n                });\n            ui.end_row();\n\n            ui.label(\"Text style of DragValue\");\n            crate::ComboBox::from_id_salt(\"drag_value_text_style\")\n                .selected_text(drag_value_text_style.to_string())\n                .show_ui(ui, |ui| {\n                    let all_text_styles = ui.style().text_styles();\n                    for style in all_text_styles {\n                        let text =\n                            crate::RichText::new(style.to_string()).text_style(style.clone());\n                        ui.selectable_value(drag_value_text_style, style, text);\n                    }\n                });\n            ui.end_row();\n\n            ui.label(\"Text Wrap Mode\");\n            crate::ComboBox::from_id_salt(\"text_wrap_mode\")\n                .selected_text(format!(\"{wrap_mode:?}\"))\n                .show_ui(ui, |ui| {\n                    let all_wrap_mode: Vec<Option<TextWrapMode>> = vec![\n                        None,\n                        Some(TextWrapMode::Extend),\n                        Some(TextWrapMode::Wrap),\n                        Some(TextWrapMode::Truncate),\n                    ];\n                    for style in all_wrap_mode {\n                        let text = crate::RichText::new(format!(\"{style:?}\"));\n                        ui.selectable_value(wrap_mode, style, text);\n                    }\n                });\n            ui.end_row();\n\n            ui.label(\"Animation duration\");\n            ui.add(\n                DragValue::new(animation_time)\n                    .range(0.0..=1.0)\n                    .speed(0.02)\n                    .suffix(\" s\"),\n            );\n            ui.end_row();\n        });\n\n        ui.collapsing(\"🔠 Text styles\", |ui| text_styles_ui(ui, text_styles));\n        ui.collapsing(\"📏 Spacing\", |ui| spacing.ui(ui));\n        ui.collapsing(\"☝ Interaction\", |ui| interaction.ui(ui));\n        ui.collapsing(\"🎨 Visuals\", |ui| visuals.ui(ui));\n        ui.collapsing(\"🔄 Scroll animation\", |ui| scroll_animation.ui(ui));\n\n        #[cfg(debug_assertions)]\n        ui.collapsing(\"🐛 Debug\", |ui| debug.ui(ui));\n\n        ui.checkbox(compact_menu_style, \"Compact menu style\");\n\n        ui.checkbox(explanation_tooltips, \"Explanation tooltips\")\n            .on_hover_text(\n                \"Show explanatory text when hovering DragValue:s and other egui widgets\",\n            );\n\n        ui.checkbox(url_in_tooltip, \"Show url when hovering links\");\n\n        ui.checkbox(always_scroll_the_only_direction, \"Always scroll the only enabled direction\")\n            .on_hover_text(\n                \"If scrolling is enabled for only one direction, allow horizontal scrolling without pressing shift\",\n            );\n\n        ui.vertical_centered(|ui| reset_button(ui, self, \"Reset style\"));\n    }\n}\n\nfn text_styles_ui(ui: &mut Ui, text_styles: &mut BTreeMap<TextStyle, FontId>) -> Response {\n    ui.vertical(|ui| {\n        crate::Grid::new(\"text_styles\").show(ui, |ui| {\n            for (text_style, font_id) in &mut *text_styles {\n                ui.label(RichText::new(text_style.to_string()).font(font_id.clone()));\n                crate::introspection::font_id_ui(ui, font_id);\n                ui.end_row();\n            }\n        });\n        crate::reset_button_with(ui, text_styles, \"Reset text styles\", default_text_styles());\n    })\n    .response\n}\n\nimpl Spacing {\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        let Self {\n            item_spacing,\n            window_margin,\n            menu_margin,\n            button_padding,\n            indent,\n            interact_size,\n            slider_width,\n            slider_rail_height,\n            combo_width,\n            text_edit_width,\n            icon_width,\n            icon_width_inner,\n            icon_spacing,\n            default_area_size,\n            tooltip_width,\n            menu_width,\n            menu_spacing,\n            indent_ends_with_horizontal_line,\n            combo_height,\n            scroll,\n        } = self;\n\n        Grid::new(\"spacing\")\n            .num_columns(2)\n            .spacing([12.0, 8.0])\n            .striped(true)\n            .show(ui, |ui| {\n                ui.label(\"Item spacing\");\n                ui.add(two_drag_values(item_spacing, 0.0..=20.0));\n                ui.end_row();\n\n                ui.label(\"Window margin\");\n                ui.add(window_margin);\n                ui.end_row();\n\n                ui.label(\"ScrollArea margin\");\n                scroll.content_margin.ui(ui);\n                ui.end_row();\n\n                ui.label(\"Menu margin\");\n                ui.add(menu_margin);\n                ui.end_row();\n\n                ui.label(\"Button padding\");\n                ui.add(two_drag_values(button_padding, 0.0..=20.0));\n                ui.end_row();\n\n                ui.label(\"Interact size\")\n                    .on_hover_text(\"Minimum size of an interactive widget\");\n                ui.add(two_drag_values(interact_size, 4.0..=60.0));\n                ui.end_row();\n\n                ui.label(\"Indent\");\n                ui.add(DragValue::new(indent).range(0.0..=100.0));\n                ui.end_row();\n\n                ui.label(\"Slider width\");\n                ui.add(DragValue::new(slider_width).range(0.0..=1000.0));\n                ui.end_row();\n\n                ui.label(\"Slider rail height\");\n                ui.add(DragValue::new(slider_rail_height).range(0.0..=50.0));\n                ui.end_row();\n\n                ui.label(\"ComboBox width\");\n                ui.add(DragValue::new(combo_width).range(0.0..=1000.0));\n                ui.end_row();\n\n                ui.label(\"Default area size\");\n                ui.add(two_drag_values(default_area_size, 0.0..=1000.0));\n                ui.end_row();\n\n                ui.label(\"TextEdit width\");\n                ui.add(DragValue::new(text_edit_width).range(0.0..=1000.0));\n                ui.end_row();\n\n                ui.label(\"Tooltip wrap width\");\n                ui.add(DragValue::new(tooltip_width).range(0.0..=1000.0));\n                ui.end_row();\n\n                ui.label(\"Default menu width\");\n                ui.add(DragValue::new(menu_width).range(0.0..=1000.0));\n                ui.end_row();\n\n                ui.label(\"Menu spacing\")\n                    .on_hover_text(\"Horizontal spacing between menus\");\n                ui.add(DragValue::new(menu_spacing).range(0.0..=10.0));\n                ui.end_row();\n\n                ui.label(\"Checkboxes etc\");\n                ui.vertical(|ui| {\n                    ui.add(\n                        DragValue::new(icon_width)\n                            .prefix(\"outer icon width:\")\n                            .range(0.0..=60.0),\n                    );\n                    ui.add(\n                        DragValue::new(icon_width_inner)\n                            .prefix(\"inner icon width:\")\n                            .range(0.0..=60.0),\n                    );\n                    ui.add(\n                        DragValue::new(icon_spacing)\n                            .prefix(\"spacing:\")\n                            .range(0.0..=10.0),\n                    );\n                });\n                ui.end_row();\n            });\n\n        ui.checkbox(\n            indent_ends_with_horizontal_line,\n            \"End indented regions with a horizontal separator\",\n        );\n\n        ui.horizontal(|ui| {\n            ui.label(\"Max height of a combo box\");\n            ui.add(DragValue::new(combo_height).range(0.0..=1000.0));\n        });\n\n        ui.collapsing(\"Scroll Area\", |ui| {\n            scroll.ui(ui);\n        });\n\n        ui.vertical_centered(|ui| reset_button(ui, self, \"Reset spacing\"));\n    }\n}\n\nimpl Interaction {\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        let Self {\n            interact_radius,\n            resize_grab_radius_side,\n            resize_grab_radius_corner,\n            show_tooltips_only_when_still,\n            tooltip_delay,\n            tooltip_grace_time,\n            selectable_labels,\n            multi_widget_text_select,\n        } = self;\n\n        ui.spacing_mut().item_spacing = vec2(12.0, 8.0);\n\n        Grid::new(\"interaction\")\n            .num_columns(2)\n            .striped(true)\n            .show(ui, |ui| {\n                ui.label(\"interact_radius\")\n                    .on_hover_text(\"Interact with the closest widget within this radius.\");\n                ui.add(DragValue::new(interact_radius).range(0.0..=20.0));\n                ui.end_row();\n\n                ui.label(\"resize_grab_radius_side\").on_hover_text(\"Radius of the interactive area of the side of a window during drag-to-resize\");\n                ui.add(DragValue::new(resize_grab_radius_side).range(0.0..=20.0));\n                ui.end_row();\n\n                ui.label(\"resize_grab_radius_corner\").on_hover_text(\"Radius of the interactive area of the corner of a window during drag-to-resize.\");\n                ui.add(DragValue::new(resize_grab_radius_corner).range(0.0..=20.0));\n                ui.end_row();\n\n                ui.label(\"Tooltip delay\").on_hover_text(\n                    \"Delay in seconds before showing tooltips after the mouse stops moving\",\n                );\n                ui.add(\n                    DragValue::new(tooltip_delay)\n                        .range(0.0..=1.0)\n                        .speed(0.05)\n                        .suffix(\" s\"),\n                );\n                ui.end_row();\n\n                ui.label(\"Tooltip grace time\").on_hover_text(\n                    \"If a tooltip is open and you hover another widget within this grace period, show the next tooltip right away\",\n                );\n                ui.add(\n                    DragValue::new(tooltip_grace_time)\n                        .range(0.0..=1.0)\n                        .speed(0.05)\n                        .suffix(\" s\"),\n                );\n                ui.end_row();\n            });\n\n        ui.checkbox(\n            show_tooltips_only_when_still,\n            \"Only show tooltips if mouse is still\",\n        );\n\n        ui.horizontal(|ui| {\n            ui.checkbox(selectable_labels, \"Selectable text in labels\");\n            if *selectable_labels {\n                ui.checkbox(multi_widget_text_select, \"Across multiple labels\");\n            }\n        });\n\n        ui.vertical_centered(|ui| reset_button(ui, self, \"Reset interaction settings\"));\n    }\n}\n\nimpl Widgets {\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        let Self {\n            active,\n            hovered,\n            inactive,\n            noninteractive,\n            open,\n        } = self;\n\n        ui.collapsing(\"Noninteractive\", |ui| {\n            ui.label(\n                \"The style of a widget that you cannot interact with, e.g. labels and separators.\",\n            );\n            noninteractive.ui(ui);\n        });\n        ui.collapsing(\"Interactive but inactive\", |ui| {\n            ui.label(\"The style of an interactive widget, such as a button, at rest.\");\n            inactive.ui(ui);\n        });\n        ui.collapsing(\"Interactive and hovered\", |ui| {\n            ui.label(\"The style of an interactive widget while you hover it.\");\n            hovered.ui(ui);\n        });\n        ui.collapsing(\"Interactive and active\", |ui| {\n            ui.label(\"The style of an interactive widget as you are clicking or dragging it.\");\n            active.ui(ui);\n        });\n        ui.collapsing(\"Open menu\", |ui| {\n            ui.label(\"The style of an open combo-box or menu button\");\n            open.ui(ui);\n        });\n\n        // ui.vertical_centered(|ui| reset_button(ui, self));\n    }\n}\n\nimpl Selection {\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        let Self { bg_fill, stroke } = self;\n        ui.label(\"Selectable labels\");\n\n        Grid::new(\"selectiom\").num_columns(2).show(ui, |ui| {\n            ui.label(\"Background fill\");\n            ui.color_edit_button_srgba(bg_fill);\n            ui.end_row();\n\n            ui.label(\"Stroke\");\n            ui.add(stroke);\n            ui.end_row();\n        });\n    }\n}\n\nimpl WidgetVisuals {\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        let Self {\n            weak_bg_fill,\n            bg_fill: mandatory_bg_fill,\n            bg_stroke,\n            corner_radius,\n            fg_stroke,\n            expansion,\n        } = self;\n\n        Grid::new(\"widget\")\n            .num_columns(2)\n            .spacing([12.0, 8.0])\n            .striped(true)\n            .show(ui, |ui| {\n                ui.label(\"Optional background fill\")\n                    .on_hover_text(\"For buttons, combo-boxes, etc\");\n                ui.color_edit_button_srgba(weak_bg_fill);\n                ui.end_row();\n\n                ui.label(\"Mandatory background fill\")\n                    .on_hover_text(\"For checkboxes, sliders, etc\");\n                ui.color_edit_button_srgba(mandatory_bg_fill);\n                ui.end_row();\n\n                ui.label(\"Background stroke\");\n                ui.add(bg_stroke);\n                ui.end_row();\n\n                ui.label(\"Corner radius\");\n                ui.add(corner_radius);\n                ui.end_row();\n\n                ui.label(\"Foreground stroke (text)\");\n                ui.add(fg_stroke);\n                ui.end_row();\n\n                ui.label(\"Expansion\")\n                    .on_hover_text(\"make shapes this much larger\");\n                ui.add(DragValue::new(expansion).speed(0.1));\n                ui.end_row();\n            });\n    }\n}\n\nimpl Visuals {\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        let Self {\n            dark_mode,\n            text_options,\n            override_text_color: _,\n            weak_text_alpha,\n            weak_text_color,\n            widgets,\n            selection,\n            hyperlink_color,\n            faint_bg_color,\n            extreme_bg_color,\n            text_edit_bg_color,\n            code_bg_color,\n            warn_fg_color,\n            error_fg_color,\n\n            window_corner_radius,\n            window_shadow,\n            window_fill,\n            window_stroke,\n            window_highlight_topmost,\n\n            menu_corner_radius,\n\n            panel_fill,\n\n            popup_shadow,\n\n            resize_corner_size,\n\n            text_cursor,\n\n            clip_rect_margin,\n            button_frame,\n            collapsing_header_frame,\n            indent_has_left_vline,\n\n            striped,\n\n            slider_trailing_fill,\n            handle_shape,\n            interact_cursor,\n\n            image_loading_spinners,\n\n            numeric_color_space,\n            disabled_alpha,\n        } = self;\n\n        fn ui_optional_color(\n            ui: &mut Ui,\n            color: &mut Option<Color32>,\n            default_value: Color32,\n            label: impl Into<WidgetText>,\n        ) -> Response {\n            let label_response = ui.label(label);\n\n            ui.horizontal(|ui| {\n                let mut set = color.is_some();\n                ui.checkbox(&mut set, \"\");\n                if set {\n                    let color = color.get_or_insert(default_value);\n                    ui.color_edit_button_srgba(color);\n                } else {\n                    *color = None;\n                }\n            });\n\n            ui.end_row();\n\n            label_response\n        }\n\n        ui.collapsing(\"Background colors\", |ui| {\n            Grid::new(\"background_colors\")\n                .num_columns(2)\n                .show(ui, |ui| {\n                    fn ui_color(\n                        ui: &mut Ui,\n                        color: &mut Color32,\n                        label: impl Into<WidgetText>,\n                    ) -> Response {\n                        let label_response = ui.label(label);\n                        ui.color_edit_button_srgba(color);\n                        ui.end_row();\n                        label_response\n                    }\n\n                    ui_color(ui, &mut widgets.inactive.weak_bg_fill, \"Buttons\");\n                    ui_color(ui, window_fill, \"Windows\");\n                    ui_color(ui, panel_fill, \"Panels\");\n                    ui_color(ui, faint_bg_color, \"Faint accent\").on_hover_text(\n                        \"Used for faint accentuation of interactive things, like striped grids.\",\n                    );\n                    ui_color(ui, extreme_bg_color, \"Extreme\")\n                        .on_hover_text(\"Background of plots and paintings\");\n\n                    ui_optional_color(ui, text_edit_bg_color, *extreme_bg_color, \"TextEdit\")\n                        .on_hover_text(\"Background of TextEdit\");\n                });\n        });\n\n        ui.collapsing(\"Text rendering\", |ui| {\n            fn ui_text_color(ui: &mut Ui, color: &mut Color32, label: impl Into<RichText>) {\n                ui.label(label.into().color(*color));\n                ui.color_edit_button_srgba(color);\n                ui.end_row();\n            }\n\n            Grid::new(\"text_color\").num_columns(2).show(ui, |ui| {\n                ui_text_color(ui, &mut widgets.noninteractive.fg_stroke.color, \"Label\");\n\n                ui_text_color(\n                    ui,\n                    &mut widgets.inactive.fg_stroke.color,\n                    \"Unhovered button\",\n                );\n                ui_text_color(ui, &mut widgets.hovered.fg_stroke.color, \"Hovered button\");\n                ui_text_color(ui, &mut widgets.active.fg_stroke.color, \"Clicked button\");\n\n                ui_text_color(ui, warn_fg_color, RichText::new(\"Warnings\"));\n                ui_text_color(ui, error_fg_color, RichText::new(\"Errors\"));\n\n                ui_text_color(ui, hyperlink_color, \"hyperlink_color\");\n\n                ui.label(RichText::new(\"Code background\").code())\n                    .on_hover_ui(|ui| {\n                        ui.horizontal(|ui| {\n                            ui.spacing_mut().item_spacing.x = 0.0;\n                            ui.label(\"For monospaced inlined text \");\n                            ui.code(\"like this\");\n                            ui.label(\".\");\n                        });\n                    });\n                ui.color_edit_button_srgba(code_bg_color);\n                ui.end_row();\n\n                ui.label(\"Weak text alpha\");\n                ui.add_enabled(\n                    weak_text_color.is_none(),\n                    DragValue::new(weak_text_alpha).speed(0.01).range(0.0..=1.0),\n                );\n                ui.end_row();\n\n                ui_optional_color(\n                    ui,\n                    weak_text_color,\n                    widgets.noninteractive.text_color(),\n                    \"Weak text color\",\n                );\n            });\n\n            ui.add_space(4.0);\n\n            let TextOptions {\n                max_texture_side: _,\n                alpha_from_coverage,\n                font_hinting,\n            } = text_options;\n\n            text_alpha_from_coverage_ui(ui, alpha_from_coverage);\n\n            ui.checkbox(font_hinting, \"Enable font hinting\");\n        });\n\n        ui.collapsing(\"Text cursor\", |ui| {\n            text_cursor.ui(ui);\n        });\n\n        ui.collapsing(\"Window\", |ui| {\n            Grid::new(\"window\")\n                .num_columns(2)\n                .spacing([12.0, 8.0])\n                .striped(true)\n                .show(ui, |ui| {\n                    ui.label(\"Fill\");\n                    ui.color_edit_button_srgba(window_fill);\n                    ui.end_row();\n\n                    ui.label(\"Stroke\");\n                    ui.add(window_stroke);\n                    ui.end_row();\n\n                    ui.label(\"Corner radius\");\n                    ui.add(window_corner_radius);\n                    ui.end_row();\n\n                    ui.label(\"Shadow\");\n                    ui.add(window_shadow);\n                    ui.end_row();\n                });\n\n            ui.checkbox(window_highlight_topmost, \"Highlight topmost Window\");\n        });\n\n        ui.collapsing(\"Menus and popups\", |ui| {\n            Grid::new(\"menus_and_popups\")\n                .num_columns(2)\n                .spacing([12.0, 8.0])\n                .striped(true)\n                .show(ui, |ui| {\n                    ui.label(\"Corner radius\");\n                    ui.add(menu_corner_radius);\n                    ui.end_row();\n\n                    ui.label(\"Shadow\");\n                    ui.add(popup_shadow);\n                    ui.end_row();\n                });\n        });\n\n        ui.collapsing(\"Widgets\", |ui| widgets.ui(ui));\n        ui.collapsing(\"Selection\", |ui| selection.ui(ui));\n\n        ui.collapsing(\"Misc\", |ui| {\n            ui.add(Slider::new(resize_corner_size, 0.0..=20.0).text(\"resize_corner_size\"));\n            ui.add(Slider::new(clip_rect_margin, 0.0..=20.0).text(\"clip_rect_margin\"));\n\n            ui.checkbox(button_frame, \"Button has a frame\");\n            ui.checkbox(collapsing_header_frame, \"Collapsing header has a frame\");\n            ui.checkbox(\n                indent_has_left_vline,\n                \"Paint a vertical line to the left of indented regions\",\n            );\n\n            ui.checkbox(striped, \"Default stripes on grids and tables\");\n\n            ui.checkbox(slider_trailing_fill, \"Add trailing color to sliders\");\n\n            handle_shape.ui(ui);\n\n            ComboBox::from_label(\"Interact cursor\")\n                .selected_text(\n                    interact_cursor.map_or_else(|| \"-\".to_owned(), |cursor| format!(\"{cursor:?}\")),\n                )\n                .show_ui(ui, |ui| {\n                    ui.selectable_value(interact_cursor, None, \"-\");\n\n                    for cursor in CursorIcon::ALL {\n                        ui.selectable_value(interact_cursor, Some(cursor), format!(\"{cursor:?}\"))\n                            .on_hover_cursor(cursor);\n                    }\n                })\n                .response\n                .on_hover_text(\"Use this cursor when hovering buttons etc\");\n\n            ui.checkbox(image_loading_spinners, \"Image loading spinners\")\n                .on_hover_text(\"Show a spinner when an Image is loading\");\n\n            ui.horizontal(|ui| {\n                ui.label(\"Color picker type\");\n                numeric_color_space.toggle_button_ui(ui);\n            });\n\n            ui.add(Slider::new(disabled_alpha, 0.0..=1.0).text(\"Disabled element alpha\"));\n        });\n\n        let dark_mode = *dark_mode;\n        ui.vertical_centered(|ui| {\n            reset_button_with(\n                ui,\n                self,\n                \"Reset visuals\",\n                if dark_mode {\n                    Self::dark()\n                } else {\n                    Self::light()\n                },\n            );\n        });\n    }\n}\n\nfn text_alpha_from_coverage_ui(ui: &mut Ui, alpha_from_coverage: &mut AlphaFromCoverage) {\n    let mut dark_mode_special =\n        *alpha_from_coverage == AlphaFromCoverage::TwoCoverageMinusCoverageSq;\n\n    ui.horizontal(|ui| {\n        ui.label(\"Text rendering:\");\n\n        ui.checkbox(&mut dark_mode_special, \"Dark-mode special\");\n\n        if dark_mode_special {\n            *alpha_from_coverage = AlphaFromCoverage::DARK_MODE_DEFAULT;\n        } else {\n            let mut gamma = match alpha_from_coverage {\n                AlphaFromCoverage::Linear => 1.0,\n                AlphaFromCoverage::Gamma(gamma) => *gamma,\n                AlphaFromCoverage::TwoCoverageMinusCoverageSq => 0.5, // approximately the same\n            };\n\n            ui.add(\n                DragValue::new(&mut gamma)\n                    .speed(0.01)\n                    .range(0.1..=4.0)\n                    .prefix(\"Gamma: \"),\n            );\n\n            if gamma == 1.0 {\n                *alpha_from_coverage = AlphaFromCoverage::Linear;\n            } else {\n                *alpha_from_coverage = AlphaFromCoverage::Gamma(gamma);\n            }\n        }\n    });\n}\n\nimpl TextCursorStyle {\n    fn ui(&mut self, ui: &mut Ui) {\n        let Self {\n            stroke,\n            preview,\n            blink,\n            on_duration,\n            off_duration,\n        } = self;\n\n        ui.horizontal(|ui| {\n            ui.label(\"Stroke\");\n            ui.add(stroke);\n        });\n\n        ui.checkbox(preview, \"Preview text cursor on hover\");\n\n        ui.checkbox(blink, \"Blink\");\n\n        if *blink {\n            Grid::new(\"cursor_blink\").show(ui, |ui| {\n                ui.label(\"On time\");\n                ui.add(\n                    DragValue::new(on_duration)\n                        .speed(0.1)\n                        .range(0.0..=2.0)\n                        .suffix(\" s\"),\n                );\n                ui.end_row();\n\n                ui.label(\"Off time\");\n                ui.add(\n                    DragValue::new(off_duration)\n                        .speed(0.1)\n                        .range(0.0..=2.0)\n                        .suffix(\" s\"),\n                );\n                ui.end_row();\n            });\n        }\n    }\n}\n\n#[cfg(debug_assertions)]\nimpl DebugOptions {\n    pub fn ui(&mut self, ui: &mut crate::Ui) {\n        let Self {\n            debug_on_hover,\n            debug_on_hover_with_all_modifiers,\n            hover_shows_next,\n            show_expand_width,\n            show_expand_height,\n            show_resize,\n            show_interactive_widgets,\n            show_widget_hits,\n            warn_if_rect_changes_id,\n            show_unaligned,\n            show_focused_widget,\n        } = self;\n\n        {\n            ui.checkbox(debug_on_hover, \"Show widget info on hover.\");\n            ui.checkbox(\n                debug_on_hover_with_all_modifiers,\n                \"Show widget info on hover if holding all modifier keys\",\n            );\n\n            ui.checkbox(hover_shows_next, \"Show next widget placement on hover\");\n        }\n\n        ui.checkbox(\n            show_expand_width,\n            \"Show which widgets make their parent wider\",\n        );\n        ui.checkbox(\n            show_expand_height,\n            \"Show which widgets make their parent higher\",\n        );\n        ui.checkbox(show_resize, \"Debug Resize\");\n\n        ui.checkbox(\n            show_interactive_widgets,\n            \"Show an overlay on all interactive widgets\",\n        );\n\n        ui.checkbox(show_widget_hits, \"Show widgets under mouse pointer\");\n\n        ui.checkbox(\n            warn_if_rect_changes_id,\n            \"Warn if a Rect changes Id between frames\",\n        );\n\n        ui.checkbox(\n            show_unaligned,\n            \"Show rectangles not aligned to integer point coordinates\",\n        );\n\n        ui.checkbox(\n            show_focused_widget,\n            \"Highlight which widget has keyboard focus\",\n        );\n\n        ui.vertical_centered(|ui| reset_button(ui, self, \"Reset debug options\"));\n    }\n}\n\n// TODO(emilk): improve and standardize\nfn two_drag_values(value: &mut Vec2, range: std::ops::RangeInclusive<f32>) -> impl Widget + '_ {\n    move |ui: &mut crate::Ui| {\n        ui.horizontal(|ui| {\n            ui.add(\n                DragValue::new(&mut value.x)\n                    .range(range.clone())\n                    .prefix(\"x: \"),\n            );\n            ui.add(\n                DragValue::new(&mut value.y)\n                    .range(range.clone())\n                    .prefix(\"y: \"),\n            );\n        })\n        .response\n    }\n}\n\nimpl HandleShape {\n    pub fn ui(&mut self, ui: &mut Ui) {\n        ui.horizontal(|ui| {\n            ui.label(\"Slider handle\");\n            ui.radio_value(self, Self::Circle, \"Circle\");\n            if ui\n                .radio(matches!(self, Self::Rect { .. }), \"Rectangle\")\n                .clicked()\n            {\n                *self = Self::Rect { aspect_ratio: 0.5 };\n            }\n            if let Self::Rect { aspect_ratio } = self {\n                ui.add(Slider::new(aspect_ratio, 0.1..=3.0).text(\"Aspect ratio\"));\n            }\n        });\n    }\n}\n\n/// How to display numeric color values.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum NumericColorSpace {\n    /// RGB is 0-255 in gamma space.\n    ///\n    /// Alpha is 0-255 in linear space.\n    GammaByte,\n\n    /// 0-1 in linear space.\n    Linear,\n    // TODO(emilk): add Hex as an option\n}\n\nimpl NumericColorSpace {\n    pub fn toggle_button_ui(&mut self, ui: &mut Ui) -> crate::Response {\n        let tooltip = match self {\n            Self::GammaByte => \"Showing color values in 0-255 gamma space\",\n            Self::Linear => \"Showing color values in 0-1 linear space\",\n        };\n\n        let mut response = ui.button(self.to_string()).on_hover_text(tooltip);\n        if response.clicked() {\n            *self = match self {\n                Self::GammaByte => Self::Linear,\n                Self::Linear => Self::GammaByte,\n            };\n            response.mark_changed();\n        }\n        response\n    }\n}\n\nimpl std::fmt::Display for NumericColorSpace {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::GammaByte => write!(f, \"U8\"),\n            Self::Linear => write!(f, \"F\"),\n        }\n    }\n}\n\nimpl Widget for &mut Margin {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let mut same = self.is_same();\n\n        let response = if same {\n            ui.horizontal(|ui| {\n                ui.checkbox(&mut same, \"same\");\n\n                let mut value = self.left;\n                ui.add(DragValue::new(&mut value).range(0.0..=100.0));\n                *self = Margin::same(value);\n            })\n            .response\n        } else {\n            ui.vertical(|ui| {\n                ui.checkbox(&mut same, \"same\");\n\n                crate::Grid::new(\"margin\").num_columns(2).show(ui, |ui| {\n                    ui.label(\"Left\");\n                    ui.add(DragValue::new(&mut self.left).range(0.0..=100.0));\n                    ui.end_row();\n\n                    ui.label(\"Right\");\n                    ui.add(DragValue::new(&mut self.right).range(0.0..=100.0));\n                    ui.end_row();\n\n                    ui.label(\"Top\");\n                    ui.add(DragValue::new(&mut self.top).range(0.0..=100.0));\n                    ui.end_row();\n\n                    ui.label(\"Bottom\");\n                    ui.add(DragValue::new(&mut self.bottom).range(0.0..=100.0));\n                    ui.end_row();\n                });\n            })\n            .response\n        };\n\n        // Apply the checkbox:\n        if same {\n            *self =\n                Margin::from((self.leftf() + self.rightf() + self.topf() + self.bottomf()) / 4.0);\n        } else {\n            // Make sure it is not same:\n            if self.is_same() {\n                if self.right == i8::MAX {\n                    self.right = i8::MAX - 1;\n                } else {\n                    self.right += 1;\n                }\n            }\n        }\n\n        response\n    }\n}\n\nimpl Widget for &mut CornerRadius {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let mut same = self.is_same();\n\n        let response = if same {\n            ui.horizontal(|ui| {\n                ui.checkbox(&mut same, \"same\");\n\n                let mut cr = self.nw;\n                ui.add(DragValue::new(&mut cr).range(0.0..=f32::INFINITY));\n                *self = CornerRadius::same(cr);\n            })\n            .response\n        } else {\n            ui.vertical(|ui| {\n                ui.checkbox(&mut same, \"same\");\n\n                crate::Grid::new(\"Corner radius\")\n                    .num_columns(2)\n                    .show(ui, |ui| {\n                        ui.label(\"NW\");\n                        ui.add(DragValue::new(&mut self.nw).range(0.0..=f32::INFINITY));\n                        ui.end_row();\n\n                        ui.label(\"NE\");\n                        ui.add(DragValue::new(&mut self.ne).range(0.0..=f32::INFINITY));\n                        ui.end_row();\n\n                        ui.label(\"SW\");\n                        ui.add(DragValue::new(&mut self.sw).range(0.0..=f32::INFINITY));\n                        ui.end_row();\n\n                        ui.label(\"SE\");\n                        ui.add(DragValue::new(&mut self.se).range(0.0..=f32::INFINITY));\n                        ui.end_row();\n                    });\n            })\n            .response\n        };\n\n        // Apply the checkbox:\n        if same {\n            *self = CornerRadius::from(self.average());\n        } else {\n            // Make sure we aren't same:\n            if self.is_same() {\n                if self.average() == 0.0 {\n                    self.se = 1;\n                } else {\n                    self.se -= 1;\n                }\n            }\n        }\n\n        response\n    }\n}\n\nimpl Widget for &mut Shadow {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let epaint::Shadow {\n            offset,\n            blur,\n            spread,\n            color,\n        } = self;\n\n        ui.vertical(|ui| {\n            crate::Grid::new(\"shadow_ui\").show(ui, |ui| {\n                ui.add(\n                    DragValue::new(&mut offset[0])\n                        .speed(1.0)\n                        .range(-100.0..=100.0)\n                        .prefix(\"x: \"),\n                );\n                ui.add(\n                    DragValue::new(&mut offset[1])\n                        .speed(1.0)\n                        .range(-100.0..=100.0)\n                        .prefix(\"y: \"),\n                );\n                ui.end_row();\n\n                ui.add(\n                    DragValue::new(blur)\n                        .speed(1.0)\n                        .range(0.0..=100.0)\n                        .prefix(\"blur: \"),\n                );\n\n                ui.add(\n                    DragValue::new(spread)\n                        .speed(1.0)\n                        .range(0.0..=100.0)\n                        .prefix(\"spread: \"),\n                );\n            });\n            ui.color_edit_button_srgba(color);\n        })\n        .response\n    }\n}\n\nimpl Widget for &mut Stroke {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let Stroke { width, color } = self;\n\n        ui.horizontal(|ui| {\n            ui.add(DragValue::new(width).speed(0.1).range(0.0..=1e9))\n                .on_hover_text(\"Width\");\n            ui.color_edit_button_srgba(color);\n\n            // stroke preview:\n            let (_id, stroke_rect) = ui.allocate_space(ui.spacing().interact_size);\n            let left = stroke_rect.left_center();\n            let right = stroke_rect.right_center();\n            ui.painter().line_segment([left, right], (*width, *color));\n        })\n        .response\n    }\n}\n\nimpl Widget for &mut crate::Frame {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let crate::Frame {\n            inner_margin,\n            outer_margin,\n            corner_radius,\n            shadow,\n            fill,\n            stroke,\n        } = self;\n\n        crate::Grid::new(\"frame\")\n            .num_columns(2)\n            .spacing([12.0, 8.0])\n            .striped(true)\n            .show(ui, |ui| {\n                ui.label(\"Inner margin\");\n                ui.add(inner_margin);\n                ui.end_row();\n\n                ui.label(\"Outer margin\");\n                // Push Id to avoid clashes in the Margin widget's Grid\n                ui.push_id(\"outer\", |ui| ui.add(outer_margin));\n                ui.end_row();\n\n                ui.label(\"Corner radius\");\n                ui.add(corner_radius);\n                ui.end_row();\n\n                ui.label(\"Shadow\");\n                ui.add(shadow);\n                ui.end_row();\n\n                ui.label(\"Fill\");\n                ui.color_edit_button_srgba(fill);\n                ui.end_row();\n\n                ui.label(\"Stroke\");\n                ui.add(stroke);\n                ui.end_row();\n            })\n            .response\n    }\n}\n\nimpl Widget for &mut FontTweak {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let original: FontTweak = self.clone();\n\n        let mut response = Grid::new(\"font_tweak\")\n            .num_columns(2)\n            .show(ui, |ui| {\n                let FontTweak {\n                    scale,\n                    y_offset_factor,\n                    y_offset,\n                    hinting_override,\n                    coords,\n                } = self;\n\n                ui.label(\"Scale\");\n                let speed = *scale * 0.01;\n                ui.add(DragValue::new(scale).range(0.01..=10.0).speed(speed));\n                ui.end_row();\n\n                ui.label(\"y_offset_factor\");\n                ui.add(DragValue::new(y_offset_factor).speed(-0.0025));\n                ui.end_row();\n\n                ui.label(\"y_offset\");\n                ui.add(DragValue::new(y_offset).speed(-0.02));\n                ui.end_row();\n\n                ui.label(\"hinting_override\");\n                ComboBox::from_id_salt(\"hinting_override\")\n                    .selected_text(match hinting_override {\n                        None => \"None\",\n                        Some(true) => \"Enable\",\n                        Some(false) => \"Disable\",\n                    })\n                    .show_ui(ui, |ui| {\n                        ui.selectable_value(hinting_override, None, \"None\");\n                        ui.selectable_value(hinting_override, Some(true), \"Enable\");\n                        ui.selectable_value(hinting_override, Some(false), \"Disable\");\n                    });\n                ui.end_row();\n\n                ui.label(\"coords\");\n                ui.end_row();\n                let mut to_remove = None;\n                for (i, (tag, value)) in coords.as_mut().iter_mut().enumerate() {\n                    let tag_text = ui.ctx().data_mut(|data| {\n                        let tag = *tag;\n                        Arc::clone(data.get_temp_mut_or_insert_with(ui.id().with(i), move || {\n                            Arc::new(Mutex::new(tag.to_string()))\n                        }))\n                    });\n\n                    let tag_text = &mut *tag_text.lock();\n                    let response = ui.text_edit_singleline(tag_text);\n                    if response.changed()\n                        && let Ok(new_tag) = Tag::new_checked(tag_text.as_bytes())\n                    {\n                        *tag = new_tag;\n                    }\n                    // Reset stale text when not actively editing\n                    // (e.g. after an item was removed and indices shifted)\n                    if !response.has_focus()\n                        && Tag::new_checked(tag_text.as_bytes()).ok() != Some(*tag)\n                    {\n                        *tag_text = tag.to_string();\n                    }\n\n                    ui.add(DragValue::new(value));\n                    if ui.small_button(\"🗑\").clicked() {\n                        to_remove = Some(i);\n                    }\n                    ui.end_row();\n                }\n                if let Some(i) = to_remove {\n                    coords.remove(i);\n                }\n                if ui.button(\"Add coord\").clicked() {\n                    coords.push(b\"wght\", 0.0);\n                }\n                if ui.button(\"Clear coords\").clicked() {\n                    coords.clear();\n                }\n                ui.end_row();\n\n                if ui.button(\"Reset\").clicked() {\n                    *self = Default::default();\n                }\n            })\n            .response;\n\n        if *self != original {\n            response.mark_changed();\n        }\n\n        response\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/text_selection/accesskit_text.rs",
    "content": "use emath::TSTransform;\n\nuse crate::{Context, Galley, Id};\n\nuse super::{CCursorRange, text_cursor_state::is_word_char};\n\n/// AccessKit's `word_starts` uses `u8` indices, so text runs cannot exceed this length.\npub(crate) const MAX_CHARS_PER_TEXT_RUN: usize = 255;\n\n/// Convert a (row, column) layout cursor position to a text run node ID and character index,\n/// accounting for rows that are split into multiple text runs.\nfn text_run_position(parent_id: Id, row: usize, column: usize) -> accesskit::TextPosition {\n    // When column lands exactly on a chunk boundary (e.g., 255), it refers to\n    // the end of the previous chunk, not the start of a new one.\n    let chunk_index = if column > 0 && column.is_multiple_of(MAX_CHARS_PER_TEXT_RUN) {\n        column / MAX_CHARS_PER_TEXT_RUN - 1\n    } else {\n        column / MAX_CHARS_PER_TEXT_RUN\n    };\n    let character_index = column - chunk_index * MAX_CHARS_PER_TEXT_RUN;\n    accesskit::TextPosition {\n        node: parent_id.with(row).with(chunk_index).accesskit_id(),\n        character_index,\n    }\n}\n\n/// Update accesskit with the current text state.\npub fn update_accesskit_for_text_widget(\n    ctx: &Context,\n    widget_id: Id,\n    cursor_range: Option<CCursorRange>,\n    role: accesskit::Role,\n    global_from_galley: TSTransform,\n    galley: &Galley,\n) {\n    let parent_id = ctx.accesskit_node_builder(widget_id, |builder| {\n        let parent_id = widget_id;\n\n        if let Some(cursor_range) = &cursor_range {\n            let anchor = galley.layout_from_cursor(cursor_range.secondary);\n            let focus = galley.layout_from_cursor(cursor_range.primary);\n            builder.set_text_selection(accesskit::TextSelection {\n                anchor: text_run_position(parent_id, anchor.row, anchor.column),\n                focus: text_run_position(parent_id, focus.row, focus.column),\n            });\n        }\n\n        builder.set_role(role);\n\n        parent_id\n    });\n\n    let Some(parent_id) = parent_id else {\n        return;\n    };\n\n    let mut prev_row_ended_with_newline = true;\n\n    for (row_index, row) in galley.rows.iter().enumerate() {\n        let glyph_count = row.glyphs.len();\n        let mut value = String::with_capacity(glyph_count);\n        let mut character_lengths = Vec::<u8>::with_capacity(glyph_count);\n        let mut character_positions = Vec::<f32>::with_capacity(glyph_count);\n        let mut character_widths = Vec::<f32>::with_capacity(glyph_count);\n        let mut word_starts = Vec::<usize>::new();\n        // For soft-wrapped continuation rows, treat the start as a word\n        // boundary so the first word character gets a `word_starts` entry.\n        // Paragraph-starting runs (first row or after a newline) get an\n        // implicit word start from AccessKit, so they don't need this.\n        let mut was_at_word_end = !prev_row_ended_with_newline;\n\n        for glyph in &row.glyphs {\n            let is_word_char = is_word_char(glyph.chr);\n            if is_word_char && was_at_word_end {\n                word_starts.push(character_lengths.len());\n            }\n            was_at_word_end = !is_word_char;\n            let old_len = value.len();\n            value.push(glyph.chr);\n            character_lengths.push((value.len() - old_len) as _);\n            character_positions.push(glyph.pos.x - row.pos.x);\n            character_widths.push(glyph.advance_width);\n        }\n\n        if row.ends_with_newline {\n            value.push('\\n');\n            character_lengths.push(1);\n            character_positions.push(row.size.x);\n            character_widths.push(0.0);\n        }\n\n        let total_chars = character_lengths.len();\n\n        if total_chars <= MAX_CHARS_PER_TEXT_RUN {\n            let run_id = parent_id.with(row_index).with(0usize);\n            ctx.register_accesskit_parent(run_id, parent_id);\n\n            ctx.accesskit_node_builder(run_id, |builder| {\n                builder.set_role(accesskit::Role::TextRun);\n                builder.set_text_direction(accesskit::TextDirection::LeftToRight);\n                // TODO(mwcampbell): Set more node fields for the row\n                // once AccessKit adapters expose text formatting info.\n\n                let rect = global_from_galley * row.rect_without_leading_space();\n                builder.set_bounds(accesskit::Rect {\n                    x0: rect.min.x.into(),\n                    y0: rect.min.y.into(),\n                    x1: rect.max.x.into(),\n                    y1: rect.max.y.into(),\n                });\n                builder.set_value(value);\n                builder.set_character_lengths(character_lengths);\n\n                let pos_offset = character_positions.first().copied().unwrap_or(0.0);\n                for p in &mut character_positions {\n                    *p -= pos_offset;\n                }\n                builder.set_character_positions(character_positions);\n                builder.set_character_widths(character_widths);\n\n                let chunk_word_starts: Vec<u8> = word_starts.iter().map(|&ws| ws as u8).collect();\n                builder.set_word_starts(chunk_word_starts);\n            });\n        } else {\n            let num_chunks = total_chars.div_ceil(MAX_CHARS_PER_TEXT_RUN);\n            let mut byte_offset = 0usize;\n\n            for chunk_idx in 0..num_chunks {\n                let char_start = chunk_idx * MAX_CHARS_PER_TEXT_RUN;\n                let char_end = (char_start + MAX_CHARS_PER_TEXT_RUN).min(total_chars);\n\n                let byte_start = byte_offset;\n                let chunk_byte_len: usize = character_lengths[char_start..char_end]\n                    .iter()\n                    .map(|&l| l as usize)\n                    .sum();\n                let byte_end = byte_start + chunk_byte_len;\n                byte_offset = byte_end;\n\n                let run_id = parent_id.with(row_index).with(chunk_idx);\n                ctx.register_accesskit_parent(run_id, parent_id);\n\n                ctx.accesskit_node_builder(run_id, |builder| {\n                    builder.set_role(accesskit::Role::TextRun);\n                    builder.set_text_direction(accesskit::TextDirection::LeftToRight);\n                    // TODO(mwcampbell): Set more node fields for the row\n                    // once AccessKit adapters expose text formatting info.\n\n                    if chunk_idx > 0 {\n                        let prev_id = parent_id.with(row_index).with(chunk_idx - 1);\n                        builder.set_previous_on_line(prev_id.accesskit_id());\n                    }\n                    if chunk_idx + 1 < num_chunks {\n                        let next_id = parent_id.with(row_index).with(chunk_idx + 1);\n                        builder.set_next_on_line(next_id.accesskit_id());\n                    }\n\n                    let row_rect = row.rect_without_leading_space();\n                    let chunk_x0 = row.pos.x + character_positions[char_start];\n                    let chunk_x1 = row.pos.x\n                        + character_positions[char_end - 1]\n                        + character_widths[char_end - 1];\n                    let chunk_rect = emath::Rect::from_min_max(\n                        emath::pos2(chunk_x0, row_rect.min.y),\n                        emath::pos2(chunk_x1, row_rect.max.y),\n                    );\n                    let rect = global_from_galley * chunk_rect;\n                    builder.set_bounds(accesskit::Rect {\n                        x0: rect.min.x.into(),\n                        y0: rect.min.y.into(),\n                        x1: rect.max.x.into(),\n                        y1: rect.max.y.into(),\n                    });\n                    builder.set_value(value[byte_start..byte_end].to_owned());\n                    builder.set_character_lengths(character_lengths[char_start..char_end].to_vec());\n\n                    let pos_offset = character_positions[char_start];\n                    let chunk_positions: Vec<f32> = character_positions[char_start..char_end]\n                        .iter()\n                        .map(|&p| p - pos_offset)\n                        .collect();\n                    builder.set_character_positions(chunk_positions);\n                    builder.set_character_widths(character_widths[char_start..char_end].to_vec());\n\n                    let chunk_word_starts: Vec<u8> = word_starts\n                        .iter()\n                        .filter(|&&ws| ws >= char_start && ws < char_end)\n                        .map(|&ws| (ws - char_start) as u8)\n                        .collect();\n                    builder.set_word_starts(chunk_word_starts);\n                });\n            }\n        }\n\n        prev_row_ended_with_newline = row.ends_with_newline;\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/text_selection/cursor_range.rs",
    "content": "use epaint::{Galley, text::cursor::CCursor};\n\nuse crate::{Event, Id, Key, Modifiers, os::OperatingSystem};\n\nuse super::text_cursor_state::{ccursor_next_word, ccursor_previous_word, slice_char_range};\n\n/// A selected text range (could be a range of length zero).\n///\n/// The selection is based on character count (NOT byte count!).\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct CCursorRange {\n    /// When selecting with a mouse, this is where the mouse was released.\n    /// When moving with e.g. shift+arrows, this is what moves.\n    /// Note that the two ends can come in any order, and also be equal (no selection).\n    pub primary: CCursor,\n\n    /// When selecting with a mouse, this is where the mouse was first pressed.\n    /// This part of the cursor does not move when shift is down.\n    pub secondary: CCursor,\n\n    /// Saved horizontal position of the cursor.\n    pub h_pos: Option<f32>,\n}\n\nimpl CCursorRange {\n    /// The empty range.\n    #[inline]\n    pub fn one(ccursor: CCursor) -> Self {\n        Self {\n            primary: ccursor,\n            secondary: ccursor,\n            h_pos: None,\n        }\n    }\n\n    #[inline]\n    pub fn two(min: impl Into<CCursor>, max: impl Into<CCursor>) -> Self {\n        Self {\n            primary: max.into(),\n            secondary: min.into(),\n            h_pos: None,\n        }\n    }\n\n    /// Select all the text in a galley\n    pub fn select_all(galley: &Galley) -> Self {\n        Self::two(galley.begin(), galley.end())\n    }\n\n    /// The range of selected character indices.\n    pub fn as_sorted_char_range(&self) -> std::ops::Range<usize> {\n        let [start, end] = self.sorted_cursors();\n        std::ops::Range {\n            start: start.index,\n            end: end.index,\n        }\n    }\n\n    /// True if the selected range contains no characters.\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.primary == self.secondary\n    }\n\n    /// Is `self` a super-set of the other range?\n    pub fn contains(&self, other: Self) -> bool {\n        let [self_min, self_max] = self.sorted_cursors();\n        let [other_min, other_max] = other.sorted_cursors();\n        self_min.index <= other_min.index && other_max.index <= self_max.index\n    }\n\n    /// If there is a selection, None is returned.\n    /// If the two ends are the same, that is returned.\n    pub fn single(&self) -> Option<CCursor> {\n        if self.is_empty() {\n            Some(self.primary)\n        } else {\n            None\n        }\n    }\n\n    #[inline]\n    pub fn is_sorted(&self) -> bool {\n        let p = self.primary;\n        let s = self.secondary;\n        (p.index, p.prefer_next_row) <= (s.index, s.prefer_next_row)\n    }\n\n    /// returns the two ends ordered\n    #[inline]\n    pub fn sorted_cursors(&self) -> [CCursor; 2] {\n        if self.is_sorted() {\n            [self.primary, self.secondary]\n        } else {\n            [self.secondary, self.primary]\n        }\n    }\n\n    #[inline]\n    #[deprecated = \"Use `self.sorted_cursors` instead.\"]\n    pub fn sorted(&self) -> [CCursor; 2] {\n        self.sorted_cursors()\n    }\n\n    pub fn slice_str<'s>(&self, text: &'s str) -> &'s str {\n        let [min, max] = self.sorted_cursors();\n        slice_char_range(text, min.index..max.index)\n    }\n\n    /// Check for key presses that are moving the cursor.\n    ///\n    /// Returns `true` if we did mutate `self`.\n    pub fn on_key_press(\n        &mut self,\n        os: OperatingSystem,\n        galley: &Galley,\n        modifiers: &Modifiers,\n        key: Key,\n    ) -> bool {\n        match key {\n            Key::A if modifiers.command => {\n                *self = Self::select_all(galley);\n                true\n            }\n\n            Key::ArrowLeft | Key::ArrowRight if modifiers.is_none() && !self.is_empty() => {\n                if key == Key::ArrowLeft {\n                    *self = Self::one(self.sorted_cursors()[0]);\n                } else {\n                    *self = Self::one(self.sorted_cursors()[1]);\n                }\n                true\n            }\n\n            Key::ArrowLeft\n            | Key::ArrowRight\n            | Key::ArrowUp\n            | Key::ArrowDown\n            | Key::Home\n            | Key::End => {\n                move_single_cursor(\n                    os,\n                    &mut self.primary,\n                    &mut self.h_pos,\n                    galley,\n                    key,\n                    modifiers,\n                );\n                if !modifiers.shift {\n                    self.secondary = self.primary;\n                }\n                true\n            }\n\n            Key::P | Key::N | Key::B | Key::F | Key::A | Key::E\n                if os == OperatingSystem::Mac && modifiers.ctrl && !modifiers.shift =>\n            {\n                move_single_cursor(\n                    os,\n                    &mut self.primary,\n                    &mut self.h_pos,\n                    galley,\n                    key,\n                    modifiers,\n                );\n                self.secondary = self.primary;\n                true\n            }\n\n            _ => false,\n        }\n    }\n\n    /// Check for events that modify the cursor range.\n    ///\n    /// Returns `true` if such an event was found and handled.\n    pub fn on_event(\n        &mut self,\n        os: OperatingSystem,\n        event: &Event,\n        galley: &Galley,\n        _widget_id: Id,\n    ) -> bool {\n        match event {\n            Event::Key {\n                modifiers,\n                key,\n                pressed: true,\n                ..\n            } => self.on_key_press(os, galley, modifiers, *key),\n\n            Event::AccessKitActionRequest(accesskit::ActionRequest {\n                action: accesskit::Action::SetTextSelection,\n                target_node,\n                target_tree,\n                data: Some(accesskit::ActionData::SetTextSelection(selection)),\n            }) => {\n                if _widget_id.accesskit_id() == *target_node\n                    && *target_tree == accesskit::TreeId::ROOT\n                {\n                    let primary =\n                        ccursor_from_accesskit_text_position(_widget_id, galley, &selection.focus);\n                    let secondary =\n                        ccursor_from_accesskit_text_position(_widget_id, galley, &selection.anchor);\n                    if let (Some(primary), Some(secondary)) = (primary, secondary) {\n                        *self = Self {\n                            primary,\n                            secondary,\n                            h_pos: None,\n                        };\n                        return true;\n                    }\n                }\n                false\n            }\n\n            _ => false,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nfn ccursor_from_accesskit_text_position(\n    id: Id,\n    galley: &Galley,\n    position: &accesskit::TextPosition,\n) -> Option<CCursor> {\n    use super::accesskit_text::MAX_CHARS_PER_TEXT_RUN;\n\n    let mut total_length = 0usize;\n    for (i, row) in galley.rows.iter().enumerate() {\n        let row_chars = row.glyphs.len() + (row.ends_with_newline as usize);\n        let num_chunks = if row_chars == 0 {\n            1\n        } else {\n            row_chars.div_ceil(MAX_CHARS_PER_TEXT_RUN)\n        };\n\n        for chunk_idx in 0..num_chunks {\n            let run_id = id.with(i).with(chunk_idx);\n            if run_id.accesskit_id() == position.node {\n                let column = chunk_idx * MAX_CHARS_PER_TEXT_RUN + position.character_index;\n                return Some(CCursor {\n                    index: total_length + column,\n                    prefer_next_row: !(column == row.glyphs.len()\n                        && !row.ends_with_newline\n                        && (i + 1) < galley.rows.len()),\n                });\n            }\n        }\n\n        total_length += row_chars;\n    }\n    None\n}\n\n// ----------------------------------------------------------------------------\n\n/// Move a text cursor based on keyboard\nfn move_single_cursor(\n    os: OperatingSystem,\n    cursor: &mut CCursor,\n    h_pos: &mut Option<f32>,\n    galley: &Galley,\n    key: Key,\n    modifiers: &Modifiers,\n) {\n    let (new_cursor, new_h_pos) =\n        if os == OperatingSystem::Mac && modifiers.ctrl && !modifiers.shift {\n            match key {\n                Key::A => (galley.cursor_begin_of_row(cursor), None),\n                Key::E => (galley.cursor_end_of_row(cursor), None),\n                Key::P => galley.cursor_up_one_row(cursor, *h_pos),\n                Key::N => galley.cursor_down_one_row(cursor, *h_pos),\n                Key::B => (galley.cursor_left_one_character(cursor), None),\n                Key::F => (galley.cursor_right_one_character(cursor), None),\n                _ => return,\n            }\n        } else {\n            match key {\n                Key::ArrowLeft => {\n                    if modifiers.alt || modifiers.ctrl {\n                        // alt on mac, ctrl on windows\n                        (ccursor_previous_word(galley, *cursor), None)\n                    } else if modifiers.mac_cmd {\n                        (galley.cursor_begin_of_row(cursor), None)\n                    } else {\n                        (galley.cursor_left_one_character(cursor), None)\n                    }\n                }\n                Key::ArrowRight => {\n                    if modifiers.alt || modifiers.ctrl {\n                        // alt on mac, ctrl on windows\n                        (ccursor_next_word(galley, *cursor), None)\n                    } else if modifiers.mac_cmd {\n                        (galley.cursor_end_of_row(cursor), None)\n                    } else {\n                        (galley.cursor_right_one_character(cursor), None)\n                    }\n                }\n                Key::ArrowUp => {\n                    if modifiers.command {\n                        // mac and windows behavior\n                        (galley.begin(), None)\n                    } else {\n                        galley.cursor_up_one_row(cursor, *h_pos)\n                    }\n                }\n                Key::ArrowDown => {\n                    if modifiers.command {\n                        // mac and windows behavior\n                        (galley.end(), None)\n                    } else {\n                        galley.cursor_down_one_row(cursor, *h_pos)\n                    }\n                }\n\n                Key::Home => {\n                    if modifiers.ctrl {\n                        // windows behavior\n                        (galley.begin(), None)\n                    } else {\n                        (galley.cursor_begin_of_row(cursor), None)\n                    }\n                }\n                Key::End => {\n                    if modifiers.ctrl {\n                        // windows behavior\n                        (galley.end(), None)\n                    } else {\n                        (galley.cursor_end_of_row(cursor), None)\n                    }\n                }\n\n                _ => unreachable!(),\n            }\n        };\n\n    *cursor = new_cursor;\n    *h_pos = new_h_pos;\n}\n"
  },
  {
    "path": "crates/egui/src/text_selection/label_text_selection.rs",
    "content": "use std::sync::Arc;\n\nuse emath::TSTransform;\n\nuse crate::{\n    Context, CursorIcon, Event, Galley, Id, LayerId, Plugin, Pos2, Rect, Response, Ui,\n    layers::ShapeIdx, text::CCursor, text_selection::CCursorRange,\n};\n\nuse super::{\n    TextCursorState,\n    text_cursor_state::cursor_rect,\n    visuals::{RowVertexIndices, paint_text_selection},\n};\n\n/// Turn on to help debug this\nconst DEBUG: bool = false; // Don't merge `true`!\n\n/// One end of a text selection, inside any widget.\n#[derive(Clone, Copy)]\nstruct WidgetTextCursor {\n    widget_id: Id,\n    ccursor: CCursor,\n\n    /// Last known screen position\n    pos: Pos2,\n}\n\nimpl WidgetTextCursor {\n    fn new(\n        widget_id: Id,\n        cursor: impl Into<CCursor>,\n        global_from_galley: TSTransform,\n        galley: &Galley,\n    ) -> Self {\n        let ccursor = cursor.into();\n        let pos = global_from_galley * pos_in_galley(galley, ccursor);\n        Self {\n            widget_id,\n            ccursor,\n            pos,\n        }\n    }\n}\n\nfn pos_in_galley(galley: &Galley, ccursor: CCursor) -> Pos2 {\n    galley.pos_from_cursor(ccursor).center()\n}\n\nimpl std::fmt::Debug for WidgetTextCursor {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"WidgetTextCursor\")\n            .field(\"widget_id\", &self.widget_id.short_debug_format())\n            .field(\"ccursor\", &self.ccursor.index)\n            .finish()\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\nstruct CurrentSelection {\n    /// The selection is in this layer.\n    ///\n    /// This is to constrain a selection to a single Window.\n    pub layer_id: LayerId,\n\n    /// When selecting with a mouse, this is where the mouse was released.\n    /// When moving with e.g. shift+arrows, this is what moves.\n    /// Note that the two ends can come in any order, and also be equal (no selection).\n    pub primary: WidgetTextCursor,\n\n    /// When selecting with a mouse, this is where the mouse was first pressed.\n    /// This part of the cursor does not move when shift is down.\n    pub secondary: WidgetTextCursor,\n}\n\n/// Handles text selection in labels (NOT in [`crate::TextEdit`])s.\n///\n/// One state for all labels, because we only support text selection in one label at a time.\n#[derive(Clone, Debug)]\npub struct LabelSelectionState {\n    /// The current selection, if any.\n    selection: Option<CurrentSelection>,\n\n    selection_bbox_last_frame: Rect,\n    selection_bbox_this_frame: Rect,\n\n    /// Any label hovered this frame?\n    any_hovered: bool,\n\n    /// Are we in drag-to-select state?\n    is_dragging: bool,\n\n    /// Have we reached the widget containing the primary selection?\n    has_reached_primary: bool,\n\n    /// Have we reached the widget containing the secondary selection?\n    has_reached_secondary: bool,\n\n    /// Accumulated text to copy.\n    text_to_copy: String,\n    last_copied_galley_rect: Option<Rect>,\n\n    /// Painted selections this frame.\n    ///\n    /// Kept so we can undo a bad selection visualization if we don't see both ends of the selection this frame.\n    painted_selections: Vec<(ShapeIdx, Vec<RowVertexIndices>)>,\n}\n\nimpl Default for LabelSelectionState {\n    fn default() -> Self {\n        Self {\n            selection: Default::default(),\n            selection_bbox_last_frame: Rect::NOTHING,\n            selection_bbox_this_frame: Rect::NOTHING,\n            any_hovered: Default::default(),\n            is_dragging: Default::default(),\n            has_reached_primary: Default::default(),\n            has_reached_secondary: Default::default(),\n            text_to_copy: Default::default(),\n            last_copied_galley_rect: Default::default(),\n            painted_selections: Default::default(),\n        }\n    }\n}\n\nimpl Plugin for LabelSelectionState {\n    fn debug_name(&self) -> &'static str {\n        \"LabelSelectionState\"\n    }\n\n    fn on_begin_pass(&mut self, ui: &mut Ui) {\n        if ui.input(|i| i.pointer.any_pressed() && !i.modifiers.shift) {\n            // Maybe a new selection is about to begin, but the old one is over:\n            // state.selection = None; // TODO(emilk): this makes sense, but doesn't work as expected.\n        }\n\n        self.selection_bbox_last_frame = self.selection_bbox_this_frame;\n        self.selection_bbox_this_frame = Rect::NOTHING;\n\n        self.any_hovered = false;\n        self.has_reached_primary = false;\n        self.has_reached_secondary = false;\n        self.text_to_copy.clear();\n        self.last_copied_galley_rect = None;\n        self.painted_selections.clear();\n    }\n\n    fn on_end_pass(&mut self, ui: &mut Ui) {\n        if self.is_dragging {\n            ui.set_cursor_icon(CursorIcon::Text);\n        }\n\n        if !self.has_reached_primary || !self.has_reached_secondary {\n            // We didn't see both cursors this frame,\n            // maybe because they are outside the visible area (scrolling),\n            // or one disappeared. In either case we will have horrible glitches, so let's just deselect.\n\n            let prev_selection = self.selection.take();\n            if let Some(selection) = prev_selection {\n                // This was the first frame of glitch, so hide the\n                // glitching by removing all painted selections:\n                ui.graphics_mut(|layers| {\n                    if let Some(list) = layers.get_mut(selection.layer_id) {\n                        for (shape_idx, row_selections) in self.painted_selections.drain(..) {\n                            list.mutate_shape(shape_idx, |shape| {\n                                if let epaint::Shape::Text(text_shape) = &mut shape.shape {\n                                    let galley = Arc::make_mut(&mut text_shape.galley);\n                                    for row_selection in row_selections {\n                                        if let Some(placed_row) =\n                                            galley.rows.get_mut(row_selection.row)\n                                        {\n                                            let row = Arc::make_mut(&mut placed_row.row);\n                                            for vertex_index in row_selection.vertex_indices {\n                                                if let Some(vertex) = row\n                                                    .visuals\n                                                    .mesh\n                                                    .vertices\n                                                    .get_mut(vertex_index as usize)\n                                                {\n                                                    vertex.color = epaint::Color32::TRANSPARENT;\n                                                }\n                                            }\n                                        }\n                                    }\n                                }\n                            });\n                        }\n                    }\n                });\n            }\n        }\n\n        let pressed_escape = ui.input(|i| i.key_pressed(crate::Key::Escape));\n        let clicked_something_else = ui.input(|i| i.pointer.any_pressed()) && !self.any_hovered;\n        let delected_everything = pressed_escape || clicked_something_else;\n\n        if delected_everything {\n            self.selection = None;\n        }\n\n        if ui.input(|i| i.pointer.any_released()) {\n            self.is_dragging = false;\n        }\n\n        let text_to_copy = std::mem::take(&mut self.text_to_copy);\n        if !text_to_copy.is_empty() {\n            ui.copy_text(text_to_copy);\n        }\n    }\n}\n\nimpl LabelSelectionState {\n    pub fn has_selection(&self) -> bool {\n        self.selection.is_some()\n    }\n\n    pub fn clear_selection(&mut self) {\n        self.selection = None;\n    }\n\n    fn copy_text(&mut self, new_galley_rect: Rect, galley: &Galley, cursor_range: &CCursorRange) {\n        let new_text = selected_text(galley, cursor_range);\n        if new_text.is_empty() {\n            return;\n        }\n\n        if self.text_to_copy.is_empty() {\n            self.text_to_copy = new_text;\n            self.last_copied_galley_rect = Some(new_galley_rect);\n            return;\n        }\n\n        let Some(last_copied_galley_rect) = self.last_copied_galley_rect else {\n            self.text_to_copy = new_text;\n            self.last_copied_galley_rect = Some(new_galley_rect);\n            return;\n        };\n\n        // We need to append or prepend the new text to the already copied text.\n        // We need to do so intelligently.\n\n        if last_copied_galley_rect.bottom() <= new_galley_rect.top() {\n            self.text_to_copy.push('\\n');\n            let vertical_distance = new_galley_rect.top() - last_copied_galley_rect.bottom();\n            if estimate_row_height(galley) * 0.5 < vertical_distance {\n                self.text_to_copy.push('\\n');\n            }\n        } else {\n            let existing_ends_with_space =\n                self.text_to_copy.chars().last().map(|c| c.is_whitespace());\n\n            let new_text_starts_with_space_or_punctuation = new_text\n                .chars()\n                .next()\n                .is_some_and(|c| c.is_whitespace() || c.is_ascii_punctuation());\n\n            if existing_ends_with_space == Some(false) && !new_text_starts_with_space_or_punctuation\n            {\n                self.text_to_copy.push(' ');\n            }\n        }\n\n        self.text_to_copy.push_str(&new_text);\n        self.last_copied_galley_rect = Some(new_galley_rect);\n    }\n\n    /// Handle text selection state for a label or similar widget.\n    ///\n    /// Make sure the widget senses clicks and drags.\n    ///\n    /// This also takes care of painting the galley.\n    pub fn label_text_selection(\n        ui: &Ui,\n        response: &Response,\n        galley_pos: Pos2,\n        mut galley: Arc<Galley>,\n        fallback_color: epaint::Color32,\n        underline: epaint::Stroke,\n    ) {\n        let plugin = ui.ctx().plugin::<Self>();\n        let mut state = plugin.lock();\n        let new_vertex_indices = state.on_label(ui, response, galley_pos, &mut galley);\n\n        let shape_idx = ui.painter().add(\n            epaint::TextShape::new(galley_pos, galley, fallback_color).with_underline(underline),\n        );\n\n        if !new_vertex_indices.is_empty() {\n            state\n                .painted_selections\n                .push((shape_idx, new_vertex_indices));\n        }\n    }\n\n    fn cursor_for(\n        &mut self,\n        ui: &Ui,\n        response: &Response,\n        global_from_galley: TSTransform,\n        galley: &Galley,\n    ) -> TextCursorState {\n        let Some(selection) = &mut self.selection else {\n            // Nothing selected.\n            return TextCursorState::default();\n        };\n\n        if selection.layer_id != response.layer_id {\n            // Selection is in another layer\n            return TextCursorState::default();\n        }\n\n        let galley_from_global = global_from_galley.inverse();\n\n        let multi_widget_text_select = ui.style().interaction.multi_widget_text_select;\n\n        let may_select_widget =\n            multi_widget_text_select || selection.primary.widget_id == response.id;\n\n        if self.is_dragging\n            && may_select_widget\n            && let Some(pointer_pos) = ui.ctx().pointer_interact_pos()\n        {\n            let galley_rect = global_from_galley * Rect::from_min_size(Pos2::ZERO, galley.size());\n            let galley_rect = galley_rect.intersect(ui.clip_rect());\n\n            let is_in_same_column = galley_rect\n                .x_range()\n                .intersects(self.selection_bbox_last_frame.x_range());\n\n            let has_reached_primary =\n                self.has_reached_primary || response.id == selection.primary.widget_id;\n            let has_reached_secondary =\n                self.has_reached_secondary || response.id == selection.secondary.widget_id;\n\n            let new_primary = if response.contains_pointer() {\n                // Dragging into this widget - easy case:\n                Some(galley.cursor_from_pos((galley_from_global * pointer_pos).to_vec2()))\n            } else if is_in_same_column\n                && !self.has_reached_primary\n                && selection.primary.pos.y <= selection.secondary.pos.y\n                && pointer_pos.y <= galley_rect.top()\n                && galley_rect.top() <= selection.secondary.pos.y\n            {\n                // The user is dragging the text selection upwards, above the first selected widget (this one):\n                if DEBUG {\n                    ui.ctx()\n                        .debug_text(format!(\"Upwards drag; include {:?}\", response.id));\n                }\n                Some(galley.begin())\n            } else if is_in_same_column\n                && has_reached_secondary\n                && has_reached_primary\n                && selection.secondary.pos.y <= selection.primary.pos.y\n                && selection.secondary.pos.y <= galley_rect.bottom()\n                && galley_rect.bottom() <= pointer_pos.y\n            {\n                // The user is dragging the text selection downwards, below this widget.\n                // We move the cursor to the end of this widget,\n                // (and we may do the same for the next widget too).\n                if DEBUG {\n                    ui.ctx()\n                        .debug_text(format!(\"Downwards drag; include {:?}\", response.id));\n                }\n                Some(galley.end())\n            } else {\n                None\n            };\n\n            if let Some(new_primary) = new_primary {\n                selection.primary =\n                    WidgetTextCursor::new(response.id, new_primary, global_from_galley, galley);\n\n                // We don't want the latency of `drag_started`.\n                let drag_started = ui.input(|i| i.pointer.any_pressed());\n                if drag_started {\n                    if selection.layer_id == response.layer_id {\n                        if ui.input(|i| i.modifiers.shift) {\n                            // A continuation of a previous selection.\n                        } else {\n                            // A new selection in the same layer.\n                            selection.secondary = selection.primary;\n                        }\n                    } else {\n                        // A new selection in a new layer.\n                        selection.layer_id = response.layer_id;\n                        selection.secondary = selection.primary;\n                    }\n                }\n            }\n        }\n\n        let has_primary = response.id == selection.primary.widget_id;\n        let has_secondary = response.id == selection.secondary.widget_id;\n\n        if has_primary {\n            selection.primary.pos =\n                global_from_galley * pos_in_galley(galley, selection.primary.ccursor);\n        }\n        if has_secondary {\n            selection.secondary.pos =\n                global_from_galley * pos_in_galley(galley, selection.secondary.ccursor);\n        }\n\n        self.has_reached_primary |= has_primary;\n        self.has_reached_secondary |= has_secondary;\n\n        let primary = has_primary.then_some(selection.primary.ccursor);\n        let secondary = has_secondary.then_some(selection.secondary.ccursor);\n\n        // The following code assumes we will encounter both ends of the cursor\n        // at some point (but in any order).\n        // If we don't (e.g. because one endpoint is outside the visible scroll areas),\n        // we will have annoying failure cases.\n\n        match (primary, secondary) {\n            (Some(primary), Some(secondary)) => {\n                // This is the only selected label.\n                TextCursorState::from(CCursorRange {\n                    primary,\n                    secondary,\n                    h_pos: None,\n                })\n            }\n\n            (Some(primary), None) => {\n                // This labels contains only the primary cursor.\n                let secondary = if self.has_reached_secondary {\n                    // Secondary was before primary.\n                    // Select everything up to the cursor.\n                    // We assume normal left-to-right and top-down layout order here.\n                    galley.begin()\n                } else {\n                    // Select everything from the cursor onward:\n                    galley.end()\n                };\n                TextCursorState::from(CCursorRange {\n                    primary,\n                    secondary,\n                    h_pos: None,\n                })\n            }\n\n            (None, Some(secondary)) => {\n                // This labels contains only the secondary cursor\n                let primary = if self.has_reached_primary {\n                    // Primary was before secondary.\n                    // Select everything up to the cursor.\n                    // We assume normal left-to-right and top-down layout order here.\n                    galley.begin()\n                } else {\n                    // Select everything from the cursor onward:\n                    galley.end()\n                };\n                TextCursorState::from(CCursorRange {\n                    primary,\n                    secondary,\n                    h_pos: None,\n                })\n            }\n\n            (None, None) => {\n                // This widget has neither the primary or secondary cursor.\n                let is_in_middle = self.has_reached_primary != self.has_reached_secondary;\n                if is_in_middle {\n                    if DEBUG {\n                        response.ctx.debug_text(format!(\n                            \"widget in middle: {:?}, between {:?} and {:?}\",\n                            response.id, selection.primary.widget_id, selection.secondary.widget_id,\n                        ));\n                    }\n                    // …but it is between the two selection endpoints, and so is fully selected.\n                    TextCursorState::from(CCursorRange::two(galley.begin(), galley.end()))\n                } else {\n                    // Outside the selected range\n                    TextCursorState::default()\n                }\n            }\n        }\n    }\n\n    /// Returns the painted selections, if any.\n    fn on_label(\n        &mut self,\n        ui: &Ui,\n        response: &Response,\n        galley_pos_in_layer: Pos2,\n        galley: &mut Arc<Galley>,\n    ) -> Vec<RowVertexIndices> {\n        let widget_id = response.id;\n\n        let global_from_layer = ui\n            .ctx()\n            .layer_transform_to_global(ui.layer_id())\n            .unwrap_or_default();\n        let layer_from_galley = TSTransform::from_translation(galley_pos_in_layer.to_vec2());\n        let galley_from_layer = layer_from_galley.inverse();\n        let layer_from_global = global_from_layer.inverse();\n        let galley_from_global = galley_from_layer * layer_from_global;\n        let global_from_galley = global_from_layer * layer_from_galley;\n\n        if response.hovered() {\n            ui.set_cursor_icon(CursorIcon::Text);\n        }\n\n        self.any_hovered |= response.hovered();\n        self.is_dragging |= response.is_pointer_button_down_on(); // we don't want the initial latency of drag vs click decision\n\n        let old_selection = self.selection;\n\n        let mut cursor_state = self.cursor_for(ui, response, global_from_galley, galley);\n\n        let old_range = cursor_state.range(galley);\n\n        if let Some(pointer_pos) = ui.ctx().pointer_interact_pos()\n            && response.contains_pointer()\n        {\n            let cursor_at_pointer =\n                galley.cursor_from_pos((galley_from_global * pointer_pos).to_vec2());\n\n            // This is where we handle start-of-drag and double-click-to-select.\n            // Actual drag-to-select happens elsewhere.\n            let dragged = false;\n            cursor_state.pointer_interaction(ui, response, cursor_at_pointer, galley, dragged);\n        }\n\n        if let Some(mut cursor_range) = cursor_state.range(galley) {\n            let galley_rect = global_from_galley * Rect::from_min_size(Pos2::ZERO, galley.size());\n            self.selection_bbox_this_frame |= galley_rect;\n\n            if let Some(selection) = &self.selection\n                && selection.primary.widget_id == response.id\n            {\n                process_selection_key_events(ui.ctx(), galley, response.id, &mut cursor_range);\n            }\n\n            if got_copy_event(ui.ctx()) {\n                self.copy_text(galley_rect, galley, &cursor_range);\n            }\n\n            cursor_state.set_char_range(Some(cursor_range));\n        }\n\n        // Look for changes due to keyboard and/or mouse interaction:\n        let new_range = cursor_state.range(galley);\n        let selection_changed = old_range != new_range;\n\n        if let (true, Some(range)) = (selection_changed, new_range) {\n            // --------------\n            // Store results:\n\n            if let Some(selection) = &mut self.selection {\n                let primary_changed = Some(range.primary) != old_range.map(|r| r.primary);\n                let secondary_changed = Some(range.secondary) != old_range.map(|r| r.secondary);\n\n                selection.layer_id = response.layer_id;\n\n                if primary_changed || !ui.style().interaction.multi_widget_text_select {\n                    selection.primary =\n                        WidgetTextCursor::new(widget_id, range.primary, global_from_galley, galley);\n                    self.has_reached_primary = true;\n                }\n                if secondary_changed || !ui.style().interaction.multi_widget_text_select {\n                    selection.secondary = WidgetTextCursor::new(\n                        widget_id,\n                        range.secondary,\n                        global_from_galley,\n                        galley,\n                    );\n                    self.has_reached_secondary = true;\n                }\n            } else {\n                // Start of a new selection\n                self.selection = Some(CurrentSelection {\n                    layer_id: response.layer_id,\n                    primary: WidgetTextCursor::new(\n                        widget_id,\n                        range.primary,\n                        global_from_galley,\n                        galley,\n                    ),\n                    secondary: WidgetTextCursor::new(\n                        widget_id,\n                        range.secondary,\n                        global_from_galley,\n                        galley,\n                    ),\n                });\n                self.has_reached_primary = true;\n                self.has_reached_secondary = true;\n            }\n        }\n\n        // Scroll containing ScrollArea on cursor change:\n        if let Some(range) = new_range {\n            let old_primary = old_selection.map(|s| s.primary);\n            let new_primary = self.selection.as_ref().map(|s| s.primary);\n            if let Some(new_primary) = new_primary {\n                let primary_changed = old_primary.is_none_or(|old| {\n                    old.widget_id != new_primary.widget_id || old.ccursor != new_primary.ccursor\n                });\n                if primary_changed && new_primary.widget_id == widget_id {\n                    let is_fully_visible = ui.clip_rect().contains_rect(response.rect); // TODO(emilk): remove this HACK workaround for https://github.com/emilk/egui/issues/1531\n                    if selection_changed && !is_fully_visible {\n                        // Scroll to keep primary cursor in view:\n                        let row_height = estimate_row_height(galley);\n                        let primary_cursor_rect =\n                            global_from_galley * cursor_rect(galley, &range.primary, row_height);\n                        ui.scroll_to_rect(primary_cursor_rect, None);\n                    }\n                }\n            }\n        }\n\n        let cursor_range = cursor_state.range(galley);\n\n        let mut new_vertex_indices = vec![];\n\n        if let Some(cursor_range) = cursor_range {\n            paint_text_selection(\n                galley,\n                ui.visuals(),\n                &cursor_range,\n                Some(&mut new_vertex_indices),\n            );\n        }\n\n        super::accesskit_text::update_accesskit_for_text_widget(\n            ui.ctx(),\n            response.id,\n            cursor_range,\n            accesskit::Role::Label,\n            global_from_galley,\n            galley,\n        );\n\n        new_vertex_indices\n    }\n}\n\nfn got_copy_event(ctx: &Context) -> bool {\n    ctx.input(|i| {\n        i.events\n            .iter()\n            .any(|e| matches!(e, Event::Copy | Event::Cut))\n    })\n}\n\n/// Returns true if the cursor changed\nfn process_selection_key_events(\n    ctx: &Context,\n    galley: &Galley,\n    widget_id: Id,\n    cursor_range: &mut CCursorRange,\n) -> bool {\n    let os = ctx.os();\n\n    let mut changed = false;\n\n    ctx.input(|i| {\n        // NOTE: we have a lock on ui/ctx here,\n        // so be careful to not call into `ui` or `ctx` again.\n        for event in &i.events {\n            changed |= cursor_range.on_event(os, event, galley, widget_id);\n        }\n    });\n\n    changed\n}\n\nfn selected_text(galley: &Galley, cursor_range: &CCursorRange) -> String {\n    // This logic means we can select everything in an elided label (including the `…`)\n    // and still copy the entire un-elided text!\n    let everything_is_selected = cursor_range.contains(CCursorRange::select_all(galley));\n\n    let copy_everything = cursor_range.is_empty() || everything_is_selected;\n\n    if copy_everything {\n        galley.text().to_owned()\n    } else {\n        cursor_range.slice_str(galley).to_owned()\n    }\n}\n\nfn estimate_row_height(galley: &Galley) -> f32 {\n    if let Some(placed_row) = galley.rows.first() {\n        placed_row.height()\n    } else {\n        galley.size().y\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/text_selection/mod.rs",
    "content": "//! Helpers regarding text selection for labels and text edit.\n\npub mod accesskit_text;\n\nmod cursor_range;\nmod label_text_selection;\npub mod text_cursor_state;\npub mod visuals;\n\npub use cursor_range::CCursorRange;\npub use label_text_selection::LabelSelectionState;\npub use text_cursor_state::TextCursorState;\n"
  },
  {
    "path": "crates/egui/src/text_selection/text_cursor_state.rs",
    "content": "//! Text cursor changes/interaction, without modifying the text.\n\nuse epaint::text::{Galley, cursor::CCursor};\nuse unicode_segmentation::UnicodeSegmentation as _;\n\nuse crate::{NumExt as _, Rect, Response, Ui, epaint};\n\nuse super::CCursorRange;\n\n/// The state of a text cursor selection.\n///\n/// Used for [`crate::TextEdit`] and [`crate::Label`].\n#[derive(Clone, Copy, Debug, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct TextCursorState {\n    ccursor_range: Option<CCursorRange>,\n}\n\nimpl From<CCursorRange> for TextCursorState {\n    fn from(ccursor_range: CCursorRange) -> Self {\n        Self {\n            ccursor_range: Some(ccursor_range),\n        }\n    }\n}\n\nimpl TextCursorState {\n    pub fn is_empty(&self) -> bool {\n        self.ccursor_range.is_none()\n    }\n\n    /// The currently selected range of characters.\n    pub fn char_range(&self) -> Option<CCursorRange> {\n        self.ccursor_range\n    }\n\n    /// The currently selected range of characters, clamped within the character\n    /// range of the given [`Galley`].\n    pub fn range(&self, galley: &Galley) -> Option<CCursorRange> {\n        self.ccursor_range.map(|mut range| {\n            range.primary = galley.clamp_cursor(&range.primary);\n            range.secondary = galley.clamp_cursor(&range.secondary);\n            range\n        })\n    }\n\n    /// Sets the currently selected range of characters.\n    pub fn set_char_range(&mut self, ccursor_range: Option<CCursorRange>) {\n        self.ccursor_range = ccursor_range;\n    }\n}\n\nimpl TextCursorState {\n    /// Handle clicking and/or dragging text.\n    ///\n    /// Returns `true` if there was interaction.\n    pub fn pointer_interaction(\n        &mut self,\n        ui: &Ui,\n        response: &Response,\n        cursor_at_pointer: CCursor,\n        galley: &Galley,\n        is_being_dragged: bool,\n    ) -> bool {\n        let text = galley.text();\n\n        if response.double_clicked() {\n            // Select word:\n            let ccursor_range = select_word_at(text, cursor_at_pointer);\n            self.set_char_range(Some(ccursor_range));\n            true\n        } else if response.triple_clicked() {\n            // Select line:\n            let ccursor_range = select_line_at(text, cursor_at_pointer);\n            self.set_char_range(Some(ccursor_range));\n            true\n        } else if response.sense.senses_drag() {\n            if response.hovered() && ui.input(|i| i.pointer.any_pressed()) {\n                // The start of a drag (or a click).\n                if ui.input(|i| i.modifiers.shift) {\n                    if let Some(mut cursor_range) = self.range(galley) {\n                        cursor_range.primary = cursor_at_pointer;\n                        self.set_char_range(Some(cursor_range));\n                    } else {\n                        self.set_char_range(Some(CCursorRange::one(cursor_at_pointer)));\n                    }\n                } else {\n                    self.set_char_range(Some(CCursorRange::one(cursor_at_pointer)));\n                }\n                true\n            } else if is_being_dragged {\n                // Drag to select text:\n                if let Some(mut cursor_range) = self.range(galley) {\n                    cursor_range.primary = cursor_at_pointer;\n                    self.set_char_range(Some(cursor_range));\n                }\n                true\n            } else {\n                false\n            }\n        } else {\n            false\n        }\n    }\n}\n\nfn select_word_at(text: &str, ccursor: CCursor) -> CCursorRange {\n    if ccursor.index == 0 {\n        CCursorRange::two(ccursor, ccursor_next_word(text, ccursor))\n    } else {\n        let it = text.chars();\n        let mut it = it.skip(ccursor.index - 1);\n        if let Some(char_before_cursor) = it.next() {\n            if let Some(char_after_cursor) = it.next() {\n                if is_word_char(char_before_cursor) && is_word_char(char_after_cursor) {\n                    let min = ccursor_previous_word(text, ccursor + 1);\n                    let max = ccursor_next_word(text, min);\n                    CCursorRange::two(min, max)\n                } else if is_word_char(char_before_cursor) {\n                    let min = ccursor_previous_word(text, ccursor);\n                    let max = ccursor_next_word(text, min);\n                    CCursorRange::two(min, max)\n                } else if is_word_char(char_after_cursor) {\n                    let max = ccursor_next_word(text, ccursor);\n                    CCursorRange::two(ccursor, max)\n                } else {\n                    let min = ccursor_previous_word(text, ccursor);\n                    let max = ccursor_next_word(text, ccursor);\n                    CCursorRange::two(min, max)\n                }\n            } else {\n                let min = ccursor_previous_word(text, ccursor);\n                CCursorRange::two(min, ccursor)\n            }\n        } else {\n            let max = ccursor_next_word(text, ccursor);\n            CCursorRange::two(ccursor, max)\n        }\n    }\n}\n\nfn select_line_at(text: &str, ccursor: CCursor) -> CCursorRange {\n    if ccursor.index == 0 {\n        CCursorRange::two(ccursor, ccursor_next_line(text, ccursor))\n    } else {\n        let it = text.chars();\n        let mut it = it.skip(ccursor.index - 1);\n        if let Some(char_before_cursor) = it.next() {\n            if let Some(char_after_cursor) = it.next() {\n                if (!is_linebreak(char_before_cursor)) && (!is_linebreak(char_after_cursor)) {\n                    let min = ccursor_previous_line(text, ccursor + 1);\n                    let max = ccursor_next_line(text, min);\n                    CCursorRange::two(min, max)\n                } else if !is_linebreak(char_before_cursor) {\n                    let min = ccursor_previous_line(text, ccursor);\n                    let max = ccursor_next_line(text, min);\n                    CCursorRange::two(min, max)\n                } else if !is_linebreak(char_after_cursor) {\n                    let max = ccursor_next_line(text, ccursor);\n                    CCursorRange::two(ccursor, max)\n                } else {\n                    let min = ccursor_previous_line(text, ccursor);\n                    let max = ccursor_next_line(text, ccursor);\n                    CCursorRange::two(min, max)\n                }\n            } else {\n                let min = ccursor_previous_line(text, ccursor);\n                CCursorRange::two(min, ccursor)\n            }\n        } else {\n            let max = ccursor_next_line(text, ccursor);\n            CCursorRange::two(ccursor, max)\n        }\n    }\n}\n\npub fn ccursor_next_word(text: &str, ccursor: CCursor) -> CCursor {\n    CCursor {\n        index: next_word_boundary_char_index(text, ccursor.index),\n        prefer_next_row: false,\n    }\n}\n\nfn ccursor_next_line(text: &str, ccursor: CCursor) -> CCursor {\n    CCursor {\n        index: next_line_boundary_char_index(text.chars(), ccursor.index),\n        prefer_next_row: false,\n    }\n}\n\npub fn ccursor_previous_word(text: &str, ccursor: CCursor) -> CCursor {\n    let num_chars = text.chars().count();\n    let reversed: String = text.graphemes(true).rev().collect();\n    CCursor {\n        index: num_chars\n            - next_word_boundary_char_index(&reversed, num_chars - ccursor.index).min(num_chars),\n        prefer_next_row: true,\n    }\n}\n\nfn ccursor_previous_line(text: &str, ccursor: CCursor) -> CCursor {\n    let num_chars = text.chars().count();\n    CCursor {\n        index: num_chars\n            - next_line_boundary_char_index(text.chars().rev(), num_chars - ccursor.index),\n        prefer_next_row: true,\n    }\n}\n\nfn next_word_boundary_char_index(text: &str, cursor_ci: usize) -> usize {\n    for (word_byte_index, word) in text.split_word_bound_indices() {\n        let word_ci = char_index_from_byte_index(text, word_byte_index);\n\n        // We consider `.` a word boundary.\n        // At least that's how Mac works when navigating something like `www.example.com`.\n        for (dot_ci_offset, chr) in word.chars().enumerate() {\n            let dot_ci = word_ci + dot_ci_offset;\n            if chr == '.' && cursor_ci < dot_ci {\n                return dot_ci;\n            }\n        }\n\n        // Splitting considers contiguous whitespace as one word, such words must be skipped,\n        // this handles cases for example ' abc' (a space and a word), the cursor is at the beginning\n        // (before space) - this jumps at the end of 'abc' (this is consistent with text editors\n        // or browsers)\n        if cursor_ci < word_ci && !all_word_chars(word) {\n            return word_ci;\n        }\n    }\n\n    char_index_from_byte_index(text, text.len())\n}\n\nfn all_word_chars(text: &str) -> bool {\n    text.chars().all(is_word_char)\n}\n\nfn next_line_boundary_char_index(it: impl Iterator<Item = char>, mut index: usize) -> usize {\n    let mut it = it.skip(index);\n    if let Some(_first) = it.next() {\n        index += 1;\n\n        if let Some(second) = it.next() {\n            index += 1;\n            for next in it {\n                if is_linebreak(next) != is_linebreak(second) {\n                    break;\n                }\n                index += 1;\n            }\n        }\n    }\n    index\n}\n\npub fn is_word_char(c: char) -> bool {\n    c.is_alphanumeric() || c == '_'\n}\n\nfn is_linebreak(c: char) -> bool {\n    c == '\\r' || c == '\\n'\n}\n\n/// Accepts and returns character offset (NOT byte offset!).\npub fn find_line_start(text: &str, current_index: CCursor) -> CCursor {\n    // We know that new lines, '\\n', are a single byte char, but we have to\n    // work with char offsets because before the new line there may be any\n    // number of multi byte chars.\n    // We need to know the char index to be able to correctly set the cursor\n    // later.\n    let chars_count = text.chars().count();\n\n    let position = text\n        .chars()\n        .rev()\n        .skip(chars_count - current_index.index)\n        .position(|x| x == '\\n');\n\n    match position {\n        Some(pos) => CCursor::new(current_index.index - pos),\n        None => CCursor::new(0),\n    }\n}\n\npub fn byte_index_from_char_index(s: &str, char_index: usize) -> usize {\n    for (ci, (bi, _)) in s.char_indices().enumerate() {\n        if ci == char_index {\n            return bi;\n        }\n    }\n    s.len()\n}\n\npub fn char_index_from_byte_index(input: &str, byte_index: usize) -> usize {\n    for (ci, (bi, _)) in input.char_indices().enumerate() {\n        if bi == byte_index {\n            return ci;\n        }\n    }\n\n    input.char_indices().last().map_or(0, |(i, _)| i + 1)\n}\n\npub fn slice_char_range(s: &str, char_range: std::ops::Range<usize>) -> &str {\n    assert!(\n        char_range.start <= char_range.end,\n        \"Invalid range, start must be less than end, but start = {}, end = {}\",\n        char_range.start,\n        char_range.end\n    );\n    let start_byte = byte_index_from_char_index(s, char_range.start);\n    let end_byte = byte_index_from_char_index(s, char_range.end);\n    &s[start_byte..end_byte]\n}\n\n/// The thin rectangle of one end of the selection, e.g. the primary cursor, in local galley coordinates.\npub fn cursor_rect(galley: &Galley, cursor: &CCursor, row_height: f32) -> Rect {\n    let mut cursor_pos = galley.pos_from_cursor(*cursor);\n\n    // Handle completely empty galleys\n    cursor_pos.max.y = cursor_pos.max.y.at_least(cursor_pos.min.y + row_height);\n\n    cursor_pos = cursor_pos.expand(1.5); // slightly above/below row\n\n    cursor_pos\n}\n\n#[cfg(test)]\nmod test {\n    use crate::text_selection::text_cursor_state::next_word_boundary_char_index;\n\n    #[test]\n    fn test_next_word_boundary_char_index() {\n        // ASCII only\n        let text = \"abc d3f g_h i-j\";\n        assert_eq!(next_word_boundary_char_index(text, 1), 3);\n        assert_eq!(next_word_boundary_char_index(text, 3), 7);\n        assert_eq!(next_word_boundary_char_index(text, 9), 11);\n        assert_eq!(next_word_boundary_char_index(text, 12), 13);\n        assert_eq!(next_word_boundary_char_index(text, 13), 15);\n        assert_eq!(next_word_boundary_char_index(text, 15), 15);\n\n        assert_eq!(next_word_boundary_char_index(\"\", 0), 0);\n        assert_eq!(next_word_boundary_char_index(\"\", 1), 0);\n\n        // ASCII only\n        let text = \"abc.def.ghi\";\n        assert_eq!(next_word_boundary_char_index(text, 1), 3);\n        assert_eq!(next_word_boundary_char_index(text, 3), 7);\n        assert_eq!(next_word_boundary_char_index(text, 7), 11);\n\n        // Unicode graphemes, some of which consist of multiple Unicode characters,\n        // !!! Unicode character is not always what is tranditionally considered a character,\n        // the values below are correct despite not seeming that way on the first look,\n        // handling of and around emojis is kind of weird and is not consistent across\n        // text editors and browsers\n        let text = \"❤️👍 skvělá knihovna 👍❤️\";\n        assert_eq!(next_word_boundary_char_index(text, 0), 2);\n        assert_eq!(next_word_boundary_char_index(text, 2), 3); // this does not skip the space between thumbs-up and 'skvělá'\n        assert_eq!(next_word_boundary_char_index(text, 6), 10);\n        assert_eq!(next_word_boundary_char_index(text, 9), 10);\n        assert_eq!(next_word_boundary_char_index(text, 12), 19);\n        assert_eq!(next_word_boundary_char_index(text, 15), 19);\n        assert_eq!(next_word_boundary_char_index(text, 19), 20);\n        assert_eq!(next_word_boundary_char_index(text, 20), 21);\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/text_selection/visuals.rs",
    "content": "use std::sync::Arc;\n\nuse crate::{Galley, Painter, Rect, Ui, Visuals, pos2, vec2};\n\nuse super::CCursorRange;\n\n#[derive(Clone, Debug)]\npub struct RowVertexIndices {\n    pub row: usize,\n    pub vertex_indices: [u32; 6],\n}\n\n/// Adds text selection rectangles to the galley.\npub fn paint_text_selection(\n    galley: &mut Arc<Galley>,\n    visuals: &Visuals,\n    cursor_range: &CCursorRange,\n    mut new_vertex_indices: Option<&mut Vec<RowVertexIndices>>,\n) {\n    if cursor_range.is_empty() {\n        return;\n    }\n\n    // We need to modify the galley (add text selection painting to it),\n    // and so we need to clone it if it is shared:\n    let galley: &mut Galley = Arc::make_mut(galley);\n\n    let background_color = visuals.selection.bg_fill;\n    let text_color = visuals.selection.stroke.color;\n\n    let [min, max] = cursor_range.sorted_cursors();\n    let min = galley.layout_from_cursor(min);\n    let max = galley.layout_from_cursor(max);\n\n    for ri in min.row..=max.row {\n        let placed_row = &mut galley.rows[ri];\n        let row = Arc::make_mut(&mut placed_row.row);\n\n        let left = if ri == min.row {\n            row.x_offset(min.column)\n        } else {\n            0.0\n        };\n        let right = if ri == max.row {\n            row.x_offset(max.column)\n        } else {\n            let newline_size = if placed_row.ends_with_newline {\n                row.height() / 2.0 // visualize that we select the newline\n            } else {\n                0.0\n            };\n            row.size.x + newline_size\n        };\n\n        let rect = Rect::from_min_max(pos2(left, 0.0), pos2(right, row.size.y));\n        let mesh = &mut row.visuals.mesh;\n\n        if !row.glyphs.is_empty() {\n            // Change color of the selected text:\n            let first_glyph_index = if ri == min.row { min.column } else { 0 };\n            let last_glyph_index = if ri == max.row {\n                max.column\n            } else {\n                row.glyphs.len() - 1\n            };\n\n            let first_vertex_index = row\n                .glyphs\n                .get(first_glyph_index)\n                .map_or(row.visuals.glyph_vertex_range.end, |g| g.first_vertex as _);\n            let last_vertex_index = row\n                .glyphs\n                .get(last_glyph_index)\n                .map_or(row.visuals.glyph_vertex_range.end, |g| g.first_vertex as _);\n\n            for vi in first_vertex_index..last_vertex_index {\n                mesh.vertices[vi].color = text_color;\n            }\n        }\n\n        // Time to insert the selection rectangle into the row mesh.\n        // It should be on top (after) of any background in the galley,\n        // but behind (before) any glyphs. The row visuals has this information:\n        let glyph_index_start = row.visuals.glyph_index_start;\n\n        // Start by appending the selection rectangle to end of the mesh, as two triangles (= 6 indices):\n        let num_indices_before = mesh.indices.len();\n        mesh.add_colored_rect(rect, background_color);\n        assert_eq!(\n            num_indices_before + 6,\n            mesh.indices.len(),\n            \"We expect exactly 6 new indices\"\n        );\n\n        // Copy out the new triangles:\n        let selection_triangles = [\n            mesh.indices[num_indices_before],\n            mesh.indices[num_indices_before + 1],\n            mesh.indices[num_indices_before + 2],\n            mesh.indices[num_indices_before + 3],\n            mesh.indices[num_indices_before + 4],\n            mesh.indices[num_indices_before + 5],\n        ];\n\n        // Move every old triangle forwards by 6 indices to make room for the new triangle:\n        for i in (glyph_index_start..num_indices_before).rev() {\n            mesh.indices.swap(i, i + 6);\n        }\n        // Put the new triangle in place:\n        mesh.indices[glyph_index_start..glyph_index_start + 6]\n            .clone_from_slice(&selection_triangles);\n\n        row.visuals.mesh_bounds = mesh.calc_bounds();\n\n        if let Some(new_vertex_indices) = &mut new_vertex_indices {\n            new_vertex_indices.push(RowVertexIndices {\n                row: ri,\n                vertex_indices: selection_triangles,\n            });\n        }\n    }\n}\n\n/// Paint one end of the selection, e.g. the primary cursor.\n///\n/// This will never blink.\npub fn paint_cursor_end(painter: &Painter, visuals: &Visuals, cursor_rect: Rect) {\n    let stroke = visuals.text_cursor.stroke;\n\n    let top = cursor_rect.center_top();\n    let bottom = cursor_rect.center_bottom();\n\n    painter.line_segment([top, bottom], (stroke.width, stroke.color));\n\n    if false {\n        // Roof/floor:\n        let extrusion = 3.0;\n        let width = 1.0;\n        painter.line_segment(\n            [top - vec2(extrusion, 0.0), top + vec2(extrusion, 0.0)],\n            (width, stroke.color),\n        );\n        painter.line_segment(\n            [bottom - vec2(extrusion, 0.0), bottom + vec2(extrusion, 0.0)],\n            (width, stroke.color),\n        );\n    }\n}\n\n/// Paint one end of the selection, e.g. the primary cursor, with blinking (if enabled).\npub fn paint_text_cursor(\n    ui: &Ui,\n    painter: &Painter,\n    primary_cursor_rect: Rect,\n    time_since_last_interaction: f64,\n) {\n    if ui.visuals().text_cursor.blink {\n        let on_duration = ui.visuals().text_cursor.on_duration;\n        let off_duration = ui.visuals().text_cursor.off_duration;\n        let total_duration = on_duration + off_duration;\n\n        let time_in_cycle = (time_since_last_interaction % (total_duration as f64)) as f32;\n\n        let wake_in = if time_in_cycle < on_duration {\n            // Cursor is visible\n            paint_cursor_end(painter, ui.visuals(), primary_cursor_rect);\n            on_duration - time_in_cycle\n        } else {\n            // Cursor is not visible\n            total_duration - time_in_cycle\n        };\n\n        ui.request_repaint_after_secs(wake_in);\n    } else {\n        paint_cursor_end(painter, ui.visuals(), primary_cursor_rect);\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/ui.rs",
    "content": "#![warn(missing_docs)] // Let's keep `Ui` well-documented.\n#![expect(clippy::use_self)]\n\nuse std::{any::Any, hash::Hash, ops::Deref, sync::Arc};\n\nuse emath::GuiRounding as _;\nuse epaint::mutex::RwLock;\n\nuse crate::containers::menu;\nuse crate::{containers::*, ecolor::*, layout::*, placer::Placer, widgets::*, *};\n// ----------------------------------------------------------------------------\n\n/// This is what you use to place widgets.\n///\n/// Represents a region of the screen with a type of layout (horizontal or vertical).\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// ui.add(egui::Label::new(\"Hello World!\"));\n/// ui.label(\"A shorter and more convenient way to add a label.\");\n/// ui.horizontal(|ui| {\n///     ui.label(\"Add widgets\");\n///     if ui.button(\"on the same row!\").clicked() {\n///         /* … */\n///     }\n/// });\n/// # });\n/// ```\npub struct Ui {\n    /// Generated based on id of parent ui together with an optional id salt.\n    ///\n    /// This should be stable from one frame to next\n    /// so it can be used as a source for storing state\n    /// (e.g. window position, or if a collapsing header is open).\n    ///\n    /// However, it is not necessarily globally unique.\n    /// For instance, sibling `Ui`s share the same [`Self::id`]\n    /// unless they where explicitly given different id salts using\n    /// [`UiBuilder::id_salt`].\n    id: Id,\n\n    /// This is a globally unique ID of this `Ui`,\n    /// based on where in the hierarchy of widgets this Ui is in.\n    ///\n    /// This means it is not _stable_, as it can change if new widgets\n    /// are added or removed prior to this one.\n    /// It should therefore only be used for transient interactions (clicks etc),\n    /// not for storing state over time.\n    unique_id: Id,\n\n    /// This is used to create a unique interact ID for some widgets.\n    ///\n    /// This value is based on where in the hierarchy of widgets this Ui is in,\n    /// and the value is increment with each added child widget.\n    /// This works as an Id source only as long as new widgets aren't added or removed.\n    /// They are therefore only good for Id:s that have no state.\n    next_auto_id_salt: u64,\n\n    /// Specifies paint layer, clip rectangle and a reference to [`Context`].\n    painter: Painter,\n\n    /// The [`Style`] (visuals, spacing, etc) of this ui.\n    /// Commonly many [`Ui`]s share the same [`Style`].\n    /// The [`Ui`] implements copy-on-write for this.\n    style: Arc<Style>,\n\n    /// Handles the [`Ui`] size and the placement of new widgets.\n    placer: Placer,\n\n    /// If false we are unresponsive to input,\n    /// and all widgets will assume a gray style.\n    enabled: bool,\n\n    /// Set to true in special cases where we do one frame\n    /// where we size up the contents of the Ui, without actually showing it.\n    sizing_pass: bool,\n\n    /// Indicates whether this Ui belongs to a Menu.\n    #[expect(deprecated)]\n    menu_state: Option<Arc<RwLock<crate::menu::MenuState>>>,\n\n    /// The [`UiStack`] for this [`Ui`].\n    stack: Arc<UiStack>,\n\n    /// The sense for the ui background.\n    sense: Sense,\n\n    /// Whether [`Ui::remember_min_rect`] should be called when the [`Ui`] is dropped.\n    /// This is an optimization, so we don't call [`Ui::remember_min_rect`] multiple times at the\n    /// end of a [`Ui::scope`].\n    min_rect_already_remembered: bool,\n}\n\n/// Allow using [`Ui`] like a [`Context`].\nimpl Deref for Ui {\n    type Target = Context;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        self.ctx()\n    }\n}\n\nimpl Ui {\n    // ------------------------------------------------------------------------\n    // Creation:\n\n    /// Create a new top-level [`Ui`].\n    ///\n    /// Normally you would not use this directly, but instead use\n    /// [`crate::Panel`], [`crate::CentralPanel`], [`crate::Window`] or [`crate::Area`].\n    pub fn new(ctx: Context, id: Id, ui_builder: UiBuilder) -> Self {\n        let UiBuilder {\n            id_salt,\n            global_scope: _,\n            ui_stack_info,\n            layer_id,\n            max_rect,\n            layout,\n            disabled,\n            invisible,\n            sizing_pass,\n            style,\n            sense,\n            accessibility_parent,\n        } = ui_builder;\n\n        let layer_id = layer_id.unwrap_or_else(LayerId::background);\n\n        debug_assert!(\n            id_salt.is_none(),\n            \"Top-level Ui:s should not have an id_salt\"\n        );\n\n        let max_rect = max_rect.unwrap_or_else(|| ctx.content_rect());\n        let clip_rect = max_rect;\n        let layout = layout.unwrap_or_default();\n        let disabled = disabled || invisible;\n        let style = style.unwrap_or_else(|| ctx.global_style());\n        let sense = sense.unwrap_or_else(Sense::hover);\n\n        let placer = Placer::new(max_rect, layout);\n        let ui_stack = UiStack {\n            id,\n            layout_direction: layout.main_dir,\n            info: ui_stack_info,\n            parent: None,\n            min_rect: placer.min_rect(),\n            max_rect: placer.max_rect(),\n        };\n        let mut ui = Ui {\n            id,\n            unique_id: id,\n            next_auto_id_salt: id.with(\"auto\").value(),\n            painter: Painter::new(ctx, layer_id, clip_rect),\n            style,\n            placer,\n            enabled: true,\n            sizing_pass,\n            menu_state: None,\n            stack: Arc::new(ui_stack),\n            sense,\n            min_rect_already_remembered: false,\n        };\n\n        if let Some(accessibility_parent) = accessibility_parent {\n            ui.ctx()\n                .register_accesskit_parent(ui.unique_id, accessibility_parent);\n        }\n\n        // Register in the widget stack early, to ensure we are behind all widgets we contain:\n        let start_rect = Rect::NOTHING; // This will be overwritten when `remember_min_rect` is called\n        ui.ctx().create_widget(\n            WidgetRect {\n                id: ui.unique_id,\n                layer_id: ui.layer_id(),\n                rect: start_rect,\n                interact_rect: start_rect,\n                sense,\n                enabled: ui.enabled,\n            },\n            true,\n            Default::default(),\n        );\n\n        if disabled {\n            ui.disable();\n        }\n        if invisible {\n            ui.set_invisible();\n        }\n\n        ui.ctx().accesskit_node_builder(ui.unique_id, |node| {\n            node.set_role(accesskit::Role::GenericContainer);\n        });\n\n        ui\n    }\n\n    /// Create a new [`Ui`] at a specific region.\n    ///\n    /// Note: calling this function twice from the same [`Ui`] will create a conflict of id. Use\n    /// [`Self::scope`] if needed.\n    ///\n    /// When in doubt, use `None` for the `UiStackInfo` argument.\n    #[deprecated = \"Use ui.new_child() instead\"]\n    pub fn child_ui(\n        &mut self,\n        max_rect: Rect,\n        layout: Layout,\n        ui_stack_info: Option<UiStackInfo>,\n    ) -> Self {\n        self.new_child(\n            UiBuilder::new()\n                .max_rect(max_rect)\n                .layout(layout)\n                .ui_stack_info(ui_stack_info.unwrap_or_default()),\n        )\n    }\n\n    /// Create a new [`Ui`] at a specific region with a specific id.\n    ///\n    /// When in doubt, use `None` for the `UiStackInfo` argument.\n    #[deprecated = \"Use ui.new_child() instead\"]\n    pub fn child_ui_with_id_source(\n        &mut self,\n        max_rect: Rect,\n        layout: Layout,\n        id_salt: impl Hash,\n        ui_stack_info: Option<UiStackInfo>,\n    ) -> Self {\n        self.new_child(\n            UiBuilder::new()\n                .id_salt(id_salt)\n                .max_rect(max_rect)\n                .layout(layout)\n                .ui_stack_info(ui_stack_info.unwrap_or_default()),\n        )\n    }\n\n    /// Create a child `Ui` with the properties of the given builder.\n    ///\n    /// This is a very low-level function.\n    /// Usually you are better off using [`Self::scope_builder`].\n    ///\n    /// Note that calling this does not allocate any space in the parent `Ui`,\n    /// so after adding widgets to the child `Ui` you probably want to allocate\n    /// the [`Ui::min_rect`] of the child in the parent `Ui` using e.g.\n    /// [`Ui::advance_cursor_after_rect`].\n    pub fn new_child(&mut self, ui_builder: UiBuilder) -> Self {\n        let UiBuilder {\n            id_salt,\n            global_scope,\n            ui_stack_info,\n            layer_id,\n            max_rect,\n            layout,\n            disabled,\n            invisible,\n            sizing_pass,\n            style,\n            sense,\n            accessibility_parent,\n        } = ui_builder;\n\n        let mut painter = self.painter.clone();\n\n        let id_salt = id_salt.unwrap_or_else(|| Id::from(\"child\"));\n        let max_rect = max_rect.unwrap_or_else(|| self.available_rect_before_wrap());\n        let mut layout = layout.unwrap_or_else(|| *self.layout());\n        let enabled = self.enabled && !disabled && !invisible;\n        if let Some(layer_id) = layer_id {\n            painter.set_layer_id(layer_id);\n        }\n        if invisible {\n            painter.set_invisible();\n        }\n        let sizing_pass = self.sizing_pass || sizing_pass;\n        let style = style.unwrap_or_else(|| Arc::clone(&self.style));\n        let sense = sense.unwrap_or_else(Sense::hover);\n\n        if sizing_pass {\n            // During the sizing pass we want widgets to use up as little space as possible,\n            // so that we measure the only the space we _need_.\n            layout.cross_justify = false;\n            if layout.cross_align == Align::Center {\n                layout.cross_align = Align::Min;\n            }\n        }\n\n        debug_assert!(!max_rect.any_nan(), \"max_rect is NaN: {max_rect:?}\");\n        let (stable_id, unique_id) = if global_scope {\n            (id_salt, id_salt)\n        } else {\n            let stable_id = self.id.with(id_salt);\n            let unique_id = stable_id.with(self.next_auto_id_salt);\n\n            (stable_id, unique_id)\n        };\n        let next_auto_id_salt = unique_id.value().wrapping_add(1);\n\n        self.next_auto_id_salt = self.next_auto_id_salt.wrapping_add(1);\n\n        let placer = Placer::new(max_rect, layout);\n        let ui_stack = UiStack {\n            id: unique_id,\n            layout_direction: layout.main_dir,\n            info: ui_stack_info,\n            parent: Some(Arc::clone(&self.stack)),\n            min_rect: placer.min_rect(),\n            max_rect: placer.max_rect(),\n        };\n        let mut child_ui = Ui {\n            id: stable_id,\n            unique_id,\n            next_auto_id_salt,\n            painter,\n            style,\n            placer,\n            enabled,\n            sizing_pass,\n            menu_state: self.menu_state.clone(),\n            stack: Arc::new(ui_stack),\n            sense,\n            min_rect_already_remembered: false,\n        };\n\n        if disabled {\n            child_ui.disable();\n        }\n\n        child_ui.ctx().register_accesskit_parent(\n            child_ui.unique_id,\n            accessibility_parent.unwrap_or(self.unique_id),\n        );\n\n        // Register in the widget stack early, to ensure we are behind all widgets we contain:\n        let start_rect = Rect::NOTHING; // This will be overwritten when `remember_min_rect` is called\n        child_ui.ctx().create_widget(\n            WidgetRect {\n                id: child_ui.unique_id,\n                layer_id: child_ui.layer_id(),\n                rect: start_rect,\n                interact_rect: start_rect,\n                sense,\n                enabled: child_ui.enabled,\n            },\n            true,\n            Default::default(),\n        );\n\n        child_ui\n            .ctx()\n            .accesskit_node_builder(child_ui.unique_id, |node| {\n                node.set_role(accesskit::Role::GenericContainer);\n            });\n\n        child_ui\n    }\n\n    // -------------------------------------------------\n\n    /// Set to true in special cases where we do one frame\n    /// where we size up the contents of the Ui, without actually showing it.\n    ///\n    /// This will also turn the Ui invisible.\n    /// Should be called right after [`Self::new`], if at all.\n    #[inline]\n    #[deprecated = \"Use UiBuilder.sizing_pass().invisible()\"]\n    pub fn set_sizing_pass(&mut self) {\n        self.sizing_pass = true;\n        self.set_invisible();\n    }\n\n    /// Set to true in special cases where we do one frame\n    /// where we size up the contents of the Ui, without actually showing it.\n    #[inline]\n    pub fn is_sizing_pass(&self) -> bool {\n        self.sizing_pass\n    }\n\n    // -------------------------------------------------\n\n    /// Generated based on id of parent ui together with an optional id salt.\n    ///\n    /// This should be stable from one frame to next\n    /// so it can be used as a source for storing state\n    /// (e.g. window position, or if a collapsing header is open).\n    ///\n    /// However, it is not necessarily globally unique.\n    /// For instance, sibling `Ui`s share the same [`Self::id`]\n    /// unless they were explicitly given different id salts using\n    /// [`UiBuilder::id_salt`].\n    #[inline]\n    pub fn id(&self) -> Id {\n        self.id\n    }\n\n    /// This is a globally unique ID of this `Ui`,\n    /// based on where in the hierarchy of widgets this Ui is in.\n    ///\n    /// This means it is not _stable_, as it can change if new widgets\n    /// are added or removed prior to this one.\n    /// It should therefore only be used for transient interactions (clicks etc),\n    /// not for storing state over time.\n    #[inline]\n    pub fn unique_id(&self) -> Id {\n        self.unique_id\n    }\n\n    /// Style options for this [`Ui`] and its children.\n    ///\n    /// Note that this may be a different [`Style`] than that of [`Context::style`].\n    #[inline]\n    pub fn style(&self) -> &Arc<Style> {\n        &self.style\n    }\n\n    /// Mutably borrow internal [`Style`].\n    /// Changes apply to this [`Ui`] and its subsequent children.\n    ///\n    /// To set the style of all [`Ui`]s, use [`Context::set_style_of`].\n    ///\n    /// Example:\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.style_mut().override_text_style = Some(egui::TextStyle::Heading);\n    /// # });\n    /// ```\n    pub fn style_mut(&mut self) -> &mut Style {\n        Arc::make_mut(&mut self.style) // clone-on-write\n    }\n\n    /// Changes apply to this [`Ui`] and its subsequent children.\n    ///\n    /// To set the style of all [`Ui`]s, use [`Context::set_style_of`].\n    pub fn set_style(&mut self, style: impl Into<Arc<Style>>) {\n        self.style = style.into();\n    }\n\n    /// Reset to the default style set in [`Context`].\n    pub fn reset_style(&mut self) {\n        self.style = self.ctx().global_style();\n    }\n\n    /// The current spacing options for this [`Ui`].\n    /// Short for `ui.style().spacing`.\n    #[inline]\n    pub fn spacing(&self) -> &crate::style::Spacing {\n        &self.style.spacing\n    }\n\n    /// Mutably borrow internal [`Spacing`].\n    /// Changes apply to this [`Ui`] and its subsequent children.\n    ///\n    /// Example:\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.spacing_mut().item_spacing = egui::vec2(10.0, 2.0);\n    /// # });\n    /// ```\n    pub fn spacing_mut(&mut self) -> &mut crate::style::Spacing {\n        &mut self.style_mut().spacing\n    }\n\n    /// The current visuals settings of this [`Ui`].\n    /// Short for `ui.style().visuals`.\n    #[inline]\n    pub fn visuals(&self) -> &crate::Visuals {\n        &self.style.visuals\n    }\n\n    /// Mutably borrow internal `visuals`.\n    /// Changes apply to this [`Ui`] and its subsequent children.\n    ///\n    /// To set the visuals of all [`Ui`]s, use [`Context::set_visuals_of`].\n    ///\n    /// Example:\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.visuals_mut().override_text_color = Some(egui::Color32::RED);\n    /// # });\n    /// ```\n    pub fn visuals_mut(&mut self) -> &mut crate::Visuals {\n        &mut self.style_mut().visuals\n    }\n\n    /// Get a reference to this [`Ui`]'s [`UiStack`].\n    #[inline]\n    pub fn stack(&self) -> &Arc<UiStack> {\n        &self.stack\n    }\n\n    /// Get a reference to the parent [`Context`].\n    #[inline]\n    pub fn ctx(&self) -> &Context {\n        self.painter.ctx()\n    }\n\n    /// Use this to paint stuff within this [`Ui`].\n    #[inline]\n    pub fn painter(&self) -> &Painter {\n        &self.painter\n    }\n\n    /// Number of physical pixels for each logical UI point.\n    #[inline]\n    pub fn pixels_per_point(&self) -> f32 {\n        self.painter.pixels_per_point()\n    }\n\n    /// If `false`, the [`Ui`] does not allow any interaction and\n    /// the widgets in it will draw with a gray look.\n    #[inline]\n    pub fn is_enabled(&self) -> bool {\n        self.enabled\n    }\n\n    /// Calling `disable()` will cause the [`Ui`] to deny all future interaction\n    /// and all the widgets will draw with a gray look.\n    ///\n    /// Usually it is more convenient to use [`Self::add_enabled_ui`] or [`Self::add_enabled`].\n    ///\n    /// Note that once disabled, there is no way to re-enable the [`Ui`].\n    ///\n    /// ### Example\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut enabled = true;\n    /// ui.group(|ui| {\n    ///     ui.checkbox(&mut enabled, \"Enable subsection\");\n    ///     if !enabled {\n    ///         ui.disable();\n    ///     }\n    ///     if ui.button(\"Button that is not always clickable\").clicked() {\n    ///         /* … */\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    pub fn disable(&mut self) {\n        self.enabled = false;\n        if self.is_visible() {\n            self.painter\n                .multiply_opacity(self.visuals().disabled_alpha());\n        }\n    }\n\n    /// Calling `set_enabled(false)` will cause the [`Ui`] to deny all future interaction\n    /// and all the widgets will draw with a gray look.\n    ///\n    /// Usually it is more convenient to use [`Self::add_enabled_ui`] or [`Self::add_enabled`].\n    ///\n    /// Calling `set_enabled(true)` has no effect - it will NOT re-enable the [`Ui`] once disabled.\n    ///\n    /// ### Example\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut enabled = true;\n    /// ui.group(|ui| {\n    ///     ui.checkbox(&mut enabled, \"Enable subsection\");\n    ///     ui.set_enabled(enabled);\n    ///     if ui.button(\"Button that is not always clickable\").clicked() {\n    ///         /* … */\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    #[deprecated = \"Use disable(), add_enabled_ui(), or add_enabled() instead\"]\n    pub fn set_enabled(&mut self, enabled: bool) {\n        if !enabled {\n            self.disable();\n        }\n    }\n\n    /// If `false`, any widgets added to the [`Ui`] will be invisible and non-interactive.\n    ///\n    /// This is `false` if any parent had [`UiBuilder::invisible`]\n    /// or if [`Context::will_discard`].\n    #[inline]\n    pub fn is_visible(&self) -> bool {\n        self.painter.is_visible()\n    }\n\n    /// Calling `set_invisible()` will cause all further widgets to be invisible,\n    /// yet still allocate space.\n    ///\n    /// The widgets will not be interactive (`set_invisible()` implies `disable()`).\n    ///\n    /// Once invisible, there is no way to make the [`Ui`] visible again.\n    ///\n    /// Usually it is more convenient to use [`Self::add_visible_ui`] or [`Self::add_visible`].\n    ///\n    /// ### Example\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut visible = true;\n    /// ui.group(|ui| {\n    ///     ui.checkbox(&mut visible, \"Show subsection\");\n    ///     if !visible {\n    ///         ui.set_invisible();\n    ///     }\n    ///     if ui.button(\"Button that is not always shown\").clicked() {\n    ///         /* … */\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    pub fn set_invisible(&mut self) {\n        self.painter.set_invisible();\n        self.disable();\n    }\n\n    /// Calling `set_visible(false)` will cause all further widgets to be invisible,\n    /// yet still allocate space.\n    ///\n    /// The widgets will not be interactive (`set_visible(false)` implies `set_enabled(false)`).\n    ///\n    /// Calling `set_visible(true)` has no effect.\n    ///\n    /// ### Example\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut visible = true;\n    /// ui.group(|ui| {\n    ///     ui.checkbox(&mut visible, \"Show subsection\");\n    ///     ui.set_visible(visible);\n    ///     if ui.button(\"Button that is not always shown\").clicked() {\n    ///         /* … */\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    #[deprecated = \"Use set_invisible(), add_visible_ui(), or add_visible() instead\"]\n    pub fn set_visible(&mut self, visible: bool) {\n        if !visible {\n            self.painter.set_invisible();\n            self.disable();\n        }\n    }\n\n    /// Make the widget in this [`Ui`] semi-transparent.\n    ///\n    /// `opacity` must be between 0.0 and 1.0, where 0.0 means fully transparent (i.e., invisible)\n    /// and 1.0 means fully opaque.\n    ///\n    /// ### Example\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.group(|ui| {\n    ///     ui.set_opacity(0.5);\n    ///     if ui.button(\"Half-transparent button\").clicked() {\n    ///         /* … */\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    ///\n    /// See also: [`Self::opacity`] and [`Self::multiply_opacity`].\n    pub fn set_opacity(&mut self, opacity: f32) {\n        self.painter.set_opacity(opacity);\n    }\n\n    /// Like [`Self::set_opacity`], but multiplies the given value with the current opacity.\n    ///\n    /// See also: [`Self::set_opacity`] and [`Self::opacity`].\n    pub fn multiply_opacity(&mut self, opacity: f32) {\n        self.painter.multiply_opacity(opacity);\n    }\n\n    /// Read the current opacity of the underlying painter.\n    ///\n    /// See also: [`Self::set_opacity`] and [`Self::multiply_opacity`].\n    #[inline]\n    pub fn opacity(&self) -> f32 {\n        self.painter.opacity()\n    }\n\n    /// Read the [`Layout`].\n    #[inline]\n    pub fn layout(&self) -> &Layout {\n        self.placer.layout()\n    }\n\n    /// Which wrap mode should the text use in this [`Ui`]?\n    ///\n    /// This is determined first by [`Style::wrap_mode`], and then by the layout of this [`Ui`].\n    pub fn wrap_mode(&self) -> TextWrapMode {\n        #[expect(deprecated)]\n        if let Some(wrap_mode) = self.style.wrap_mode {\n            wrap_mode\n        }\n        // `wrap` handling for backward compatibility\n        else if let Some(wrap) = self.style.wrap {\n            if wrap {\n                TextWrapMode::Wrap\n            } else {\n                TextWrapMode::Extend\n            }\n        } else if let Some(grid) = self.placer.grid() {\n            if grid.wrap_text() {\n                TextWrapMode::Wrap\n            } else {\n                TextWrapMode::Extend\n            }\n        } else {\n            let layout = self.layout();\n            if layout.is_vertical() || layout.is_horizontal() && layout.main_wrap() {\n                TextWrapMode::Wrap\n            } else {\n                TextWrapMode::Extend\n            }\n        }\n    }\n\n    /// Should text wrap in this [`Ui`]?\n    ///\n    /// This is determined first by [`Style::wrap_mode`], and then by the layout of this [`Ui`].\n    #[deprecated = \"Use `wrap_mode` instead\"]\n    pub fn wrap_text(&self) -> bool {\n        self.wrap_mode() == TextWrapMode::Wrap\n    }\n\n    /// How to vertically align text\n    #[inline]\n    pub fn text_valign(&self) -> Align {\n        self.style()\n            .override_text_valign\n            .unwrap_or_else(|| self.layout().vertical_align())\n    }\n\n    /// Create a painter for a sub-region of this Ui.\n    ///\n    /// The clip-rect of the returned [`Painter`] will be the intersection\n    /// of the given rectangle and the `clip_rect()` of this [`Ui`].\n    pub fn painter_at(&self, rect: Rect) -> Painter {\n        self.painter().with_clip_rect(rect)\n    }\n\n    /// Use this to paint stuff within this [`Ui`].\n    #[inline]\n    pub fn layer_id(&self) -> LayerId {\n        self.painter().layer_id()\n    }\n\n    /// The height of text of this text style.\n    ///\n    /// Returns a value rounded to [`emath::GUI_ROUNDING`].\n    pub fn text_style_height(&self, style: &TextStyle) -> f32 {\n        self.fonts_mut(|f| f.row_height(&style.resolve(self.style())))\n    }\n\n    /// Screen-space rectangle for clipping what we paint in this ui.\n    /// This is used, for instance, to avoid painting outside a window that is smaller than its contents.\n    #[inline]\n    pub fn clip_rect(&self) -> Rect {\n        self.painter.clip_rect()\n    }\n\n    /// Constrain the rectangle in which we can paint.\n    ///\n    /// Short for `ui.set_clip_rect(ui.clip_rect().intersect(new_clip_rect))`.\n    ///\n    /// See also: [`Self::clip_rect`] and [`Self::set_clip_rect`].\n    #[inline]\n    pub fn shrink_clip_rect(&mut self, new_clip_rect: Rect) {\n        self.painter.shrink_clip_rect(new_clip_rect);\n    }\n\n    /// Screen-space rectangle for clipping what we paint in this ui.\n    /// This is used, for instance, to avoid painting outside a window that is smaller than its contents.\n    ///\n    /// Warning: growing the clip rect might cause unexpected results!\n    /// When in doubt, use [`Self::shrink_clip_rect`] instead.\n    pub fn set_clip_rect(&mut self, clip_rect: Rect) {\n        self.painter.set_clip_rect(clip_rect);\n    }\n\n    /// Can be used for culling: if `false`, then no part of `rect` will be visible on screen.\n    ///\n    /// This is false if the whole `Ui` is invisible (see [`UiBuilder::invisible`])\n    /// or if [`Context::will_discard`] is true.\n    pub fn is_rect_visible(&self, rect: Rect) -> bool {\n        self.is_visible() && rect.intersects(self.clip_rect())\n    }\n}\n\n// ------------------------------------------------------------------------\n\n/// # Sizes etc\nimpl Ui {\n    /// Where and how large the [`Ui`] is already.\n    /// All widgets that have been added to this [`Ui`] fits within this rectangle.\n    ///\n    /// No matter what, the final Ui will be at least this large.\n    ///\n    /// This will grow as new widgets are added, but never shrink.\n    pub fn min_rect(&self) -> Rect {\n        self.placer.min_rect()\n    }\n\n    /// Size of content; same as `min_rect().size()`\n    pub fn min_size(&self) -> Vec2 {\n        self.min_rect().size()\n    }\n\n    /// New widgets will *try* to fit within this rectangle.\n    ///\n    /// Text labels will wrap to fit within `max_rect`.\n    /// Separator lines will span the `max_rect`.\n    ///\n    /// If a new widget doesn't fit within the `max_rect` then the\n    /// [`Ui`] will make room for it by expanding both `min_rect` and `max_rect`.\n    pub fn max_rect(&self) -> Rect {\n        self.placer.max_rect()\n    }\n\n    /// Used for animation, kind of hacky\n    pub(crate) fn force_set_min_rect(&mut self, min_rect: Rect) {\n        self.placer.force_set_min_rect(min_rect);\n    }\n\n    // ------------------------------------------------------------------------\n\n    /// Set the maximum size of the ui.\n    /// You won't be able to shrink it below the current minimum size.\n    pub fn set_max_size(&mut self, size: Vec2) {\n        self.set_max_width(size.x);\n        self.set_max_height(size.y);\n    }\n\n    /// Set the maximum width of the ui.\n    /// You won't be able to shrink it below the current minimum size.\n    pub fn set_max_width(&mut self, width: f32) {\n        self.placer.set_max_width(width);\n    }\n\n    /// Set the maximum height of the ui.\n    /// You won't be able to shrink it below the current minimum size.\n    pub fn set_max_height(&mut self, height: f32) {\n        self.placer.set_max_height(height);\n    }\n\n    // ------------------------------------------------------------------------\n\n    /// Set the minimum size of the ui.\n    /// This can't shrink the ui, only make it larger.\n    pub fn set_min_size(&mut self, size: Vec2) {\n        self.set_min_width(size.x);\n        self.set_min_height(size.y);\n    }\n\n    /// Set the minimum width of the ui.\n    /// This can't shrink the ui, only make it larger.\n    pub fn set_min_width(&mut self, width: f32) {\n        debug_assert!(\n            0.0 <= width,\n            \"Negative width makes no sense, but got: {width}\"\n        );\n        self.placer.set_min_width(width);\n    }\n\n    /// Set the minimum height of the ui.\n    /// This can't shrink the ui, only make it larger.\n    pub fn set_min_height(&mut self, height: f32) {\n        debug_assert!(\n            0.0 <= height,\n            \"Negative height makes no sense, but got: {height}\"\n        );\n        self.placer.set_min_height(height);\n    }\n\n    /// Makes the ui always fill up the available space.\n    ///\n    /// This can be useful to call inside a panel with `resizable == true`\n    /// to make sure the resized space is used.\n    pub fn take_available_space(&mut self) {\n        self.set_min_size(self.available_size());\n    }\n\n    /// Makes the ui always fill up the available space in the x axis.\n    ///\n    /// This can be useful to call inside a side panel with\n    /// `resizable == true` to make sure the resized space is used.\n    pub fn take_available_width(&mut self) {\n        self.set_min_width(self.available_width());\n    }\n\n    /// Makes the ui always fill up the available space in the y axis.\n    ///\n    /// This can be useful to call inside a top bottom panel with\n    /// `resizable == true` to make sure the resized space is used.\n    pub fn take_available_height(&mut self) {\n        self.set_min_height(self.available_height());\n    }\n\n    // ------------------------------------------------------------------------\n\n    /// Helper: shrinks the max width to the current width,\n    /// so further widgets will try not to be wider than previous widgets.\n    /// Useful for normal vertical layouts.\n    pub fn shrink_width_to_current(&mut self) {\n        self.set_max_width(self.min_rect().width());\n    }\n\n    /// Helper: shrinks the max height to the current height,\n    /// so further widgets will try not to be taller than previous widgets.\n    pub fn shrink_height_to_current(&mut self) {\n        self.set_max_height(self.min_rect().height());\n    }\n\n    /// Expand the `min_rect` and `max_rect` of this ui to include a child at the given rect.\n    pub fn expand_to_include_rect(&mut self, rect: Rect) {\n        self.placer.expand_to_include_rect(rect);\n    }\n\n    /// `ui.set_width_range(min..=max);` is equivalent to `ui.set_min_width(min); ui.set_max_width(max);`.\n    pub fn set_width_range(&mut self, width: impl Into<Rangef>) {\n        let width = width.into();\n        self.set_min_width(width.min);\n        self.set_max_width(width.max);\n    }\n\n    /// `ui.set_height_range(min..=max);` is equivalent to `ui.set_min_height(min); ui.set_max_height(max);`.\n    pub fn set_height_range(&mut self, height: impl Into<Rangef>) {\n        let height = height.into();\n        self.set_min_height(height.min);\n        self.set_max_height(height.max);\n    }\n\n    /// Set both the minimum and maximum width.\n    pub fn set_width(&mut self, width: f32) {\n        self.set_min_width(width);\n        self.set_max_width(width);\n    }\n\n    /// Set both the minimum and maximum height.\n    pub fn set_height(&mut self, height: f32) {\n        self.set_min_height(height);\n        self.set_max_height(height);\n    }\n\n    /// Ensure we are big enough to contain the given x-coordinate.\n    /// This is sometimes useful to expand a ui to stretch to a certain place.\n    pub fn expand_to_include_x(&mut self, x: f32) {\n        self.placer.expand_to_include_x(x);\n    }\n\n    /// Ensure we are big enough to contain the given y-coordinate.\n    /// This is sometimes useful to expand a ui to stretch to a certain place.\n    pub fn expand_to_include_y(&mut self, y: f32) {\n        self.placer.expand_to_include_y(y);\n    }\n\n    // ------------------------------------------------------------------------\n    // Layout related measures:\n\n    /// The available space at the moment, given the current cursor.\n    ///\n    /// This how much more space we can take up without overflowing our parent.\n    /// Shrinks as widgets allocate space and the cursor moves.\n    /// A small size should be interpreted as \"as little as possible\".\n    /// An infinite size should be interpreted as \"as much as you want\".\n    pub fn available_size(&self) -> Vec2 {\n        self.placer.available_size()\n    }\n\n    /// The available width at the moment, given the current cursor.\n    ///\n    /// See [`Self::available_size`] for more information.\n    pub fn available_width(&self) -> f32 {\n        self.available_size().x\n    }\n\n    /// The available height at the moment, given the current cursor.\n    ///\n    /// See [`Self::available_size`] for more information.\n    pub fn available_height(&self) -> f32 {\n        self.available_size().y\n    }\n\n    /// In case of a wrapping layout, how much space is left on this row/column?\n    ///\n    /// If the layout does not wrap, this will return the same value as [`Self::available_size`].\n    pub fn available_size_before_wrap(&self) -> Vec2 {\n        self.placer.available_rect_before_wrap().size()\n    }\n\n    /// In case of a wrapping layout, how much space is left on this row/column?\n    ///\n    /// If the layout does not wrap, this will return the same value as [`Self::available_size`].\n    pub fn available_rect_before_wrap(&self) -> Rect {\n        self.placer.available_rect_before_wrap()\n    }\n}\n\n/// # [`Id`] creation\nimpl Ui {\n    /// Use this to generate widget ids for widgets that have persistent state in [`Memory`].\n    pub fn make_persistent_id<IdSource>(&self, id_salt: IdSource) -> Id\n    where\n        IdSource: Hash,\n    {\n        self.id.with(&id_salt)\n    }\n\n    /// This is the `Id` that will be assigned to the next widget added to this `Ui`.\n    pub fn next_auto_id(&self) -> Id {\n        Id::new(self.next_auto_id_salt)\n    }\n\n    /// Same as `ui.next_auto_id().with(id_salt)`\n    pub fn auto_id_with<IdSource>(&self, id_salt: IdSource) -> Id\n    where\n        IdSource: Hash,\n    {\n        Id::new(self.next_auto_id_salt).with(id_salt)\n    }\n\n    /// Pretend like `count` widgets have been allocated.\n    pub fn skip_ahead_auto_ids(&mut self, count: usize) {\n        self.next_auto_id_salt = self.next_auto_id_salt.wrapping_add(count as u64);\n    }\n}\n\n/// # Interaction\nimpl Ui {\n    /// Check for clicks, drags and/or hover on a specific region of this [`Ui`].\n    pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {\n        self.interact_opt(rect, id, sense, Default::default())\n    }\n\n    /// Check for clicks, drags and/or hover on a specific region of this [`Ui`].\n    pub fn interact_opt(\n        &self,\n        rect: Rect,\n        id: Id,\n        sense: Sense,\n        options: crate::InteractOptions,\n    ) -> Response {\n        self.ctx().register_accesskit_parent(id, self.unique_id);\n\n        self.ctx().create_widget(\n            WidgetRect {\n                id,\n                layer_id: self.layer_id(),\n                rect,\n                interact_rect: self.clip_rect().intersect(rect),\n                sense,\n                enabled: self.enabled,\n            },\n            true,\n            options,\n        )\n    }\n\n    /// Deprecated: use [`Self::interact`] instead.\n    #[deprecated = \"The contains_pointer argument is ignored. Use `ui.interact` instead.\"]\n    pub fn interact_with_hovered(\n        &self,\n        rect: Rect,\n        _contains_pointer: bool,\n        id: Id,\n        sense: Sense,\n    ) -> Response {\n        self.interact(rect, id, sense)\n    }\n\n    /// Read the [`Ui`]'s background [`Response`].\n    /// Its [`Sense`] will be based on the [`UiBuilder::sense`] used to create this [`Ui`].\n    ///\n    /// The rectangle of the [`Response`] (and interactive area) will be [`Self::min_rect`]\n    /// of the last pass.\n    ///\n    /// The very first time when the [`Ui`] is created, this will return a [`Response`] with a\n    /// [`Rect`] of [`Rect::NOTHING`].\n    pub fn response(&self) -> Response {\n        // This is the inverse of Context::read_response. We prefer a response\n        // based on last frame's widget rect since the one from this frame is Rect::NOTHING until\n        // Ui::interact_bg is called or the Ui is dropped.\n        let mut response = self\n            .ctx()\n            .viewport(|viewport| {\n                viewport\n                    .prev_pass\n                    .widgets\n                    .get(self.unique_id)\n                    .or_else(|| viewport.this_pass.widgets.get(self.unique_id))\n                    .copied()\n            })\n            .map(|widget_rect| self.ctx().get_response(widget_rect))\n            .expect(\n                \"Since we always call Context::create_widget in Ui::new, this should never be None\",\n            );\n        if self.should_close() {\n            response.set_close();\n        }\n        response\n    }\n\n    /// Update the [`WidgetRect`] created in [`Ui::new`] or [`Ui::new_child`] with the current\n    /// [`Ui::min_rect`].\n    fn remember_min_rect(&mut self) -> Response {\n        self.min_rect_already_remembered = true;\n        // We remove the id from used_ids to prevent a duplicate id warning from showing\n        // when the ui was created with `UiBuilder::sense`.\n        // This is a bit hacky, is there a better way?\n        self.ctx().pass_state_mut(|fs| {\n            fs.used_ids.remove(&self.unique_id);\n        });\n        // This will update the WidgetRect that was first created in `Ui::new`.\n        let mut response = self.ctx().create_widget(\n            WidgetRect {\n                id: self.unique_id,\n                layer_id: self.layer_id(),\n                rect: self.min_rect(),\n                interact_rect: self.clip_rect().intersect(self.min_rect()),\n                sense: self.sense,\n                enabled: self.enabled,\n            },\n            false,\n            Default::default(),\n        );\n        if self.should_close() {\n            response.set_close();\n        }\n        response\n    }\n\n    /// Interact with the background of this [`Ui`],\n    /// i.e. behind all the widgets.\n    ///\n    /// The rectangle of the [`Response`] (and interactive area) will be [`Self::min_rect`].\n    #[deprecated = \"Use UiBuilder::sense with Ui::response instead\"]\n    pub fn interact_bg(&self, sense: Sense) -> Response {\n        // This will update the WidgetRect that was first created in `Ui::new`.\n        self.interact(self.min_rect(), self.unique_id, sense)\n    }\n\n    /// Is the pointer (mouse/touch) above this rectangle in this [`Ui`]?\n    ///\n    /// The `clip_rect` and layer of this [`Ui`] will be respected, so, for instance,\n    /// if this [`Ui`] is behind some other window, this will always return `false`.\n    ///\n    /// However, this will NOT check if any other _widget_ in the same layer is covering this widget. For that, use [`Response::contains_pointer`] instead.\n    pub fn rect_contains_pointer(&self, rect: Rect) -> bool {\n        self.ctx()\n            .rect_contains_pointer(self.layer_id(), self.clip_rect().intersect(rect))\n    }\n\n    /// Is the pointer (mouse/touch) above the current [`Ui`]?\n    ///\n    /// Equivalent to `ui.rect_contains_pointer(ui.min_rect())`\n    ///\n    /// Note that this tests against the _current_ [`Ui::min_rect`].\n    /// If you want to test against the final `min_rect`,\n    /// use [`Self::response`] instead.\n    pub fn ui_contains_pointer(&self) -> bool {\n        self.rect_contains_pointer(self.min_rect())\n    }\n\n    /// Find and close the first closable parent.\n    ///\n    /// Use [`UiBuilder::closable`] to make a [`Ui`] closable.\n    /// You can then use [`Ui::should_close`] to check if it should be closed.\n    ///\n    /// This is implemented for all egui containers, e.g. [`crate::Popup`], [`crate::Modal`],\n    /// [`crate::Area`], [`crate::Window`], [`crate::CollapsingHeader`], etc.\n    ///\n    /// What exactly happens when you close a container depends on the container implementation.\n    /// [`crate::Area`] e.g. will return true from its [`Response::should_close`] method.\n    ///\n    /// If you want to close a specific kind of container, use [`Ui::close_kind`] instead.\n    ///\n    /// Also note that this won't bubble up across [`crate::Area`]s. If needed, you can check\n    /// `response.should_close()` and close the parent manually. ([`menu`] does this for example).\n    ///\n    /// See also:\n    /// - [`Ui::close_kind`]\n    /// - [`Ui::should_close`]\n    /// - [`Ui::will_parent_close`]\n    pub fn close(&self) {\n        let tag = self.stack.iter().find_map(|stack| {\n            stack\n                .info\n                .tags\n                .get_downcast::<ClosableTag>(ClosableTag::NAME)\n        });\n        if let Some(tag) = tag {\n            tag.set_close();\n        } else {\n            log::warn!(\"Called ui.close() on a Ui that has no closable parent.\");\n        }\n    }\n\n    /// Find and close the first closable parent of a specific [`UiKind`].\n    ///\n    /// This is useful if you want to e.g. close a [`crate::Window`]. Since it contains a\n    /// `Collapsible`, [`Ui::close`] would close the `Collapsible` instead.\n    /// You can close the [`crate::Window`] by calling `ui.close_kind(UiKind::Window)`.\n    ///\n    /// See also:\n    /// - [`Ui::close`]\n    /// - [`Ui::should_close`]\n    /// - [`Ui::will_parent_close`]\n    pub fn close_kind(&self, ui_kind: UiKind) {\n        let tag = self\n            .stack\n            .iter()\n            .filter(|stack| stack.info.kind == Some(ui_kind))\n            .find_map(|stack| {\n                stack\n                    .info\n                    .tags\n                    .get_downcast::<ClosableTag>(ClosableTag::NAME)\n            });\n        if let Some(tag) = tag {\n            tag.set_close();\n        } else {\n            log::warn!(\"Called ui.close_kind({ui_kind:?}) on ui with no such closable parent.\");\n        }\n    }\n\n    /// Was [`Ui::close`] called on this [`Ui`] or any of its children?\n    /// Only works if the [`Ui`] was created with [`UiBuilder::closable`].\n    ///\n    /// You can also check via this [`Ui`]'s [`Response::should_close`].\n    ///\n    /// See also:\n    /// - [`Ui::will_parent_close`]\n    /// - [`Ui::close`]\n    /// - [`Ui::close_kind`]\n    /// - [`Response::should_close`]\n    pub fn should_close(&self) -> bool {\n        self.stack\n            .info\n            .tags\n            .get_downcast(ClosableTag::NAME)\n            .is_some_and(|tag: &ClosableTag| tag.should_close())\n    }\n\n    /// Will this [`Ui`] or any of its parents close this frame?\n    ///\n    /// See also\n    /// - [`Ui::should_close`]\n    /// - [`Ui::close`]\n    /// - [`Ui::close_kind`]\n    pub fn will_parent_close(&self) -> bool {\n        self.stack.iter().any(|stack| {\n            stack\n                .info\n                .tags\n                .get_downcast::<ClosableTag>(ClosableTag::NAME)\n                .is_some_and(|tag| tag.should_close())\n        })\n    }\n}\n\n/// # Allocating space: where do I put my widgets?\nimpl Ui {\n    /// Allocate space for a widget and check for interaction in the space.\n    /// Returns a [`Response`] which contains a rectangle, id, and interaction info.\n    ///\n    /// ## How sizes are negotiated\n    /// Each widget should have a *minimum desired size* and a *desired size*.\n    /// When asking for space, ask AT LEAST for your minimum, and don't ask for more than you need.\n    /// If you want to fill the space, ask about [`Ui::available_size`] and use that.\n    ///\n    /// You may get MORE space than you asked for, for instance\n    /// for justified layouts, like in menus.\n    ///\n    /// You will never get a rectangle that is smaller than the amount of space you asked for.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let response = ui.allocate_response(egui::vec2(100.0, 200.0), egui::Sense::click());\n    /// if response.clicked() { /* … */ }\n    /// ui.painter().rect_stroke(response.rect, 0.0, (1.0, egui::Color32::WHITE), egui::StrokeKind::Inside);\n    /// # });\n    /// ```\n    pub fn allocate_response(&mut self, desired_size: Vec2, sense: Sense) -> Response {\n        let (id, rect) = self.allocate_space(desired_size);\n        let mut response = self.interact(rect, id, sense);\n        response.intrinsic_size = Some(desired_size);\n        response\n    }\n\n    /// Returns a [`Rect`] with exactly what you asked for.\n    ///\n    /// The response rect will be larger if this is part of a justified layout or similar.\n    /// This means that if this is a narrow widget in a wide justified layout, then\n    /// the widget will react to interactions outside the returned [`Rect`].\n    pub fn allocate_exact_size(&mut self, desired_size: Vec2, sense: Sense) -> (Rect, Response) {\n        let response = self.allocate_response(desired_size, sense);\n        let rect = self\n            .placer\n            .align_size_within_rect(desired_size, response.rect);\n        (rect, response)\n    }\n\n    /// Allocate at least as much space as needed, and interact with that rect.\n    ///\n    /// The returned [`Rect`] will be the same size as `Response::rect`.\n    pub fn allocate_at_least(&mut self, desired_size: Vec2, sense: Sense) -> (Rect, Response) {\n        let response = self.allocate_response(desired_size, sense);\n        (response.rect, response)\n    }\n\n    /// Reserve this much space and move the cursor.\n    /// Returns where to put the widget.\n    ///\n    /// ## How sizes are negotiated\n    /// Each widget should have a *minimum desired size* and a *desired size*.\n    /// When asking for space, ask AT LEAST for your minimum, and don't ask for more than you need.\n    /// If you want to fill the space, ask about [`Ui::available_size`] and use that.\n    ///\n    /// You may get MORE space than you asked for, for instance\n    /// for justified layouts, like in menus.\n    ///\n    /// You will never get a rectangle that is smaller than the amount of space you asked for.\n    ///\n    /// Returns an automatic [`Id`] (which you can use for interaction) and the [`Rect`] of where to put your widget.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let (id, rect) = ui.allocate_space(egui::vec2(100.0, 200.0));\n    /// let response = ui.interact(rect, id, egui::Sense::click());\n    /// # });\n    /// ```\n    pub fn allocate_space(&mut self, desired_size: Vec2) -> (Id, Rect) {\n        #[cfg(debug_assertions)]\n        let original_available = self.available_size_before_wrap();\n\n        let rect = self.allocate_space_impl(desired_size);\n\n        #[cfg(debug_assertions)]\n        {\n            let too_wide = desired_size.x > original_available.x;\n            let too_high = desired_size.y > original_available.y;\n\n            let debug_expand_width = self.style().debug.show_expand_width;\n            let debug_expand_height = self.style().debug.show_expand_height;\n\n            if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {\n                self.painter.rect_stroke(\n                    rect,\n                    0.0,\n                    (1.0, Color32::LIGHT_BLUE),\n                    crate::StrokeKind::Inside,\n                );\n\n                let stroke = crate::Stroke::new(2.5, Color32::from_rgb(200, 0, 0));\n                let paint_line_seg = |a, b| self.painter().line_segment([a, b], stroke);\n\n                if debug_expand_width && too_wide {\n                    paint_line_seg(rect.left_top(), rect.left_bottom());\n                    paint_line_seg(rect.left_center(), rect.right_center());\n                    paint_line_seg(\n                        pos2(rect.left() + original_available.x, rect.top()),\n                        pos2(rect.left() + original_available.x, rect.bottom()),\n                    );\n                    paint_line_seg(rect.right_top(), rect.right_bottom());\n                }\n\n                if debug_expand_height && too_high {\n                    paint_line_seg(rect.left_top(), rect.right_top());\n                    paint_line_seg(rect.center_top(), rect.center_bottom());\n                    paint_line_seg(rect.left_bottom(), rect.right_bottom());\n                }\n            }\n        }\n\n        let id = Id::new(self.next_auto_id_salt);\n        self.next_auto_id_salt = self.next_auto_id_salt.wrapping_add(1);\n\n        (id, rect)\n    }\n\n    /// Reserve this much space and move the cursor.\n    /// Returns where to put the widget.\n    fn allocate_space_impl(&mut self, desired_size: Vec2) -> Rect {\n        let item_spacing = self.spacing().item_spacing;\n        let frame_rect = self.placer.next_space(desired_size, item_spacing);\n        debug_assert!(!frame_rect.any_nan(), \"frame_rect is nan in allocate_space\");\n        let widget_rect = self.placer.justify_and_align(frame_rect, desired_size);\n\n        self.placer\n            .advance_after_rects(frame_rect, widget_rect, item_spacing);\n\n        register_rect(self, widget_rect);\n\n        widget_rect\n    }\n\n    /// Allocate a specific part of the [`Ui`].\n    ///\n    /// Ignore the layout of the [`Ui`]: just put my widget here!\n    /// The layout cursor will advance to past this `rect`.\n    pub fn allocate_rect(&mut self, rect: Rect, sense: Sense) -> Response {\n        let rect = rect.round_ui();\n        let id = self.advance_cursor_after_rect(rect);\n        self.interact(rect, id, sense)\n    }\n\n    /// Allocate a rect without interacting with it.\n    pub fn advance_cursor_after_rect(&mut self, rect: Rect) -> Id {\n        debug_assert!(!rect.any_nan(), \"rect is nan in advance_cursor_after_rect\");\n        let rect = rect.round_ui();\n\n        let item_spacing = self.spacing().item_spacing;\n        self.placer.advance_after_rects(rect, rect, item_spacing);\n        register_rect(self, rect);\n\n        let id = Id::new(self.next_auto_id_salt);\n        self.next_auto_id_salt = self.next_auto_id_salt.wrapping_add(1);\n        id\n    }\n\n    pub(crate) fn placer(&self) -> &Placer {\n        &self.placer\n    }\n\n    /// Where the next widget will be put.\n    ///\n    /// One side of this will always be infinite: the direction in which new widgets will be added.\n    /// The opposing side is what is incremented.\n    /// The crossing sides are initialized to `max_rect`.\n    ///\n    /// So one can think of `cursor` as a constraint on the available region.\n    ///\n    /// If something has already been added, this will point to `style.spacing.item_spacing` beyond the latest child.\n    /// The cursor can thus be `style.spacing.item_spacing` pixels outside of the `min_rect`.\n    pub fn cursor(&self) -> Rect {\n        self.placer.cursor()\n    }\n\n    pub(crate) fn set_cursor(&mut self, cursor: Rect) {\n        self.placer.set_cursor(cursor);\n    }\n\n    /// Where do we expect a zero-sized widget to be placed?\n    pub fn next_widget_position(&self) -> Pos2 {\n        self.placer.next_widget_position()\n    }\n\n    /// Allocated the given space and then adds content to that space.\n    /// If the contents overflow, more space will be allocated.\n    /// When finished, the amount of space actually used (`min_rect`) will be allocated.\n    /// So you can request a lot of space and then use less.\n    #[inline]\n    pub fn allocate_ui<R>(\n        &mut self,\n        desired_size: Vec2,\n        add_contents: impl FnOnce(&mut Self) -> R,\n    ) -> InnerResponse<R> {\n        self.allocate_ui_with_layout(desired_size, *self.layout(), add_contents)\n    }\n\n    /// Allocated the given space and then adds content to that space.\n    /// If the contents overflow, more space will be allocated.\n    /// When finished, the amount of space actually used (`min_rect`) will be allocated.\n    /// So you can request a lot of space and then use less.\n    #[inline]\n    pub fn allocate_ui_with_layout<R>(\n        &mut self,\n        desired_size: Vec2,\n        layout: Layout,\n        add_contents: impl FnOnce(&mut Self) -> R,\n    ) -> InnerResponse<R> {\n        self.allocate_ui_with_layout_dyn(desired_size, layout, Box::new(add_contents))\n    }\n\n    fn allocate_ui_with_layout_dyn<'c, R>(\n        &mut self,\n        desired_size: Vec2,\n        layout: Layout,\n        add_contents: Box<dyn FnOnce(&mut Self) -> R + 'c>,\n    ) -> InnerResponse<R> {\n        debug_assert!(\n            desired_size.x >= 0.0 && desired_size.y >= 0.0,\n            \"Negative desired size: {desired_size:?}\"\n        );\n        let item_spacing = self.spacing().item_spacing;\n        let frame_rect = self.placer.next_space(desired_size, item_spacing);\n        let child_rect = self.placer.justify_and_align(frame_rect, desired_size);\n        self.scope_dyn(\n            UiBuilder::new().max_rect(child_rect).layout(layout),\n            add_contents,\n        )\n    }\n\n    /// Allocated the given rectangle and then adds content to that rectangle.\n    ///\n    /// If the contents overflow, more space will be allocated.\n    /// When finished, the amount of space actually used (`min_rect`) will be allocated.\n    /// So you can request a lot of space and then use less.\n    #[deprecated = \"Use `allocate_new_ui` instead\"]\n    pub fn allocate_ui_at_rect<R>(\n        &mut self,\n        max_rect: Rect,\n        add_contents: impl FnOnce(&mut Self) -> R,\n    ) -> InnerResponse<R> {\n        self.scope_builder(UiBuilder::new().max_rect(max_rect), add_contents)\n    }\n\n    /// Allocated space (`UiBuilder::max_rect`) and then add content to it.\n    ///\n    /// If the contents overflow, more space will be allocated.\n    /// When finished, the amount of space actually used (`min_rect`) will be allocated in the parent.\n    /// So you can request a lot of space and then use less.\n    #[deprecated = \"Use `scope_builder` instead\"]\n    pub fn allocate_new_ui<R>(\n        &mut self,\n        ui_builder: UiBuilder,\n        add_contents: impl FnOnce(&mut Self) -> R,\n    ) -> InnerResponse<R> {\n        self.scope_dyn(ui_builder, Box::new(add_contents))\n    }\n\n    /// Convenience function to get a region to paint on.\n    ///\n    /// Note that egui uses screen coordinates for everything.\n    ///\n    /// ```\n    /// # use egui::*;\n    /// # use std::f32::consts::TAU;\n    /// # egui::__run_test_ui(|ui| {\n    /// let size = Vec2::splat(16.0);\n    /// let (response, painter) = ui.allocate_painter(size, Sense::hover());\n    /// let rect = response.rect;\n    /// let c = rect.center();\n    /// let r = rect.width() / 2.0 - 1.0;\n    /// let color = Color32::from_gray(128);\n    /// let stroke = Stroke::new(1.0, color);\n    /// painter.circle_stroke(c, r, stroke);\n    /// painter.line_segment([c - vec2(0.0, r), c + vec2(0.0, r)], stroke);\n    /// painter.line_segment([c, c + r * Vec2::angled(TAU * 1.0 / 8.0)], stroke);\n    /// painter.line_segment([c, c + r * Vec2::angled(TAU * 3.0 / 8.0)], stroke);\n    /// # });\n    /// ```\n    pub fn allocate_painter(&mut self, desired_size: Vec2, sense: Sense) -> (Response, Painter) {\n        let response = self.allocate_response(desired_size, sense);\n        let clip_rect = self.clip_rect().intersect(response.rect); // Make sure we don't paint out of bounds\n        let painter = self.painter().with_clip_rect(clip_rect);\n        (response, painter)\n    }\n}\n\n/// # Scrolling\nimpl Ui {\n    /// Adjust the scroll position of any parent [`crate::ScrollArea`] so that the given [`Rect`] becomes visible.\n    ///\n    /// If `align` is [`Align::TOP`] it means \"put the top of the rect at the top of the scroll area\", etc.\n    /// If `align` is `None`, it'll scroll enough to bring the cursor into view.\n    ///\n    /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to_cursor`]. [`Ui::scroll_with_delta`]..\n    ///\n    /// ```\n    /// # use egui::Align;\n    /// # egui::__run_test_ui(|ui| {\n    /// egui::ScrollArea::vertical().show(ui, |ui| {\n    ///     // …\n    ///     let response = ui.button(\"Center on me.\");\n    ///     if response.clicked() {\n    ///         ui.scroll_to_rect(response.rect, Some(Align::Center));\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    pub fn scroll_to_rect(&self, rect: Rect, align: Option<Align>) {\n        self.scroll_to_rect_animation(rect, align, self.style.scroll_animation);\n    }\n\n    /// Same as [`Self::scroll_to_rect`], but allows you to specify the [`style::ScrollAnimation`].\n    pub fn scroll_to_rect_animation(\n        &self,\n        rect: Rect,\n        align: Option<Align>,\n        animation: style::ScrollAnimation,\n    ) {\n        for d in 0..2 {\n            let range = Rangef::new(rect.min[d], rect.max[d]);\n            self.ctx().pass_state_mut(|state| {\n                state.scroll_target[d] =\n                    Some(pass_state::ScrollTarget::new(range, align, animation));\n            });\n        }\n    }\n\n    /// Adjust the scroll position of any parent [`crate::ScrollArea`] so that the cursor (where the next widget goes) becomes visible.\n    ///\n    /// If `align` is [`Align::TOP`] it means \"put the top of the rect at the top of the scroll area\", etc.\n    /// If `align` is not provided, it'll scroll enough to bring the cursor into view.\n    ///\n    /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to_rect`]. [`Ui::scroll_with_delta`].\n    ///\n    /// ```\n    /// # use egui::Align;\n    /// # egui::__run_test_ui(|ui| {\n    /// egui::ScrollArea::vertical().show(ui, |ui| {\n    ///     let scroll_bottom = ui.button(\"Scroll to bottom.\").clicked();\n    ///     for i in 0..1000 {\n    ///         ui.label(format!(\"Item {}\", i));\n    ///     }\n    ///\n    ///     if scroll_bottom {\n    ///         ui.scroll_to_cursor(Some(Align::BOTTOM));\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    pub fn scroll_to_cursor(&self, align: Option<Align>) {\n        self.scroll_to_cursor_animation(align, self.style.scroll_animation);\n    }\n\n    /// Same as [`Self::scroll_to_cursor`], but allows you to specify the [`style::ScrollAnimation`].\n    pub fn scroll_to_cursor_animation(\n        &self,\n        align: Option<Align>,\n        animation: style::ScrollAnimation,\n    ) {\n        let target = self.next_widget_position();\n        for d in 0..2 {\n            let target = Rangef::point(target[d]);\n            self.ctx().pass_state_mut(|state| {\n                state.scroll_target[d] =\n                    Some(pass_state::ScrollTarget::new(target, align, animation));\n            });\n        }\n    }\n\n    /// Scroll this many points in the given direction, in the parent [`crate::ScrollArea`].\n    ///\n    /// The delta dictates how the _content_ (i.e. this UI) should move.\n    ///\n    /// A positive X-value indicates the content is being moved right,\n    /// as when swiping right on a touch-screen or track-pad with natural scrolling.\n    ///\n    /// A positive Y-value indicates the content is being moved down,\n    /// as when swiping down on a touch-screen or track-pad with natural scrolling.\n    ///\n    /// If this is called multiple times per frame for the same [`crate::ScrollArea`], the deltas will be summed.\n    ///\n    /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to_rect`], [`Ui::scroll_to_cursor`]\n    ///\n    /// ```\n    /// # use egui::{Align, Vec2};\n    /// # egui::__run_test_ui(|ui| {\n    /// let mut scroll_delta = Vec2::ZERO;\n    /// if ui.button(\"Scroll down\").clicked() {\n    ///     scroll_delta.y -= 64.0; // move content up\n    /// }\n    /// egui::ScrollArea::vertical().show(ui, |ui| {\n    ///     ui.scroll_with_delta(scroll_delta);\n    ///     for i in 0..1000 {\n    ///         ui.label(format!(\"Item {}\", i));\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    pub fn scroll_with_delta(&self, delta: Vec2) {\n        self.scroll_with_delta_animation(delta, self.style.scroll_animation);\n    }\n\n    /// Same as [`Self::scroll_with_delta`], but allows you to specify the [`style::ScrollAnimation`].\n    pub fn scroll_with_delta_animation(&self, delta: Vec2, animation: style::ScrollAnimation) {\n        self.ctx().pass_state_mut(|state| {\n            state.scroll_delta.0 += delta;\n            state.scroll_delta.1 = animation;\n        });\n    }\n}\n\n/// # Adding widgets\nimpl Ui {\n    /// Add a [`Widget`] to this [`Ui`] at a location dependent on the current [`Layout`].\n    ///\n    /// The returned [`Response`] can be used to check for interactions,\n    /// as well as adding tooltips using [`Response::on_hover_text`].\n    ///\n    /// See also [`Self::add_sized`], [`Self::place`] and [`Self::put`].\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_value = 42;\n    /// let response = ui.add(egui::Slider::new(&mut my_value, 0..=100));\n    /// response.on_hover_text(\"Drag me!\");\n    /// # });\n    /// ```\n    #[inline]\n    pub fn add(&mut self, widget: impl Widget) -> Response {\n        widget.ui(self)\n    }\n\n    /// Add a [`Widget`] to this [`Ui`] with a given size.\n    /// The widget will attempt to fit within the given size, but some widgets may overflow.\n    ///\n    /// To fill all remaining area, use `ui.add_sized(ui.available_size(), widget);`\n    ///\n    /// See also [`Self::add`], [`Self::place`] and [`Self::put`].\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_value = 42;\n    /// ui.add_sized([40.0, 20.0], egui::DragValue::new(&mut my_value));\n    /// # });\n    /// ```\n    pub fn add_sized(&mut self, max_size: impl Into<Vec2>, widget: impl Widget) -> Response {\n        // TODO(emilk): configure to overflow to main_dir instead of centered overflow\n        // to handle the bug mentioned at https://github.com/emilk/egui/discussions/318#discussioncomment-627578\n        // and fixed in https://github.com/emilk/egui/commit/035166276322b3f2324bd8b97ffcedc63fa8419f\n        //\n        // Make sure we keep the same main direction since it changes e.g. how text is wrapped:\n        let layout = Layout::centered_and_justified(self.layout().main_dir());\n        self.allocate_ui_with_layout(max_size.into(), layout, |ui| ui.add(widget))\n            .inner\n    }\n\n    /// Add a [`Widget`] to this [`Ui`] at a specific location (manual layout) without\n    /// affecting this [`Ui`]s cursor.\n    ///\n    /// See also [`Self::add`] and [`Self::add_sized`] and [`Self::put`].\n    pub fn place(&mut self, max_rect: Rect, widget: impl Widget) -> Response {\n        self.new_child(\n            UiBuilder::new()\n                .max_rect(max_rect)\n                .layout(Layout::centered_and_justified(Direction::TopDown)),\n        )\n        .add(widget)\n    }\n\n    /// Add a [`Widget`] to this [`Ui`] at a specific location (manual layout) and advance the\n    /// cursor after the widget.\n    ///\n    /// See also [`Self::add`], [`Self::add_sized`], and [`Self::place`].\n    pub fn put(&mut self, max_rect: Rect, widget: impl Widget) -> Response {\n        self.scope_builder(\n            UiBuilder::new()\n                .max_rect(max_rect)\n                .layout(Layout::centered_and_justified(Direction::TopDown)),\n            |ui| ui.add(widget),\n        )\n        .inner\n    }\n\n    /// Add a single [`Widget`] that is possibly disabled, i.e. greyed out and non-interactive.\n    ///\n    /// If you call `add_enabled` from within an already disabled [`Ui`],\n    /// the widget will always be disabled, even if the `enabled` argument is true.\n    ///\n    /// See also [`Self::add_enabled_ui`] and [`Self::is_enabled`].\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.add_enabled(false, egui::Button::new(\"Can't click this\"));\n    /// # });\n    /// ```\n    pub fn add_enabled(&mut self, enabled: bool, widget: impl Widget) -> Response {\n        if self.is_enabled() && !enabled {\n            let old_painter = self.painter.clone();\n            self.disable();\n            let response = self.add(widget);\n            self.enabled = true;\n            self.painter = old_painter;\n            response\n        } else {\n            self.add(widget)\n        }\n    }\n\n    /// Add a section that is possibly disabled, i.e. greyed out and non-interactive.\n    ///\n    /// If you call `add_enabled_ui` from within an already disabled [`Ui`],\n    /// the result will always be disabled, even if the `enabled` argument is true.\n    ///\n    /// See also [`Self::add_enabled`] and [`Self::is_enabled`].\n    ///\n    /// ### Example\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut enabled = true;\n    /// ui.checkbox(&mut enabled, \"Enable subsection\");\n    /// ui.add_enabled_ui(enabled, |ui| {\n    ///     if ui.button(\"Button that is not always clickable\").clicked() {\n    ///         /* … */\n    ///     }\n    /// });\n    /// # });\n    /// ```\n    pub fn add_enabled_ui<R>(\n        &mut self,\n        enabled: bool,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.scope(|ui| {\n            if !enabled {\n                ui.disable();\n            }\n            add_contents(ui)\n        })\n    }\n\n    /// Add a single [`Widget`] that is possibly invisible.\n    ///\n    /// An invisible widget still takes up the same space as if it were visible.\n    ///\n    /// If you call `add_visible` from within an already invisible [`Ui`],\n    /// the widget will always be invisible, even if the `visible` argument is true.\n    ///\n    /// See also [`Self::add_visible_ui`], [`Self::set_visible`] and [`Self::is_visible`].\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.add_visible(false, egui::Label::new(\"You won't see me!\"));\n    /// # });\n    /// ```\n    pub fn add_visible(&mut self, visible: bool, widget: impl Widget) -> Response {\n        if self.is_visible() && !visible {\n            // temporary make us invisible:\n            let old_painter = self.painter.clone();\n            let old_enabled = self.enabled;\n\n            self.set_invisible();\n\n            let response = self.add(widget);\n\n            self.painter = old_painter;\n            self.enabled = old_enabled;\n            response\n        } else {\n            self.add(widget)\n        }\n    }\n\n    /// Add a section that is possibly invisible, i.e. greyed out and non-interactive.\n    ///\n    /// An invisible ui still takes up the same space as if it were visible.\n    ///\n    /// If you call `add_visible_ui` from within an already invisible [`Ui`],\n    /// the result will always be invisible, even if the `visible` argument is true.\n    ///\n    /// See also [`Self::add_visible`], [`Self::set_visible`] and [`Self::is_visible`].\n    ///\n    /// ### Example\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut visible = true;\n    /// ui.checkbox(&mut visible, \"Show subsection\");\n    /// ui.add_visible_ui(visible, |ui| {\n    ///     ui.label(\"Maybe you see this, maybe you don't!\");\n    /// });\n    /// # });\n    /// ```\n    #[deprecated = \"Use 'ui.scope_builder' instead\"]\n    pub fn add_visible_ui<R>(\n        &mut self,\n        visible: bool,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        let mut ui_builder = UiBuilder::new();\n        if !visible {\n            ui_builder = ui_builder.invisible();\n        }\n        self.scope_builder(ui_builder, add_contents)\n    }\n\n    /// Add extra space before the next widget.\n    ///\n    /// The direction is dependent on the layout.\n    /// Note that `add_space` isn't supported when in a grid layout.\n    ///\n    /// This will be in addition to the [`crate::style::Spacing::item_spacing`]\n    /// that is always added, but `item_spacing` won't be added _again_ by `add_space`.\n    ///\n    /// [`Self::min_rect`] will expand to contain the space.\n    #[inline]\n    pub fn add_space(&mut self, amount: f32) {\n        debug_assert!(!self.is_grid(), \"add_space makes no sense in a grid layout\");\n        self.placer.advance_cursor(amount.round_ui());\n    }\n\n    /// Show some text.\n    ///\n    /// Shortcut for `add(Label::new(text))`\n    ///\n    /// See also [`Label`].\n    ///\n    /// ### Example\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// use egui::{RichText, FontId, Color32};\n    /// ui.label(\"Normal text\");\n    /// ui.label(RichText::new(\"Large text\").font(FontId::proportional(40.0)));\n    /// ui.label(RichText::new(\"Red text\").color(Color32::RED));\n    /// # });\n    /// ```\n    #[inline]\n    pub fn label(&mut self, text: impl Into<WidgetText>) -> Response {\n        Label::new(text).ui(self)\n    }\n\n    /// Show colored text.\n    ///\n    /// Shortcut for `ui.label(RichText::new(text).color(color))`\n    pub fn colored_label(\n        &mut self,\n        color: impl Into<Color32>,\n        text: impl Into<RichText>,\n    ) -> Response {\n        Label::new(text.into().color(color)).ui(self)\n    }\n\n    /// Show large text.\n    ///\n    /// Shortcut for `ui.label(RichText::new(text).heading())`\n    pub fn heading(&mut self, text: impl Into<RichText>) -> Response {\n        Label::new(text.into().heading()).ui(self)\n    }\n\n    /// Show monospace (fixed width) text.\n    ///\n    /// Shortcut for `ui.label(RichText::new(text).monospace())`\n    pub fn monospace(&mut self, text: impl Into<RichText>) -> Response {\n        Label::new(text.into().monospace()).ui(self)\n    }\n\n    /// Show text as monospace with a gray background.\n    ///\n    /// Shortcut for `ui.label(RichText::new(text).code())`\n    pub fn code(&mut self, text: impl Into<RichText>) -> Response {\n        Label::new(text.into().code()).ui(self)\n    }\n\n    /// Show small text.\n    ///\n    /// Shortcut for `ui.label(RichText::new(text).small())`\n    pub fn small(&mut self, text: impl Into<RichText>) -> Response {\n        Label::new(text.into().small()).ui(self)\n    }\n\n    /// Show text that stand out a bit (e.g. slightly brighter).\n    ///\n    /// Shortcut for `ui.label(RichText::new(text).strong())`\n    pub fn strong(&mut self, text: impl Into<RichText>) -> Response {\n        Label::new(text.into().strong()).ui(self)\n    }\n\n    /// Show text that is weaker (fainter color).\n    ///\n    /// Shortcut for `ui.label(RichText::new(text).weak())`\n    pub fn weak(&mut self, text: impl Into<RichText>) -> Response {\n        Label::new(text.into().weak()).ui(self)\n    }\n\n    /// Looks like a hyperlink.\n    ///\n    /// Shortcut for `add(Link::new(text))`.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// if ui.link(\"Documentation\").clicked() {\n    ///     // …\n    /// }\n    /// # });\n    /// ```\n    ///\n    /// See also [`Link`].\n    #[must_use = \"You should check if the user clicked this with `if ui.link(…).clicked() { … } \"]\n    pub fn link(&mut self, text: impl Into<WidgetText>) -> Response {\n        Link::new(text).ui(self)\n    }\n\n    /// Link to a web page.\n    ///\n    /// Shortcut for `add(Hyperlink::new(url))`.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.hyperlink(\"https://www.egui.rs/\");\n    /// # });\n    /// ```\n    ///\n    /// See also [`Hyperlink`].\n    pub fn hyperlink(&mut self, url: impl ToString) -> Response {\n        Hyperlink::new(url).ui(self)\n    }\n\n    /// Shortcut for `add(Hyperlink::from_label_and_url(label, url))`.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.hyperlink_to(\"egui on GitHub\", \"https://www.github.com/emilk/egui/\");\n    /// # });\n    /// ```\n    ///\n    /// See also [`Hyperlink`].\n    pub fn hyperlink_to(&mut self, label: impl Into<WidgetText>, url: impl ToString) -> Response {\n        Hyperlink::from_label_and_url(label, url).ui(self)\n    }\n\n    /// No newlines (`\\n`) allowed. Pressing enter key will result in the [`TextEdit`] losing focus (`response.lost_focus`).\n    ///\n    /// See also [`TextEdit`].\n    pub fn text_edit_singleline<S: widgets::text_edit::TextBuffer>(\n        &mut self,\n        text: &mut S,\n    ) -> Response {\n        TextEdit::singleline(text).ui(self)\n    }\n\n    /// A [`TextEdit`] for multiple lines. Pressing enter key will create a new line.\n    ///\n    /// See also [`TextEdit`].\n    pub fn text_edit_multiline<S: widgets::text_edit::TextBuffer>(\n        &mut self,\n        text: &mut S,\n    ) -> Response {\n        TextEdit::multiline(text).ui(self)\n    }\n\n    /// A [`TextEdit`] for code editing.\n    ///\n    /// This will be multiline, monospace, and will insert tabs instead of moving focus.\n    ///\n    /// See also [`TextEdit::code_editor`].\n    pub fn code_editor<S: widgets::text_edit::TextBuffer>(&mut self, text: &mut S) -> Response {\n        self.add(TextEdit::multiline(text).code_editor())\n    }\n\n    /// Usage: `if ui.button(\"Click me\").clicked() { … }`\n    ///\n    /// Shortcut for `add(Button::new(text))`\n    ///\n    /// See also [`Button`].\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// if ui.button(\"Click me!\").clicked() {\n    ///     // …\n    /// }\n    ///\n    /// # use egui::{RichText, Color32};\n    /// if ui.button(RichText::new(\"delete\").color(Color32::RED)).clicked() {\n    ///     // …\n    /// }\n    /// # });\n    /// ```\n    #[must_use = \"You should check if the user clicked this with `if ui.button(…).clicked() { … } \"]\n    #[inline]\n    pub fn button<'a>(&mut self, atoms: impl IntoAtoms<'a>) -> Response {\n        Button::new(atoms).ui(self)\n    }\n\n    /// A button as small as normal body text.\n    ///\n    /// Usage: `if ui.small_button(\"Click me\").clicked() { … }`\n    ///\n    /// Shortcut for `add(Button::new(text).small())`\n    #[must_use = \"You should check if the user clicked this with `if ui.small_button(…).clicked() { … } \"]\n    pub fn small_button(&mut self, text: impl Into<WidgetText>) -> Response {\n        Button::new(text).small().ui(self)\n    }\n\n    /// Show a checkbox.\n    ///\n    /// See also [`Self::toggle_value`].\n    #[inline]\n    pub fn checkbox<'a>(&mut self, checked: &'a mut bool, atoms: impl IntoAtoms<'a>) -> Response {\n        Checkbox::new(checked, atoms).ui(self)\n    }\n\n    /// Acts like a checkbox, but looks like a [`Button::selectable`].\n    ///\n    /// Click to toggle to bool.\n    ///\n    /// See also [`Self::checkbox`].\n    pub fn toggle_value<'a>(&mut self, selected: &mut bool, atoms: impl IntoAtoms<'a>) -> Response {\n        let mut response = self.selectable_label(*selected, atoms);\n        if response.clicked() {\n            *selected = !*selected;\n            response.mark_changed();\n        }\n        response\n    }\n\n    /// Show a [`RadioButton`].\n    /// Often you want to use [`Self::radio_value`] instead.\n    #[must_use = \"You should check if the user clicked this with `if ui.radio(…).clicked() { … } \"]\n    #[inline]\n    pub fn radio<'a>(&mut self, selected: bool, atoms: impl IntoAtoms<'a>) -> Response {\n        RadioButton::new(selected, atoms).ui(self)\n    }\n\n    /// Show a [`RadioButton`]. It is selected if `*current_value == selected_value`.\n    /// If clicked, `selected_value` is assigned to `*current_value`.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    ///\n    /// #[derive(PartialEq)]\n    /// enum Enum { First, Second, Third }\n    /// let mut my_enum = Enum::First;\n    ///\n    /// ui.radio_value(&mut my_enum, Enum::First, \"First\");\n    ///\n    /// // is equivalent to:\n    ///\n    /// if ui.add(egui::RadioButton::new(my_enum == Enum::First, \"First\")).clicked() {\n    ///     my_enum = Enum::First\n    /// }\n    /// # });\n    /// ```\n    pub fn radio_value<'a, Value: PartialEq>(\n        &mut self,\n        current_value: &mut Value,\n        alternative: Value,\n        atoms: impl IntoAtoms<'a>,\n    ) -> Response {\n        let mut response = self.radio(*current_value == alternative, atoms);\n        if response.clicked() && *current_value != alternative {\n            *current_value = alternative;\n            response.mark_changed();\n        }\n        response\n    }\n\n    /// Show a label which can be selected or not.\n    ///\n    /// See also [`Button::selectable`] and [`Self::toggle_value`].\n    #[must_use = \"You should check if the user clicked this with `if ui.selectable_label(…).clicked() { … } \"]\n    pub fn selectable_label<'a>(&mut self, checked: bool, text: impl IntoAtoms<'a>) -> Response {\n        Button::selectable(checked, text).ui(self)\n    }\n\n    /// Show selectable text. It is selected if `*current_value == selected_value`.\n    /// If clicked, `selected_value` is assigned to `*current_value`.\n    ///\n    /// Example: `ui.selectable_value(&mut my_enum, Enum::Alternative, \"Alternative\")`.\n    ///\n    /// See also [`Button::selectable`] and [`Self::toggle_value`].\n    pub fn selectable_value<'a, Value: PartialEq>(\n        &mut self,\n        current_value: &mut Value,\n        selected_value: Value,\n        text: impl IntoAtoms<'a>,\n    ) -> Response {\n        let mut response = self.selectable_label(*current_value == selected_value, text);\n        if response.clicked() && *current_value != selected_value {\n            *current_value = selected_value;\n            response.mark_changed();\n        }\n        response\n    }\n\n    /// Shortcut for `add(Separator::default())`\n    ///\n    /// See also [`Separator`].\n    #[inline]\n    pub fn separator(&mut self) -> Response {\n        Separator::default().ui(self)\n    }\n\n    /// Shortcut for `add(Spinner::new())`\n    ///\n    /// See also [`Spinner`].\n    #[inline]\n    pub fn spinner(&mut self) -> Response {\n        Spinner::new().ui(self)\n    }\n\n    /// Modify an angle. The given angle should be in radians, but is shown to the user in degrees.\n    /// The angle is NOT wrapped, so the user may select, for instance 720° = 2𝞃 = 4π\n    pub fn drag_angle(&mut self, radians: &mut f32) -> Response {\n        let mut degrees = radians.to_degrees();\n        let mut response = self.add(DragValue::new(&mut degrees).speed(1.0).suffix(\"°\"));\n\n        // only touch `*radians` if we actually changed the degree value\n        if degrees != radians.to_degrees() {\n            *radians = degrees.to_radians();\n            response.mark_changed();\n        }\n\n        response\n    }\n\n    /// Modify an angle. The given angle should be in radians,\n    /// but is shown to the user in fractions of one Tau (i.e. fractions of one turn).\n    /// The angle is NOT wrapped, so the user may select, for instance 2𝞃 (720°)\n    pub fn drag_angle_tau(&mut self, radians: &mut f32) -> Response {\n        use std::f32::consts::TAU;\n\n        let mut taus = *radians / TAU;\n        let mut response = self.add(DragValue::new(&mut taus).speed(0.01).suffix(\"τ\"));\n\n        if self.style().explanation_tooltips {\n            response =\n                response.on_hover_text(\"1τ = one turn, 0.5τ = half a turn, etc. 0.25τ = 90°\");\n        }\n\n        // only touch `*radians` if we actually changed the value\n        if taus != *radians / TAU {\n            *radians = taus * TAU;\n            response.mark_changed();\n        }\n\n        response\n    }\n\n    /// Show an image available at the given `uri`.\n    ///\n    /// ⚠ This will do nothing unless you install some image loaders first!\n    /// The easiest way to do this is via [`egui_extras::install_image_loaders`](https://docs.rs/egui_extras/latest/egui_extras/loaders/fn.install_image_loaders.html).\n    ///\n    /// The loaders handle caching image data, sampled textures, etc. across frames, so calling this is immediate-mode safe.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.image(\"https://picsum.photos/480\");\n    /// ui.image(\"file://assets/ferris.png\");\n    /// ui.image(egui::include_image!(\"../assets/ferris.png\"));\n    /// ui.add(\n    ///     egui::Image::new(egui::include_image!(\"../assets/ferris.png\"))\n    ///         .max_width(200.0)\n    ///         .corner_radius(10),\n    /// );\n    /// # });\n    /// ```\n    ///\n    /// Using [`crate::include_image`] is often the most ergonomic, and the path\n    /// will be resolved at compile-time and embedded in the binary.\n    /// When using a \"file://\" url on the other hand, you need to make sure\n    /// the files can be found in the right spot at runtime!\n    ///\n    /// See also [`crate::Image`], [`crate::ImageSource`].\n    #[inline]\n    pub fn image<'a>(&mut self, source: impl Into<ImageSource<'a>>) -> Response {\n        Image::new(source).ui(self)\n    }\n}\n\n/// # Colors\nimpl Ui {\n    /// Shows a button with the given color.\n    ///\n    /// If the user clicks the button, a full color picker is shown.\n    pub fn color_edit_button_srgba(&mut self, srgba: &mut Color32) -> Response {\n        color_picker::color_edit_button_srgba(self, srgba, color_picker::Alpha::BlendOrAdditive)\n    }\n\n    /// Shows a button with the given color.\n    ///\n    /// If the user clicks the button, a full color picker is shown.\n    pub fn color_edit_button_hsva(&mut self, hsva: &mut Hsva) -> Response {\n        color_picker::color_edit_button_hsva(self, hsva, color_picker::Alpha::BlendOrAdditive)\n    }\n\n    /// Shows a button with the given color.\n    ///\n    /// If the user clicks the button, a full color picker is shown.\n    /// The given color is in `sRGB` space.\n    pub fn color_edit_button_srgb(&mut self, srgb: &mut [u8; 3]) -> Response {\n        color_picker::color_edit_button_srgb(self, srgb)\n    }\n\n    /// Shows a button with the given color.\n    ///\n    /// If the user clicks the button, a full color picker is shown.\n    /// The given color is in linear RGB space.\n    pub fn color_edit_button_rgb(&mut self, rgb: &mut [f32; 3]) -> Response {\n        color_picker::color_edit_button_rgb(self, rgb)\n    }\n\n    /// Shows a button with the given color.\n    ///\n    /// If the user clicks the button, a full color picker is shown.\n    /// The given color is in `sRGBA` space with premultiplied alpha\n    pub fn color_edit_button_srgba_premultiplied(&mut self, srgba: &mut [u8; 4]) -> Response {\n        let mut color = Color32::from_rgba_premultiplied(srgba[0], srgba[1], srgba[2], srgba[3]);\n        let response = self.color_edit_button_srgba(&mut color);\n        *srgba = color.to_array();\n        response\n    }\n\n    /// Shows a button with the given color.\n    ///\n    /// If the user clicks the button, a full color picker is shown.\n    /// The given color is in `sRGBA` space without premultiplied alpha.\n    /// If unsure what \"premultiplied alpha\" is, then this is probably the function you want to use.\n    pub fn color_edit_button_srgba_unmultiplied(&mut self, srgba: &mut [u8; 4]) -> Response {\n        let mut rgba = Rgba::from_srgba_unmultiplied(srgba[0], srgba[1], srgba[2], srgba[3]);\n        let response =\n            color_picker::color_edit_button_rgba(self, &mut rgba, color_picker::Alpha::OnlyBlend);\n        *srgba = rgba.to_srgba_unmultiplied();\n        response\n    }\n\n    /// Shows a button with the given color.\n    ///\n    /// If the user clicks the button, a full color picker is shown.\n    /// The given color is in linear RGBA space with premultiplied alpha\n    pub fn color_edit_button_rgba_premultiplied(&mut self, rgba_premul: &mut [f32; 4]) -> Response {\n        let mut rgba = Rgba::from_rgba_premultiplied(\n            rgba_premul[0],\n            rgba_premul[1],\n            rgba_premul[2],\n            rgba_premul[3],\n        );\n        let response = color_picker::color_edit_button_rgba(\n            self,\n            &mut rgba,\n            color_picker::Alpha::BlendOrAdditive,\n        );\n        *rgba_premul = rgba.to_array();\n        response\n    }\n\n    /// Shows a button with the given color.\n    ///\n    /// If the user clicks the button, a full color picker is shown.\n    /// The given color is in linear RGBA space without premultiplied alpha.\n    /// If unsure, what \"premultiplied alpha\" is, then this is probably the function you want to use.\n    pub fn color_edit_button_rgba_unmultiplied(&mut self, rgba_unmul: &mut [f32; 4]) -> Response {\n        let mut rgba = Rgba::from_rgba_unmultiplied(\n            rgba_unmul[0],\n            rgba_unmul[1],\n            rgba_unmul[2],\n            rgba_unmul[3],\n        );\n        let response =\n            color_picker::color_edit_button_rgba(self, &mut rgba, color_picker::Alpha::OnlyBlend);\n        *rgba_unmul = rgba.to_rgba_unmultiplied();\n        response\n    }\n}\n\n/// # Adding Containers / Sub-uis:\nimpl Ui {\n    /// Put into a [`Frame::group`], visually grouping the contents together\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.group(|ui| {\n    ///     ui.label(\"Within a frame\");\n    /// });\n    /// # });\n    /// ```\n    ///\n    /// See also [`Self::scope`].\n    pub fn group<R>(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {\n        crate::Frame::group(self.style()).show(self, add_contents)\n    }\n\n    /// Create a child Ui with an explicit [`Id`].\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// for i in 0..10 {\n    ///     // ui.collapsing(\"Same header\", |ui| { }); // this will cause an ID clash because of the same title!\n    ///\n    ///     ui.push_id(i, |ui| {\n    ///         ui.collapsing(\"Same header\", |ui| { }); // this is fine!\n    ///     });\n    /// }\n    /// # });\n    /// ```\n    pub fn push_id<R>(\n        &mut self,\n        id_salt: impl Hash,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.scope_dyn(UiBuilder::new().id_salt(id_salt), Box::new(add_contents))\n    }\n\n    /// Push another level onto the [`UiStack`].\n    ///\n    /// You can use this, for instance, to tag a group of widgets.\n    #[deprecated = \"Use 'ui.scope_builder' instead\"]\n    pub fn push_stack_info<R>(\n        &mut self,\n        ui_stack_info: UiStackInfo,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.scope_dyn(\n            UiBuilder::new().ui_stack_info(ui_stack_info),\n            Box::new(add_contents),\n        )\n    }\n\n    /// Create a scoped child ui.\n    ///\n    /// You can use this to temporarily change the [`Style`] of a sub-region, for instance:\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.scope(|ui| {\n    ///     ui.spacing_mut().slider_width = 200.0; // Temporary change\n    ///     // …\n    /// });\n    /// # });\n    /// ```\n    pub fn scope<R>(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {\n        self.scope_dyn(UiBuilder::new(), Box::new(add_contents))\n    }\n\n    /// Create a child, add content to it, and then allocate only what was used in the parent `Ui`.\n    pub fn scope_builder<R>(\n        &mut self,\n        ui_builder: UiBuilder,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.scope_dyn(ui_builder, Box::new(add_contents))\n    }\n\n    /// Create a child, add content to it, and then allocate only what was used in the parent `Ui`.\n    pub fn scope_dyn<'c, R>(\n        &mut self,\n        ui_builder: UiBuilder,\n        add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> InnerResponse<R> {\n        let next_auto_id_salt = self.next_auto_id_salt;\n        let mut child_ui = self.new_child(ui_builder);\n        self.next_auto_id_salt = next_auto_id_salt; // HACK: we want `scope` to only increment this once, so that `ui.scope` is equivalent to `ui.allocate_space`.\n        let ret = add_contents(&mut child_ui);\n        let response = child_ui.remember_min_rect();\n        self.advance_cursor_after_rect(child_ui.min_rect());\n        InnerResponse::new(ret, response)\n    }\n\n    /// Redirect shapes to another paint layer.\n    ///\n    /// ```\n    /// # use egui::{LayerId, Order, Id};\n    /// # egui::__run_test_ui(|ui| {\n    /// let layer_id = LayerId::new(Order::Tooltip, Id::new(\"my_floating_ui\"));\n    /// ui.with_layer_id(layer_id, |ui| {\n    ///     ui.label(\"This is now in a different layer\");\n    /// });\n    /// # });\n    /// ```\n    #[deprecated = \"Use ui.scope_builder(UiBuilder::new().layer_id(…), …) instead\"]\n    pub fn with_layer_id<R>(\n        &mut self,\n        layer_id: LayerId,\n        add_contents: impl FnOnce(&mut Self) -> R,\n    ) -> InnerResponse<R> {\n        self.scope_builder(UiBuilder::new().layer_id(layer_id), add_contents)\n    }\n\n    /// A [`CollapsingHeader`] that starts out collapsed.\n    ///\n    /// The name must be unique within the current parent,\n    /// or you need to use [`CollapsingHeader::id_salt`].\n    pub fn collapsing<R>(\n        &mut self,\n        heading: impl Into<WidgetText>,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> CollapsingResponse<R> {\n        CollapsingHeader::new(heading).show(self, add_contents)\n    }\n\n    /// Create a child ui which is indented to the right.\n    ///\n    /// The `id_salt` here be anything at all.\n    // TODO(emilk): remove `id_salt` argument?\n    #[inline]\n    pub fn indent<R>(\n        &mut self,\n        id_salt: impl Hash,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.indent_dyn(id_salt, Box::new(add_contents))\n    }\n\n    fn indent_dyn<'c, R>(\n        &mut self,\n        id_salt: impl Hash,\n        add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> InnerResponse<R> {\n        assert!(\n            self.layout().is_vertical(),\n            \"You can only indent vertical layouts, found {:?}\",\n            self.layout()\n        );\n\n        let indent = self.spacing().indent;\n        let mut child_rect = self.placer.available_rect_before_wrap();\n        child_rect.min.x += indent;\n\n        let mut child_ui = self.new_child(UiBuilder::new().id_salt(id_salt).max_rect(child_rect));\n        let ret = add_contents(&mut child_ui);\n\n        let left_vline = self.visuals().indent_has_left_vline;\n        let end_with_horizontal_line = self.spacing().indent_ends_with_horizontal_line;\n\n        if left_vline || end_with_horizontal_line {\n            if end_with_horizontal_line {\n                child_ui.add_space(4.0);\n            }\n\n            let stroke = self.visuals().widgets.noninteractive.bg_stroke;\n            let left_top = child_rect.min - 0.5 * indent * Vec2::X;\n            let left_bottom = pos2(left_top.x, child_ui.min_rect().bottom() - 2.0);\n\n            if left_vline {\n                // draw a faint line on the left to mark the indented section\n                self.painter.line_segment([left_top, left_bottom], stroke);\n            }\n\n            if end_with_horizontal_line {\n                let fudge = 2.0; // looks nicer with button rounding in collapsing headers\n                let right_bottom = pos2(child_ui.min_rect().right() - fudge, left_bottom.y);\n                self.painter\n                    .line_segment([left_bottom, right_bottom], stroke);\n            }\n        }\n\n        let response = self.allocate_rect(child_ui.min_rect(), Sense::hover());\n        InnerResponse::new(ret, response)\n    }\n\n    /// Start a ui with horizontal layout.\n    /// After you have called this, the function registers the contents as any other widget.\n    ///\n    /// Elements will be centered on the Y axis, i.e.\n    /// adjusted up and down to lie in the center of the horizontal layout.\n    /// The initial height is `style.spacing.interact_size.y`.\n    /// Centering is almost always what you want if you are\n    /// planning to mix widgets or use different types of text.\n    ///\n    /// If you don't want the contents to be centered, use [`Self::horizontal_top`] instead.\n    ///\n    /// The returned [`Response`] will only have checked for mouse hover\n    /// but can be used for tooltips (`on_hover_text`).\n    /// It also contains the [`Rect`] used by the horizontal layout.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.horizontal(|ui| {\n    ///     ui.label(\"Same\");\n    ///     ui.label(\"row\");\n    /// });\n    /// # });\n    /// ```\n    ///\n    /// See also [`Self::with_layout`] for more options.\n    #[inline]\n    pub fn horizontal<R>(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {\n        self.horizontal_with_main_wrap_dyn(false, Box::new(add_contents))\n    }\n\n    /// Like [`Self::horizontal`], but allocates the full vertical height and then centers elements vertically.\n    pub fn horizontal_centered<R>(\n        &mut self,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        let initial_size = self.available_size_before_wrap();\n        let layout = if self.placer.prefer_right_to_left() {\n            Layout::right_to_left(Align::Center)\n        } else {\n            Layout::left_to_right(Align::Center)\n        }\n        .with_cross_align(Align::Center);\n        self.allocate_ui_with_layout_dyn(initial_size, layout, Box::new(add_contents))\n    }\n\n    /// Like [`Self::horizontal`], but aligns content with top.\n    pub fn horizontal_top<R>(\n        &mut self,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        let initial_size = self.available_size_before_wrap();\n        let layout = if self.placer.prefer_right_to_left() {\n            Layout::right_to_left(Align::Center)\n        } else {\n            Layout::left_to_right(Align::Center)\n        }\n        .with_cross_align(Align::Min);\n        self.allocate_ui_with_layout_dyn(initial_size, layout, Box::new(add_contents))\n    }\n\n    /// Start a ui with horizontal layout that wraps to a new row\n    /// when it reaches the right edge of the `max_size`.\n    /// After you have called this, the function registers the contents as any other widget.\n    ///\n    /// Elements will be centered on the Y axis, i.e.\n    /// adjusted up and down to lie in the center of the horizontal layout.\n    /// The initial height is `style.spacing.interact_size.y`.\n    /// Centering is almost always what you want if you are\n    /// planning to mix widgets or use different types of text.\n    ///\n    /// The returned [`Response`] will only have checked for mouse hover\n    /// but can be used for tooltips (`on_hover_text`).\n    /// It also contains the [`Rect`] used by the horizontal layout.\n    ///\n    /// See also [`Self::with_layout`] for more options.\n    pub fn horizontal_wrapped<R>(\n        &mut self,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.horizontal_with_main_wrap_dyn(true, Box::new(add_contents))\n    }\n\n    fn horizontal_with_main_wrap_dyn<'c, R>(\n        &mut self,\n        main_wrap: bool,\n        add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,\n    ) -> InnerResponse<R> {\n        let initial_size = vec2(\n            self.available_size_before_wrap().x,\n            self.spacing().interact_size.y, // Assume there will be something interactive on the horizontal layout\n        );\n\n        let layout = if self.placer.prefer_right_to_left() {\n            Layout::right_to_left(Align::Center)\n        } else {\n            Layout::left_to_right(Align::Center)\n        }\n        .with_main_wrap(main_wrap);\n\n        self.allocate_ui_with_layout_dyn(initial_size, layout, add_contents)\n    }\n\n    /// Start a ui with vertical layout.\n    /// Widgets will be left-justified.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.vertical(|ui| {\n    ///     ui.label(\"over\");\n    ///     ui.label(\"under\");\n    /// });\n    /// # });\n    /// ```\n    ///\n    /// See also [`Self::with_layout`] for more options.\n    #[inline]\n    pub fn vertical<R>(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {\n        self.scope_builder(\n            UiBuilder::new().layout(Layout::top_down(Align::Min)),\n            add_contents,\n        )\n    }\n\n    /// Start a ui with vertical layout.\n    /// Widgets will be horizontally centered.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.vertical_centered(|ui| {\n    ///     ui.label(\"over\");\n    ///     ui.label(\"under\");\n    /// });\n    /// # });\n    /// ```\n    #[inline]\n    pub fn vertical_centered<R>(\n        &mut self,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.scope_builder(\n            UiBuilder::new().layout(Layout::top_down(Align::Center)),\n            add_contents,\n        )\n    }\n\n    /// Start a ui with vertical layout.\n    /// Widgets will be horizontally centered and justified (fill full width).\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.vertical_centered_justified(|ui| {\n    ///     ui.label(\"over\");\n    ///     ui.label(\"under\");\n    /// });\n    /// # });\n    /// ```\n    pub fn vertical_centered_justified<R>(\n        &mut self,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<R> {\n        self.scope_builder(\n            UiBuilder::new().layout(Layout::top_down(Align::Center).with_cross_justify(true)),\n            add_contents,\n        )\n    }\n\n    /// The new layout will take up all available space.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {\n    ///     ui.label(\"world!\");\n    ///     ui.label(\"Hello\");\n    /// });\n    /// # });\n    /// ```\n    ///\n    /// If you don't want to use up all available space, use [`Self::allocate_ui_with_layout`].\n    ///\n    /// See also the helpers [`Self::horizontal`], [`Self::vertical`], etc.\n    #[inline]\n    pub fn with_layout<R>(\n        &mut self,\n        layout: Layout,\n        add_contents: impl FnOnce(&mut Self) -> R,\n    ) -> InnerResponse<R> {\n        self.scope_builder(UiBuilder::new().layout(layout), add_contents)\n    }\n\n    /// This will make the next added widget centered and justified in the available space.\n    ///\n    /// Only one widget may be added to the inner `Ui`!\n    pub fn centered_and_justified<R>(\n        &mut self,\n        add_contents: impl FnOnce(&mut Self) -> R,\n    ) -> InnerResponse<R> {\n        self.scope_builder(\n            UiBuilder::new().layout(Layout::centered_and_justified(Direction::TopDown)),\n            add_contents,\n        )\n    }\n\n    pub(crate) fn set_grid(&mut self, grid: grid::GridLayout) {\n        self.placer.set_grid(grid);\n    }\n\n    pub(crate) fn save_grid(&mut self) {\n        self.placer.save_grid();\n    }\n\n    pub(crate) fn is_grid(&self) -> bool {\n        self.placer.is_grid()\n    }\n\n    /// Move to the next row in a grid layout or wrapping layout.\n    /// Otherwise does nothing.\n    pub fn end_row(&mut self) {\n        self.placer\n            .end_row(self.spacing().item_spacing, &self.painter().clone());\n    }\n\n    /// Set row height in horizontal wrapping layout.\n    pub fn set_row_height(&mut self, height: f32) {\n        self.placer.set_row_height(height);\n    }\n\n    /// Temporarily split a [`Ui`] into several columns.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.columns(2, |columns| {\n    ///     columns[0].label(\"First column\");\n    ///     columns[1].label(\"Second column\");\n    /// });\n    /// # });\n    /// ```\n    #[inline]\n    pub fn columns<R>(\n        &mut self,\n        num_columns: usize,\n        add_contents: impl FnOnce(&mut [Self]) -> R,\n    ) -> R {\n        self.columns_dyn(num_columns, Box::new(add_contents))\n    }\n\n    fn columns_dyn<'c, R>(\n        &mut self,\n        num_columns: usize,\n        add_contents: Box<dyn FnOnce(&mut [Self]) -> R + 'c>,\n    ) -> R {\n        // TODO(emilk): ensure there is space\n        let spacing = self.spacing().item_spacing.x;\n        let total_spacing = spacing * (num_columns as f32 - 1.0);\n        let column_width = (self.available_width() - total_spacing) / (num_columns as f32);\n        let top_left = self.cursor().min;\n\n        let mut columns: Vec<Self> = (0..num_columns)\n            .map(|col_idx| {\n                let pos = top_left + vec2((col_idx as f32) * (column_width + spacing), 0.0);\n                let child_rect = Rect::from_min_max(\n                    pos,\n                    pos2(pos.x + column_width, self.max_rect().right_bottom().y),\n                );\n                let mut column_ui = self.new_child(\n                    UiBuilder::new()\n                        .max_rect(child_rect)\n                        .layout(Layout::top_down_justified(Align::LEFT)),\n                );\n                column_ui.set_width(column_width);\n                column_ui\n            })\n            .collect();\n\n        let result = add_contents(&mut columns[..]);\n\n        let mut max_column_width = column_width;\n        let mut max_height = 0.0;\n        for column in &columns {\n            max_column_width = max_column_width.max(column.min_rect().width());\n            max_height = column.min_size().y.max(max_height);\n        }\n\n        // Make sure we fit everything next frame:\n        let total_required_width = total_spacing + max_column_width * (num_columns as f32);\n\n        let size = vec2(self.available_width().max(total_required_width), max_height);\n        self.advance_cursor_after_rect(Rect::from_min_size(top_left, size));\n        result\n    }\n\n    /// Temporarily split a [`Ui`] into several columns.\n    ///\n    /// The same as [`Self::columns()`], but uses a constant for the column count.\n    /// This allows for compile-time bounds checking, and makes the compiler happy.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.columns_const(|[col_1, col_2]| {\n    ///     col_1.label(\"First column\");\n    ///     col_2.label(\"Second column\");\n    /// });\n    /// # });\n    /// ```\n    #[inline]\n    pub fn columns_const<const NUM_COL: usize, R>(\n        &mut self,\n        add_contents: impl FnOnce(&mut [Self; NUM_COL]) -> R,\n    ) -> R {\n        // TODO(emilk): ensure there is space\n        let spacing = self.spacing().item_spacing.x;\n        let total_spacing = spacing * (NUM_COL as f32 - 1.0);\n        let column_width = (self.available_width() - total_spacing) / (NUM_COL as f32);\n        let top_left = self.cursor().min;\n\n        let mut columns = std::array::from_fn(|col_idx| {\n            let pos = top_left + vec2((col_idx as f32) * (column_width + spacing), 0.0);\n            let child_rect = Rect::from_min_max(\n                pos,\n                pos2(pos.x + column_width, self.max_rect().right_bottom().y),\n            );\n            let mut column_ui = self.new_child(\n                UiBuilder::new()\n                    .max_rect(child_rect)\n                    .layout(Layout::top_down_justified(Align::LEFT)),\n            );\n            column_ui.set_width(column_width);\n            column_ui\n        });\n        let result = add_contents(&mut columns);\n\n        let mut max_column_width = column_width;\n        let mut max_height = 0.0;\n        for column in &columns {\n            max_column_width = max_column_width.max(column.min_rect().width());\n            max_height = column.min_size().y.max(max_height);\n        }\n\n        // Make sure we fit everything next frame:\n        let total_required_width = total_spacing + max_column_width * (NUM_COL as f32);\n\n        let size = vec2(self.available_width().max(total_required_width), max_height);\n        self.advance_cursor_after_rect(Rect::from_min_size(top_left, size));\n        result\n    }\n\n    /// Create something that can be drag-and-dropped.\n    ///\n    /// The `id` needs to be globally unique.\n    /// The payload is what will be dropped if the user starts dragging.\n    ///\n    /// In contrast to [`Response::dnd_set_drag_payload`],\n    /// this function will paint the widget at the mouse cursor while the user is dragging.\n    #[doc(alias = \"drag and drop\")]\n    pub fn dnd_drag_source<Payload, R>(\n        &mut self,\n        id: Id,\n        payload: Payload,\n        add_contents: impl FnOnce(&mut Self) -> R,\n    ) -> InnerResponse<R>\n    where\n        Payload: Any + Send + Sync,\n    {\n        let is_being_dragged = self.ctx().is_being_dragged(id);\n\n        if is_being_dragged {\n            crate::DragAndDrop::set_payload(self.ctx(), payload);\n\n            // Paint the body to a new layer:\n            let layer_id = LayerId::new(Order::Tooltip, id);\n            let InnerResponse { inner, response } =\n                self.scope_builder(UiBuilder::new().layer_id(layer_id), add_contents);\n\n            // Now we move the visuals of the body to where the mouse is.\n            // Normally you need to decide a location for a widget first,\n            // because otherwise that widget cannot interact with the mouse.\n            // However, a dragged component cannot be interacted with anyway\n            // (anything with `Order::Tooltip` always gets an empty [`Response`])\n            // So this is fine!\n\n            if let Some(pointer_pos) = self.ctx().pointer_interact_pos() {\n                let delta = pointer_pos - response.rect.center();\n                self.ctx()\n                    .transform_layer_shapes(layer_id, emath::TSTransform::from_translation(delta));\n            }\n\n            InnerResponse::new(inner, response)\n        } else {\n            let InnerResponse { inner, response } = self.scope(add_contents);\n\n            // Check for drags:\n            let dnd_response = self\n                .interact(response.rect, id, Sense::drag())\n                .on_hover_cursor(CursorIcon::Grab);\n\n            InnerResponse::new(inner, dnd_response | response)\n        }\n    }\n\n    /// Surround the given ui with a frame which\n    /// changes colors when you can drop something onto it.\n    ///\n    /// Returns the dropped item, if it was released this frame.\n    ///\n    /// The given frame is used for its margins, but the color is ignored.\n    #[doc(alias = \"drag and drop\")]\n    pub fn dnd_drop_zone<Payload, R>(\n        &mut self,\n        frame: Frame,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> (InnerResponse<R>, Option<Arc<Payload>>)\n    where\n        Payload: Any + Send + Sync,\n    {\n        let is_anything_being_dragged = DragAndDrop::has_any_payload(self.ctx());\n        let can_accept_what_is_being_dragged =\n            DragAndDrop::has_payload_of_type::<Payload>(self.ctx());\n\n        let mut frame = frame.begin(self);\n        let inner = add_contents(&mut frame.content_ui);\n        let response = frame.allocate_space(self);\n\n        // NOTE: we use `response.contains_pointer` here instead of `hovered`, because\n        // `hovered` is always false when another widget is being dragged.\n        let style = if is_anything_being_dragged\n            && can_accept_what_is_being_dragged\n            && response.contains_pointer()\n        {\n            self.visuals().widgets.active\n        } else {\n            self.visuals().widgets.inactive\n        };\n\n        let mut fill = style.bg_fill;\n        let mut stroke = style.bg_stroke;\n\n        if is_anything_being_dragged && !can_accept_what_is_being_dragged {\n            // When dragging something else, show that it can't be dropped here:\n            fill = self.visuals().disable(fill);\n            stroke.color = self.visuals().disable(stroke.color);\n        }\n\n        frame.frame.fill = fill;\n        frame.frame.stroke = stroke;\n\n        frame.paint(self);\n\n        let payload = response.dnd_release_payload::<Payload>();\n\n        (InnerResponse { inner, response }, payload)\n    }\n\n    /// Create a new Scope and transform its contents via a [`emath::TSTransform`].\n    /// This only affects visuals, inputs will not be transformed. So this is mostly useful\n    /// to create visual effects on interactions, e.g. scaling a button on hover / click.\n    ///\n    /// Check out [`Context::set_transform_layer`] for a persistent transform that also affects\n    /// inputs.\n    pub fn with_visual_transform<R>(\n        &mut self,\n        transform: emath::TSTransform,\n        add_contents: impl FnOnce(&mut Self) -> R,\n    ) -> InnerResponse<R> {\n        let start_idx = self.ctx().graphics(|gx| {\n            gx.get(self.layer_id())\n                .map_or(crate::layers::ShapeIdx(0), |l| l.next_idx())\n        });\n\n        let r = self.scope_dyn(UiBuilder::new(), Box::new(add_contents));\n\n        self.ctx().graphics_mut(|g| {\n            let list = g.entry(self.layer_id());\n            let end_idx = list.next_idx();\n            list.transform_range(start_idx, end_idx, transform);\n        });\n\n        r\n    }\n}\n\n/// # Menus\nimpl Ui {\n    /// Close the menu we are in (including submenus), if any.\n    ///\n    /// See also: [`Self::menu_button`] and [`Response::context_menu`].\n    #[deprecated = \"Use `ui.close()` or `ui.close_kind(UiKind::Menu)` instead\"]\n    pub fn close_menu(&self) {\n        self.close_kind(UiKind::Menu);\n    }\n\n    #[expect(deprecated)]\n    pub(crate) fn set_menu_state(\n        &mut self,\n        menu_state: Option<Arc<RwLock<crate::menu::MenuState>>>,\n    ) {\n        self.menu_state = menu_state;\n    }\n\n    #[inline]\n    /// Create a menu button that when clicked will show the given menu.\n    ///\n    /// If called from within a menu this will instead create a button for a sub-menu.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.menu_button(\"My menu\", |ui| {\n    ///     ui.menu_button(\"My sub-menu\", |ui| {\n    ///         if ui.button(\"Close the menu\").clicked() {\n    ///             ui.close();\n    ///         }\n    ///     });\n    /// });\n    /// # });\n    /// ```\n    ///\n    /// See also: [`Self::close`] and [`Response::context_menu`].\n    pub fn menu_button<'a, R>(\n        &mut self,\n        atoms: impl IntoAtoms<'a>,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<Option<R>> {\n        let (response, inner) = if menu::is_in_menu(self) {\n            menu::SubMenuButton::new(atoms).ui(self, add_contents)\n        } else {\n            menu::MenuButton::new(atoms).ui(self, add_contents)\n        };\n        InnerResponse::new(inner.map(|i| i.inner), response)\n    }\n\n    /// Create a menu button with an image that when clicked will show the given menu.\n    ///\n    /// If called from within a menu this will instead create a button for a sub-menu.\n    ///\n    /// ```ignore\n    /// # egui::__run_test_ui(|ui| {\n    /// let img = egui::include_image!(\"../assets/ferris.png\");\n    ///\n    /// ui.menu_image_button(title, img, |ui| {\n    ///     ui.menu_button(\"My sub-menu\", |ui| {\n    ///         if ui.button(\"Close the menu\").clicked() {\n    ///             ui.close();\n    ///         }\n    ///     });\n    /// });\n    /// # });\n    /// ```\n    ///\n    ///\n    /// See also: [`Self::close`] and [`Response::context_menu`].\n    #[inline]\n    pub fn menu_image_button<'a, R>(\n        &mut self,\n        image: impl Into<Image<'a>>,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<Option<R>> {\n        let (response, inner) = if menu::is_in_menu(self) {\n            menu::SubMenuButton::from_button(\n                Button::image(image).right_text(menu::SubMenuButton::RIGHT_ARROW),\n            )\n            .ui(self, add_contents)\n        } else {\n            menu::MenuButton::from_button(Button::image(image)).ui(self, add_contents)\n        };\n        InnerResponse::new(inner.map(|i| i.inner), response)\n    }\n\n    /// Create a menu button with an image and a text that when clicked will show the given menu.\n    ///\n    /// If called from within a menu this will instead create a button for a sub-menu.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let img = egui::include_image!(\"../assets/ferris.png\");\n    /// let title = \"My Menu\";\n    ///\n    /// ui.menu_image_text_button(img, title, |ui| {\n    ///     ui.menu_button(\"My sub-menu\", |ui| {\n    ///         if ui.button(\"Close the menu\").clicked() {\n    ///             ui.close();\n    ///         }\n    ///     });\n    /// });\n    /// # });\n    /// ```\n    ///\n    /// See also: [`Self::close`] and [`Response::context_menu`].\n    #[inline]\n    pub fn menu_image_text_button<'a, R>(\n        &mut self,\n        image: impl Into<Image<'a>>,\n        title: impl Into<WidgetText>,\n        add_contents: impl FnOnce(&mut Ui) -> R,\n    ) -> InnerResponse<Option<R>> {\n        let (response, inner) = if menu::is_in_menu(self) {\n            menu::SubMenuButton::from_button(\n                Button::image_and_text(image, title).right_text(menu::SubMenuButton::RIGHT_ARROW),\n            )\n            .ui(self, add_contents)\n        } else {\n            menu::MenuButton::from_button(Button::image_and_text(image, title))\n                .ui(self, add_contents)\n        };\n        InnerResponse::new(inner.map(|i| i.inner), response)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// # Debug stuff\nimpl Ui {\n    /// Shows where the next widget is going to be placed\n    #[cfg(debug_assertions)]\n    pub fn debug_paint_cursor(&self) {\n        self.placer.debug_paint_cursor(&self.painter, \"next\");\n    }\n}\n\nimpl Drop for Ui {\n    fn drop(&mut self) {\n        if !self.min_rect_already_remembered {\n            // Register our final `min_rect`\n            self.remember_min_rect();\n        }\n        #[cfg(debug_assertions)]\n        register_rect(self, self.min_rect());\n    }\n}\n\n/// Show this rectangle to the user if certain debug options are set.\n#[cfg(debug_assertions)]\nfn register_rect(ui: &Ui, rect: Rect) {\n    use emath::{Align2, GuiRounding as _};\n\n    let debug = ui.style().debug;\n\n    if debug.show_unaligned {\n        let unaligned_line = |p0: Pos2, p1: Pos2| {\n            let color = Color32::ORANGE;\n            let font_id = TextStyle::Monospace.resolve(ui.style());\n            ui.painter().line_segment([p0, p1], (1.0, color));\n            ui.painter()\n                .text(p0, Align2::LEFT_TOP, \"Unaligned\", font_id, color);\n        };\n\n        if rect.left() != rect.left().round_ui() {\n            unaligned_line(rect.left_top(), rect.left_bottom());\n        }\n        if rect.right() != rect.right().round_ui() {\n            unaligned_line(rect.right_top(), rect.right_bottom());\n        }\n        if rect.top() != rect.top().round_ui() {\n            unaligned_line(rect.left_top(), rect.right_top());\n        }\n        if rect.bottom() != rect.bottom().round_ui() {\n            unaligned_line(rect.left_bottom(), rect.right_bottom());\n        }\n    }\n\n    let show_callstacks = debug.debug_on_hover\n        || debug.debug_on_hover_with_all_modifiers && ui.input(|i| i.modifiers.all());\n\n    if !show_callstacks {\n        return;\n    }\n\n    if !ui.rect_contains_pointer(rect) {\n        return;\n    }\n\n    let is_clicking = ui.input(|i| i.pointer.could_any_button_be_click());\n\n    #[cfg(feature = \"callstack\")]\n    let callstack = crate::callstack::capture();\n\n    #[cfg(not(feature = \"callstack\"))]\n    let callstack = String::default();\n\n    // We only show one debug rectangle, or things get confusing:\n    let debug_rect = pass_state::DebugRect {\n        rect,\n        callstack,\n        is_clicking,\n    };\n\n    let mut kept = false;\n    ui.ctx().pass_state_mut(|fs| {\n        if let Some(final_debug_rect) = &mut fs.debug_rect {\n            // or maybe pick the one with deepest callstack?\n            if final_debug_rect.rect.contains_rect(rect) {\n                *final_debug_rect = debug_rect;\n                kept = true;\n            }\n        } else {\n            fs.debug_rect = Some(debug_rect);\n            kept = true;\n        }\n    });\n    if !kept {\n        return;\n    }\n\n    // ----------------------------------------------\n\n    // Use the debug-painter to avoid clip rect,\n    // otherwise the content of the widget may cover what we paint here!\n    let painter = ui.debug_painter();\n\n    if debug.hover_shows_next {\n        ui.placer.debug_paint_cursor(&painter, \"next\");\n    }\n}\n\n#[cfg(not(debug_assertions))]\nfn register_rect(_ui: &Ui, _rect: Rect) {}\n\n#[test]\nfn ui_impl_send_sync() {\n    fn assert_send_sync<T: Send + Sync>() {}\n    assert_send_sync::<Ui>();\n}\n"
  },
  {
    "path": "crates/egui/src/ui_builder.rs",
    "content": "use std::{hash::Hash, sync::Arc};\n\nuse crate::ClosableTag;\n#[expect(unused_imports)] // Used for doclinks\nuse crate::Ui;\nuse crate::{Id, LayerId, Layout, Rect, Sense, Style, UiStackInfo};\n\n/// Build a [`Ui`] as the child of another [`Ui`].\n///\n/// By default, everything is inherited from the parent,\n/// except for `max_rect` which by default is set to\n/// the parent [`Ui::available_rect_before_wrap`].\n#[must_use]\n#[derive(Clone, Default)]\npub struct UiBuilder {\n    pub id_salt: Option<Id>,\n    pub global_scope: bool,\n    pub ui_stack_info: UiStackInfo,\n    pub layer_id: Option<LayerId>,\n    pub max_rect: Option<Rect>,\n    pub layout: Option<Layout>,\n    pub disabled: bool,\n    pub invisible: bool,\n    pub sizing_pass: bool,\n    pub style: Option<Arc<Style>>,\n    pub sense: Option<Sense>,\n    pub accessibility_parent: Option<Id>,\n}\n\nimpl UiBuilder {\n    #[inline]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Seed the child `Ui` with this `id_salt`, which will be mixed\n    /// with the [`Ui::id`] of the parent.\n    ///\n    /// You should give each [`Ui`] an `id_salt` that is unique\n    /// within the parent, or give it none at all.\n    #[inline]\n    pub fn id_salt(mut self, id_salt: impl Hash) -> Self {\n        self.id_salt = Some(Id::new(id_salt));\n        self\n    }\n\n    /// Set an id of the new `Ui` that is independent of the parent `Ui`.\n    /// This way child widgets can be moved in the ui tree without losing state.\n    /// You have to ensure that in a frame the child widgets do not get rendered in multiple places.\n    ///\n    /// You should set the same unique `id` at every place in the ui tree where you want the\n    /// child widgets to share state.\n    /// If the child widgets are not moved in the ui tree, use [`UiBuilder::id_salt`] instead.\n    ///\n    /// This is a shortcut for `.id_salt(my_id).global_scope(true)`.\n    #[inline]\n    pub fn id(mut self, id: Id) -> Self {\n        self.id_salt = Some(id);\n        self.global_scope = true;\n        self\n    }\n\n    /// Make the new `Ui` child ids independent of the parent `Ui`.\n    /// This way child widgets can be moved in the ui tree without losing state.\n    /// You have to ensure that in a frame the child widgets do not get rendered in multiple places.\n    ///\n    /// You should set the same globally unique `id_salt` at every place in the ui tree where you want the\n    /// child widgets to share state.\n    #[inline]\n    pub fn global_scope(mut self, global_scope: bool) -> Self {\n        self.global_scope = global_scope;\n        self\n    }\n\n    /// Provide some information about the new `Ui` being built.\n    #[inline]\n    pub fn ui_stack_info(mut self, ui_stack_info: UiStackInfo) -> Self {\n        self.ui_stack_info = ui_stack_info;\n        self\n    }\n\n    /// Show the [`Ui`] in a different [`LayerId`] from its parent.\n    #[inline]\n    pub fn layer_id(mut self, layer_id: LayerId) -> Self {\n        self.layer_id = Some(layer_id);\n        self\n    }\n\n    /// Set the max rectangle, within which widgets will go.\n    ///\n    /// New widgets will *try* to fit within this rectangle.\n    ///\n    /// Text labels will wrap to fit within `max_rect`.\n    /// Separator lines will span the `max_rect`.\n    ///\n    /// If a new widget doesn't fit within the `max_rect` then the\n    /// [`Ui`] will make room for it by expanding both `min_rect` and\n    ///\n    /// If not set, this will be set to the parent\n    /// [`Ui::available_rect_before_wrap`].\n    #[inline]\n    pub fn max_rect(mut self, max_rect: Rect) -> Self {\n        self.max_rect = Some(max_rect);\n        self\n    }\n\n    /// Override the layout.\n    ///\n    /// Will otherwise be inherited from the parent.\n    #[inline]\n    pub fn layout(mut self, layout: Layout) -> Self {\n        self.layout = Some(layout);\n        self\n    }\n\n    /// Make the new `Ui` disabled, i.e. grayed-out and non-interactive.\n    ///\n    /// Note that if the parent `Ui` is disabled, the child will always be disabled.\n    #[inline]\n    pub fn disabled(mut self) -> Self {\n        self.disabled = true;\n        self\n    }\n\n    /// Make the contents invisible.\n    ///\n    /// Will also disable the `Ui` (see [`Self::disabled`]).\n    ///\n    /// If the parent `Ui` is invisible, the child will always be invisible.\n    #[inline]\n    pub fn invisible(mut self) -> Self {\n        self.invisible = true;\n        self.disabled = true;\n        self\n    }\n\n    /// Set to true in special cases where we do one frame\n    /// where we size up the contents of the Ui, without actually showing it.\n    ///\n    /// If the `sizing_pass` flag is set on the parent,\n    /// the child will inherit it automatically.\n    #[inline]\n    pub fn sizing_pass(mut self) -> Self {\n        self.sizing_pass = true;\n        self\n    }\n\n    /// Override the style.\n    ///\n    /// Otherwise will inherit the style of the parent.\n    #[inline]\n    pub fn style(mut self, style: impl Into<Arc<Style>>) -> Self {\n        self.style = Some(style.into());\n        self\n    }\n\n    /// Set if you want sense clicks and/or drags. Default is [`Sense::hover`].\n    ///\n    /// The sense will be registered below the Senses of any widgets contained in this [`Ui`], so\n    /// if the user clicks a button contained within this [`Ui`], that button will receive the click\n    /// instead.\n    ///\n    /// The response can be read early with [`Ui::response`].\n    #[inline]\n    pub fn sense(mut self, sense: Sense) -> Self {\n        self.sense = Some(sense);\n        self\n    }\n\n    /// Make this [`Ui`] closable.\n    ///\n    /// Calling [`Ui::close`] in a child [`Ui`] will mark this [`Ui`] for closing.\n    /// After [`Ui::close`] was called, [`Ui::should_close`] and [`crate::Response::should_close`] will\n    /// return `true` (for this frame).\n    ///\n    /// This works by adding a [`ClosableTag`] to the [`UiStackInfo`].\n    #[inline]\n    pub fn closable(mut self) -> Self {\n        self.ui_stack_info\n            .tags\n            .insert(ClosableTag::NAME, Some(Arc::new(ClosableTag::default())));\n        self\n    }\n\n    /// Set the accessibility parent for this [`Ui`].\n    ///\n    /// This will override the automatic parent assignment for accessibility purposes.\n    /// If not set, the parent [`Ui`]'s ID will be used as the accessibility parent.\n    #[inline]\n    pub fn accessibility_parent(mut self, parent_id: Id) -> Self {\n        self.accessibility_parent = Some(parent_id);\n        self\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/ui_stack.rs",
    "content": "use std::sync::Arc;\nuse std::{any::Any, iter::FusedIterator};\n\nuse crate::{Direction, Frame, Id, Rect};\n\n/// What kind is this [`crate::Ui`]?\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum UiKind {\n    /// A [`crate::Window`].\n    Window,\n\n    /// A [`crate::CentralPanel`].\n    CentralPanel,\n\n    /// A left [`crate::Panel`].\n    LeftPanel,\n\n    /// A right [`crate::Panel`].\n    RightPanel,\n\n    /// A top [`crate::Panel`].\n    TopPanel,\n\n    /// A bottom [`crate::Panel`].\n    BottomPanel,\n\n    /// A modal [`crate::Modal`].\n    Modal,\n\n    /// A [`crate::Frame`].\n    Frame,\n\n    /// A [`crate::ScrollArea`].\n    ScrollArea,\n\n    /// A [`crate::Resize`].\n    Resize,\n\n    /// The content of a regular menu.\n    Menu,\n\n    /// The content of a popup menu.\n    Popup,\n\n    /// A tooltip, as shown by e.g. [`crate::Response::on_hover_ui`].\n    Tooltip,\n\n    /// A picker, such as color picker.\n    Picker,\n\n    /// A table cell (from the `egui_extras` crate).\n    TableCell,\n\n    /// An [`crate::Area`] that is not of any other kind.\n    GenericArea,\n\n    /// A collapsible container, e.g. a [`crate::CollapsingHeader`].\n    Collapsible,\n}\n\nimpl UiKind {\n    /// Is this any kind of panel?\n    #[inline]\n    pub fn is_panel(&self) -> bool {\n        matches!(\n            self,\n            Self::CentralPanel\n                | Self::LeftPanel\n                | Self::RightPanel\n                | Self::TopPanel\n                | Self::BottomPanel\n        )\n    }\n\n    /// Is this any kind of [`crate::Area`]?\n    #[inline]\n    pub fn is_area(&self) -> bool {\n        match self {\n            Self::CentralPanel\n            | Self::LeftPanel\n            | Self::RightPanel\n            | Self::TopPanel\n            | Self::BottomPanel\n            | Self::Frame\n            | Self::ScrollArea\n            | Self::Resize\n            | Self::Collapsible\n            | Self::TableCell => false,\n\n            Self::Window\n            | Self::Menu\n            | Self::Modal\n            | Self::Popup\n            | Self::Tooltip\n            | Self::Picker\n            | Self::GenericArea => true,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Information about a [`crate::Ui`] to be included in the corresponding [`UiStack`].\n#[derive(Clone, Default, Debug)]\npub struct UiStackInfo {\n    pub kind: Option<UiKind>,\n    pub frame: Frame,\n    pub tags: UiTags,\n}\n\nimpl UiStackInfo {\n    /// Create a new [`UiStackInfo`] with the given kind and an empty frame.\n    #[inline]\n    pub fn new(kind: UiKind) -> Self {\n        Self {\n            kind: Some(kind),\n            ..Default::default()\n        }\n    }\n\n    #[inline]\n    pub fn with_frame(mut self, frame: Frame) -> Self {\n        self.frame = frame;\n        self\n    }\n\n    /// Insert a tag with no value.\n    #[inline]\n    pub fn with_tag(mut self, key: impl Into<String>) -> Self {\n        self.tags.insert(key, None);\n        self\n    }\n\n    /// Insert a tag with some value.\n    #[inline]\n    pub fn with_tag_value(\n        mut self,\n        key: impl Into<String>,\n        value: impl Any + Send + Sync + 'static,\n    ) -> Self {\n        self.tags.insert(key, Some(Arc::new(value)));\n        self\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// User-chosen tags.\n///\n/// You can use this in any way you want,\n/// i.e. to set some tag on a [`crate::Ui`] and then in your own widget check\n/// for the existence of this tag up the [`UiStack`].\n///\n/// Note that egui never sets any tags itself, so this is purely for user code.\n///\n/// All tagging is transient, and will only live as long as the parent [`crate::Ui`], i.e. within a single render frame.\n#[derive(Clone, Default, Debug)]\npub struct UiTags(pub ahash::HashMap<String, Option<Arc<dyn Any + Send + Sync + 'static>>>);\n\nimpl UiTags {\n    #[inline]\n    pub fn insert(\n        &mut self,\n        key: impl Into<String>,\n        value: Option<Arc<dyn Any + Send + Sync + 'static>>,\n    ) {\n        self.0.insert(key.into(), value);\n    }\n\n    #[inline]\n    pub fn contains(&self, key: &str) -> bool {\n        self.0.contains_key(key)\n    }\n\n    /// Get the value of a tag.\n    ///\n    /// Note that `None` is returned both if the key is set to the value `None`,\n    /// and if the key is not set at all.\n    #[inline]\n    pub fn get_any(&self, key: &str) -> Option<&Arc<dyn Any + Send + Sync + 'static>> {\n        self.0.get(key)?.as_ref()\n    }\n\n    /// Get the value of a tag.\n    ///\n    /// Note that `None` is returned both if the key is set to the value `None`,\n    /// and if the key is not set at all.\n    pub fn get_downcast<T: Any + Send + Sync + 'static>(&self, key: &str) -> Option<&T> {\n        self.0.get(key)?.as_ref().and_then(|any| any.downcast_ref())\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Information about a [`crate::Ui`] and its parents.\n///\n/// [`UiStack`] serves to keep track of the current hierarchy of [`crate::Ui`]s, such\n/// that nested widgets or user code may adapt to the surrounding context or obtain layout information\n/// from a [`crate::Ui`] that might be several steps higher in the hierarchy.\n///\n/// Note: since [`UiStack`] contains a reference to its parent, it is both a stack, and a node within\n/// that stack. Most of its methods are about the specific node, but some methods walk up the\n/// hierarchy to provide information about the entire stack.\n#[derive(Debug)]\npub struct UiStack {\n    // stuff that `Ui::child_ui` can deal with directly\n    pub id: Id,\n    pub info: UiStackInfo,\n    pub layout_direction: Direction,\n    pub min_rect: Rect,\n    pub max_rect: Rect,\n    pub parent: Option<Arc<Self>>,\n}\n\n// these methods act on this specific node\nimpl UiStack {\n    #[inline]\n    pub fn kind(&self) -> Option<UiKind> {\n        self.info.kind\n    }\n\n    #[inline]\n    pub fn frame(&self) -> &Frame {\n        &self.info.frame\n    }\n\n    /// User tags.\n    #[inline]\n    pub fn tags(&self) -> &UiTags {\n        &self.info.tags\n    }\n\n    /// Is this [`crate::Ui`] a panel?\n    #[inline]\n    pub fn is_panel_ui(&self) -> bool {\n        self.kind().is_some_and(|kind| kind.is_panel())\n    }\n\n    /// Is this [`crate::Ui`] an [`crate::Area`]?\n    #[inline]\n    pub fn is_area_ui(&self) -> bool {\n        self.kind().is_some_and(|kind| kind.is_area())\n    }\n\n    /// Is this a root [`crate::Ui`], i.e. created with [`crate::Ui::new()`]?\n    #[inline]\n    pub fn is_root_ui(&self) -> bool {\n        self.parent.is_none()\n    }\n\n    /// This this [`crate::Ui`] a [`crate::Frame`] with a visible stroke?\n    #[inline]\n    pub fn has_visible_frame(&self) -> bool {\n        !self.info.frame.stroke.is_empty()\n    }\n}\n\n// these methods act on the entire stack\nimpl UiStack {\n    /// Return an iterator that walks the stack from this node to the root.\n    #[expect(clippy::iter_without_into_iter)]\n    pub fn iter(&self) -> UiStackIterator<'_> {\n        UiStackIterator { next: Some(self) }\n    }\n\n    /// Check if this node is or is contained in a [`crate::Ui`] of a specific kind.\n    pub fn contained_in(&self, kind: UiKind) -> bool {\n        self.iter().any(|frame| frame.kind() == Some(kind))\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Iterator that walks up a stack of `StackFrame`s.\n///\n/// See [`UiStack::iter`].\npub struct UiStackIterator<'a> {\n    next: Option<&'a UiStack>,\n}\n\nimpl<'a> Iterator for UiStackIterator<'a> {\n    type Item = &'a UiStack;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        let current = self.next;\n        self.next = current.and_then(|frame| frame.parent.as_deref());\n        current\n    }\n}\n\nimpl FusedIterator for UiStackIterator<'_> {}\n"
  },
  {
    "path": "crates/egui/src/util/fixed_cache.rs",
    "content": "use epaint::util::hash;\n\nconst FIXED_CACHE_SIZE: usize = 1024; // must be small for web/WASM build (for unknown reason)\n\n/// Very stupid/simple key-value cache. TODO(emilk): improve\n#[derive(Clone)]\npub(crate) struct FixedCache<K, V>([Option<(K, V)>; FIXED_CACHE_SIZE]);\n\nimpl<K, V> Default for FixedCache<K, V>\nwhere\n    K: Copy,\n    V: Copy,\n{\n    fn default() -> Self {\n        Self([None; FIXED_CACHE_SIZE])\n    }\n}\n\nimpl<K, V> std::fmt::Debug for FixedCache<K, V> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"Cache\")\n    }\n}\n\nimpl<K, V> FixedCache<K, V>\nwhere\n    K: std::hash::Hash + PartialEq,\n{\n    pub fn get(&self, key: &K) -> Option<&V> {\n        let bucket = (hash(key) % (FIXED_CACHE_SIZE as u64)) as usize;\n        match &self.0[bucket] {\n            Some((k, v)) if k == key => Some(v),\n            _ => None,\n        }\n    }\n\n    pub fn set(&mut self, key: K, value: V) {\n        let bucket = (hash(&key) % (FIXED_CACHE_SIZE as u64)) as usize;\n        self.0[bucket] = Some((key, value));\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/util/id_type_map.rs",
    "content": "// TODO(emilk): it is possible we can simplify `Element` further by\n// assuming everything is possibly serializable, and by supplying serialize/deserialize functions for them.\n// For non-serializable types, these simply return `None`.\n// This will also allow users to pick their own serialization format per type.\n\nuse std::{any::Any, sync::Arc};\n\n// -----------------------------------------------------------------------------------------------\n\n/// Like [`std::any::TypeId`], but can be serialized and deserialized.\n#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]\n#[cfg_attr(feature = \"persistence\", derive(serde::Deserialize, serde::Serialize))]\npub struct TypeId(u64);\n\nimpl TypeId {\n    #[inline]\n    pub fn of<T: Any + 'static>() -> Self {\n        std::any::TypeId::of::<T>().into()\n    }\n\n    #[inline(always)]\n    pub(crate) fn value(&self) -> u64 {\n        self.0\n    }\n}\n\nimpl From<std::any::TypeId> for TypeId {\n    #[inline]\n    fn from(id: std::any::TypeId) -> Self {\n        Self(epaint::util::hash(id))\n    }\n}\n\nimpl nohash_hasher::IsEnabled for TypeId {}\n\n// -----------------------------------------------------------------------------------------------\n\n#[cfg(feature = \"persistence\")]\npub trait SerializableAny:\n    'static + Any + Clone + serde::Serialize + for<'a> serde::Deserialize<'a> + Send + Sync\n{\n}\n\n#[cfg(feature = \"persistence\")]\nimpl<T> SerializableAny for T where\n    T: 'static + Any + Clone + serde::Serialize + for<'a> serde::Deserialize<'a> + Send + Sync\n{\n}\n\n#[cfg(not(feature = \"persistence\"))]\npub trait SerializableAny: 'static + Any + Clone + for<'a> Send + Sync {}\n\n#[cfg(not(feature = \"persistence\"))]\nimpl<T> SerializableAny for T where T: 'static + Any + Clone + for<'a> Send + Sync {}\n\n// -----------------------------------------------------------------------------------------------\n\n#[cfg_attr(feature = \"persistence\", derive(serde::Deserialize, serde::Serialize))]\n#[derive(Clone, Debug)]\nstruct SerializedElement {\n    /// The type of value we are storing.\n    type_id: TypeId,\n\n    /// The ron data we can deserialize.\n    ron: Arc<str>,\n\n    /// Increased by one each time we re-serialize an element that was never deserialized.\n    ///\n    /// Large value = old value that hasn't been read in a while.\n    ///\n    /// Used to garbage collect old values that hasn't been read in a while.\n    generation: usize,\n}\n\n#[cfg(feature = \"persistence\")]\ntype Serializer = fn(&Box<dyn Any + 'static + Send + Sync>) -> Option<String>;\n\nenum Element {\n    /// A value, maybe serializable.\n    Value {\n        /// The actual value.\n        value: Box<dyn Any + 'static + Send + Sync>,\n\n        /// How to clone the value.\n        clone_fn: fn(&Box<dyn Any + 'static + Send + Sync>) -> Box<dyn Any + 'static + Send + Sync>,\n\n        /// How to serialize the value.\n        /// None if non-serializable type.\n        #[cfg(feature = \"persistence\")]\n        serialize_fn: Option<Serializer>,\n    },\n\n    /// A serialized value\n    Serialized(SerializedElement),\n}\n\nimpl Clone for Element {\n    fn clone(&self) -> Self {\n        match &self {\n            Self::Value {\n                value,\n                clone_fn,\n                #[cfg(feature = \"persistence\")]\n                serialize_fn,\n            } => Self::Value {\n                value: clone_fn(value),\n                clone_fn: *clone_fn,\n                #[cfg(feature = \"persistence\")]\n                serialize_fn: *serialize_fn,\n            },\n\n            Self::Serialized(element) => Self::Serialized(element.clone()),\n        }\n    }\n}\n\nimpl std::fmt::Debug for Element {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match &self {\n            Self::Value { value, .. } => f\n                .debug_struct(\"Element::Value\")\n                .field(\"type_id\", &(**value).type_id())\n                .finish_non_exhaustive(),\n            Self::Serialized(SerializedElement {\n                type_id,\n                ron,\n                generation,\n            }) => f\n                .debug_struct(\"Element::Serialized\")\n                .field(\"type_id\", type_id)\n                .field(\"ron\", ron)\n                .field(\"generation\", generation)\n                .finish(),\n        }\n    }\n}\n\nimpl Element {\n    /// Create a value that won't be persisted.\n    #[inline]\n    pub(crate) fn new_temp<T: 'static + Any + Clone + Send + Sync>(t: T) -> Self {\n        Self::Value {\n            value: Box::new(t),\n            clone_fn: |x| {\n                // This unwrap will never panic, because we always construct this type using this `new` function and because we return &mut reference only with this type `T`, so type cannot change.\n                #[expect(clippy::unwrap_used)]\n                let x = x.downcast_ref::<T>().unwrap();\n                Box::new(x.clone())\n            },\n            #[cfg(feature = \"persistence\")]\n            serialize_fn: None,\n        }\n    }\n\n    /// Create a value that will be persisted.\n    #[inline]\n    pub(crate) fn new_persisted<T: SerializableAny>(t: T) -> Self {\n        Self::Value {\n            value: Box::new(t),\n            clone_fn: |x| {\n                // This unwrap will never panic, because we always construct this type using this `new` function and because we return &mut reference only with this type `T`, so type cannot change.\n                #[expect(clippy::unwrap_used)]\n                let x = x.downcast_ref::<T>().unwrap();\n                Box::new(x.clone())\n            },\n            #[cfg(feature = \"persistence\")]\n            serialize_fn: Some(|x| {\n                // This will never panic too, for same reason.\n                #[expect(clippy::unwrap_used)]\n                let x = x.downcast_ref::<T>().unwrap();\n                ron::to_string(x).ok()\n            }),\n        }\n    }\n\n    /// The type of the stored value.\n    #[inline]\n    pub(crate) fn type_id(&self) -> TypeId {\n        match self {\n            Self::Value { value, .. } => (**value).type_id().into(),\n            Self::Serialized(SerializedElement { type_id, .. }) => *type_id,\n        }\n    }\n\n    #[inline]\n    pub(crate) fn get_temp<T: 'static>(&self) -> Option<&T> {\n        match self {\n            Self::Value { value, .. } => value.downcast_ref(),\n            Self::Serialized(_) => None,\n        }\n    }\n\n    #[inline]\n    pub(crate) fn get_mut_temp<T: 'static>(&mut self) -> Option<&mut T> {\n        match self {\n            Self::Value { value, .. } => value.downcast_mut(),\n            Self::Serialized(_) => None,\n        }\n    }\n\n    #[inline]\n    pub(crate) fn get_temp_mut_or_insert_with<T: 'static + Any + Clone + Send + Sync>(\n        &mut self,\n        insert_with: impl FnOnce() -> T,\n    ) -> &mut T {\n        match self {\n            Self::Value { value, .. } => {\n                if !value.is::<T>() {\n                    *self = Self::new_temp(insert_with());\n                }\n            }\n            Self::Serialized(_) => {\n                *self = Self::new_temp(insert_with());\n            }\n        }\n\n        match self {\n            // This unwrap will never panic because we already converted object to required type\n            #[expect(clippy::unwrap_used)]\n            Self::Value { value, .. } => value.downcast_mut().unwrap(),\n            Self::Serialized(_) => unreachable!(),\n        }\n    }\n\n    #[inline]\n    pub(crate) fn get_persisted_mut_or_insert_with<T: SerializableAny>(\n        &mut self,\n        insert_with: impl FnOnce() -> T,\n    ) -> &mut T {\n        match self {\n            Self::Value { value, .. } => {\n                if !value.is::<T>() {\n                    *self = Self::new_persisted(insert_with());\n                }\n            }\n\n            #[cfg(feature = \"persistence\")]\n            Self::Serialized(SerializedElement { ron, .. }) => {\n                *self = Self::new_persisted(from_ron_str::<T>(ron).unwrap_or_else(insert_with));\n            }\n\n            #[cfg(not(feature = \"persistence\"))]\n            Self::Serialized(_) => {\n                *self = Self::new_persisted(insert_with());\n            }\n        }\n\n        match self {\n            // This unwrap will never panic because we already converted object to required type\n            #[expect(clippy::unwrap_used)]\n            Self::Value { value, .. } => value.downcast_mut().unwrap(),\n            Self::Serialized(_) => unreachable!(),\n        }\n    }\n\n    pub(crate) fn get_mut_persisted<T: SerializableAny>(&mut self) -> Option<&mut T> {\n        match self {\n            Self::Value { value, .. } => value.downcast_mut(),\n\n            #[cfg(feature = \"persistence\")]\n            Self::Serialized(SerializedElement { ron, .. }) => {\n                *self = Self::new_persisted(from_ron_str::<T>(ron)?);\n\n                match self {\n                    Self::Value { value, .. } => value.downcast_mut(),\n                    Self::Serialized(_) => unreachable!(),\n                }\n            }\n\n            #[cfg(not(feature = \"persistence\"))]\n            Self::Serialized(_) => None,\n        }\n    }\n\n    #[cfg(feature = \"persistence\")]\n    fn to_serialize(&self) -> Option<SerializedElement> {\n        match self {\n            Self::Value {\n                value,\n                serialize_fn,\n                ..\n            } => {\n                if let Some(serialize_fn) = serialize_fn {\n                    let ron = serialize_fn(value)?;\n                    Some(SerializedElement {\n                        type_id: (**value).type_id().into(),\n                        ron: ron.into(),\n                        generation: 1,\n                    })\n                } else {\n                    None\n                }\n            }\n            Self::Serialized(element) => Some(element.clone()),\n        }\n    }\n}\n\n#[cfg(feature = \"persistence\")]\nfn from_ron_str<T: serde::de::DeserializeOwned>(ron: &str) -> Option<T> {\n    match ron::from_str::<T>(ron) {\n        Ok(value) => Some(value),\n        Err(_err) => {\n            log::warn!(\n                \"egui: Failed to deserialize {} from memory: {}, ron error: {:?}\",\n                std::any::type_name::<T>(),\n                _err,\n                ron\n            );\n            None\n        }\n    }\n}\n\n// -----------------------------------------------------------------------------------------------\n\nuse crate::Id;\n\n// TODO(emilk): make IdTypeMap generic over the key (`Id`), and make a library of IdTypeMap.\n/// Stores values identified by an [`Id`] AND the [`std::any::TypeId`] of the value.\n///\n/// In other words, it maps `(Id, TypeId)` to any value you want.\n///\n/// Values are cloned when read, so keep them small and light.\n/// If you want to store something bigger, wrap them in `Arc<Mutex<…>>`.\n/// Also try `Arc<ArcSwap<…>>`.\n///\n/// Values can either be \"persisted\" (serializable) or \"temporary\" (cleared when egui is shut down).\n///\n/// You can store state using the key [`Id::NULL`]. The state will then only be identified by its type.\n///\n/// ```\n/// # use egui::{Id, util::IdTypeMap};\n/// let a = Id::new(\"a\");\n/// let b = Id::new(\"b\");\n/// let mut map: IdTypeMap = Default::default();\n///\n/// // `a` associated with an f64 and an i32\n/// map.insert_persisted(a, 3.14);\n/// map.insert_temp(a, 42);\n///\n/// // `b` associated with an f64 and a `&'static str`\n/// map.insert_persisted(b, 13.37);\n/// map.insert_temp(b, \"Hello World\".to_owned());\n///\n/// // we can retrieve all four values:\n/// assert_eq!(map.get_temp::<f64>(a), Some(3.14));\n/// assert_eq!(map.get_temp::<i32>(a), Some(42));\n/// assert_eq!(map.get_temp::<f64>(b), Some(13.37));\n/// assert_eq!(map.get_temp::<String>(b), Some(\"Hello World\".to_owned()));\n///\n/// // we can retrieve them like so also:\n/// assert_eq!(map.get_persisted::<f64>(a), Some(3.14));\n/// assert_eq!(map.get_persisted::<i32>(a), Some(42));\n/// assert_eq!(map.get_persisted::<f64>(b), Some(13.37));\n/// assert_eq!(map.get_temp::<String>(b), Some(\"Hello World\".to_owned()));\n/// ```\n#[derive(Clone, Debug)]\n// We use `id XOR typeid` as a key, so we don't need to hash again!\npub struct IdTypeMap {\n    map: nohash_hasher::IntMap<u64, Element>,\n\n    max_bytes_per_type: usize,\n}\n\nimpl Default for IdTypeMap {\n    fn default() -> Self {\n        Self {\n            map: Default::default(),\n            max_bytes_per_type: 256 * 1024,\n        }\n    }\n}\n\nimpl IdTypeMap {\n    /// Insert a value that will not be persisted.\n    #[inline]\n    pub fn insert_temp<T: 'static + Any + Clone + Send + Sync>(&mut self, id: Id, value: T) {\n        let hash = hash(TypeId::of::<T>(), id);\n        self.map.insert(hash, Element::new_temp(value));\n    }\n\n    /// Insert a value that will be persisted next time you start the app.\n    #[inline]\n    pub fn insert_persisted<T: SerializableAny>(&mut self, id: Id, value: T) {\n        let hash = hash(TypeId::of::<T>(), id);\n        self.map.insert(hash, Element::new_persisted(value));\n    }\n\n    /// Read a value without trying to deserialize a persisted value.\n    ///\n    /// The call clones the value (if found), so make sure it is cheap to clone!\n    #[inline]\n    pub fn get_temp<T: 'static + Clone>(&self, id: Id) -> Option<T> {\n        let hash = hash(TypeId::of::<T>(), id);\n        self.map.get(&hash).and_then(|x| x.get_temp()).cloned()\n    }\n\n    /// Read a value, optionally deserializing it if available.\n    ///\n    /// NOTE: A mutable `self` is needed because internally this deserializes on first call\n    /// and caches the result (caching requires self-mutability).\n    ///\n    /// The call clones the value (if found), so make sure it is cheap to clone!\n    #[inline]\n    pub fn get_persisted<T: SerializableAny>(&mut self, id: Id) -> Option<T> {\n        let hash = hash(TypeId::of::<T>(), id);\n        self.map\n            .get_mut(&hash)\n            .and_then(|x| x.get_mut_persisted())\n            .cloned()\n    }\n\n    #[inline]\n    pub fn get_temp_mut_or<T: 'static + Any + Clone + Send + Sync>(\n        &mut self,\n        id: Id,\n        or_insert: T,\n    ) -> &mut T {\n        self.get_temp_mut_or_insert_with(id, || or_insert)\n    }\n\n    #[inline]\n    pub fn get_persisted_mut_or<T: SerializableAny>(&mut self, id: Id, or_insert: T) -> &mut T {\n        self.get_persisted_mut_or_insert_with(id, || or_insert)\n    }\n\n    #[inline]\n    pub fn get_temp_mut_or_default<T: 'static + Any + Clone + Send + Sync + Default>(\n        &mut self,\n        id: Id,\n    ) -> &mut T {\n        self.get_temp_mut_or_insert_with(id, Default::default)\n    }\n\n    #[inline]\n    pub fn get_persisted_mut_or_default<T: SerializableAny + Default>(&mut self, id: Id) -> &mut T {\n        self.get_persisted_mut_or_insert_with(id, Default::default)\n    }\n\n    pub fn get_temp_mut_or_insert_with<T: 'static + Any + Clone + Send + Sync>(\n        &mut self,\n        id: Id,\n        insert_with: impl FnOnce() -> T,\n    ) -> &mut T {\n        let hash = hash(TypeId::of::<T>(), id);\n        use std::collections::hash_map::Entry;\n        match self.map.entry(hash) {\n            Entry::Vacant(vacant) => {\n                // this unwrap will never panic, because we insert correct type right now\n                #[expect(clippy::unwrap_used)]\n                vacant\n                    .insert(Element::new_temp(insert_with()))\n                    .get_mut_temp()\n                    .unwrap()\n            }\n            Entry::Occupied(occupied) => {\n                occupied.into_mut().get_temp_mut_or_insert_with(insert_with)\n            }\n        }\n    }\n\n    pub fn get_persisted_mut_or_insert_with<T: SerializableAny>(\n        &mut self,\n        id: Id,\n        insert_with: impl FnOnce() -> T,\n    ) -> &mut T {\n        let hash = hash(TypeId::of::<T>(), id);\n        use std::collections::hash_map::Entry;\n        match self.map.entry(hash) {\n            Entry::Vacant(vacant) => {\n                // this unwrap will never panic, because we insert correct type right now\n                #[expect(clippy::unwrap_used)]\n                vacant\n                    .insert(Element::new_persisted(insert_with()))\n                    .get_mut_persisted()\n                    .unwrap()\n            }\n            Entry::Occupied(occupied) => occupied\n                .into_mut()\n                .get_persisted_mut_or_insert_with(insert_with),\n        }\n    }\n\n    /// For tests\n    #[cfg(feature = \"persistence\")]\n    #[allow(clippy::allow_attributes, unused)]\n    fn get_generation<T: SerializableAny>(&self, id: Id) -> Option<usize> {\n        let element = self.map.get(&hash(TypeId::of::<T>(), id))?;\n        match element {\n            Element::Value { .. } => Some(0),\n            Element::Serialized(SerializedElement { generation, .. }) => Some(*generation),\n        }\n    }\n\n    /// Remove the state of this type and id.\n    #[inline]\n    pub fn remove<T: 'static>(&mut self, id: Id) {\n        let hash = hash(TypeId::of::<T>(), id);\n        self.map.remove(&hash);\n    }\n\n    /// Remove and fetch the state of this type and id.\n    #[inline]\n    pub fn remove_temp<T: 'static + Default>(&mut self, id: Id) -> Option<T> {\n        let hash = hash(TypeId::of::<T>(), id);\n        let mut element = self.map.remove(&hash)?;\n        Some(std::mem::take(element.get_mut_temp()?))\n    }\n\n    /// Note all state of the given type.\n    pub fn remove_by_type<T: 'static>(&mut self) {\n        let key = TypeId::of::<T>();\n        self.map.retain(|_, e| {\n            let e: &Element = e;\n            e.type_id() != key\n        });\n    }\n\n    #[inline]\n    pub fn clear(&mut self) {\n        self.map.clear();\n    }\n\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.map.is_empty()\n    }\n\n    #[inline]\n    pub fn len(&self) -> usize {\n        self.map.len()\n    }\n\n    /// Count how many values are stored but not yet deserialized.\n    #[inline]\n    pub fn count_serialized(&self) -> usize {\n        self.map\n            .values()\n            .filter(|e| matches!(e, Element::Serialized(_)))\n            .count()\n    }\n\n    /// Count the number of values are stored with the given type.\n    pub fn count<T: 'static>(&self) -> usize {\n        let key = TypeId::of::<T>();\n        self.map\n            .iter()\n            .filter(|(_, e)| {\n                let e: &Element = e;\n                e.type_id() == key\n            })\n            .count()\n    }\n\n    /// The maximum number of bytes that will be used to\n    /// store the persisted state of a single widget type.\n    ///\n    /// Some egui widgets store persisted state that is\n    /// serialized to disk by some backends (e.g. `eframe`).\n    ///\n    /// Example of such widgets is `CollapsingHeader` and `Window`.\n    /// If you keep creating widgets with unique ids (e.g. `Windows` with many different names),\n    /// egui will use up more and more space for these widgets, until this limit is reached.\n    ///\n    /// Once this limit is reached, the state that was read the longest time ago will be dropped first.\n    ///\n    /// This value in itself will not be serialized.\n    pub fn max_bytes_per_type(&self) -> usize {\n        self.max_bytes_per_type\n    }\n\n    /// See [`Self::max_bytes_per_type`].\n    pub fn set_max_bytes_per_type(&mut self, max_bytes_per_type: usize) {\n        self.max_bytes_per_type = max_bytes_per_type;\n    }\n}\n\n#[inline(always)]\nfn hash(type_id: TypeId, id: Id) -> u64 {\n    type_id.value() ^ id.value()\n}\n\n// ----------------------------------------------------------------------------\n\n/// How [`IdTypeMap`] is persisted.\n#[cfg(feature = \"persistence\")]\n#[cfg_attr(feature = \"persistence\", derive(serde::Deserialize, serde::Serialize))]\nstruct PersistedMap(Vec<(u64, SerializedElement)>);\n\n#[cfg(feature = \"persistence\")]\nimpl PersistedMap {\n    fn from_map(map: &IdTypeMap) -> Self {\n        #![expect(clippy::iter_over_hash_type)] // the serialized order doesn't matter\n\n        profiling::function_scope!();\n\n        use std::collections::BTreeMap;\n\n        let mut types_map: nohash_hasher::IntMap<TypeId, TypeStats> = Default::default();\n        #[derive(Default)]\n        struct TypeStats {\n            num_bytes: usize,\n            generations: BTreeMap<usize, GenerationStats>,\n        }\n        #[derive(Default)]\n        struct GenerationStats {\n            num_bytes: usize,\n            elements: Vec<(u64, SerializedElement)>,\n        }\n\n        let max_bytes_per_type = map.max_bytes_per_type;\n\n        {\n            profiling::scope!(\"gather\");\n            for (hash, element) in &map.map {\n                if let Some(element) = element.to_serialize() {\n                    let stats = types_map.entry(element.type_id).or_default();\n                    stats.num_bytes += element.ron.len();\n                    let generation_stats = stats.generations.entry(element.generation).or_default();\n                    generation_stats.num_bytes += element.ron.len();\n                    generation_stats.elements.push((*hash, element));\n                } else {\n                    // temporary value that shouldn't be serialized\n                }\n            }\n        }\n\n        let mut persisted = vec![];\n\n        {\n            profiling::scope!(\"gc\");\n            for stats in types_map.values() {\n                let mut bytes_written = 0;\n\n                // Start with the most recently read values, and then go as far as we are allowed.\n                // Always include at least one generation.\n                for generation in stats.generations.values() {\n                    if bytes_written == 0\n                        || bytes_written + generation.num_bytes <= max_bytes_per_type\n                    {\n                        persisted.append(&mut generation.elements.clone());\n                        bytes_written += generation.num_bytes;\n                    } else {\n                        // Omit the rest. The user hasn't read the values in a while.\n                        break;\n                    }\n                }\n            }\n        }\n\n        Self(persisted)\n    }\n\n    fn into_map(self) -> IdTypeMap {\n        profiling::function_scope!();\n        let map = self\n            .0\n            .into_iter()\n            .map(\n                |(\n                    hash,\n                    SerializedElement {\n                        type_id,\n                        ron,\n                        generation,\n                    },\n                )| {\n                    (\n                        hash,\n                        Element::Serialized(SerializedElement {\n                            type_id,\n                            ron,\n                            generation: generation + 1, // This is where we increment the generation!\n                        }),\n                    )\n                },\n            )\n            .collect();\n        IdTypeMap {\n            map,\n            ..Default::default()\n        }\n    }\n}\n\n#[cfg(feature = \"persistence\")]\nimpl serde::Serialize for IdTypeMap {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        profiling::scope!(\"IdTypeMap::serialize\");\n        PersistedMap::from_map(self).serialize(serializer)\n    }\n}\n\n#[cfg(feature = \"persistence\")]\nimpl<'de> serde::Deserialize<'de> for IdTypeMap {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        profiling::scope!(\"IdTypeMap::deserialize\");\n        <PersistedMap>::deserialize(deserializer).map(PersistedMap::into_map)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[test]\nfn test_two_id_two_type() {\n    let a = Id::new(\"a\");\n    let b = Id::new(\"b\");\n\n    let mut map: IdTypeMap = Default::default();\n    map.insert_persisted(a, 13.37);\n    map.insert_temp(b, 42);\n    assert_eq!(map.get_persisted::<f64>(a), Some(13.37));\n    assert_eq!(map.get_persisted::<i32>(b), Some(42));\n    assert_eq!(map.get_temp::<f64>(a), Some(13.37));\n    assert_eq!(map.get_temp::<i32>(b), Some(42));\n}\n\n#[test]\nfn test_two_id_x_two_types() {\n    #![expect(clippy::approx_constant)]\n\n    let a = Id::new(\"a\");\n    let b = Id::new(\"b\");\n    let mut map: IdTypeMap = Default::default();\n\n    // `a` associated with an f64 and an i32\n    map.insert_persisted(a, 3.14);\n    map.insert_temp(a, 42);\n\n    // `b` associated with an f64 and a `&'static str`\n    map.insert_persisted(b, 13.37);\n    map.insert_temp(b, \"Hello World\".to_owned());\n\n    // we can retrieve all four values:\n    assert_eq!(map.get_temp::<f64>(a), Some(3.14));\n    assert_eq!(map.get_temp::<i32>(a), Some(42));\n    assert_eq!(map.get_temp::<f64>(b), Some(13.37));\n    assert_eq!(map.get_temp::<String>(b), Some(\"Hello World\".to_owned()));\n\n    // we can retrieve them like so also:\n    assert_eq!(map.get_persisted::<f64>(a), Some(3.14));\n    assert_eq!(map.get_persisted::<i32>(a), Some(42));\n    assert_eq!(map.get_persisted::<f64>(b), Some(13.37));\n    assert_eq!(map.get_temp::<String>(b), Some(\"Hello World\".to_owned()));\n}\n\n#[test]\nfn test_one_id_two_types() {\n    let id = Id::new(\"a\");\n\n    let mut map: IdTypeMap = Default::default();\n    map.insert_persisted(id, 13.37);\n    map.insert_temp(id, 42);\n\n    assert_eq!(map.get_temp::<f64>(id), Some(13.37));\n    assert_eq!(map.get_persisted::<f64>(id), Some(13.37));\n    assert_eq!(map.get_temp::<i32>(id), Some(42));\n\n    // ------------\n    // Test removal:\n\n    // We can remove:\n    map.remove::<i32>(id);\n    assert_eq!(map.get_temp::<i32>(id), None);\n\n    // Other type is still there, even though it is the same if:\n    assert_eq!(map.get_temp::<f64>(id), Some(13.37));\n    assert_eq!(map.get_persisted::<f64>(id), Some(13.37));\n\n    // But we can still remove the last:\n    map.remove::<f64>(id);\n    assert_eq!(map.get_temp::<f64>(id), None);\n    assert_eq!(map.get_persisted::<f64>(id), None);\n}\n\n#[test]\nfn test_mix() {\n    #[cfg_attr(feature = \"persistence\", derive(serde::Deserialize, serde::Serialize))]\n    #[derive(Clone, Debug, PartialEq)]\n    struct Foo(i32);\n\n    #[derive(Clone, Debug, PartialEq)]\n    struct Bar(f32);\n\n    let id = Id::new(\"a\");\n\n    let mut map: IdTypeMap = Default::default();\n    map.insert_persisted(id, Foo(555));\n    map.insert_temp(id, Bar(1.0));\n\n    assert_eq!(map.get_temp::<Foo>(id), Some(Foo(555)));\n    assert_eq!(map.get_persisted::<Foo>(id), Some(Foo(555)));\n    assert_eq!(map.get_temp::<Bar>(id), Some(Bar(1.0)));\n\n    // ------------\n    // Test removal:\n\n    // We can remove:\n    map.remove::<Bar>(id);\n    assert_eq!(map.get_temp::<Bar>(id), None);\n\n    // Other type is still there, even though it is the same if:\n    assert_eq!(map.get_temp::<Foo>(id), Some(Foo(555)));\n    assert_eq!(map.get_persisted::<Foo>(id), Some(Foo(555)));\n\n    // But we can still remove the last:\n    map.remove::<Foo>(id);\n    assert_eq!(map.get_temp::<Foo>(id), None);\n    assert_eq!(map.get_persisted::<Foo>(id), None);\n}\n\n#[cfg(feature = \"persistence\")]\n#[test]\nfn test_mix_serialize() {\n    use serde::{Deserialize, Serialize};\n\n    #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]\n    struct Serializable(i32);\n\n    #[derive(Clone, Debug, PartialEq)]\n    struct NonSerializable(f32);\n\n    let id = Id::new(\"a\");\n\n    let mut map: IdTypeMap = Default::default();\n    map.insert_persisted(id, Serializable(555));\n    map.insert_temp(id, NonSerializable(1.0));\n\n    assert_eq!(map.get_temp::<Serializable>(id), Some(Serializable(555)));\n    assert_eq!(\n        map.get_persisted::<Serializable>(id),\n        Some(Serializable(555))\n    );\n    assert_eq!(\n        map.get_temp::<NonSerializable>(id),\n        Some(NonSerializable(1.0))\n    );\n\n    // -----------\n\n    let serialized = ron::to_string(&map).unwrap();\n\n    // ------------\n    // Test removal:\n\n    // We can remove:\n    map.remove::<NonSerializable>(id);\n    assert_eq!(map.get_temp::<NonSerializable>(id), None);\n\n    // Other type is still there, even though it is the same if:\n    assert_eq!(map.get_temp::<Serializable>(id), Some(Serializable(555)));\n    assert_eq!(\n        map.get_persisted::<Serializable>(id),\n        Some(Serializable(555))\n    );\n\n    // But we can still remove the last:\n    map.remove::<Serializable>(id);\n    assert_eq!(map.get_temp::<Serializable>(id), None);\n    assert_eq!(map.get_persisted::<Serializable>(id), None);\n\n    // --------------------\n    // Test deserialization:\n\n    let mut map: IdTypeMap = ron::from_str(&serialized).unwrap();\n    assert_eq!(map.get_temp::<Serializable>(id), None);\n    assert_eq!(\n        map.get_persisted::<Serializable>(id),\n        Some(Serializable(555))\n    );\n    assert_eq!(map.get_temp::<Serializable>(id), Some(Serializable(555)));\n}\n\n#[cfg(feature = \"persistence\")]\n#[test]\nfn test_serialize_generations() {\n    use serde::{Deserialize, Serialize};\n\n    fn serialize_and_deserialize(map: &IdTypeMap) -> IdTypeMap {\n        let serialized = ron::to_string(map).unwrap();\n        ron::from_str(&serialized).unwrap()\n    }\n\n    #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]\n    struct A(i32);\n\n    let mut map: IdTypeMap = Default::default();\n    for i in 0..3 {\n        map.insert_persisted(Id::new(i), A(i));\n    }\n    for i in 0..3 {\n        assert_eq!(map.get_generation::<A>(Id::new(i)), Some(0));\n    }\n\n    map = serialize_and_deserialize(&map);\n\n    // We use generation 0 for non-serilized,\n    // 1 for things that have been serialized but never deserialized,\n    // and then we increment with 1 on each deserialize.\n    // So we should have generation 2 now:\n    for i in 0..3 {\n        assert_eq!(map.get_generation::<A>(Id::new(i)), Some(2));\n    }\n\n    // Reading should reset:\n    assert_eq!(map.get_persisted::<A>(Id::new(0)), Some(A(0)));\n    assert_eq!(map.get_generation::<A>(Id::new(0)), Some(0));\n\n    // Generations should increment:\n    map = serialize_and_deserialize(&map);\n    assert_eq!(map.get_generation::<A>(Id::new(0)), Some(2));\n    assert_eq!(map.get_generation::<A>(Id::new(1)), Some(3));\n}\n\n#[cfg(feature = \"persistence\")]\n#[test]\nfn test_serialize_gc() {\n    use serde::{Deserialize, Serialize};\n\n    fn serialize_and_deserialize(mut map: IdTypeMap, max_bytes_per_type: usize) -> IdTypeMap {\n        map.set_max_bytes_per_type(max_bytes_per_type);\n        let serialized = ron::to_string(&map).unwrap();\n        ron::from_str(&serialized).unwrap()\n    }\n\n    #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]\n    struct A(usize);\n\n    #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]\n    struct B(usize);\n\n    let mut map: IdTypeMap = Default::default();\n\n    let num_a = 1_000;\n    let num_b = 10;\n\n    for i in 0..num_a {\n        map.insert_persisted(Id::new(i), A(i));\n    }\n    for i in 0..num_b {\n        map.insert_persisted(Id::new(i), B(i));\n    }\n\n    map = serialize_and_deserialize(map, 100);\n\n    // We always serialize at least one generation:\n    assert_eq!(map.count::<A>(), num_a);\n    assert_eq!(map.count::<B>(), num_b);\n\n    // Create a new small generation:\n    map.insert_persisted(Id::new(1_000_000), A(1_000_000));\n    map.insert_persisted(Id::new(1_000_000), B(1_000_000));\n\n    assert_eq!(map.count::<A>(), num_a + 1);\n    assert_eq!(map.count::<B>(), num_b + 1);\n\n    // And read a value:\n    assert_eq!(map.get_persisted::<A>(Id::new(0)), Some(A(0)));\n    assert_eq!(map.get_persisted::<B>(Id::new(0)), Some(B(0)));\n\n    map = serialize_and_deserialize(map, 100);\n\n    assert_eq!(\n        map.count::<A>(),\n        2,\n        \"We should have dropped the oldest generation, but kept the new value and the read value\"\n    );\n    assert_eq!(\n        map.count::<B>(),\n        num_b + 1,\n        \"B should fit under the byte limit\"\n    );\n\n    // Create another small generation:\n    map.insert_persisted(Id::new(2_000_000), A(2_000_000));\n    map.insert_persisted(Id::new(2_000_000), B(2_000_000));\n\n    map = serialize_and_deserialize(map, 100);\n\n    assert_eq!(map.count::<A>(), 3); // The read value, plus the two new ones\n    assert_eq!(map.count::<B>(), num_b + 2); // all the old ones, plus two new ones\n\n    // Lower the limit, and we should only have the latest generation:\n\n    map = serialize_and_deserialize(map, 1);\n\n    assert_eq!(map.count::<A>(), 1);\n    assert_eq!(map.count::<B>(), 1);\n\n    assert_eq!(\n        map.get_persisted::<A>(Id::new(2_000_000)),\n        Some(A(2_000_000))\n    );\n    assert_eq!(\n        map.get_persisted::<B>(Id::new(2_000_000)),\n        Some(B(2_000_000))\n    );\n}\n"
  },
  {
    "path": "crates/egui/src/util/mod.rs",
    "content": "//! Miscellaneous tools used by the rest of egui.\n\npub(crate) mod fixed_cache;\npub mod id_type_map;\npub mod undoer;\n\npub use id_type_map::IdTypeMap;\n\npub use epaint::emath::History;\npub use epaint::util::{hash, hash_with};\n\n/// Deprecated alias for [`crate::cache`].\n#[deprecated = \"Use egui::cache instead\"]\npub use crate::cache;\n"
  },
  {
    "path": "crates/egui/src/util/undoer.rs",
    "content": "use std::collections::VecDeque;\n\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Settings {\n    /// Maximum number of undos.\n    /// If your state is resource intensive, you should keep this low.\n    ///\n    /// Default: `100`\n    pub max_undos: usize,\n\n    /// When that state hasn't changed for this many seconds,\n    /// create a new undo point (if one is needed).\n    ///\n    /// Default value: `1.0` seconds.\n    pub stable_time: f32,\n\n    /// If the state is changing so often that we never get to `stable_time`,\n    /// then still create a save point every `auto_save_interval` seconds,\n    /// so we have something to undo to.\n    ///\n    /// Default value: `30` seconds.\n    pub auto_save_interval: f32,\n}\n\nimpl Default for Settings {\n    fn default() -> Self {\n        Self {\n            max_undos: 100,\n            stable_time: 1.0,\n            auto_save_interval: 30.0,\n        }\n    }\n}\n\n/// Automatic undo system.\n///\n/// Every frame you feed it the most recent state.\n/// The [`Undoer`] compares it with the latest undo point\n/// and if there is a change it may create a new undo point.\n///\n/// [`Undoer`] follows two simple rules:\n///\n/// 1) If the state has changed since the latest undo point, but has\n///    remained stable for `stable_time` seconds, an new undo point is created.\n/// 2) If the state does not stabilize within `auto_save_interval` seconds, an undo point is created.\n///\n/// Rule 1) will make sure an undo point is not created until you _stop_ dragging that slider.\n/// Rule 2) will make sure that you will get some undo points even if you are constantly changing the state.\n#[derive(Clone)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Undoer<State> {\n    settings: Settings,\n\n    /// New undoes are added to the back.\n    /// Two adjacent undo points are never equal.\n    /// The latest undo point may (often) be the current state.\n    undos: VecDeque<State>,\n\n    /// Stores redos immediately after a sequence of undos.\n    /// Gets cleared every time the state changes.\n    /// Does not need to be a deque, because there can only be up to `undos.len()` redos,\n    /// which is already limited to `settings.max_undos`.\n    redos: Vec<State>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    flux: Option<Flux<State>>,\n}\n\nimpl<State> std::fmt::Debug for Undoer<State> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let Self { undos, redos, .. } = self;\n        f.debug_struct(\"Undoer\")\n            .field(\"undo count\", &undos.len())\n            .field(\"redo count\", &redos.len())\n            .finish()\n    }\n}\n\nimpl<State> Default for Undoer<State>\nwhere\n    State: Clone + PartialEq,\n{\n    #[inline]\n    fn default() -> Self {\n        Self {\n            settings: Settings::default(),\n            undos: VecDeque::new(),\n            redos: Vec::new(),\n            flux: None,\n        }\n    }\n}\n\n/// Represents how the current state is changing\n#[derive(Clone)]\nstruct Flux<State> {\n    start_time: f64,\n    latest_change_time: f64,\n    latest_state: State,\n}\n\nimpl<State> Undoer<State>\nwhere\n    State: Clone + PartialEq,\n{\n    /// Create a new [`Undoer`] with the given [`Settings`].\n    pub fn with_settings(settings: Settings) -> Self {\n        Self {\n            settings,\n            ..Default::default()\n        }\n    }\n\n    /// Do we have an undo point different from the given state?\n    pub fn has_undo(&self, current_state: &State) -> bool {\n        match self.undos.len() {\n            0 => false,\n            1 => self.undos.back() != Some(current_state),\n            _ => true,\n        }\n    }\n\n    pub fn has_redo(&self, current_state: &State) -> bool {\n        !self.redos.is_empty() && self.undos.back() == Some(current_state)\n    }\n\n    /// Return true if the state is currently changing\n    pub fn is_in_flux(&self) -> bool {\n        self.flux.is_some()\n    }\n\n    pub fn undo(&mut self, current_state: &State) -> Option<&State> {\n        if self.has_undo(current_state) {\n            self.flux = None;\n\n            if self.undos.back() == Some(current_state) {\n                #[expect(clippy::unwrap_used)] // we just checked that undos is not empty\n                self.redos.push(self.undos.pop_back().unwrap());\n            } else {\n                self.redos.push(current_state.clone());\n            }\n\n            // Note: we keep the undo point intact.\n            self.undos.back()\n        } else {\n            None\n        }\n    }\n\n    pub fn redo(&mut self, current_state: &State) -> Option<&State> {\n        if !self.undos.is_empty() && self.undos.back() != Some(current_state) {\n            // state changed since the last undo, redos should be cleared.\n            self.redos.clear();\n            None\n        } else if let Some(state) = self.redos.pop() {\n            self.undos.push_back(state);\n            self.undos.back()\n        } else {\n            None\n        }\n    }\n\n    /// Add an undo point if, and only if, there has been a change since the latest undo point.\n    pub fn add_undo(&mut self, current_state: &State) {\n        if self.undos.back() != Some(current_state) {\n            self.undos.push_back(current_state.clone());\n        }\n        while self.undos.len() > self.settings.max_undos {\n            self.undos.pop_front();\n        }\n        self.flux = None;\n    }\n\n    /// Call this as often as you want (e.g. every frame)\n    /// and [`Undoer`] will determine if a new undo point should be created.\n    ///\n    /// * `current_time`: current time in seconds.\n    pub fn feed_state(&mut self, current_time: f64, current_state: &State) {\n        match self.undos.back() {\n            None => {\n                // First time feed_state is called.\n                // always create an undo point:\n                self.add_undo(current_state);\n            }\n            Some(latest_undo) => {\n                if latest_undo == current_state {\n                    self.flux = None;\n                } else {\n                    self.redos.clear();\n\n                    match self.flux.as_mut() {\n                        None => {\n                            self.flux = Some(Flux {\n                                start_time: current_time,\n                                latest_change_time: current_time,\n                                latest_state: current_state.clone(),\n                            });\n                        }\n                        Some(flux) => {\n                            if &flux.latest_state == current_state {\n                                let time_since_latest_change =\n                                    (current_time - flux.latest_change_time) as f32;\n                                if time_since_latest_change >= self.settings.stable_time {\n                                    self.add_undo(current_state);\n                                }\n                            } else {\n                                let time_since_flux_start = (current_time - flux.start_time) as f32;\n                                if time_since_flux_start >= self.settings.auto_save_interval {\n                                    self.add_undo(current_state);\n                                } else {\n                                    flux.latest_change_time = current_time;\n                                    flux.latest_state = current_state.clone();\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/viewport.rs",
    "content": "//! egui supports multiple viewports, corresponding to multiple native windows.\n//!\n//! Not all egui backends support multiple viewports, but `eframe` native does\n//! (but not on web).\n//!\n//! You can spawn a new viewport using [`Context::show_viewport_deferred`] and [`Context::show_viewport_immediate`].\n//! These needs to be called every frame the viewport should be visible.\n//!\n//! This is implemented by the native `eframe` backend, but not the web one.\n//!\n//! ## Viewport classes\n//! The viewports form a tree of parent-child relationships.\n//!\n//! There are different classes of viewports.\n//!\n//! ### Root viewport\n//! The root viewport is the original viewport, and cannot be closed without closing the application.\n//!\n//! ### Deferred viewports\n//! These are created with [`Context::show_viewport_deferred`].\n//! Deferred viewports take a closure that is called by the integration at a later time, perhaps multiple times.\n//! Deferred viewports are repainted independently of the parent viewport.\n//! This means communication with them needs to be done via channels, or `Arc/Mutex`.\n//!\n//! This is the most performant type of child viewport, though a bit more cumbersome to work with compared to immediate viewports.\n//!\n//! ### Immediate viewports\n//! These are created with [`Context::show_viewport_immediate`].\n//! Immediate viewports take a `FnOnce` closure, similar to other egui functions, and is called immediately.\n//! This makes communication with them much simpler than with deferred viewports, but this simplicity comes at a cost: whenever the parent viewports needs to be repainted, so will the child viewport, and vice versa.\n//! This means that if you have `N` viewports you are potentially doing `N` times as much CPU work. However, if all your viewports are showing animations, and thus are repainting constantly anyway, this doesn't matter.\n//!\n//! In short: immediate viewports are simpler to use, but can waste a lot of CPU time.\n//!\n//! ### Embedded viewports\n//! These are not real, independent viewports, but is a fallback mode for when the integration does not support real viewports.\n//! In your callback is called with [`ViewportClass::EmbeddedWindow`] it means the viewport is embedded inside of\n//! a regular [`crate::Window`], trapped in the parent viewport.\n//!\n//!\n//! ## Using the viewports\n//! Only one viewport is active at any one time, identified with [`Context::viewport_id`].\n//! You can modify the current (change the title, resize the window, etc) by sending\n//! a [`ViewportCommand`] to it using [`Context::send_viewport_cmd`].\n//! You can interact with other viewports using [`Context::send_viewport_cmd_to`].\n//!\n//! There is an example in <https://github.com/emilk/egui/tree/main/examples/multiple_viewports/src/main.rs>.\n//!\n//! You can find all available viewports in [`crate::RawInput::viewports`] and the active viewport in\n//! [`crate::InputState::viewport`]:\n//!\n//! ```no_run\n//! # let ctx = &egui::Context::default();\n//! ctx.input(|i| {\n//!     dbg!(&i.viewport()); // Current viewport\n//!     dbg!(&i.raw.viewports); // All viewports\n//! });\n//! ```\n//!\n//! ## For integrations\n//! * There is a [`crate::InputState::viewport`] with information about the current viewport.\n//! * There is a [`crate::RawInput::viewports`] with information about all viewports.\n//! * The repaint callback set by [`Context::set_request_repaint_callback`] points to which viewport should be repainted.\n//! * [`crate::FullOutput::viewport_output`] is a list of viewports which should result in their own independent windows.\n//! * To support immediate viewports you need to call [`Context::set_immediate_viewport_renderer`].\n//! * If you support viewports, you need to call [`Context::set_embed_viewports`] with `false`, or all new viewports will be embedded (the default behavior).\n//!\n//! ## Future work\n//! There are several more things related to viewports that we want to add.\n//! Read more at <https://github.com/emilk/egui/issues/3556>.\n\nuse std::sync::Arc;\n\nuse epaint::{Pos2, Vec2};\n\nuse crate::{Context, Id, Ui};\n\n// ----------------------------------------------------------------------------\n\n/// The different types of viewports supported by egui.\n#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum ViewportClass {\n    /// The root viewport; i.e. the original window.\n    #[default]\n    Root,\n\n    /// A viewport run independently from the parent viewport.\n    ///\n    /// This is the preferred type of viewport from a performance perspective.\n    ///\n    /// Create these with [`crate::Context::show_viewport_deferred`].\n    Deferred,\n\n    /// A viewport run inside the parent viewport.\n    ///\n    /// This is the easier type of viewport to use, but it is less performant\n    /// at it requires both parent and child to repaint if any one of them needs repainting,\n    /// which effectively produces double work for two viewports, and triple work for three viewports, etc.\n    ///\n    /// Create these with [`crate::Context::show_viewport_immediate`].\n    Immediate,\n\n    /// The fallback, when the egui integration doesn't support viewports,\n    /// or [`crate::Context::embed_viewports`] is set to `true`.\n    ///\n    /// If you get this, it is because you are already wrapped in a [`crate::Window`]\n    /// inside of the parent viewport.\n    EmbeddedWindow,\n}\n\n// ----------------------------------------------------------------------------\n\n/// A unique identifier of a viewport.\n///\n/// This is returned by [`Context::viewport_id`] and [`Context::parent_viewport_id`].\n#[derive(Clone, Copy, Hash, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct ViewportId(pub Id);\n\n// We implement `PartialOrd` and `Ord` so we can use `ViewportId` in a `BTreeMap`,\n// which allows predicatable iteration order, frame-to-frame.\nimpl PartialOrd for ViewportId {\n    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Ord for ViewportId {\n    fn cmp(&self, other: &Self) -> std::cmp::Ordering {\n        self.0.value().cmp(&other.0.value())\n    }\n}\n\nimpl Default for ViewportId {\n    #[inline]\n    fn default() -> Self {\n        Self::ROOT\n    }\n}\n\nimpl std::fmt::Debug for ViewportId {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        self.0.short_debug_format().fmt(f)\n    }\n}\n\nimpl ViewportId {\n    /// The `ViewportId` of the root viewport.\n    pub const ROOT: Self = Self(Id::NULL);\n\n    #[inline]\n    pub fn from_hash_of(source: impl std::hash::Hash) -> Self {\n        Self(Id::new(source))\n    }\n}\n\nimpl From<ViewportId> for Id {\n    #[inline]\n    fn from(id: ViewportId) -> Self {\n        id.0\n    }\n}\n\nimpl nohash_hasher::IsEnabled for ViewportId {}\n\n/// A fast hash set of [`ViewportId`].\npub type ViewportIdSet = nohash_hasher::IntSet<ViewportId>;\n\n/// A fast hash map from [`ViewportId`] to `T`.\npub type ViewportIdMap<T> = nohash_hasher::IntMap<ViewportId, T>;\n\n/// An order map from [`ViewportId`] to `T`.\npub type OrderedViewportIdMap<T> = std::collections::BTreeMap<ViewportId, T>;\n\n// ----------------------------------------------------------------------------\n\n/// Image data for an application icon.\n///\n/// Use a square image, e.g. 256x256 pixels.\n/// You can use a transparent background.\n#[derive(Clone, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct IconData {\n    /// RGBA pixels, with separate/unmultiplied alpha.\n    pub rgba: Vec<u8>,\n\n    /// Image width. This should be a multiple of 4.\n    pub width: u32,\n\n    /// Image height. This should be a multiple of 4.\n    pub height: u32,\n}\n\nimpl IconData {\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.rgba.is_empty()\n    }\n}\n\nimpl std::fmt::Debug for IconData {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"IconData\")\n            .field(\"width\", &self.width)\n            .field(\"height\", &self.height)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl From<IconData> for epaint::ColorImage {\n    fn from(icon: IconData) -> Self {\n        profiling::function_scope!();\n        let IconData {\n            rgba,\n            width,\n            height,\n        } = icon;\n        Self::from_rgba_premultiplied([width as usize, height as usize], &rgba)\n    }\n}\n\nimpl From<&IconData> for epaint::ColorImage {\n    fn from(icon: &IconData) -> Self {\n        profiling::function_scope!();\n        let IconData {\n            rgba,\n            width,\n            height,\n        } = icon;\n        Self::from_rgba_premultiplied([*width as usize, *height as usize], rgba)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A pair of [`ViewportId`], used to identify a viewport and its parent.\n#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct ViewportIdPair {\n    pub this: ViewportId,\n    pub parent: ViewportId,\n}\n\nimpl Default for ViewportIdPair {\n    #[inline]\n    fn default() -> Self {\n        Self::ROOT\n    }\n}\n\nimpl ViewportIdPair {\n    /// The `ViewportIdPair` of the root viewport, which is its own parent.\n    pub const ROOT: Self = Self {\n        this: ViewportId::ROOT,\n        parent: ViewportId::ROOT,\n    };\n\n    #[inline]\n    pub fn from_self_and_parent(this: ViewportId, parent: ViewportId) -> Self {\n        Self { this, parent }\n    }\n}\n\n/// The user-code that shows the ui in the viewport, used for deferred viewports.\npub type DeferredViewportUiCallback = dyn Fn(&mut Ui) + Sync + Send;\n\n/// Render the given viewport, calling the given ui callback.\npub type ImmediateViewportRendererCallback = dyn for<'a> Fn(&Context, ImmediateViewport<'a>);\n\n/// Control the building of a new egui viewport (i.e. native window).\n///\n/// See [`crate::viewport`] for how to build new viewports (native windows).\n///\n/// The fields are public, but you should use the builder pattern to set them,\n/// and that's where you'll find the documentation too.\n///\n/// Since egui is immediate mode, `ViewportBuilder` is accumulative in nature.\n/// Setting any option to `None` means \"keep the current value\",\n/// or \"Use the default\" if it is the first call.\n///\n/// The default values are implementation defined, so you may want to explicitly\n/// configure the size of the window, and what buttons are shown.\n#[derive(Clone, Debug, Default, Eq, PartialEq)]\npub struct ViewportBuilder {\n    /// The title of the viewport.\n    /// `eframe` will use this as the title of the native window.\n    pub title: Option<String>,\n\n    /// This is wayland only. See [`Self::with_app_id`].\n    pub app_id: Option<String>,\n\n    /// The desired outer position of the window.\n    pub position: Option<Pos2>,\n    pub inner_size: Option<Vec2>,\n    pub min_inner_size: Option<Vec2>,\n    pub max_inner_size: Option<Vec2>,\n\n    /// Whether clamp the window's size to monitor's size. The default is `true` on linux, otherwise it is `false`.\n    ///\n    /// Note: On some Linux systems, a window size larger than the monitor causes crashes\n    pub clamp_size_to_monitor_size: Option<bool>,\n\n    pub fullscreen: Option<bool>,\n    pub maximized: Option<bool>,\n    pub resizable: Option<bool>,\n    pub transparent: Option<bool>,\n    pub decorations: Option<bool>,\n    pub icon: Option<Arc<IconData>>,\n    pub active: Option<bool>,\n    pub visible: Option<bool>,\n\n    // macOS:\n    pub fullsize_content_view: Option<bool>,\n    pub movable_by_window_background: Option<bool>,\n    pub title_shown: Option<bool>,\n    pub titlebar_buttons_shown: Option<bool>,\n    pub titlebar_shown: Option<bool>,\n    pub has_shadow: Option<bool>,\n\n    // windows:\n    pub drag_and_drop: Option<bool>,\n    pub taskbar: Option<bool>,\n\n    pub close_button: Option<bool>,\n    pub minimize_button: Option<bool>,\n    pub maximize_button: Option<bool>,\n\n    pub window_level: Option<WindowLevel>,\n\n    pub mouse_passthrough: Option<bool>,\n\n    // X11\n    pub window_type: Option<X11WindowType>,\n    pub override_redirect: Option<bool>,\n}\n\nimpl ViewportBuilder {\n    /// Sets the initial title of the window in the title bar.\n    ///\n    /// Look at winit for more details\n    #[inline]\n    pub fn with_title(mut self, title: impl Into<String>) -> Self {\n        self.title = Some(title.into());\n        self\n    }\n\n    /// Sets whether the window should have a border, a title bar, etc.\n    ///\n    /// The default is `true`.\n    ///\n    /// Look at winit for more details\n    #[inline]\n    pub fn with_decorations(mut self, decorations: bool) -> Self {\n        self.decorations = Some(decorations);\n        self\n    }\n\n    /// Sets whether the window should be put into fullscreen upon creation.\n    ///\n    /// The default is `None`.\n    ///\n    /// Look at winit for more details\n    /// This will use borderless\n    #[inline]\n    pub fn with_fullscreen(mut self, fullscreen: bool) -> Self {\n        self.fullscreen = Some(fullscreen);\n        self\n    }\n\n    /// Request that the window is maximized upon creation.\n    ///\n    /// The default is `false`.\n    ///\n    /// Look at winit for more details\n    #[inline]\n    pub fn with_maximized(mut self, maximized: bool) -> Self {\n        self.maximized = Some(maximized);\n        self\n    }\n\n    /// Sets whether the window is resizable or not.\n    ///\n    /// The default is `true`.\n    ///\n    /// Look at winit for more details\n    #[inline]\n    pub fn with_resizable(mut self, resizable: bool) -> Self {\n        self.resizable = Some(resizable);\n        self\n    }\n\n    /// Sets whether the background of the window should be transparent.\n    ///\n    /// You should avoid having a [`crate::CentralPanel`], or make sure its frame is also transparent.\n    ///\n    /// In `eframe` you control the transparency with `eframe::App::clear_color()`.\n    ///\n    /// If this is `true`, writing colors with alpha values different than\n    /// `1.0` will produce a transparent window. On some platforms this\n    /// is more of a hint for the system and you'd still have the alpha\n    /// buffer.\n    ///\n    /// The default is `false`.\n    /// If this is not working, it's because the graphic context doesn't support transparency,\n    /// you will need to set the transparency in the eframe!\n    ///\n    /// ## Platform-specific\n    ///\n    /// **macOS:** When using this feature to create an overlay-like UI, you likely want to combine this with [`Self::with_has_shadow`] set to `false` in order to avoid ghosting artifacts.\n    #[inline]\n    pub fn with_transparent(mut self, transparent: bool) -> Self {\n        self.transparent = Some(transparent);\n        self\n    }\n\n    /// The application icon, e.g. in the Windows task bar or the alt-tab menu.\n    ///\n    /// The default icon is a white `e` on a black background (for \"egui\" or \"eframe\").\n    /// If you prefer the OS default, set this to `IconData::default()`.\n    #[inline]\n    pub fn with_icon(mut self, icon: impl Into<Arc<IconData>>) -> Self {\n        self.icon = Some(icon.into());\n        self\n    }\n\n    /// Whether the window will be initially focused or not.\n    ///\n    /// The window should be assumed as not focused by default\n    ///\n    /// ## Platform-specific:\n    ///\n    /// **Android / iOS / X11 / Wayland / Orbital:** Unsupported.\n    ///\n    /// Look at winit for more details\n    #[inline]\n    pub fn with_active(mut self, active: bool) -> Self {\n        self.active = Some(active);\n        self\n    }\n\n    /// Sets whether the window will be initially visible or hidden.\n    ///\n    /// The default is to show the window.\n    ///\n    /// Look at winit for more details\n    #[inline]\n    pub fn with_visible(mut self, visible: bool) -> Self {\n        self.visible = Some(visible);\n        self\n    }\n\n    /// macOS: Makes the window content appear behind the titlebar.\n    ///\n    /// You often want to combine this with [`Self::with_titlebar_shown`]\n    /// and [`Self::with_title_shown`].\n    #[inline]\n    pub fn with_fullsize_content_view(mut self, value: bool) -> Self {\n        self.fullsize_content_view = Some(value);\n        self\n    }\n\n    /// macOS: Set to `true` to allow the window to be moved by dragging the background.\n    /// Enabling this feature can result in unexpected behavior with draggable UI widgets such as sliders.\n    #[inline]\n    pub fn with_movable_by_background(mut self, value: bool) -> Self {\n        self.movable_by_window_background = Some(value);\n        self\n    }\n\n    /// macOS: Set to `false` to hide the window title.\n    #[inline]\n    pub fn with_title_shown(mut self, title_shown: bool) -> Self {\n        self.title_shown = Some(title_shown);\n        self\n    }\n\n    /// macOS: Set to `false` to hide the titlebar button (close, minimize, maximize)\n    #[inline]\n    pub fn with_titlebar_buttons_shown(mut self, titlebar_buttons_shown: bool) -> Self {\n        self.titlebar_buttons_shown = Some(titlebar_buttons_shown);\n        self\n    }\n\n    /// macOS: Set to `false` to make the titlebar transparent, allowing the content to appear behind it.\n    #[inline]\n    pub fn with_titlebar_shown(mut self, shown: bool) -> Self {\n        self.titlebar_shown = Some(shown);\n        self\n    }\n\n    /// macOS: Set to `false` to make the window render without a drop shadow.\n    ///\n    /// The default is `true`.\n    ///\n    /// Disabling this feature can solve ghosting issues experienced if using [`Self::with_transparent`].\n    ///\n    /// Look at winit for more details\n    #[inline]\n    pub fn with_has_shadow(mut self, has_shadow: bool) -> Self {\n        self.has_shadow = Some(has_shadow);\n        self\n    }\n\n    /// windows: Whether show or hide the window icon in the taskbar.\n    #[inline]\n    pub fn with_taskbar(mut self, show: bool) -> Self {\n        self.taskbar = Some(show);\n        self\n    }\n\n    /// Requests the window to be of specific dimensions.\n    ///\n    /// If this is not set, some platform-specific dimensions will be used.\n    ///\n    /// Should be bigger than 0\n    /// Look at winit for more details\n    #[inline]\n    pub fn with_inner_size(mut self, size: impl Into<Vec2>) -> Self {\n        self.inner_size = Some(size.into());\n        self\n    }\n\n    /// Sets the minimum dimensions a window can have.\n    ///\n    /// If this is not set, the window will have no minimum dimensions (aside\n    /// from reserved).\n    ///\n    /// Should be bigger than 0\n    /// Look at winit for more details\n    #[inline]\n    pub fn with_min_inner_size(mut self, size: impl Into<Vec2>) -> Self {\n        self.min_inner_size = Some(size.into());\n        self\n    }\n\n    /// Sets the maximum dimensions a window can have.\n    ///\n    /// If this is not set, the window will have no maximum or will be set to\n    /// the primary monitor's dimensions by the platform.\n    ///\n    /// Should be bigger than 0\n    /// Look at winit for more details\n    #[inline]\n    pub fn with_max_inner_size(mut self, size: impl Into<Vec2>) -> Self {\n        self.max_inner_size = Some(size.into());\n        self\n    }\n\n    /// Sets whether clamp the window's size to monitor's size. The default is `true` on linux, otherwise it is `false`.\n    ///\n    /// Note: On some Linux systems, a window size larger than the monitor causes crashes\n    #[inline]\n    pub fn with_clamp_size_to_monitor_size(mut self, value: bool) -> Self {\n        self.clamp_size_to_monitor_size = Some(value);\n        self\n    }\n\n    /// Does not work on X11.\n    #[inline]\n    pub fn with_close_button(mut self, value: bool) -> Self {\n        self.close_button = Some(value);\n        self\n    }\n\n    /// Does not work on X11.\n    #[inline]\n    pub fn with_minimize_button(mut self, value: bool) -> Self {\n        self.minimize_button = Some(value);\n        self\n    }\n\n    /// Does not work on X11.\n    #[inline]\n    pub fn with_maximize_button(mut self, value: bool) -> Self {\n        self.maximize_button = Some(value);\n        self\n    }\n\n    /// On Windows: enable drag and drop support. Drag and drop can\n    /// not be disabled on other platforms.\n    ///\n    /// See [winit's documentation][drag_and_drop] for information on why you\n    /// might want to disable this on windows.\n    ///\n    /// [drag_and_drop]: https://docs.rs/winit/latest/x86_64-pc-windows-msvc/winit/platform/windows/trait.WindowAttributesExtWindows.html#tymethod.with_drag_and_drop\n    #[inline]\n    pub fn with_drag_and_drop(mut self, value: bool) -> Self {\n        self.drag_and_drop = Some(value);\n        self\n    }\n\n    /// The initial \"outer\" position of the window,\n    /// i.e. where the top-left corner of the frame/chrome should be.\n    ///\n    /// **`eframe` notes**:\n    ///\n    /// - **iOS:** Sets the top left coordinates of the window in the screen space coordinate system.\n    /// - **Web:** Sets the top-left coordinates relative to the viewport. Doesn't account for CSS\n    ///   [`transform`].\n    /// - **Android / Wayland:** Unsupported.\n    ///\n    /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform\n    #[inline]\n    pub fn with_position(mut self, pos: impl Into<Pos2>) -> Self {\n        self.position = Some(pos.into());\n        self\n    }\n\n    /// ### On Wayland\n    /// On Wayland this sets the Application ID for the window.\n    ///\n    /// The application ID is used in several places of the compositor, e.g. for\n    /// grouping windows of the same application. It is also important for\n    /// connecting the configuration of a `.desktop` file with the window, by\n    /// using the application ID as file name. This allows e.g. a proper icon\n    /// handling under Wayland.\n    ///\n    /// See [Waylands XDG shell documentation][xdg-shell] for more information\n    /// on this Wayland-specific option.\n    ///\n    /// The `app_id` should match the `.desktop` file distributed with your program.\n    ///\n    /// For details about application ID conventions, see the\n    /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)\n    ///\n    /// [xdg-shell]: https://wayland.app/protocols/xdg-shell#xdg_toplevel:request:set_app_id\n    ///\n    /// ### eframe\n    /// On eframe, the `app_id` of the root window is also used to determine\n    /// the storage location of persistence files.\n    #[inline]\n    pub fn with_app_id(mut self, app_id: impl Into<String>) -> Self {\n        self.app_id = Some(app_id.into());\n        self\n    }\n\n    /// Control if window is always-on-top, always-on-bottom, or neither.\n    ///\n    /// For platform compatibility see [`crate::viewport::WindowLevel`] documentation\n    #[inline]\n    pub fn with_window_level(mut self, level: WindowLevel) -> Self {\n        self.window_level = Some(level);\n        self\n    }\n\n    /// This window is always on top\n    ///\n    /// For platform compatibility see [`crate::viewport::WindowLevel`] documentation\n    #[inline]\n    pub fn with_always_on_top(self) -> Self {\n        self.with_window_level(WindowLevel::AlwaysOnTop)\n    }\n\n    /// On desktop: mouse clicks pass through the window, used for non-interactable overlays.\n    ///\n    /// Generally you would use this in conjunction with [`Self::with_transparent`]\n    /// and [`Self::with_always_on_top`].\n    #[inline]\n    pub fn with_mouse_passthrough(mut self, value: bool) -> Self {\n        self.mouse_passthrough = Some(value);\n        self\n    }\n\n    /// ### On X11\n    /// This sets the window type.\n    /// Maps directly to [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm/1.5/ar01s05.html#id-1.6.7).\n    #[inline]\n    pub fn with_window_type(mut self, value: X11WindowType) -> Self {\n        self.window_type = Some(value);\n        self\n    }\n\n    /// ### On X11\n    /// This sets the override-redirect flag. When this is set to true the window type should be specified.\n    /// Maps directly to [`Override-redirect windows`](https://specifications.freedesktop.org/wm/1.5/ar01s02.html#id-1.3.13).\n    #[inline]\n    pub fn with_override_redirect(mut self, value: bool) -> Self {\n        self.override_redirect = Some(value);\n        self\n    }\n\n    /// Update this `ViewportBuilder` with a delta,\n    /// returning a list of commands and a bool indicating if the window needs to be recreated.\n    #[must_use]\n    pub fn patch(&mut self, new_vp_builder: Self) -> (Vec<ViewportCommand>, bool) {\n        #![expect(clippy::useless_let_if_seq)] // False positive\n\n        let Self {\n            title: new_title,\n            app_id: new_app_id,\n            position: new_position,\n            inner_size: new_inner_size,\n            min_inner_size: new_min_inner_size,\n            max_inner_size: new_max_inner_size,\n            clamp_size_to_monitor_size: new_clamp_size_to_monitor_size,\n            fullscreen: new_fullscreen,\n            maximized: new_maximized,\n            resizable: new_resizable,\n            transparent: new_transparent,\n            decorations: new_decorations,\n            icon: new_icon,\n            active: new_active,\n            visible: new_visible,\n            drag_and_drop: new_drag_and_drop,\n            fullsize_content_view: new_fullsize_content_view,\n            movable_by_window_background: new_movable_by_window_background,\n            title_shown: new_title_shown,\n            titlebar_buttons_shown: new_titlebar_buttons_shown,\n            titlebar_shown: new_titlebar_shown,\n            has_shadow: new_has_shadow,\n            close_button: new_close_button,\n            minimize_button: new_minimize_button,\n            maximize_button: new_maximize_button,\n            window_level: new_window_level,\n            mouse_passthrough: new_mouse_passthrough,\n            taskbar: new_taskbar,\n            window_type: new_window_type,\n            override_redirect: new_override_redirect,\n        } = new_vp_builder;\n\n        let mut commands = Vec::new();\n\n        if let Some(new_title) = new_title\n            && Some(&new_title) != self.title.as_ref()\n        {\n            self.title = Some(new_title.clone());\n            commands.push(ViewportCommand::Title(new_title));\n        }\n\n        if let Some(new_position) = new_position\n            && Some(new_position) != self.position\n        {\n            self.position = Some(new_position);\n            commands.push(ViewportCommand::OuterPosition(new_position));\n        }\n\n        if let Some(new_inner_size) = new_inner_size\n            && Some(new_inner_size) != self.inner_size\n        {\n            self.inner_size = Some(new_inner_size);\n            commands.push(ViewportCommand::InnerSize(new_inner_size));\n        }\n\n        if let Some(new_min_inner_size) = new_min_inner_size\n            && Some(new_min_inner_size) != self.min_inner_size\n        {\n            self.min_inner_size = Some(new_min_inner_size);\n            commands.push(ViewportCommand::MinInnerSize(new_min_inner_size));\n        }\n\n        if let Some(new_max_inner_size) = new_max_inner_size\n            && Some(new_max_inner_size) != self.max_inner_size\n        {\n            self.max_inner_size = Some(new_max_inner_size);\n            commands.push(ViewportCommand::MaxInnerSize(new_max_inner_size));\n        }\n\n        if let Some(new_fullscreen) = new_fullscreen\n            && Some(new_fullscreen) != self.fullscreen\n        {\n            self.fullscreen = Some(new_fullscreen);\n            commands.push(ViewportCommand::Fullscreen(new_fullscreen));\n        }\n\n        if let Some(new_maximized) = new_maximized\n            && Some(new_maximized) != self.maximized\n        {\n            self.maximized = Some(new_maximized);\n            commands.push(ViewportCommand::Maximized(new_maximized));\n        }\n\n        if let Some(new_resizable) = new_resizable\n            && Some(new_resizable) != self.resizable\n        {\n            self.resizable = Some(new_resizable);\n            commands.push(ViewportCommand::Resizable(new_resizable));\n        }\n\n        if let Some(new_transparent) = new_transparent\n            && Some(new_transparent) != self.transparent\n        {\n            self.transparent = Some(new_transparent);\n            commands.push(ViewportCommand::Transparent(new_transparent));\n        }\n\n        if let Some(new_decorations) = new_decorations\n            && Some(new_decorations) != self.decorations\n        {\n            self.decorations = Some(new_decorations);\n            commands.push(ViewportCommand::Decorations(new_decorations));\n        }\n\n        if let Some(new_icon) = new_icon {\n            let is_new = match &self.icon {\n                Some(existing) => !Arc::ptr_eq(&new_icon, existing),\n                None => true,\n            };\n\n            if is_new {\n                commands.push(ViewportCommand::Icon(Some(Arc::clone(&new_icon))));\n                self.icon = Some(new_icon);\n            }\n        }\n\n        if let Some(new_visible) = new_visible\n            && Some(new_visible) != self.visible\n        {\n            self.visible = Some(new_visible);\n            commands.push(ViewportCommand::Visible(new_visible));\n        }\n\n        if let Some(new_mouse_passthrough) = new_mouse_passthrough\n            && Some(new_mouse_passthrough) != self.mouse_passthrough\n        {\n            self.mouse_passthrough = Some(new_mouse_passthrough);\n            commands.push(ViewportCommand::MousePassthrough(new_mouse_passthrough));\n        }\n\n        if let Some(new_window_level) = new_window_level\n            && Some(new_window_level) != self.window_level\n        {\n            self.window_level = Some(new_window_level);\n            commands.push(ViewportCommand::WindowLevel(new_window_level));\n        }\n\n        // --------------------------------------------------------------\n        // Things we don't have commands for require a full window recreation.\n        // The reason we don't have commands for them is that `winit` doesn't support\n        // changing them without recreating the window.\n\n        let mut recreate_window = false;\n\n        if new_clamp_size_to_monitor_size.is_some()\n            && self.clamp_size_to_monitor_size != new_clamp_size_to_monitor_size\n        {\n            self.clamp_size_to_monitor_size = new_clamp_size_to_monitor_size;\n            recreate_window = true;\n        }\n\n        if new_active.is_some() && self.active != new_active {\n            self.active = new_active;\n            recreate_window = true;\n        }\n\n        if new_app_id.is_some() && self.app_id != new_app_id {\n            self.app_id = new_app_id;\n            recreate_window = true;\n        }\n\n        if new_close_button.is_some() && self.close_button != new_close_button {\n            self.close_button = new_close_button;\n            recreate_window = true;\n        }\n\n        if new_minimize_button.is_some() && self.minimize_button != new_minimize_button {\n            self.minimize_button = new_minimize_button;\n            recreate_window = true;\n        }\n\n        if new_maximize_button.is_some() && self.maximize_button != new_maximize_button {\n            self.maximize_button = new_maximize_button;\n            recreate_window = true;\n        }\n\n        if new_title_shown.is_some() && self.title_shown != new_title_shown {\n            self.title_shown = new_title_shown;\n            recreate_window = true;\n        }\n\n        if new_titlebar_buttons_shown.is_some()\n            && self.titlebar_buttons_shown != new_titlebar_buttons_shown\n        {\n            self.titlebar_buttons_shown = new_titlebar_buttons_shown;\n            recreate_window = true;\n        }\n\n        if new_titlebar_shown.is_some() && self.titlebar_shown != new_titlebar_shown {\n            self.titlebar_shown = new_titlebar_shown;\n            recreate_window = true;\n        }\n\n        if new_has_shadow.is_some() && self.has_shadow != new_has_shadow {\n            self.has_shadow = new_has_shadow;\n            recreate_window = true;\n        }\n\n        if new_taskbar.is_some() && self.taskbar != new_taskbar {\n            self.taskbar = new_taskbar;\n            recreate_window = true;\n        }\n\n        if new_fullsize_content_view.is_some()\n            && self.fullsize_content_view != new_fullsize_content_view\n        {\n            self.fullsize_content_view = new_fullsize_content_view;\n            recreate_window = true;\n        }\n\n        if new_movable_by_window_background.is_some()\n            && self.movable_by_window_background != new_movable_by_window_background\n        {\n            self.movable_by_window_background = new_movable_by_window_background;\n            recreate_window = true;\n        }\n\n        if new_drag_and_drop.is_some() && self.drag_and_drop != new_drag_and_drop {\n            self.drag_and_drop = new_drag_and_drop;\n            recreate_window = true;\n        }\n\n        if new_window_type.is_some() && self.window_type != new_window_type {\n            self.window_type = new_window_type;\n            recreate_window = true;\n        }\n\n        if new_override_redirect.is_some() && self.override_redirect != new_override_redirect {\n            self.override_redirect = new_override_redirect;\n            recreate_window = true;\n        }\n\n        (commands, recreate_window)\n    }\n}\n\n/// For winit platform compatibility, see [`winit::WindowLevel` documentation](https://docs.rs/winit/latest/winit/window/enum.WindowLevel.html#platform-specific)\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum WindowLevel {\n    #[default]\n    Normal,\n    AlwaysOnBottom,\n    AlwaysOnTop,\n}\n\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum X11WindowType {\n    /// This is a normal, top-level window.\n    #[default]\n    Normal,\n\n    /// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the\n    /// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying\n    /// root window clicks.\n    Desktop,\n\n    /// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.\n    Dock,\n\n    /// Toolbar windows. \"Torn off\" from the main application.\n    Toolbar,\n\n    /// Pinnable menu windows. \"Torn off\" from the main application.\n    Menu,\n\n    /// A small persistent utility window, such as a palette or toolbox.\n    Utility,\n\n    /// The window is a splash screen displayed as an application is starting up.\n    Splash,\n\n    /// This is a dialog window.\n    Dialog,\n\n    /// A dropdown menu that usually appears when the user clicks on an item in a menu bar.\n    /// This property is typically used on override-redirect windows.\n    DropdownMenu,\n\n    /// A popup menu that usually appears when the user right clicks on an object.\n    /// This property is typically used on override-redirect windows.\n    PopupMenu,\n\n    /// A tooltip window. Usually used to show additional information when hovering over an object with the cursor.\n    /// This property is typically used on override-redirect windows.\n    Tooltip,\n\n    /// The window is a notification.\n    /// This property is typically used on override-redirect windows.\n    Notification,\n\n    /// This should be used on the windows that are popped up by combo boxes.\n    /// This property is typically used on override-redirect windows.\n    Combo,\n\n    /// This indicates the window is being dragged.\n    /// This property is typically used on override-redirect windows.\n    Dnd,\n}\n\n#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum IMEPurpose {\n    #[default]\n    Normal,\n    Password,\n    Terminal,\n}\n\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum SystemTheme {\n    #[default]\n    SystemDefault,\n    Light,\n    Dark,\n}\n\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum CursorGrab {\n    #[default]\n    None,\n    Confined,\n    Locked,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum ResizeDirection {\n    North,\n    South,\n    East,\n    West,\n    NorthEast,\n    SouthEast,\n    NorthWest,\n    SouthWest,\n}\n\n/// An output [viewport](crate::viewport)-command from egui to the backend, e.g. to change the window title or size.\n///\n/// You can send a [`ViewportCommand`] to the viewport with [`Context::send_viewport_cmd`].\n///\n/// See [`crate::viewport`] for how to build new viewports (native windows).\n///\n/// All coordinates are in logical points.\n///\n/// [`ViewportCommand`] is essentially a way to diff [`ViewportBuilder`]s.\n///\n/// Only commands specific to a viewport are part of [`ViewportCommand`].\n/// Other commands should be put in [`crate::OutputCommand`].\n#[derive(Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum ViewportCommand {\n    /// Request this viewport to be closed.\n    ///\n    /// For the root viewport, this usually results in the application shutting down.\n    /// For other viewports, the [`crate::ViewportInfo::close_requested`] flag will be set.\n    Close,\n\n    /// Cancel the closing that was signaled by [`crate::ViewportInfo::close_requested`].\n    CancelClose,\n\n    /// Set the window title.\n    Title(String),\n\n    /// Turn the window transparent or not.\n    Transparent(bool),\n\n    /// Set the visibility of the window.\n    Visible(bool),\n\n    /// Moves the window with the left mouse button until the button is released.\n    ///\n    /// There's no guarantee that this will work unless the left mouse button was pressed\n    /// immediately before this function is called.\n    StartDrag,\n\n    /// Set the outer position of the viewport, i.e. moves the window.\n    OuterPosition(Pos2),\n\n    /// Should be bigger than 0\n    InnerSize(Vec2),\n\n    /// Should be bigger than 0\n    MinInnerSize(Vec2),\n\n    /// Should be bigger than 0\n    MaxInnerSize(Vec2),\n\n    /// Should be bigger than 0\n    ResizeIncrements(Option<Vec2>),\n\n    /// Begin resizing the viewport with the left mouse button until the button is released.\n    ///\n    /// There's no guarantee that this will work unless the left mouse button was pressed\n    /// immediately before this function is called.\n    BeginResize(ResizeDirection),\n\n    /// Can the window be resized?\n    Resizable(bool),\n\n    /// Set which window buttons are enabled\n    EnableButtons {\n        close: bool,\n        minimized: bool,\n        maximize: bool,\n    },\n    Minimized(bool),\n\n    /// Maximize or unmaximize window.\n    Maximized(bool),\n\n    /// Turn borderless fullscreen on/off.\n    Fullscreen(bool),\n\n    /// Show window decorations, i.e. the chrome around the content\n    /// with the title bar, close buttons, resize handles, etc.\n    Decorations(bool),\n\n    /// Set window to be always-on-top, always-on-bottom, or neither.\n    WindowLevel(WindowLevel),\n\n    /// The window icon.\n    Icon(Option<Arc<IconData>>),\n\n    /// Set the IME cursor editing area.\n    IMERect(crate::Rect),\n    IMEAllowed(bool),\n    IMEPurpose(IMEPurpose),\n\n    /// Bring the window into focus (native only).\n    ///\n    /// This command puts the window on top of other applications and takes input focus away from them,\n    /// which, if unexpected, will disturb the user.\n    ///\n    /// Has no effect on Wayland, or if the window is minimized or invisible.\n    Focus,\n\n    /// If the window is unfocused, attract the user's attention (native only).\n    ///\n    /// Typically, this means that the window will flash on the taskbar, or bounce, until it is interacted with.\n    ///\n    /// When the window comes into focus, or if `None` is passed, the attention request will be automatically reset.\n    ///\n    /// See [winit's documentation][user_attention_details] for platform-specific effect details.\n    ///\n    /// [user_attention_details]: https://docs.rs/winit/latest/winit/window/enum.UserAttentionType.html\n    RequestUserAttention(crate::UserAttentionType),\n\n    SetTheme(SystemTheme),\n\n    ContentProtected(bool),\n\n    /// Will probably not work as expected!\n    CursorPosition(Pos2),\n\n    CursorGrab(CursorGrab),\n\n    CursorVisible(bool),\n\n    /// Enable mouse pass-through: mouse clicks pass through the window, used for non-interactable overlays.\n    MousePassthrough(bool),\n\n    /// Take a screenshot of the next frame after this.\n    ///\n    /// The results are returned in [`crate::Event::Screenshot`].\n    Screenshot(crate::UserData),\n\n    /// Request cut of the current selection\n    ///\n    /// This is equivalent to the system keyboard shortcut for cut (e.g. CTRL + X).\n    RequestCut,\n\n    /// Request a copy of the current selection.\n    ///\n    /// This is equivalent to the system keyboard shortcut for copy (e.g. CTRL + C).\n    RequestCopy,\n\n    /// Request a paste from the clipboard to the current focused `TextEdit` if any.\n    ///\n    /// This is equivalent to the system keyboard shortcut for paste (e.g. CTRL + V).\n    RequestPaste,\n}\n\nimpl ViewportCommand {\n    /// Construct a command to center the viewport on the monitor, if possible.\n    pub fn center_on_screen(ctx: &crate::Context) -> Option<Self> {\n        ctx.input(|i| {\n            let outer_rect = i.viewport().outer_rect?;\n            let size = outer_rect.size();\n            let monitor_size = i.viewport().monitor_size?;\n            if 1.0 < monitor_size.x && 1.0 < monitor_size.y {\n                let x = (monitor_size.x - size.x) / 2.0;\n                let y = (monitor_size.y - size.y) / 2.0;\n                Some(Self::OuterPosition([x, y].into()))\n            } else {\n                None\n            }\n        })\n    }\n\n    /// This command requires the parent viewport to repaint.\n    pub fn requires_parent_repaint(&self) -> bool {\n        self == &Self::Close\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Describes a viewport, i.e. a native window.\n///\n/// This is returned by [`crate::Context::run`] on each frame, and should be applied\n/// by the integration.\n#[derive(Clone)]\npub struct ViewportOutput {\n    /// Id of our parent viewport.\n    pub parent: ViewportId,\n\n    /// What type of viewport are we?\n    ///\n    /// This will never be [`ViewportClass::EmbeddedWindow`],\n    /// since those don't result in real viewports.\n    pub class: ViewportClass,\n\n    /// The window attributes such as title, position, size, etc.\n    ///\n    /// Use this when first constructing the native window.\n    /// Also check for changes in it using [`ViewportBuilder::patch`],\n    /// and apply them as needed.\n    pub builder: ViewportBuilder,\n\n    /// The user-code that shows the GUI, used for deferred viewports.\n    ///\n    /// `None` for immediate viewports and the ROOT viewport.\n    pub viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,\n\n    /// Commands to change the viewport, e.g. window title and size.\n    pub commands: Vec<ViewportCommand>,\n\n    /// Schedule a repaint of this viewport after this delay.\n    ///\n    /// It is preferable to instead install a [`Context::set_request_repaint_callback`],\n    /// but if you haven't, you can use this instead.\n    ///\n    /// If the duration is zero, schedule a repaint immediately.\n    pub repaint_delay: std::time::Duration,\n}\n\nimpl ViewportOutput {\n    /// Add on new output.\n    pub fn append(&mut self, newer: Self) {\n        let Self {\n            parent,\n            class,\n            builder,\n            viewport_ui_cb,\n            mut commands,\n            repaint_delay,\n        } = newer;\n\n        self.parent = parent;\n        self.class = class;\n        let _ = self.builder.patch(builder); // we ignore the returned command, because `self.builder` will be the basis of a new patch\n        self.viewport_ui_cb = viewport_ui_cb;\n        self.commands.append(&mut commands);\n        self.repaint_delay = self.repaint_delay.min(repaint_delay);\n    }\n}\n\n/// Viewport for immediate rendering.\npub struct ImmediateViewport<'a> {\n    /// Id of us and our parent.\n    pub ids: ViewportIdPair,\n\n    pub builder: ViewportBuilder,\n\n    /// The user-code that shows the GUI.\n    pub viewport_ui_cb: Box<dyn FnMut(&mut Ui) + 'a>,\n}\n"
  },
  {
    "path": "crates/egui/src/widget_rect.rs",
    "content": "use ahash::HashMap;\n\nuse crate::{Id, IdMap, LayerId, Rect, Sense, WidgetInfo};\n\n/// Used to store each widget's [Id], [Rect] and [Sense] each frame.\n///\n/// Used to check which widget gets input when a user clicks somewhere.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub struct WidgetRect {\n    /// The globally unique widget id.\n    ///\n    /// For interactive widgets, this better be globally unique.\n    /// If not there will be weird bugs,\n    /// and also big red warning test on the screen in debug builds\n    /// (see [`crate::Options::warn_on_id_clash`]).\n    ///\n    /// You can ensure globally unique ids using [`crate::Ui::push_id`].\n    pub id: Id,\n\n    /// What layer the widget is on.\n    pub layer_id: LayerId,\n\n    /// The full widget rectangle, in local layer coordinates.\n    pub rect: Rect,\n\n    /// Where the widget is, in local layer coordinates.\n    ///\n    /// This is after clipping with the parent ui clip rect.\n    pub interact_rect: Rect,\n\n    /// How the widget responds to interaction.\n    ///\n    /// Note: if [`Self::enabled`] is `false`, then\n    /// the widget _effectively_ doesn't sense anything,\n    /// but can still have the same `Sense`.\n    /// This is because the sense informs the styling of the widget,\n    /// but we don't want to change the style when a widget is disabled\n    /// (that is handled by the `Painter` directly).\n    pub sense: Sense,\n\n    /// Is the widget enabled?\n    pub enabled: bool,\n}\n\nimpl WidgetRect {\n    pub fn transform(self, transform: emath::TSTransform) -> Self {\n        let Self {\n            id,\n            layer_id,\n            rect,\n            interact_rect,\n            sense,\n            enabled,\n        } = self;\n        Self {\n            id,\n            layer_id,\n            rect: transform * rect,\n            interact_rect: transform * interact_rect,\n            sense,\n            enabled,\n        }\n    }\n}\n\n/// How to handle multiple calls to [`crate::Response::interact`] and [`crate::Ui::interact_opt`].\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub struct InteractOptions {\n    /// If we call interact on the same widget multiple times,\n    /// should we move it to the top on subsequent calls?\n    pub move_to_top: bool,\n}\n\n#[expect(clippy::derivable_impls)] // Nice to be explicit\nimpl Default for InteractOptions {\n    fn default() -> Self {\n        Self { move_to_top: false }\n    }\n}\n\n/// Stores the [`WidgetRect`]s of all widgets generated during a single egui update/frame.\n///\n/// All [`crate::Ui`]s have a [`WidgetRect`]. It is created in [`crate::Ui::new`] with [`Rect::NOTHING`]\n/// and updated with the correct [`Rect`] when the [`crate::Ui`] is dropped.\n#[derive(Default, Clone)]\npub struct WidgetRects {\n    /// All widgets, in painting order.\n    by_layer: HashMap<LayerId, Vec<WidgetRect>>,\n\n    /// All widgets, by id, and their order in their respective layer\n    by_id: IdMap<(usize, WidgetRect)>,\n\n    /// Info about some widgets.\n    ///\n    /// Only filled in if the widget is interacted with,\n    /// or if this is a debug build.\n    infos: IdMap<WidgetInfo>,\n}\n\nimpl PartialEq for WidgetRects {\n    fn eq(&self, other: &Self) -> bool {\n        self.by_layer == other.by_layer\n    }\n}\n\nimpl WidgetRects {\n    /// All known layers with widgets.\n    pub fn layer_ids(&self) -> impl ExactSizeIterator<Item = LayerId> + '_ {\n        self.by_layer.keys().copied()\n    }\n\n    pub fn layers(&self) -> impl Iterator<Item = (&LayerId, &[WidgetRect])> + '_ {\n        self.by_layer\n            .iter()\n            .map(|(layer_id, rects)| (layer_id, &rects[..]))\n    }\n\n    #[inline]\n    pub fn get(&self, id: Id) -> Option<&WidgetRect> {\n        self.by_id.get(&id).map(|(_, w)| w)\n    }\n\n    /// In which layer, and in which order in that layer?\n    pub fn order(&self, id: Id) -> Option<(LayerId, usize)> {\n        self.by_id.get(&id).map(|(idx, w)| (w.layer_id, *idx))\n    }\n\n    #[inline]\n    pub fn contains(&self, id: Id) -> bool {\n        self.by_id.contains_key(&id)\n    }\n\n    /// All widgets in this layer, sorted back-to-front.\n    #[inline]\n    pub fn get_layer(&self, layer_id: LayerId) -> impl Iterator<Item = &WidgetRect> + '_ {\n        self.by_layer.get(&layer_id).into_iter().flatten()\n    }\n\n    /// Clear the contents while retaining allocated memory.\n    pub fn clear(&mut self) {\n        let Self {\n            by_layer,\n            by_id,\n            infos,\n        } = self;\n\n        #[expect(clippy::iter_over_hash_type)]\n        for rects in by_layer.values_mut() {\n            rects.clear();\n        }\n\n        by_id.clear();\n\n        infos.clear();\n    }\n\n    /// Insert the given widget rect in the given layer.\n    pub fn insert(&mut self, layer_id: LayerId, widget_rect: WidgetRect, options: InteractOptions) {\n        let Self {\n            by_layer,\n            by_id,\n            infos: _,\n        } = self;\n\n        let InteractOptions { move_to_top } = options;\n\n        let mut shift_layer_index_after = None;\n\n        let layer_widgets = by_layer.entry(layer_id).or_default();\n\n        match by_id.entry(widget_rect.id) {\n            std::collections::hash_map::Entry::Vacant(entry) => {\n                // A new widget\n                let idx_in_layer = layer_widgets.len();\n                entry.insert((idx_in_layer, widget_rect));\n                layer_widgets.push(widget_rect);\n            }\n            std::collections::hash_map::Entry::Occupied(mut entry) => {\n                // This is a known widget, but we might need to update it!\n                // e.g. calling `response.interact(…)` to add more interaction.\n                let (idx_in_layer, existing) = entry.get_mut();\n\n                // Update it:\n                existing.rect = widget_rect.rect; // last wins\n                existing.interact_rect = widget_rect.interact_rect; // last wins\n                existing.sense |= widget_rect.sense;\n                existing.enabled |= widget_rect.enabled;\n\n                if existing.layer_id == widget_rect.layer_id {\n                    if move_to_top {\n                        layer_widgets.remove(*idx_in_layer);\n                        shift_layer_index_after = Some(*idx_in_layer);\n                        *idx_in_layer = layer_widgets.len();\n                        layer_widgets.push(*existing);\n                    } else {\n                        layer_widgets[*idx_in_layer] = *existing;\n                    }\n                } else if cfg!(debug_assertions) {\n                    panic!(\n                        \"DEBUG ASSERT: Widget {:?} changed layer_id during the frame from {:?} to {:?}\",\n                        widget_rect.id, existing.layer_id, widget_rect.layer_id\n                    );\n                }\n            }\n        }\n\n        if let Some(shift_start) = shift_layer_index_after {\n            #[expect(clippy::needless_range_loop)]\n            for i in shift_start..layer_widgets.len() {\n                let w = &layer_widgets[i];\n                if let Some((idx_in_by_id, _)) = by_id.get_mut(&w.id) {\n                    *idx_in_by_id = i;\n                }\n            }\n        }\n    }\n\n    pub fn set_info(&mut self, id: Id, info: WidgetInfo) {\n        self.infos.insert(id, info);\n    }\n\n    pub fn info(&self, id: Id) -> Option<&WidgetInfo> {\n        self.infos.get(&id)\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widget_style.rs",
    "content": "use emath::Vec2;\nuse epaint::{Color32, FontId, Shadow, Stroke, text::TextWrapMode};\n\nuse crate::{\n    Frame, Response, Style, TextStyle,\n    style::{WidgetVisuals, Widgets},\n};\n\n/// General text style\npub struct TextVisuals {\n    /// Font used\n    pub font_id: FontId,\n\n    /// Font color\n    pub color: Color32,\n\n    /// Text decoration\n    pub underline: Stroke,\n    pub strikethrough: Stroke,\n}\n\n/// General widget style\npub struct WidgetStyle {\n    pub frame: Frame,\n\n    pub text: TextVisuals,\n\n    pub stroke: Stroke,\n}\n\npub struct ButtonStyle {\n    pub frame: Frame,\n    pub text_style: TextVisuals,\n}\n\npub struct CheckboxStyle {\n    /// Frame around\n    pub frame: Frame,\n\n    /// Text next to it\n    pub text_style: TextVisuals,\n\n    /// Checkbox size\n    pub checkbox_size: f32,\n\n    /// Checkmark size\n    pub check_size: f32,\n\n    /// Frame of the checkbox itself\n    pub checkbox_frame: Frame,\n\n    /// Checkmark stroke\n    pub check_stroke: Stroke,\n}\n\npub struct LabelStyle {\n    /// Frame around\n    pub frame: Frame,\n\n    /// Text style\n    pub text: TextVisuals,\n\n    /// Wrap mode used\n    pub wrap_mode: TextWrapMode,\n}\n\npub struct SeparatorStyle {\n    /// How much space is allocated in the layout direction\n    pub spacing: f32,\n\n    /// How to paint it\n    pub stroke: Stroke,\n}\n\n#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]\npub enum WidgetState {\n    Noninteractive,\n    #[default]\n    Inactive,\n    Hovered,\n    Active,\n}\n\nimpl Widgets {\n    pub fn state(&self, state: WidgetState) -> &WidgetVisuals {\n        match state {\n            WidgetState::Noninteractive => &self.noninteractive,\n            WidgetState::Inactive => &self.inactive,\n            WidgetState::Hovered => &self.hovered,\n            WidgetState::Active => &self.active,\n        }\n    }\n}\n\nimpl Response {\n    pub fn widget_state(&self) -> WidgetState {\n        if !self.sense.interactive() {\n            WidgetState::Noninteractive\n        } else if self.is_pointer_button_down_on() || self.has_focus() || self.clicked() {\n            WidgetState::Active\n        } else if self.hovered() || self.highlighted() {\n            WidgetState::Hovered\n        } else {\n            WidgetState::Inactive\n        }\n    }\n}\n\nimpl Style {\n    pub fn widget_style(&self, state: WidgetState) -> WidgetStyle {\n        let visuals = self.visuals.widgets.state(state);\n        let font_id = self.override_font_id.clone();\n        WidgetStyle {\n            frame: Frame {\n                fill: visuals.bg_fill,\n                stroke: visuals.bg_stroke,\n                corner_radius: visuals.corner_radius,\n                inner_margin: self.spacing.button_padding.into(),\n                ..Default::default()\n            },\n            stroke: visuals.fg_stroke,\n            text: TextVisuals {\n                color: self\n                    .visuals\n                    .override_text_color\n                    .unwrap_or_else(|| visuals.text_color()),\n                font_id: font_id.unwrap_or_else(|| TextStyle::Body.resolve(self)),\n                strikethrough: Stroke::NONE,\n                underline: Stroke::NONE,\n            },\n        }\n    }\n\n    pub fn button_style(&self, state: WidgetState, selected: bool) -> ButtonStyle {\n        let mut visuals = *self.visuals.widgets.state(state);\n        let mut ws = self.widget_style(state);\n\n        if selected {\n            visuals.weak_bg_fill = self.visuals.selection.bg_fill;\n            visuals.bg_fill = self.visuals.selection.bg_fill;\n            visuals.fg_stroke = self.visuals.selection.stroke;\n            ws.text.color = self.visuals.selection.stroke.color;\n        }\n\n        ButtonStyle {\n            frame: Frame {\n                fill: visuals.weak_bg_fill,\n                stroke: visuals.bg_stroke,\n                corner_radius: visuals.corner_radius,\n                outer_margin: (-Vec2::splat(visuals.expansion)).into(),\n                inner_margin: (self.spacing.button_padding + Vec2::splat(visuals.expansion)\n                    - Vec2::splat(visuals.bg_stroke.width))\n                .into(),\n                ..Default::default()\n            },\n            text_style: ws.text,\n        }\n    }\n\n    pub fn checkbox_style(&self, state: WidgetState) -> CheckboxStyle {\n        let visuals = self.visuals.widgets.state(state);\n        let ws = self.widget_style(state);\n        CheckboxStyle {\n            frame: Frame::new(),\n            checkbox_size: self.spacing.icon_width,\n            check_size: self.spacing.icon_width_inner,\n            checkbox_frame: Frame {\n                fill: visuals.bg_fill,\n                corner_radius: visuals.corner_radius,\n                stroke: visuals.bg_stroke,\n                // Use the inner_margin for the expansion\n                inner_margin: visuals.expansion.into(),\n                ..Default::default()\n            },\n            text_style: ws.text,\n            check_stroke: ws.stroke,\n        }\n    }\n\n    pub fn label_style(&self, state: WidgetState) -> LabelStyle {\n        let ws = self.widget_style(state);\n        LabelStyle {\n            frame: Frame {\n                fill: ws.frame.fill,\n                inner_margin: 0.0.into(),\n                outer_margin: 0.0.into(),\n                stroke: Stroke::NONE,\n                shadow: Shadow::NONE,\n                corner_radius: 0.into(),\n            },\n            text: ws.text,\n            wrap_mode: TextWrapMode::Wrap,\n        }\n    }\n\n    pub fn separator_style(&self, _state: WidgetState) -> SeparatorStyle {\n        let visuals = self.visuals.noninteractive();\n        SeparatorStyle {\n            spacing: 6.0,\n            stroke: visuals.bg_stroke,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widget_text.rs",
    "content": "use emath::GuiRounding as _;\nuse epaint::text::{IntoTag, TextFormat, VariationCoords};\nuse std::fmt::Formatter;\nuse std::{borrow::Cow, sync::Arc};\n\nuse crate::{\n    Align, Color32, FontFamily, FontSelection, Galley, Style, TextStyle, TextWrapMode, Ui, Visuals,\n    text::{LayoutJob, TextWrapping},\n};\n\n/// Text and optional style choices for it.\n///\n/// The style choices (font, color) are applied to the entire text.\n/// For more detailed control, use [`crate::text::LayoutJob`] instead.\n///\n/// A [`RichText`] can be used in most widgets and helper functions, e.g. [`Ui::label`] and [`Ui::button`].\n///\n/// ### Example\n/// ```\n/// use egui::{RichText, Color32};\n///\n/// RichText::new(\"Plain\");\n/// RichText::new(\"colored\").color(Color32::RED);\n/// RichText::new(\"Large and underlined\").size(20.0).underline();\n/// ```\n#[derive(Clone, Debug, PartialEq)]\npub struct RichText {\n    text: String,\n    size: Option<f32>,\n    extra_letter_spacing: f32,\n    line_height: Option<f32>,\n    family: Option<FontFamily>,\n    text_style: Option<TextStyle>,\n    background_color: Color32,\n    expand_bg: f32,\n    text_color: Option<Color32>,\n    coords: VariationCoords,\n    code: bool,\n    strong: bool,\n    weak: bool,\n    strikethrough: bool,\n    underline: bool,\n    italics: bool,\n    raised: bool,\n}\n\nimpl Default for RichText {\n    fn default() -> Self {\n        Self {\n            text: Default::default(),\n            size: Default::default(),\n            extra_letter_spacing: Default::default(),\n            line_height: Default::default(),\n            family: Default::default(),\n            text_style: Default::default(),\n            background_color: Default::default(),\n            expand_bg: 1.0,\n            text_color: Default::default(),\n            coords: Default::default(),\n            code: Default::default(),\n            strong: Default::default(),\n            weak: Default::default(),\n            strikethrough: Default::default(),\n            underline: Default::default(),\n            italics: Default::default(),\n            raised: Default::default(),\n        }\n    }\n}\n\nimpl From<&str> for RichText {\n    #[inline]\n    fn from(text: &str) -> Self {\n        Self::new(text)\n    }\n}\n\nimpl From<&String> for RichText {\n    #[inline]\n    fn from(text: &String) -> Self {\n        Self::new(text)\n    }\n}\n\nimpl From<&mut String> for RichText {\n    #[inline]\n    fn from(text: &mut String) -> Self {\n        Self::new(text.clone())\n    }\n}\n\nimpl From<String> for RichText {\n    #[inline]\n    fn from(text: String) -> Self {\n        Self::new(text)\n    }\n}\n\nimpl From<&Box<str>> for RichText {\n    #[inline]\n    fn from(text: &Box<str>) -> Self {\n        Self::new(text.clone())\n    }\n}\n\nimpl From<&mut Box<str>> for RichText {\n    #[inline]\n    fn from(text: &mut Box<str>) -> Self {\n        Self::new(text.clone())\n    }\n}\n\nimpl From<Box<str>> for RichText {\n    #[inline]\n    fn from(text: Box<str>) -> Self {\n        Self::new(text)\n    }\n}\n\nimpl From<Cow<'_, str>> for RichText {\n    #[inline]\n    fn from(text: Cow<'_, str>) -> Self {\n        Self::new(text)\n    }\n}\n\nimpl RichText {\n    #[inline]\n    pub fn new(text: impl Into<String>) -> Self {\n        Self {\n            text: text.into(),\n            ..Default::default()\n        }\n    }\n\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.text.is_empty()\n    }\n\n    #[inline]\n    pub fn text(&self) -> &str {\n        &self.text\n    }\n\n    /// Select the font size (in points).\n    /// This overrides the value from [`Self::text_style`].\n    #[inline]\n    pub fn size(mut self, size: f32) -> Self {\n        self.size = Some(size);\n        self\n    }\n\n    /// Extra spacing between letters, in points.\n    ///\n    /// Default: 0.0.\n    ///\n    /// For even text it is recommended you round this to an even number of _pixels_,\n    /// e.g. using [`crate::Painter::round_to_pixel`].\n    #[inline]\n    pub fn extra_letter_spacing(mut self, extra_letter_spacing: f32) -> Self {\n        self.extra_letter_spacing = extra_letter_spacing;\n        self\n    }\n\n    /// Explicit line height of the text in points.\n    ///\n    /// This is the distance between the bottom row of two subsequent lines of text.\n    ///\n    /// If `None` (the default), the line height is determined by the font.\n    ///\n    /// For even text it is recommended you round this to an even number of _pixels_,\n    /// e.g. using [`crate::Painter::round_to_pixel`].\n    #[inline]\n    pub fn line_height(mut self, line_height: Option<f32>) -> Self {\n        self.line_height = line_height;\n        self\n    }\n\n    /// Select the font family.\n    ///\n    /// This overrides the value from [`Self::text_style`].\n    ///\n    /// Only the families available in [`crate::FontDefinitions::families`] may be used.\n    #[inline]\n    pub fn family(mut self, family: FontFamily) -> Self {\n        self.family = Some(family);\n        self\n    }\n\n    /// Select the font and size.\n    /// This overrides the value from [`Self::text_style`].\n    #[inline]\n    pub fn font(mut self, font_id: crate::FontId) -> Self {\n        let crate::FontId { size, family } = font_id;\n        self.size = Some(size);\n        self.family = Some(family);\n        self\n    }\n\n    /// Add a variation coordinate.\n    #[inline]\n    pub fn variation(mut self, tag: impl IntoTag, coord: f32) -> Self {\n        self.coords.push(tag, coord);\n        self\n    }\n\n    /// Override the variation coordinates completely.\n    #[inline]\n    pub fn variations<T: IntoTag>(\n        mut self,\n        variations: impl IntoIterator<Item = (T, f32)>,\n    ) -> Self {\n        self.coords = VariationCoords::new(variations);\n        self\n    }\n\n    /// Override the [`TextStyle`].\n    #[inline]\n    pub fn text_style(mut self, text_style: TextStyle) -> Self {\n        self.text_style = Some(text_style);\n        self\n    }\n\n    /// Set the [`TextStyle`] unless it has already been set\n    #[inline]\n    pub fn fallback_text_style(mut self, text_style: TextStyle) -> Self {\n        self.text_style.get_or_insert(text_style);\n        self\n    }\n\n    /// Use [`TextStyle::Heading`].\n    #[inline]\n    pub fn heading(self) -> Self {\n        self.text_style(TextStyle::Heading)\n    }\n\n    /// Use [`TextStyle::Monospace`].\n    #[inline]\n    pub fn monospace(self) -> Self {\n        self.text_style(TextStyle::Monospace)\n    }\n\n    /// Monospace label with different background color.\n    #[inline]\n    pub fn code(mut self) -> Self {\n        self.code = true;\n        self.text_style(TextStyle::Monospace)\n    }\n\n    /// Extra strong text (stronger color).\n    #[inline]\n    pub fn strong(mut self) -> Self {\n        self.strong = true;\n        self\n    }\n\n    /// Extra weak text (fainter color).\n    #[inline]\n    pub fn weak(mut self) -> Self {\n        self.weak = true;\n        self\n    }\n\n    /// Draw a line under the text.\n    ///\n    /// If you want to control the line color, use [`LayoutJob`] instead.\n    #[inline]\n    pub fn underline(mut self) -> Self {\n        self.underline = true;\n        self\n    }\n\n    /// Draw a line through the text, crossing it out.\n    ///\n    /// If you want to control the strikethrough line color, use [`LayoutJob`] instead.\n    #[inline]\n    pub fn strikethrough(mut self) -> Self {\n        self.strikethrough = true;\n        self\n    }\n\n    /// Tilt the characters to the right.\n    #[inline]\n    pub fn italics(mut self) -> Self {\n        self.italics = true;\n        self\n    }\n\n    /// Smaller text.\n    #[inline]\n    pub fn small(self) -> Self {\n        self.text_style(TextStyle::Small)\n    }\n\n    /// For e.g. exponents.\n    #[inline]\n    pub fn small_raised(self) -> Self {\n        self.text_style(TextStyle::Small).raised()\n    }\n\n    /// Align text to top. Only applicable together with [`Self::small()`].\n    #[inline]\n    pub fn raised(mut self) -> Self {\n        self.raised = true;\n        self\n    }\n\n    /// Fill-color behind the text.\n    #[inline]\n    pub fn background_color(mut self, background_color: impl Into<Color32>) -> Self {\n        self.background_color = background_color.into();\n        self\n    }\n\n    /// Override text color.\n    ///\n    /// If not set, [`Color32::PLACEHOLDER`] will be used,\n    /// which will be replaced with a color chosen by the widget that paints the text.\n    #[inline]\n    pub fn color(mut self, color: impl Into<Color32>) -> Self {\n        self.text_color = Some(color.into());\n        self\n    }\n\n    /// Read the font height of the selected text style.\n    ///\n    /// Returns a value rounded to [`emath::GUI_ROUNDING`].\n    pub fn font_height(&self, fonts: &mut epaint::FontsView<'_>, style: &Style) -> f32 {\n        let mut font_id = self.text_style.as_ref().map_or_else(\n            || FontSelection::Default.resolve(style),\n            |text_style| text_style.resolve(style),\n        );\n\n        if let Some(size) = self.size {\n            font_id.size = size;\n        }\n        if let Some(family) = &self.family {\n            font_id.family = family.clone();\n        }\n        fonts.row_height(&font_id)\n    }\n\n    /// Append to an existing [`LayoutJob`]\n    ///\n    /// Note that the color of the [`RichText`] must be set, or may default to an undesirable color.\n    ///\n    /// ### Example\n    /// ```\n    /// use egui::{Style, RichText, text::LayoutJob, Color32, FontSelection, Align};\n    ///\n    /// let style = Style::default();\n    /// let mut layout_job = LayoutJob::default();\n    /// RichText::new(\"Normal\")\n    ///     .color(style.visuals.text_color())\n    ///     .append_to(\n    ///         &mut layout_job,\n    ///         &style,\n    ///         FontSelection::Default,\n    ///         Align::Center,\n    ///     );\n    /// RichText::new(\"Large and underlined\")\n    ///     .color(style.visuals.text_color())\n    ///     .size(20.0)\n    ///     .underline()\n    ///     .append_to(\n    ///         &mut layout_job,\n    ///         &style,\n    ///         FontSelection::Default,\n    ///         Align::Center,\n    ///     );\n    /// ```\n    pub fn append_to(\n        self,\n        layout_job: &mut LayoutJob,\n        style: &Style,\n        fallback_font: FontSelection,\n        default_valign: Align,\n    ) {\n        let (text, format) = self.into_text_and_format(style, fallback_font, default_valign);\n\n        layout_job.append(&text, 0.0, format);\n    }\n\n    fn into_layout_job(\n        self,\n        style: &Style,\n        fallback_font: FontSelection,\n        default_valign: Align,\n    ) -> LayoutJob {\n        let (text, text_format) = self.into_text_and_format(style, fallback_font, default_valign);\n        LayoutJob::single_section(text, text_format)\n    }\n\n    fn into_text_and_format(\n        self,\n        style: &Style,\n        fallback_font: FontSelection,\n        default_valign: Align,\n    ) -> (String, crate::text::TextFormat) {\n        let text_color = self.get_text_color(&style.visuals);\n\n        let Self {\n            text,\n            size,\n            extra_letter_spacing,\n            line_height,\n            family,\n            text_style,\n            background_color,\n            expand_bg,\n            text_color: _, // already used by `get_text_color`\n            coords,\n            code,\n            strong: _, // already used by `get_text_color`\n            weak: _,   // already used by `get_text_color`\n            strikethrough,\n            underline,\n            italics,\n            raised,\n        } = self;\n\n        let line_color = text_color.unwrap_or_else(|| style.visuals.text_color());\n        let text_color = text_color.unwrap_or(crate::Color32::PLACEHOLDER);\n\n        let font_id = {\n            let mut font_id = style.override_font_id.clone().unwrap_or_else(|| {\n                (text_style.as_ref().or(style.override_text_style.as_ref()))\n                    .map(|text_style| text_style.resolve(style))\n                    .unwrap_or_else(|| fallback_font.resolve(style))\n            });\n            if let Some(size) = size {\n                font_id.size = size;\n            }\n            if let Some(family) = family {\n                font_id.family = family;\n            }\n            font_id\n        };\n\n        let background_color = if code {\n            style.visuals.code_bg_color\n        } else {\n            background_color\n        };\n\n        let underline = if underline {\n            crate::Stroke::new(1.0, line_color)\n        } else {\n            crate::Stroke::NONE\n        };\n        let strikethrough = if strikethrough {\n            crate::Stroke::new(1.0, line_color)\n        } else {\n            crate::Stroke::NONE\n        };\n\n        let valign = if raised {\n            crate::Align::TOP\n        } else {\n            default_valign\n        };\n\n        (\n            text,\n            crate::text::TextFormat {\n                font_id,\n                extra_letter_spacing,\n                line_height,\n                color: text_color,\n                background: background_color,\n                coords,\n                italics,\n                underline,\n                strikethrough,\n                valign,\n                expand_bg,\n            },\n        )\n    }\n\n    fn get_text_color(&self, visuals: &Visuals) -> Option<Color32> {\n        if let Some(text_color) = self.text_color {\n            Some(text_color)\n        } else if self.strong {\n            Some(visuals.strong_text_color())\n        } else if self.weak {\n            Some(visuals.weak_text_color())\n        } else {\n            visuals.override_text_color\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// This is how you specify text for a widget.\n///\n/// A lot of widgets use `impl Into<WidgetText>` as an argument,\n/// allowing you to pass in [`String`], [`RichText`], [`LayoutJob`], and more.\n///\n/// Often a [`WidgetText`] is just a simple [`String`],\n/// but it can be a [`RichText`] (text with color, style, etc),\n/// a [`LayoutJob`] (for when you want full control of how the text looks)\n/// or text that has already been laid out in a [`Galley`].\n///\n/// You can color the text however you want, or use [`Color32::PLACEHOLDER`]\n/// which will be replaced with a color chosen by the widget that paints the text.\n#[derive(Clone)]\npub enum WidgetText {\n    /// Plain unstyled text.\n    ///\n    /// We have this as a special case, as it is the common-case,\n    /// and it uses less memory than [`Self::RichText`].\n    Text(String),\n\n    /// Text and optional style choices for it.\n    ///\n    /// Prefer [`Self::Text`] if there is no styling, as it will be faster.\n    RichText(Arc<RichText>),\n\n    /// Use this [`LayoutJob`] when laying out the text.\n    ///\n    /// Only [`LayoutJob::text`] and [`LayoutJob::sections`] are guaranteed to be respected.\n    ///\n    /// [`TextWrapping::max_width`](epaint::text::TextWrapping::max_width), [`LayoutJob::halign`], [`LayoutJob::justify`]\n    /// and [`LayoutJob::first_row_min_height`] will likely be determined by the [`crate::Layout`]\n    /// of the [`Ui`] the widget is placed in.\n    /// If you want all parts of the [`LayoutJob`] respected, then convert it to a\n    /// [`Galley`] and use [`Self::Galley`] instead.\n    ///\n    /// You can color the text however you want, or use [`Color32::PLACEHOLDER`]\n    /// which will be replaced with a color chosen by the widget that paints the text.\n    LayoutJob(Arc<LayoutJob>),\n\n    /// Use exactly this galley when painting the text.\n    ///\n    /// You can color the text however you want, or use [`Color32::PLACEHOLDER`]\n    /// which will be replaced with a color chosen by the widget that paints the text.\n    Galley(Arc<Galley>),\n}\n\nimpl std::fmt::Debug for WidgetText {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        let text = self.text();\n        match self {\n            Self::Text(_) => write!(f, \"Text({text:?})\"),\n            Self::RichText(_) => write!(f, \"RichText({text:?})\"),\n            Self::LayoutJob(_) => write!(f, \"LayoutJob({text:?})\"),\n            Self::Galley(_) => write!(f, \"Galley({text:?})\"),\n        }\n    }\n}\n\nimpl Default for WidgetText {\n    fn default() -> Self {\n        Self::Text(String::new())\n    }\n}\n\nimpl WidgetText {\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        match self {\n            Self::Text(text) => text.is_empty(),\n            Self::RichText(text) => text.is_empty(),\n            Self::LayoutJob(job) => job.is_empty(),\n            Self::Galley(galley) => galley.is_empty(),\n        }\n    }\n\n    #[inline]\n    pub fn text(&self) -> &str {\n        match self {\n            Self::Text(text) => text,\n            Self::RichText(text) => text.text(),\n            Self::LayoutJob(job) => &job.text,\n            Self::Galley(galley) => galley.text(),\n        }\n    }\n\n    /// Map the contents based on the provided closure.\n    ///\n    /// - [`Self::Text`] => convert to [`RichText`] and call f\n    /// - [`Self::RichText`] => call f\n    /// - else do nothing\n    #[must_use]\n    fn map_rich_text<F>(self, f: F) -> Self\n    where\n        F: FnOnce(RichText) -> RichText,\n    {\n        match self {\n            Self::Text(text) => Self::RichText(Arc::new(f(RichText::new(text)))),\n            Self::RichText(text) => Self::RichText(Arc::new(f(Arc::unwrap_or_clone(text)))),\n            other => other,\n        }\n    }\n\n    /// Override the [`TextStyle`] if, and only if, this is a [`RichText`].\n    ///\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn text_style(self, text_style: TextStyle) -> Self {\n        self.map_rich_text(|text| text.text_style(text_style))\n    }\n\n    /// Set the [`TextStyle`] unless it has already been set\n    ///\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn fallback_text_style(self, text_style: TextStyle) -> Self {\n        self.map_rich_text(|text| text.fallback_text_style(text_style))\n    }\n\n    /// Override text color if, and only if, this is a [`RichText`].\n    ///\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn color(self, color: impl Into<Color32>) -> Self {\n        self.map_rich_text(|text| text.color(color))\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn heading(self) -> Self {\n        self.map_rich_text(|text| text.heading())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn monospace(self) -> Self {\n        self.map_rich_text(|text| text.monospace())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn code(self) -> Self {\n        self.map_rich_text(|text| text.code())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn strong(self) -> Self {\n        self.map_rich_text(|text| text.strong())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn weak(self) -> Self {\n        self.map_rich_text(|text| text.weak())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn underline(self) -> Self {\n        self.map_rich_text(|text| text.underline())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn strikethrough(self) -> Self {\n        self.map_rich_text(|text| text.strikethrough())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn italics(self) -> Self {\n        self.map_rich_text(|text| text.italics())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn small(self) -> Self {\n        self.map_rich_text(|text| text.small())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn small_raised(self) -> Self {\n        self.map_rich_text(|text| text.small_raised())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn raised(self) -> Self {\n        self.map_rich_text(|text| text.raised())\n    }\n\n    /// Prefer using [`RichText`] directly!\n    #[inline]\n    pub fn background_color(self, background_color: impl Into<Color32>) -> Self {\n        self.map_rich_text(|text| text.background_color(background_color))\n    }\n\n    /// Returns a value rounded to [`emath::GUI_ROUNDING`].\n    pub(crate) fn font_height(&self, fonts: &mut epaint::FontsView<'_>, style: &Style) -> f32 {\n        match self {\n            Self::Text(_) => fonts.row_height(&FontSelection::Default.resolve(style)),\n            Self::RichText(text) => text.font_height(fonts, style),\n            Self::LayoutJob(job) => job.font_height(fonts),\n            Self::Galley(galley) => {\n                if let Some(placed_row) = galley.rows.first() {\n                    placed_row.height().round_ui()\n                } else {\n                    galley.size().y.round_ui()\n                }\n            }\n        }\n    }\n\n    pub fn into_layout_job(\n        self,\n        style: &Style,\n        fallback_font: FontSelection,\n        default_valign: Align,\n    ) -> Arc<LayoutJob> {\n        match self {\n            Self::Text(text) => Arc::new(LayoutJob::simple_format(\n                text,\n                TextFormat {\n                    font_id: FontSelection::Default.resolve(style),\n                    color: crate::Color32::PLACEHOLDER,\n                    valign: default_valign,\n                    ..Default::default()\n                },\n            )),\n            Self::RichText(text) => Arc::new(Arc::unwrap_or_clone(text).into_layout_job(\n                style,\n                fallback_font,\n                default_valign,\n            )),\n            Self::LayoutJob(job) => job,\n            Self::Galley(galley) => Arc::clone(&galley.job),\n        }\n    }\n\n    /// Layout with wrap mode based on the containing [`Ui`].\n    ///\n    /// `wrap_mode`: override for [`Ui::wrap_mode`]\n    pub fn into_galley(\n        self,\n        ui: &Ui,\n        wrap_mode: Option<TextWrapMode>,\n        available_width: f32,\n        fallback_font: impl Into<FontSelection>,\n    ) -> Arc<Galley> {\n        let valign = ui.text_valign();\n        let style = ui.style();\n\n        let wrap_mode = wrap_mode.unwrap_or_else(|| ui.wrap_mode());\n        let text_wrapping = TextWrapping::from_wrap_mode_and_width(wrap_mode, available_width);\n\n        self.into_galley_impl(ui.ctx(), style, text_wrapping, fallback_font.into(), valign)\n    }\n\n    pub fn into_galley_impl(\n        self,\n        ctx: &crate::Context,\n        style: &Style,\n        text_wrapping: TextWrapping,\n        fallback_font: FontSelection,\n        default_valign: Align,\n    ) -> Arc<Galley> {\n        match self {\n            Self::Text(text) => {\n                let color = style\n                    .visuals\n                    .override_text_color\n                    .unwrap_or(crate::Color32::PLACEHOLDER);\n                let mut layout_job = LayoutJob::simple_format(\n                    text,\n                    TextFormat {\n                        // We want the style overrides to take precedence over the fallback font\n                        font_id: FontSelection::default()\n                            .resolve_with_fallback(style, fallback_font),\n                        color,\n                        valign: default_valign,\n                        ..Default::default()\n                    },\n                );\n                layout_job.wrap = text_wrapping;\n                ctx.fonts_mut(|f| f.layout_job(layout_job))\n            }\n            Self::RichText(text) => {\n                let mut layout_job = Arc::unwrap_or_clone(text).into_layout_job(\n                    style,\n                    fallback_font,\n                    default_valign,\n                );\n                layout_job.wrap = text_wrapping;\n                ctx.fonts_mut(|f| f.layout_job(layout_job))\n            }\n            Self::LayoutJob(job) => {\n                let mut job = Arc::unwrap_or_clone(job);\n                job.wrap = text_wrapping;\n                ctx.fonts_mut(|f| f.layout_job(job))\n            }\n            Self::Galley(galley) => galley,\n        }\n    }\n}\n\nimpl From<&str> for WidgetText {\n    #[inline]\n    fn from(text: &str) -> Self {\n        Self::Text(text.to_owned())\n    }\n}\n\nimpl From<&String> for WidgetText {\n    #[inline]\n    fn from(text: &String) -> Self {\n        Self::Text(text.clone())\n    }\n}\n\nimpl From<String> for WidgetText {\n    #[inline]\n    fn from(text: String) -> Self {\n        Self::Text(text)\n    }\n}\n\nimpl From<&Box<str>> for WidgetText {\n    #[inline]\n    fn from(text: &Box<str>) -> Self {\n        Self::Text(text.to_string())\n    }\n}\n\nimpl From<Box<str>> for WidgetText {\n    #[inline]\n    fn from(text: Box<str>) -> Self {\n        Self::Text(text.into())\n    }\n}\n\nimpl From<Cow<'_, str>> for WidgetText {\n    #[inline]\n    fn from(text: Cow<'_, str>) -> Self {\n        Self::Text(text.into_owned())\n    }\n}\n\nimpl From<RichText> for WidgetText {\n    #[inline]\n    fn from(rich_text: RichText) -> Self {\n        Self::RichText(Arc::new(rich_text))\n    }\n}\n\nimpl From<Arc<RichText>> for WidgetText {\n    #[inline]\n    fn from(rich_text: Arc<RichText>) -> Self {\n        Self::RichText(rich_text)\n    }\n}\n\nimpl From<LayoutJob> for WidgetText {\n    #[inline]\n    fn from(layout_job: LayoutJob) -> Self {\n        Self::LayoutJob(Arc::new(layout_job))\n    }\n}\n\nimpl From<Arc<LayoutJob>> for WidgetText {\n    #[inline]\n    fn from(layout_job: Arc<LayoutJob>) -> Self {\n        Self::LayoutJob(layout_job)\n    }\n}\n\nimpl From<Arc<Galley>> for WidgetText {\n    #[inline]\n    fn from(galley: Arc<Galley>) -> Self {\n        Self::Galley(galley)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::WidgetText;\n\n    #[test]\n    fn ensure_small_widget_text() {\n        assert_eq!(size_of::<WidgetText>(), size_of::<String>());\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/button.rs",
    "content": "use epaint::Margin;\n\nuse crate::{\n    Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Color32, CornerRadius, Frame,\n    Image, IntoAtoms, NumExt as _, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2,\n    Widget, WidgetInfo, WidgetText, WidgetType,\n    widget_style::{ButtonStyle, WidgetState},\n};\n\n/// Clickable button with text.\n///\n/// See also [`Ui::button`].\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # fn do_stuff() {}\n///\n/// if ui.add(egui::Button::new(\"Click me\")).clicked() {\n///     do_stuff();\n/// }\n///\n/// // A greyed-out and non-interactive button:\n/// if ui.add_enabled(false, egui::Button::new(\"Can't click this\")).clicked() {\n///     unreachable!();\n/// }\n/// # });\n/// ```\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct Button<'a> {\n    layout: AtomLayout<'a>,\n    fill: Option<Color32>,\n    stroke: Option<Stroke>,\n    small: bool,\n    frame: Option<bool>,\n    frame_when_inactive: bool,\n    min_size: Vec2,\n    corner_radius: Option<CornerRadius>,\n    selected: bool,\n    image_tint_follows_text_color: bool,\n    limit_image_size: bool,\n}\n\nimpl<'a> Button<'a> {\n    pub fn new(atoms: impl IntoAtoms<'a>) -> Self {\n        Self {\n            layout: AtomLayout::new(atoms.into_atoms())\n                .sense(Sense::click())\n                .fallback_font(TextStyle::Button),\n            fill: None,\n            stroke: None,\n            small: false,\n            frame: None,\n            frame_when_inactive: true,\n            min_size: Vec2::ZERO,\n            corner_radius: None,\n            selected: false,\n            image_tint_follows_text_color: false,\n            limit_image_size: false,\n        }\n    }\n\n    /// Show a selectable button.\n    ///\n    /// Equivalent to:\n    /// ```rust\n    /// # use egui::{Button, IntoAtoms, __run_test_ui};\n    /// # __run_test_ui(|ui| {\n    /// let selected = true;\n    /// ui.add(Button::new(\"toggle me\").selected(selected).frame_when_inactive(!selected).frame(true));\n    /// # });\n    /// ```\n    ///\n    /// See also:\n    ///   - [`Ui::selectable_value`]\n    ///   - [`Ui::selectable_label`]\n    pub fn selectable(selected: bool, atoms: impl IntoAtoms<'a>) -> Self {\n        Self::new(atoms)\n            .selected(selected)\n            .frame_when_inactive(selected)\n            .frame(true)\n    }\n\n    /// Creates a button with an image. The size of the image as displayed is defined by the provided size.\n    ///\n    /// Note: In contrast to [`Button::new`], this limits the image size to the default font height\n    /// (using [`crate::AtomExt::atom_max_height_font_size`]).\n    pub fn image(image: impl Into<Image<'a>>) -> Self {\n        Self::opt_image_and_text(Some(image.into()), None)\n    }\n\n    /// Creates a button with an image to the left of the text.\n    ///\n    /// Note: In contrast to [`Button::new`], this limits the image size to the default font height\n    /// (using [`crate::AtomExt::atom_max_height_font_size`]).\n    pub fn image_and_text(image: impl Into<Image<'a>>, text: impl Into<WidgetText>) -> Self {\n        Self::opt_image_and_text(Some(image.into()), Some(text.into()))\n    }\n\n    /// Create a button with an optional image and optional text.\n    ///\n    /// Note: In contrast to [`Button::new`], this limits the image size to the default font height\n    /// (using [`crate::AtomExt::atom_max_height_font_size`]).\n    pub fn opt_image_and_text(image: Option<Image<'a>>, text: Option<WidgetText>) -> Self {\n        let mut button = Self::new(());\n        if let Some(image) = image {\n            button.layout.push_right(image);\n        }\n        if let Some(text) = text {\n            button.layout.push_right(text);\n        }\n        button.limit_image_size = true;\n        button\n    }\n\n    /// Set the wrap mode for the text.\n    ///\n    /// By default, [`crate::Ui::wrap_mode`] will be used, which can be overridden with [`crate::Style::wrap_mode`].\n    ///\n    /// Note that any `\\n` in the text will always produce a new line.\n    #[inline]\n    pub fn wrap_mode(mut self, wrap_mode: TextWrapMode) -> Self {\n        self.layout = self.layout.wrap_mode(wrap_mode);\n        self\n    }\n\n    /// Set [`Self::wrap_mode`] to [`TextWrapMode::Wrap`].\n    #[inline]\n    pub fn wrap(self) -> Self {\n        self.wrap_mode(TextWrapMode::Wrap)\n    }\n\n    /// Set [`Self::wrap_mode`] to [`TextWrapMode::Truncate`].\n    #[inline]\n    pub fn truncate(self) -> Self {\n        self.wrap_mode(TextWrapMode::Truncate)\n    }\n\n    /// Override background fill color. Note that this will override any on-hover effects.\n    /// Calling this will also turn on the frame.\n    #[inline]\n    pub fn fill(mut self, fill: impl Into<Color32>) -> Self {\n        self.fill = Some(fill.into());\n        self\n    }\n\n    /// Override button stroke. Note that this will override any on-hover effects.\n    /// Calling this will also turn on the frame.\n    #[inline]\n    pub fn stroke(mut self, stroke: impl Into<Stroke>) -> Self {\n        self.stroke = Some(stroke.into());\n        self.frame = Some(true);\n        self\n    }\n\n    /// Make this a small button, suitable for embedding into text.\n    #[inline]\n    pub fn small(mut self) -> Self {\n        self.small = true;\n        self\n    }\n\n    /// Turn off the frame\n    #[inline]\n    pub fn frame(mut self, frame: bool) -> Self {\n        self.frame = Some(frame);\n        self\n    }\n\n    /// If `false`, the button will not have a frame when inactive.\n    ///\n    /// Default: `true`.\n    ///\n    /// Note: When [`Self::frame`] (or `ui.visuals().button_frame`) is `false`, this setting\n    /// has no effect.\n    #[inline]\n    pub fn frame_when_inactive(mut self, frame_when_inactive: bool) -> Self {\n        self.frame_when_inactive = frame_when_inactive;\n        self\n    }\n\n    /// By default, buttons senses clicks.\n    /// Change this to a drag-button with `Sense::drag()`.\n    #[inline]\n    pub fn sense(mut self, sense: Sense) -> Self {\n        self.layout = self.layout.sense(sense);\n        self\n    }\n\n    /// Set the minimum size of the button.\n    #[inline]\n    pub fn min_size(mut self, min_size: Vec2) -> Self {\n        self.min_size = min_size;\n        self\n    }\n\n    /// Set the rounding of the button.\n    #[inline]\n    pub fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {\n        self.corner_radius = Some(corner_radius.into());\n        self\n    }\n\n    #[inline]\n    #[deprecated = \"Renamed to `corner_radius`\"]\n    pub fn rounding(self, corner_radius: impl Into<CornerRadius>) -> Self {\n        self.corner_radius(corner_radius)\n    }\n\n    /// If true, the tint of the image is multiplied by the widget text color.\n    ///\n    /// This makes sense for images that are white, that should have the same color as the text color.\n    /// This will also make the icon color depend on hover state.\n    ///\n    /// Default: `false`.\n    #[inline]\n    pub fn image_tint_follows_text_color(mut self, image_tint_follows_text_color: bool) -> Self {\n        self.image_tint_follows_text_color = image_tint_follows_text_color;\n        self\n    }\n\n    /// Show some text on the right side of the button, in weak color.\n    ///\n    /// Designed for menu buttons, for setting a keyboard shortcut text (e.g. `Ctrl+S`).\n    ///\n    /// The text can be created with [`crate::Context::format_shortcut`].\n    ///\n    /// See also [`Self::right_text`].\n    #[inline]\n    pub fn shortcut_text(mut self, shortcut_text: impl IntoAtoms<'a>) -> Self {\n        self.layout.push_right(Atom::grow());\n\n        for mut atom in shortcut_text.into_atoms() {\n            atom.kind = match atom.kind {\n                AtomKind::Text(text) => AtomKind::Text(text.weak()),\n                other => other,\n            };\n            self.layout.push_right(atom);\n        }\n\n        self\n    }\n\n    /// Show some text on the right side of the button.\n    #[inline]\n    pub fn right_text(mut self, right_text: impl IntoAtoms<'a>) -> Self {\n        self.layout.push_right(Atom::grow());\n\n        for atom in right_text.into_atoms() {\n            self.layout.push_right(atom);\n        }\n\n        self\n    }\n\n    /// If `true`, mark this button as \"selected\".\n    #[inline]\n    pub fn selected(mut self, selected: bool) -> Self {\n        self.selected = selected;\n        self\n    }\n\n    /// Set the gap between atoms.\n    #[inline]\n    pub fn gap(mut self, gap: f32) -> Self {\n        self.layout = self.layout.gap(gap);\n        self\n    }\n\n    /// Show the button and return a [`AtomLayoutResponse`] for painting custom contents.\n    pub fn atom_ui(self, ui: &mut Ui) -> AtomLayoutResponse {\n        let Button {\n            mut layout,\n            fill,\n            stroke,\n            small,\n            frame,\n            frame_when_inactive,\n            mut min_size,\n            corner_radius,\n            selected,\n            image_tint_follows_text_color,\n            limit_image_size,\n        } = self;\n\n        // Min size height always equal or greater than interact size if not small\n        if !small {\n            min_size.y = min_size.y.at_least(ui.spacing().interact_size.y);\n        }\n\n        if limit_image_size {\n            layout.map_atoms(|atom| {\n                if matches!(&atom.kind, AtomKind::Image(_)) {\n                    atom.atom_max_height_font_size(ui)\n                } else {\n                    atom\n                }\n            });\n        }\n\n        let text = layout.text().map(String::from);\n\n        let has_frame_margin = frame.unwrap_or_else(|| ui.visuals().button_frame);\n\n        let id = ui.next_auto_id();\n        let response: Option<Response> = ui.ctx().read_response(id);\n        let state = response.map(|r| r.widget_state()).unwrap_or_default();\n\n        let ButtonStyle { frame, text_style } = ui.style().button_style(state, selected);\n\n        let mut button_padding = if has_frame_margin {\n            frame.inner_margin\n        } else {\n            Margin::ZERO\n        };\n\n        if small {\n            button_padding.bottom = 0;\n            button_padding.top = 0;\n        }\n\n        // Override global style by local style\n        let mut frame = frame;\n        if let Some(fill) = fill {\n            frame = frame.fill(fill);\n        }\n        if let Some(corner_radius) = corner_radius {\n            frame = frame.corner_radius(corner_radius);\n        }\n        if let Some(stroke) = stroke {\n            frame = frame.stroke(stroke);\n        }\n\n        frame = frame.inner_margin(button_padding);\n\n        // Apply the style font and color as fallback\n        layout = layout\n            .fallback_font(text_style.font_id.clone())\n            .fallback_text_color(text_style.color);\n\n        // Retrocompatibility with button settings\n        layout = if has_frame_margin && (state != WidgetState::Inactive || frame_when_inactive) {\n            layout.frame(frame)\n        } else {\n            layout.frame(Frame::new().inner_margin(frame.inner_margin))\n        };\n\n        let mut prepared = layout.min_size(min_size).allocate(ui);\n\n        // Get AtomLayoutResponse, empty if not visible\n        let response = if ui.is_rect_visible(prepared.response.rect) {\n            if image_tint_follows_text_color {\n                prepared.map_images(|image| image.tint(text_style.color));\n            }\n\n            prepared.fallback_text_color = text_style.color;\n\n            prepared.paint(ui)\n        } else {\n            AtomLayoutResponse::empty(prepared.response)\n        };\n\n        response.response.widget_info(|| {\n            if let Some(text) = &text {\n                WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), text)\n            } else {\n                WidgetInfo::new(WidgetType::Button)\n            }\n        });\n\n        response\n    }\n}\n\nimpl Widget for Button<'_> {\n    fn ui(self, ui: &mut Ui) -> Response {\n        self.atom_ui(ui).response\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/checkbox.rs",
    "content": "use emath::Rect;\n\nuse crate::{\n    Atom, AtomLayout, Atoms, Id, IntoAtoms, NumExt as _, Response, Sense, Shape, Ui, Vec2, Widget,\n    WidgetInfo, WidgetType, epaint, pos2, widget_style::CheckboxStyle,\n};\n\n// TODO(emilk): allow checkbox without a text label\n/// Boolean on/off control with text label.\n///\n/// Usually you'd use [`Ui::checkbox`] instead.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # let mut my_bool = true;\n/// // These are equivalent:\n/// ui.checkbox(&mut my_bool, \"Checked\");\n/// ui.add(egui::Checkbox::new(&mut my_bool, \"Checked\"));\n/// # });\n/// ```\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct Checkbox<'a> {\n    checked: &'a mut bool,\n    atoms: Atoms<'a>,\n    indeterminate: bool,\n}\n\nimpl<'a> Checkbox<'a> {\n    pub fn new(checked: &'a mut bool, atoms: impl IntoAtoms<'a>) -> Self {\n        Checkbox {\n            checked,\n            atoms: atoms.into_atoms(),\n            indeterminate: false,\n        }\n    }\n\n    pub fn without_text(checked: &'a mut bool) -> Self {\n        Self::new(checked, ())\n    }\n\n    /// Display an indeterminate state (neither checked nor unchecked)\n    ///\n    /// This only affects the checkbox's appearance. It will still toggle its boolean value when\n    /// clicked.\n    #[inline]\n    pub fn indeterminate(mut self, indeterminate: bool) -> Self {\n        self.indeterminate = indeterminate;\n        self\n    }\n}\n\nimpl Widget for Checkbox<'_> {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let Checkbox {\n            checked,\n            mut atoms,\n            indeterminate,\n        } = self;\n\n        // Get the widget style by reading the response from the previous pass\n        let id = ui.next_auto_id();\n        let response: Option<Response> = ui.ctx().read_response(id);\n        let state = response.map(|r| r.widget_state()).unwrap_or_default();\n\n        let CheckboxStyle {\n            check_size,\n            checkbox_frame,\n            checkbox_size,\n            frame,\n            check_stroke,\n            text_style,\n        } = ui.style().checkbox_style(state);\n\n        let mut min_size = Vec2::splat(ui.spacing().interact_size.y);\n        min_size.y = min_size.y.at_least(checkbox_size);\n\n        // In order to center the checkbox based on min_size we set the icon height to at least min_size.y\n        let mut icon_size = Vec2::splat(checkbox_size);\n        icon_size.y = icon_size.y.at_least(min_size.y);\n        let rect_id = Id::new(\"egui::checkbox\");\n        atoms.push_left(Atom::custom(rect_id, icon_size));\n\n        let text = atoms.text().map(String::from);\n\n        let mut prepared = AtomLayout::new(atoms)\n            .sense(Sense::click())\n            .min_size(min_size)\n            .frame(frame)\n            .allocate(ui);\n\n        if prepared.response.clicked() {\n            *checked = !*checked;\n            prepared.response.mark_changed();\n        }\n        prepared.response.widget_info(|| {\n            if indeterminate {\n                WidgetInfo::labeled(\n                    WidgetType::Checkbox,\n                    ui.is_enabled(),\n                    text.as_deref().unwrap_or(\"\"),\n                )\n            } else {\n                WidgetInfo::selected(\n                    WidgetType::Checkbox,\n                    ui.is_enabled(),\n                    *checked,\n                    text.as_deref().unwrap_or(\"\"),\n                )\n            }\n        });\n\n        if ui.is_rect_visible(prepared.response.rect) {\n            prepared.fallback_text_color = text_style.color;\n            let response = prepared.paint(ui);\n\n            if let Some(rect) = response.rect(rect_id) {\n                let big_icon_rect = Rect::from_center_size(\n                    pos2(rect.left() + checkbox_size / 2.0, rect.center().y),\n                    Vec2::splat(checkbox_size),\n                );\n                let small_icon_rect =\n                    Rect::from_center_size(big_icon_rect.center(), Vec2::splat(check_size));\n                ui.painter().add(epaint::RectShape::new(\n                    big_icon_rect.expand(checkbox_frame.inner_margin.left.into()),\n                    checkbox_frame.corner_radius,\n                    checkbox_frame.fill,\n                    checkbox_frame.stroke,\n                    epaint::StrokeKind::Inside,\n                ));\n\n                if indeterminate {\n                    // Horizontal line:\n                    ui.painter().add(Shape::hline(\n                        small_icon_rect.x_range(),\n                        small_icon_rect.center().y,\n                        check_stroke,\n                    ));\n                } else if *checked {\n                    // Check mark:\n                    ui.painter().add(Shape::line(\n                        vec![\n                            pos2(small_icon_rect.left(), small_icon_rect.center().y),\n                            pos2(small_icon_rect.center().x, small_icon_rect.bottom()),\n                            pos2(small_icon_rect.right(), small_icon_rect.top()),\n                        ],\n                        check_stroke,\n                    ));\n                }\n            }\n            response.response\n        } else {\n            prepared.response\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/color_picker.rs",
    "content": "//! Color picker widgets.\n\nuse crate::util::fixed_cache::FixedCache;\nuse crate::{\n    Context, DragValue, Id, Painter, Popup, PopupCloseBehavior, Response, Sense, Ui, Widget as _,\n    WidgetInfo, WidgetType, epaint, lerp, remap_clamp,\n};\nuse epaint::{\n    Mesh, Rect, Shape, Stroke, StrokeKind, Vec2,\n    ecolor::{Color32, Hsva, HsvaGamma, Rgba},\n    pos2, vec2,\n};\n\nfn contrast_color(color: impl Into<Rgba>) -> Color32 {\n    if color.into().intensity() < 0.5 {\n        Color32::WHITE\n    } else {\n        Color32::BLACK\n    }\n}\n\n/// Number of vertices per dimension in the color sliders.\n/// We need at least 6 for hues, and more for smooth 2D areas.\n/// Should always be a multiple of 6 to hit the peak hues in HSV/HSL (every 60°).\nconst N: u32 = 6 * 6;\n\nfn background_checkers(painter: &Painter, rect: Rect) {\n    let rect = rect.shrink(0.5); // Small hack to avoid the checkers from peeking through the sides\n    if !rect.is_positive() {\n        return;\n    }\n\n    let dark_color = Color32::from_gray(32);\n    let bright_color = Color32::from_gray(128);\n\n    let checker_size = Vec2::splat(rect.height() / 2.0);\n    let n = (rect.width() / checker_size.x).round() as u32;\n\n    let mut mesh = Mesh::default();\n    mesh.add_colored_rect(rect, dark_color);\n\n    let mut top = true;\n    for i in 0..n {\n        let x = lerp(rect.left()..=rect.right(), i as f32 / (n as f32));\n        let small_rect = if top {\n            Rect::from_min_size(pos2(x, rect.top()), checker_size)\n        } else {\n            Rect::from_min_size(pos2(x, rect.center().y), checker_size)\n        };\n        mesh.add_colored_rect(small_rect, bright_color);\n        top = !top;\n    }\n    painter.add(Shape::mesh(mesh));\n}\n\n/// Show a color with background checkers to demonstrate transparency (if any).\npub fn show_color(ui: &mut Ui, color: impl Into<Color32>, desired_size: Vec2) -> Response {\n    show_color32(ui, color.into(), desired_size)\n}\n\nfn show_color32(ui: &mut Ui, color: Color32, desired_size: Vec2) -> Response {\n    let (rect, response) = ui.allocate_at_least(desired_size, Sense::hover());\n    if ui.is_rect_visible(rect) {\n        show_color_at(ui.painter(), color, rect);\n    }\n    response\n}\n\n/// Show a color with background checkers to demonstrate transparency (if any).\npub fn show_color_at(painter: &Painter, color: Color32, rect: Rect) {\n    if color.is_opaque() {\n        painter.rect_filled(rect, 0.0, color);\n    } else {\n        // Transparent: how both the transparent and opaque versions of the color\n        background_checkers(painter, rect);\n\n        if color == Color32::TRANSPARENT {\n            // There is no opaque version, so just show the background checkers\n        } else {\n            let left = Rect::from_min_max(rect.left_top(), rect.center_bottom());\n            let right = Rect::from_min_max(rect.center_top(), rect.right_bottom());\n            painter.rect_filled(left, 0.0, color);\n            painter.rect_filled(right, 0.0, color.to_opaque());\n        }\n    }\n}\n\nfn color_button(ui: &mut Ui, color: Color32, open: bool) -> Response {\n    let size = ui.spacing().interact_size;\n    let (rect, response) = ui.allocate_exact_size(size, Sense::click());\n    response.widget_info(|| WidgetInfo::new(WidgetType::ColorButton));\n\n    if ui.is_rect_visible(rect) {\n        let visuals = if open {\n            &ui.visuals().widgets.open\n        } else {\n            ui.style().interact(&response)\n        };\n        let rect = rect.expand(visuals.expansion);\n\n        let stroke_width = 1.0;\n        show_color_at(ui.painter(), color, rect.shrink(stroke_width));\n\n        let corner_radius = visuals.corner_radius.at_most(2); // Can't do more rounding because the background grid doesn't do any rounding\n        ui.painter().rect_stroke(\n            rect,\n            corner_radius,\n            (stroke_width, visuals.bg_fill), // Using fill for stroke is intentional, because default style has no border\n            StrokeKind::Inside,\n        );\n    }\n\n    response\n}\n\nfn color_slider_1d(ui: &mut Ui, value: &mut f32, color_at: impl Fn(f32) -> Color32) -> Response {\n    #![expect(clippy::identity_op)]\n\n    let desired_size = vec2(ui.spacing().slider_width, ui.spacing().interact_size.y);\n    let (rect, response) = ui.allocate_at_least(desired_size, Sense::click_and_drag());\n\n    if let Some(mpos) = response.interact_pointer_pos() {\n        *value = remap_clamp(mpos.x, rect.left()..=rect.right(), 0.0..=1.0);\n    }\n\n    if ui.is_rect_visible(rect) {\n        let visuals = ui.style().interact(&response);\n\n        background_checkers(ui.painter(), rect); // for alpha:\n\n        {\n            // fill color:\n            let mut mesh = Mesh::default();\n            for i in 0..=N {\n                let t = i as f32 / (N as f32);\n                let color = color_at(t);\n                let x = lerp(rect.left()..=rect.right(), t);\n                mesh.colored_vertex(pos2(x, rect.top()), color);\n                mesh.colored_vertex(pos2(x, rect.bottom()), color);\n                if i < N {\n                    mesh.add_triangle(2 * i + 0, 2 * i + 1, 2 * i + 2);\n                    mesh.add_triangle(2 * i + 1, 2 * i + 2, 2 * i + 3);\n                }\n            }\n            ui.painter().add(Shape::mesh(mesh));\n        }\n\n        ui.painter()\n            .rect_stroke(rect, 0.0, visuals.bg_stroke, StrokeKind::Inside); // outline\n\n        {\n            // Show where the slider is at:\n            let x = lerp(rect.left()..=rect.right(), *value);\n            let r = rect.height() / 4.0;\n            let picked_color = color_at(*value);\n            ui.painter().add(Shape::convex_polygon(\n                vec![\n                    pos2(x, rect.center().y),   // tip\n                    pos2(x + r, rect.bottom()), // right bottom\n                    pos2(x - r, rect.bottom()), // left bottom\n                ],\n                picked_color,\n                Stroke::new(visuals.fg_stroke.width, contrast_color(picked_color)),\n            ));\n        }\n    }\n\n    response\n}\n\n/// # Arguments\n/// * `x_value` - X axis, either saturation or value (0.0-1.0).\n/// * `y_value` - Y axis, either saturation or value (0.0-1.0).\n/// * `color_at` - A function that dictates how the mix of saturation and value will be displayed in the 2d slider.\n///\n/// e.g.: `|x_value, y_value| HsvaGamma { h: 1.0, s: x_value, v: y_value, a: 1.0 }.into()` displays the colors as follows:\n/// * top-left: white `[s: 0.0, v: 1.0]`\n/// * top-right: fully saturated color `[s: 1.0, v: 1.0]`\n/// * bottom-right: black `[s: 0.0, v: 1.0].`\nfn color_slider_2d(\n    ui: &mut Ui,\n    x_value: &mut f32,\n    y_value: &mut f32,\n    color_at: impl Fn(f32, f32) -> Color32,\n) -> Response {\n    let desired_size = Vec2::splat(ui.spacing().slider_width);\n    let (rect, response) = ui.allocate_at_least(desired_size, Sense::click_and_drag());\n\n    if let Some(mpos) = response.interact_pointer_pos() {\n        *x_value = remap_clamp(mpos.x, rect.left()..=rect.right(), 0.0..=1.0);\n        *y_value = remap_clamp(mpos.y, rect.bottom()..=rect.top(), 0.0..=1.0);\n    }\n\n    if ui.is_rect_visible(rect) {\n        let visuals = ui.style().interact(&response);\n        let mut mesh = Mesh::default();\n\n        for xi in 0..=N {\n            for yi in 0..=N {\n                let xt = xi as f32 / (N as f32);\n                let yt = yi as f32 / (N as f32);\n                let color = color_at(xt, yt);\n                let x = lerp(rect.left()..=rect.right(), xt);\n                let y = lerp(rect.bottom()..=rect.top(), yt);\n                mesh.colored_vertex(pos2(x, y), color);\n\n                if xi < N && yi < N {\n                    let x_offset = 1;\n                    let y_offset = N + 1;\n                    let tl = yi * y_offset + xi;\n                    mesh.add_triangle(tl, tl + x_offset, tl + y_offset);\n                    mesh.add_triangle(tl + x_offset, tl + y_offset, tl + y_offset + x_offset);\n                }\n            }\n        }\n        ui.painter().add(Shape::mesh(mesh)); // fill\n\n        ui.painter()\n            .rect_stroke(rect, 0.0, visuals.bg_stroke, StrokeKind::Inside); // outline\n\n        // Show where the slider is at:\n        let x = lerp(rect.left()..=rect.right(), *x_value);\n        let y = lerp(rect.bottom()..=rect.top(), *y_value);\n        let picked_color = color_at(*x_value, *y_value);\n        ui.painter().add(epaint::CircleShape {\n            center: pos2(x, y),\n            radius: rect.width() / 12.0,\n            fill: picked_color,\n            stroke: Stroke::new(visuals.fg_stroke.width, contrast_color(picked_color)),\n        });\n    }\n\n    response\n}\n\n/// We use a negative alpha for additive colors within this file (a bit ironic).\n///\n/// We use alpha=0 to mean \"transparent\".\nfn is_additive_alpha(a: f32) -> bool {\n    a < 0.0\n}\n\n/// What options to show for alpha\n#[derive(Clone, Copy, PartialEq, Eq)]\npub enum Alpha {\n    /// Set alpha to 1.0, and show no option for it.\n    Opaque,\n\n    /// Only show normal blend options for alpha.\n    OnlyBlend,\n\n    /// Show both blend and additive options.\n    BlendOrAdditive,\n}\n\nfn color_picker_hsvag_2d(ui: &mut Ui, hsvag: &mut HsvaGamma, alpha: Alpha) {\n    use crate::style::NumericColorSpace;\n\n    let alpha_control = if is_additive_alpha(hsvag.a) {\n        Alpha::Opaque // no alpha control for additive colors\n    } else {\n        alpha\n    };\n\n    match ui.style().visuals.numeric_color_space {\n        NumericColorSpace::GammaByte => {\n            let mut srgba_unmultiplied = Hsva::from(*hsvag).to_srgba_unmultiplied();\n            // Only update if changed to avoid rounding issues.\n            if srgba_edit_ui(ui, &mut srgba_unmultiplied, alpha_control) {\n                if is_additive_alpha(hsvag.a) {\n                    let alpha = hsvag.a;\n\n                    *hsvag = HsvaGamma::from(Hsva::from_additive_srgb([\n                        srgba_unmultiplied[0],\n                        srgba_unmultiplied[1],\n                        srgba_unmultiplied[2],\n                    ]));\n\n                    // Don't edit the alpha:\n                    hsvag.a = alpha;\n                } else {\n                    // Normal blending.\n                    *hsvag = HsvaGamma::from(Hsva::from_srgba_unmultiplied(srgba_unmultiplied));\n                }\n            }\n        }\n\n        NumericColorSpace::Linear => {\n            let mut rgba_unmultiplied = Hsva::from(*hsvag).to_rgba_unmultiplied();\n            // Only update if changed to avoid rounding issues.\n            if rgba_edit_ui(ui, &mut rgba_unmultiplied, alpha_control) {\n                if is_additive_alpha(hsvag.a) {\n                    let alpha = hsvag.a;\n\n                    *hsvag = HsvaGamma::from(Hsva::from_rgb([\n                        rgba_unmultiplied[0],\n                        rgba_unmultiplied[1],\n                        rgba_unmultiplied[2],\n                    ]));\n\n                    // Don't edit the alpha:\n                    hsvag.a = alpha;\n                } else {\n                    // Normal blending.\n                    *hsvag = HsvaGamma::from(Hsva::from_rgba_unmultiplied(\n                        rgba_unmultiplied[0],\n                        rgba_unmultiplied[1],\n                        rgba_unmultiplied[2],\n                        rgba_unmultiplied[3],\n                    ));\n                }\n            }\n        }\n    }\n\n    let current_color_size = vec2(ui.spacing().slider_width, ui.spacing().interact_size.y);\n    show_color(ui, *hsvag, current_color_size).on_hover_text(\"Selected color\");\n\n    if alpha == Alpha::BlendOrAdditive {\n        let a = &mut hsvag.a;\n        let mut additive = is_additive_alpha(*a);\n        ui.horizontal(|ui| {\n            ui.label(\"Blending:\");\n            ui.radio_value(&mut additive, false, \"Normal\");\n            ui.radio_value(&mut additive, true, \"Additive\");\n\n            if additive {\n                *a = -a.abs();\n            }\n\n            if !additive {\n                *a = a.abs();\n            }\n        });\n    }\n\n    let opaque = HsvaGamma { a: 1.0, ..*hsvag };\n\n    let HsvaGamma { h, s, v, a: _ } = hsvag;\n\n    if false {\n        color_slider_1d(ui, s, |s| HsvaGamma { s, ..opaque }.into()).on_hover_text(\"Saturation\");\n    }\n\n    if false {\n        color_slider_1d(ui, v, |v| HsvaGamma { v, ..opaque }.into()).on_hover_text(\"Value\");\n    }\n\n    color_slider_2d(ui, s, v, |s, v| HsvaGamma { s, v, ..opaque }.into());\n\n    color_slider_1d(ui, h, |h| {\n        HsvaGamma {\n            h,\n            s: 1.0,\n            v: 1.0,\n            a: 1.0,\n        }\n        .into()\n    })\n    .on_hover_text(\"Hue\");\n\n    let additive = is_additive_alpha(hsvag.a);\n\n    if alpha == Alpha::Opaque {\n        hsvag.a = 1.0;\n    } else {\n        let a = &mut hsvag.a;\n\n        if alpha == Alpha::OnlyBlend {\n            if is_additive_alpha(*a) {\n                *a = 0.5; // was additive, but isn't allowed to be\n            }\n            color_slider_1d(ui, a, |a| HsvaGamma { a, ..opaque }.into()).on_hover_text(\"Alpha\");\n        } else if !additive {\n            color_slider_1d(ui, a, |a| HsvaGamma { a, ..opaque }.into()).on_hover_text(\"Alpha\");\n        }\n    }\n}\n\nfn input_type_button_ui(ui: &mut Ui) {\n    let mut input_type = ui.global_style().visuals.numeric_color_space;\n    if input_type.toggle_button_ui(ui).changed() {\n        ui.ctx().all_styles_mut(|s| {\n            s.visuals.numeric_color_space = input_type;\n        });\n    }\n}\n\n/// Shows 4 `DragValue` widgets to be used to edit the RGBA u8 values.\n/// Alpha's `DragValue` is hidden when `Alpha::Opaque`.\n///\n/// Returns `true` on change.\nfn srgba_edit_ui(ui: &mut Ui, [r, g, b, a]: &mut [u8; 4], alpha: Alpha) -> bool {\n    let mut edited = false;\n\n    ui.horizontal(|ui| {\n        input_type_button_ui(ui);\n\n        if ui\n            .button(\"📋\")\n            .on_hover_text(\"Click to copy color values\")\n            .clicked()\n        {\n            if alpha == Alpha::Opaque {\n                ui.copy_text(format!(\"{r}, {g}, {b}\"));\n            } else {\n                ui.copy_text(format!(\"{r}, {g}, {b}, {a}\"));\n            }\n        }\n        edited |= DragValue::new(r).speed(0.5).prefix(\"R \").ui(ui).changed();\n        edited |= DragValue::new(g).speed(0.5).prefix(\"G \").ui(ui).changed();\n        edited |= DragValue::new(b).speed(0.5).prefix(\"B \").ui(ui).changed();\n        if alpha != Alpha::Opaque {\n            edited |= DragValue::new(a).speed(0.5).prefix(\"A \").ui(ui).changed();\n        }\n    });\n\n    edited\n}\n\n/// Shows 4 `DragValue` widgets to be used to edit the RGBA f32 values.\n/// Alpha's `DragValue` is hidden when `Alpha::Opaque`.\n///\n/// Returns `true` on change.\nfn rgba_edit_ui(ui: &mut Ui, [r, g, b, a]: &mut [f32; 4], alpha: Alpha) -> bool {\n    fn drag_value(ui: &mut Ui, prefix: &str, value: &mut f32) -> Response {\n        DragValue::new(value)\n            .speed(0.003)\n            .prefix(prefix)\n            .range(0.0..=1.0)\n            .custom_formatter(|n, _| format!(\"{n:.03}\"))\n            .ui(ui)\n    }\n\n    let mut edited = false;\n\n    ui.horizontal(|ui| {\n        input_type_button_ui(ui);\n\n        if ui\n            .button(\"📋\")\n            .on_hover_text(\"Click to copy color values\")\n            .clicked()\n        {\n            if alpha == Alpha::Opaque {\n                ui.copy_text(format!(\"{r:.03}, {g:.03}, {b:.03}\"));\n            } else {\n                ui.copy_text(format!(\"{r:.03}, {g:.03}, {b:.03}, {a:.03}\"));\n            }\n        }\n\n        edited |= drag_value(ui, \"R \", r).changed();\n        edited |= drag_value(ui, \"G \", g).changed();\n        edited |= drag_value(ui, \"B \", b).changed();\n        if alpha != Alpha::Opaque {\n            edited |= drag_value(ui, \"A \", a).changed();\n        }\n    });\n\n    edited\n}\n\n/// Shows a color picker where the user can change the given [`Hsva`] color.\n///\n/// Returns `true` on change.\npub fn color_picker_hsva_2d(ui: &mut Ui, hsva: &mut Hsva, alpha: Alpha) -> bool {\n    let mut hsvag = HsvaGamma::from(*hsva);\n    ui.vertical(|ui| {\n        color_picker_hsvag_2d(ui, &mut hsvag, alpha);\n    });\n    let new_hasva = Hsva::from(hsvag);\n    if *hsva == new_hasva {\n        false\n    } else {\n        *hsva = new_hasva;\n        true\n    }\n}\n\n/// Shows a color picker where the user can change the given [`Color32`] color.\n///\n/// Returns `true` on change.\npub fn color_picker_color32(ui: &mut Ui, srgba: &mut Color32, alpha: Alpha) -> bool {\n    let mut hsva = color_cache_get(ui.ctx(), *srgba);\n    let changed = color_picker_hsva_2d(ui, &mut hsva, alpha);\n    *srgba = Color32::from(hsva);\n    color_cache_set(ui.ctx(), *srgba, hsva);\n    changed\n}\n\npub fn color_edit_button_hsva(ui: &mut Ui, hsva: &mut Hsva, alpha: Alpha) -> Response {\n    let popup_id = ui.auto_id_with(\"popup\");\n    let open = Popup::is_id_open(ui.ctx(), popup_id);\n    let mut button_response = color_button(ui, (*hsva).into(), open);\n    if ui.style().explanation_tooltips {\n        button_response = button_response.on_hover_text(\"Click to edit color\");\n    }\n\n    const COLOR_SLIDER_WIDTH: f32 = 275.0;\n\n    Popup::menu(&button_response)\n        .id(popup_id)\n        .close_behavior(PopupCloseBehavior::CloseOnClickOutside)\n        .show(|ui| {\n            ui.spacing_mut().slider_width = COLOR_SLIDER_WIDTH;\n            if color_picker_hsva_2d(ui, hsva, alpha) {\n                button_response.mark_changed();\n            }\n        });\n\n    button_response\n}\n\n/// Shows a button with the given color.\n/// If the user clicks the button, a full color picker is shown.\npub fn color_edit_button_srgba(ui: &mut Ui, srgba: &mut Color32, alpha: Alpha) -> Response {\n    let mut hsva = color_cache_get(ui.ctx(), *srgba);\n    let response = color_edit_button_hsva(ui, &mut hsva, alpha);\n    *srgba = Color32::from(hsva);\n    color_cache_set(ui.ctx(), *srgba, hsva);\n    response\n}\n\n/// Shows a button with the given color.\n/// If the user clicks the button, a full color picker is shown.\n/// The given color is in `sRGB` space.\npub fn color_edit_button_srgb(ui: &mut Ui, srgb: &mut [u8; 3]) -> Response {\n    let mut srgba = Color32::from_rgb(srgb[0], srgb[1], srgb[2]);\n    let response = color_edit_button_srgba(ui, &mut srgba, Alpha::Opaque);\n    srgb[0] = srgba[0];\n    srgb[1] = srgba[1];\n    srgb[2] = srgba[2];\n    response\n}\n\n/// Shows a button with the given color.\n/// If the user clicks the button, a full color picker is shown.\npub fn color_edit_button_rgba(ui: &mut Ui, rgba: &mut Rgba, alpha: Alpha) -> Response {\n    let mut hsva = color_cache_get(ui.ctx(), *rgba);\n    let response = color_edit_button_hsva(ui, &mut hsva, alpha);\n    *rgba = Rgba::from(hsva);\n    color_cache_set(ui.ctx(), *rgba, hsva);\n    response\n}\n\n/// Shows a button with the given color.\n/// If the user clicks the button, a full color picker is shown.\npub fn color_edit_button_rgb(ui: &mut Ui, rgb: &mut [f32; 3]) -> Response {\n    let mut rgba = Rgba::from_rgb(rgb[0], rgb[1], rgb[2]);\n    let response = color_edit_button_rgba(ui, &mut rgba, Alpha::Opaque);\n    rgb[0] = rgba[0];\n    rgb[1] = rgba[1];\n    rgb[2] = rgba[2];\n    response\n}\n\n// To ensure we keep hue slider when `srgba` is gray we store the full [`Hsva`] in a cache:\nfn color_cache_get(ctx: &Context, rgba: impl Into<Rgba>) -> Hsva {\n    let rgba = rgba.into();\n    use_color_cache(ctx, |cc| cc.get(&rgba).copied()).unwrap_or_else(|| Hsva::from(rgba))\n}\n\n// To ensure we keep hue slider when `srgba` is gray we store the full [`Hsva`] in a cache:\nfn color_cache_set(ctx: &Context, rgba: impl Into<Rgba>, hsva: Hsva) {\n    let rgba = rgba.into();\n    use_color_cache(ctx, |cc| cc.set(rgba, hsva));\n}\n\n// To ensure we keep hue slider when `srgba` is gray we store the full [`Hsva`] in a cache:\nfn use_color_cache<R>(ctx: &Context, f: impl FnOnce(&mut FixedCache<Rgba, Hsva>) -> R) -> R {\n    ctx.data_mut(|d| f(d.get_temp_mut_or_default(Id::NULL)))\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/drag_value.rs",
    "content": "use crate::{\n    Atom, AtomExt as _, AtomKind, Atoms, Button, CursorIcon, Id, IntoAtoms, Key, MINUS_CHAR_STR,\n    Modifiers, NumExt as _, Response, RichText, Sense, TextEdit, TextWrapMode, Ui, Widget,\n    WidgetInfo, emath, text,\n};\nuse emath::Vec2;\nuse std::{cmp::Ordering, ops::RangeInclusive};\n\n// ----------------------------------------------------------------------------\n\ntype NumFormatter<'a> = Box<dyn 'a + Fn(f64, RangeInclusive<usize>) -> String>;\ntype NumParser<'a> = Box<dyn 'a + Fn(&str) -> Option<f64>>;\n\n// ----------------------------------------------------------------------------\n\n/// Combined into one function (rather than two) to make it easier\n/// for the borrow checker.\ntype GetSetValue<'a> = Box<dyn 'a + FnMut(Option<f64>) -> f64>;\n\nfn get(get_set_value: &mut GetSetValue<'_>) -> f64 {\n    (get_set_value)(None)\n}\n\nfn set(get_set_value: &mut GetSetValue<'_>, value: f64) {\n    (get_set_value)(Some(value));\n}\n\n/// A numeric value that you can change by dragging the number. More compact than a [`crate::Slider`].\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # let mut my_f32: f32 = 0.0;\n/// ui.add(egui::DragValue::new(&mut my_f32).speed(0.1));\n/// # });\n/// ```\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct DragValue<'a> {\n    get_set_value: GetSetValue<'a>,\n    speed: f64,\n    atoms: Atoms<'a>,\n    range: RangeInclusive<f64>,\n    clamp_existing_to_range: bool,\n    min_decimals: usize,\n    max_decimals: Option<usize>,\n    custom_formatter: Option<NumFormatter<'a>>,\n    custom_parser: Option<NumParser<'a>>,\n    update_while_editing: bool,\n}\n\nimpl<'a> DragValue<'a> {\n    const ATOM_ID: &'static str = \"drag_item\";\n\n    pub fn new<Num: emath::Numeric>(value: &'a mut Num) -> Self {\n        let slf = Self::from_get_set(move |v: Option<f64>| {\n            if let Some(v) = v {\n                *value = Num::from_f64(v);\n            }\n            value.to_f64()\n        });\n\n        if Num::INTEGRAL {\n            slf.max_decimals(0).range(Num::MIN..=Num::MAX).speed(0.25)\n        } else {\n            slf\n        }\n    }\n\n    pub fn from_get_set(get_set_value: impl 'a + FnMut(Option<f64>) -> f64) -> Self {\n        let atoms = Atoms::new(Atom::custom(Id::new(Self::ATOM_ID), Vec2::ZERO).atom_grow(true));\n\n        Self {\n            get_set_value: Box::new(get_set_value),\n            speed: 1.0,\n            atoms,\n            range: f64::NEG_INFINITY..=f64::INFINITY,\n            clamp_existing_to_range: true,\n            min_decimals: 0,\n            max_decimals: None,\n            custom_formatter: None,\n            custom_parser: None,\n            update_while_editing: true,\n        }\n    }\n\n    /// How much the value changes when dragged one point (logical pixel).\n    ///\n    /// Should be finite and greater than zero.\n    #[inline]\n    pub fn speed(mut self, speed: impl Into<f64>) -> Self {\n        self.speed = speed.into();\n        self\n    }\n\n    /// Sets valid range for the value.\n    ///\n    /// By default all values are clamped to this range, even when not interacted with.\n    /// You can change this behavior by passing `false` to [`Self::clamp_existing_to_range`].\n    #[deprecated = \"Use `range` instead\"]\n    #[inline]\n    pub fn clamp_range<Num: emath::Numeric>(self, range: RangeInclusive<Num>) -> Self {\n        self.range(range)\n    }\n\n    /// Sets valid range for dragging the value.\n    ///\n    /// By default all values are clamped to this range, even when not interacted with.\n    /// You can change this behavior by passing `false` to [`Self::clamp_existing_to_range`].\n    #[inline]\n    pub fn range<Num: emath::Numeric>(mut self, range: RangeInclusive<Num>) -> Self {\n        self.range = range.start().to_f64()..=range.end().to_f64();\n        self\n    }\n\n    /// If set to `true`, existing values will be clamped to [`Self::range`].\n    ///\n    /// If `false`, only values entered by the user (via dragging or text editing)\n    /// will be clamped to the range.\n    ///\n    /// ### Without calling `range`\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let mut my_value: f32 = 1337.0;\n    /// ui.add(egui::DragValue::new(&mut my_value));\n    /// assert_eq!(my_value, 1337.0, \"No range, no clamp\");\n    /// # });\n    /// ```\n    ///\n    /// ### With `.clamp_existing_to_range(true)` (default)\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let mut my_value: f32 = 1337.0;\n    /// ui.add(egui::DragValue::new(&mut my_value).range(0.0..=1.0));\n    /// assert!(0.0 <= my_value && my_value <= 1.0, \"Existing values should be clamped\");\n    /// # });\n    /// ```\n    ///\n    /// ### With `.clamp_existing_to_range(false)`\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let mut my_value: f32 = 1337.0;\n    /// let response = ui.add(\n    ///     egui::DragValue::new(&mut my_value).range(0.0..=1.0)\n    ///         .clamp_existing_to_range(false)\n    /// );\n    /// if response.dragged() {\n    ///     // The user edited the value, so it should be clamped to the range\n    ///     assert!(0.0 <= my_value && my_value <= 1.0);\n    /// } else {\n    ///     // The user didn't edit, so our original value should still be here:\n    ///     assert_eq!(my_value, 1337.0);\n    /// }\n    /// # });\n    /// ```\n    #[inline]\n    pub fn clamp_existing_to_range(mut self, clamp_existing_to_range: bool) -> Self {\n        self.clamp_existing_to_range = clamp_existing_to_range;\n        self\n    }\n\n    #[inline]\n    #[deprecated = \"Renamed clamp_existing_to_range\"]\n    pub fn clamp_to_range(self, clamp_to_range: bool) -> Self {\n        self.clamp_existing_to_range(clamp_to_range)\n    }\n\n    /// Show a prefix before the number, e.g. \"x: \"\n    #[inline]\n    pub fn prefix(mut self, prefix: impl IntoAtoms<'a>) -> Self {\n        self.atoms.extend_left(prefix.into_atoms());\n        self\n    }\n\n    /// Add a suffix to the number, this can be e.g. a unit (\"°\" or \" m\")\n    #[inline]\n    pub fn suffix(mut self, suffix: impl IntoAtoms<'a>) -> Self {\n        self.atoms.extend_right(suffix.into_atoms());\n        self\n    }\n\n    // TODO(emilk): we should also have a \"min precision\".\n    /// Set a minimum number of decimals to display.\n    /// Normally you don't need to pick a precision, as the slider will intelligently pick a precision for you.\n    /// Regardless of precision the slider will use \"smart aim\" to help the user select nice, round values.\n    #[inline]\n    pub fn min_decimals(mut self, min_decimals: usize) -> Self {\n        self.min_decimals = min_decimals;\n        self\n    }\n\n    // TODO(emilk): we should also have a \"max precision\".\n    /// Set a maximum number of decimals to display.\n    /// Values will also be rounded to this number of decimals.\n    /// Normally you don't need to pick a precision, as the slider will intelligently pick a precision for you.\n    /// Regardless of precision the slider will use \"smart aim\" to help the user select nice, round values.\n    #[inline]\n    pub fn max_decimals(mut self, max_decimals: usize) -> Self {\n        self.max_decimals = Some(max_decimals);\n        self\n    }\n\n    #[inline]\n    pub fn max_decimals_opt(mut self, max_decimals: Option<usize>) -> Self {\n        self.max_decimals = max_decimals;\n        self\n    }\n\n    /// Set an exact number of decimals to display.\n    /// Values will also be rounded to this number of decimals.\n    /// Normally you don't need to pick a precision, as the slider will intelligently pick a precision for you.\n    /// Regardless of precision the slider will use \"smart aim\" to help the user select nice, round values.\n    #[inline]\n    pub fn fixed_decimals(mut self, num_decimals: usize) -> Self {\n        self.min_decimals = num_decimals;\n        self.max_decimals = Some(num_decimals);\n        self\n    }\n\n    /// Set custom formatter defining how numbers are converted into text.\n    ///\n    /// A custom formatter takes a `f64` for the numeric value and a `RangeInclusive<usize>` representing\n    /// the decimal range i.e. minimum and maximum number of decimal places shown.\n    ///\n    /// The default formatter is [`crate::Style::number_formatter`].\n    ///\n    /// See also: [`DragValue::custom_parser`]\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_i32: i32 = 0;\n    /// ui.add(egui::DragValue::new(&mut my_i32)\n    ///     .range(0..=((60 * 60 * 24) - 1))\n    ///     .custom_formatter(|n, _| {\n    ///         let n = n as i32;\n    ///         let hours = n / (60 * 60);\n    ///         let mins = (n / 60) % 60;\n    ///         let secs = n % 60;\n    ///         format!(\"{hours:02}:{mins:02}:{secs:02}\")\n    ///     })\n    ///     .custom_parser(|s| {\n    ///         let parts: Vec<&str> = s.split(':').collect();\n    ///         if parts.len() == 3 {\n    ///             parts[0].parse::<i32>().and_then(|h| {\n    ///                 parts[1].parse::<i32>().and_then(|m| {\n    ///                     parts[2].parse::<i32>().map(|s| {\n    ///                         ((h * 60 * 60) + (m * 60) + s) as f64\n    ///                     })\n    ///                 })\n    ///             })\n    ///             .ok()\n    ///         } else {\n    ///             None\n    ///         }\n    ///     }));\n    /// # });\n    /// ```\n    pub fn custom_formatter(\n        mut self,\n        formatter: impl 'a + Fn(f64, RangeInclusive<usize>) -> String,\n    ) -> Self {\n        self.custom_formatter = Some(Box::new(formatter));\n        self\n    }\n\n    /// Set custom parser defining how the text input is parsed into a number.\n    ///\n    /// A custom parser takes an `&str` to parse into a number and returns a `f64` if it was successfully parsed\n    /// or `None` otherwise.\n    ///\n    /// See also: [`DragValue::custom_formatter`]\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_i32: i32 = 0;\n    /// ui.add(egui::DragValue::new(&mut my_i32)\n    ///     .range(0..=((60 * 60 * 24) - 1))\n    ///     .custom_formatter(|n, _| {\n    ///         let n = n as i32;\n    ///         let hours = n / (60 * 60);\n    ///         let mins = (n / 60) % 60;\n    ///         let secs = n % 60;\n    ///         format!(\"{hours:02}:{mins:02}:{secs:02}\")\n    ///     })\n    ///     .custom_parser(|s| {\n    ///         let parts: Vec<&str> = s.split(':').collect();\n    ///         if parts.len() == 3 {\n    ///             parts[0].parse::<i32>().and_then(|h| {\n    ///                 parts[1].parse::<i32>().and_then(|m| {\n    ///                     parts[2].parse::<i32>().map(|s| {\n    ///                         ((h * 60 * 60) + (m * 60) + s) as f64\n    ///                     })\n    ///                 })\n    ///             })\n    ///             .ok()\n    ///         } else {\n    ///             None\n    ///         }\n    ///     }));\n    /// # });\n    /// ```\n    #[inline]\n    pub fn custom_parser(mut self, parser: impl 'a + Fn(&str) -> Option<f64>) -> Self {\n        self.custom_parser = Some(Box::new(parser));\n        self\n    }\n\n    /// Set `custom_formatter` and `custom_parser` to display and parse numbers as binary integers. Floating point\n    /// numbers are *not* supported.\n    ///\n    /// `min_width` specifies the minimum number of displayed digits; if the number is shorter than this, it will be\n    /// prefixed with additional 0s to match `min_width`.\n    ///\n    /// If `twos_complement` is true, negative values will be displayed as the 2's complement representation. Otherwise\n    /// they will be prefixed with a '-' sign.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `min_width` is 0.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_i32: i32 = 0;\n    /// ui.add(egui::DragValue::new(&mut my_i32).binary(64, false));\n    /// # });\n    /// ```\n    pub fn binary(self, min_width: usize, twos_complement: bool) -> Self {\n        assert!(\n            min_width > 0,\n            \"DragValue::binary: `min_width` must be greater than 0\"\n        );\n        if twos_complement {\n            self.custom_formatter(move |n, _| format!(\"{:0>min_width$b}\", n as i64))\n        } else {\n            self.custom_formatter(move |n, _| {\n                let sign = if n < 0.0 { MINUS_CHAR_STR } else { \"\" };\n                format!(\"{sign}{:0>min_width$b}\", n.abs() as i64)\n            })\n        }\n        .custom_parser(|s| i64::from_str_radix(s, 2).map(|n| n as f64).ok())\n    }\n\n    /// Set `custom_formatter` and `custom_parser` to display and parse numbers as octal integers. Floating point\n    /// numbers are *not* supported.\n    ///\n    /// `min_width` specifies the minimum number of displayed digits; if the number is shorter than this, it will be\n    /// prefixed with additional 0s to match `min_width`.\n    ///\n    /// If `twos_complement` is true, negative values will be displayed as the 2's complement representation. Otherwise\n    /// they will be prefixed with a '-' sign.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `min_width` is 0.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_i32: i32 = 0;\n    /// ui.add(egui::DragValue::new(&mut my_i32).octal(22, false));\n    /// # });\n    /// ```\n    pub fn octal(self, min_width: usize, twos_complement: bool) -> Self {\n        assert!(\n            min_width > 0,\n            \"DragValue::octal: `min_width` must be greater than 0\"\n        );\n        if twos_complement {\n            self.custom_formatter(move |n, _| format!(\"{:0>min_width$o}\", n as i64))\n        } else {\n            self.custom_formatter(move |n, _| {\n                let sign = if n < 0.0 { MINUS_CHAR_STR } else { \"\" };\n                format!(\"{sign}{:0>min_width$o}\", n.abs() as i64)\n            })\n        }\n        .custom_parser(|s| i64::from_str_radix(s, 8).map(|n| n as f64).ok())\n    }\n\n    /// Set `custom_formatter` and `custom_parser` to display and parse numbers as hexadecimal integers. Floating point\n    /// numbers are *not* supported.\n    ///\n    /// `min_width` specifies the minimum number of displayed digits; if the number is shorter than this, it will be\n    /// prefixed with additional 0s to match `min_width`.\n    ///\n    /// If `twos_complement` is true, negative values will be displayed as the 2's complement representation. Otherwise\n    /// they will be prefixed with a '-' sign.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `min_width` is 0.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_i32: i32 = 0;\n    /// ui.add(egui::DragValue::new(&mut my_i32).hexadecimal(16, false, true));\n    /// # });\n    /// ```\n    pub fn hexadecimal(self, min_width: usize, twos_complement: bool, upper: bool) -> Self {\n        assert!(\n            min_width > 0,\n            \"DragValue::hexadecimal: `min_width` must be greater than 0\"\n        );\n        match (twos_complement, upper) {\n            (true, true) => {\n                self.custom_formatter(move |n, _| format!(\"{:0>min_width$X}\", n as i64))\n            }\n            (true, false) => {\n                self.custom_formatter(move |n, _| format!(\"{:0>min_width$x}\", n as i64))\n            }\n            (false, true) => self.custom_formatter(move |n, _| {\n                let sign = if n < 0.0 { MINUS_CHAR_STR } else { \"\" };\n                format!(\"{sign}{:0>min_width$X}\", n.abs() as i64)\n            }),\n            (false, false) => self.custom_formatter(move |n, _| {\n                let sign = if n < 0.0 { MINUS_CHAR_STR } else { \"\" };\n                format!(\"{sign}{:0>min_width$x}\", n.abs() as i64)\n            }),\n        }\n        .custom_parser(|s| i64::from_str_radix(s, 16).map(|n| n as f64).ok())\n    }\n\n    /// Update the value on each key press when text-editing the value.\n    ///\n    /// Default: `true`.\n    /// If `false`, the value will only be updated when user presses enter or deselects the value.\n    #[inline]\n    pub fn update_while_editing(mut self, update: bool) -> Self {\n        self.update_while_editing = update;\n        self\n    }\n}\n\nimpl Widget for DragValue<'_> {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let Self {\n            mut get_set_value,\n            speed,\n            range,\n            clamp_existing_to_range,\n            mut atoms,\n            min_decimals,\n            max_decimals,\n            custom_formatter,\n            custom_parser,\n            update_while_editing,\n        } = self;\n\n        let mut prefix_text = String::new();\n        let mut suffix_text = String::new();\n        let mut past_value = false;\n        let atom_id = Id::new(Self::ATOM_ID);\n        for atom in atoms.iter() {\n            if atom.id == Some(atom_id) {\n                past_value = true;\n            }\n            if let AtomKind::Text(text) = &atom.kind {\n                if past_value {\n                    suffix_text.push_str(text.text());\n                } else {\n                    prefix_text.push_str(text.text());\n                }\n            }\n        }\n\n        let shift = ui.input(|i| i.modifiers.shift_only());\n        // The widget has the same ID whether it's in edit or button mode.\n        let id = ui.next_auto_id();\n        let is_slow_speed = shift && ui.ctx().is_being_dragged(id);\n\n        // The following ensures that when a `DragValue` receives focus,\n        // it is immediately rendered in edit mode, rather than being rendered\n        // in button mode for just one frame. This is important for\n        // screen readers.\n        let is_kb_editing = ui.is_enabled()\n            && ui.memory_mut(|mem| {\n                mem.interested_in_focus(id, ui.layer_id());\n                mem.has_focus(id)\n            });\n\n        if ui.memory_mut(|mem| mem.gained_focus(id)) {\n            ui.data_mut(|data| data.remove::<String>(id));\n        }\n\n        let old_value = get(&mut get_set_value);\n        let mut value = old_value;\n        let aim_rad = ui.input(|i| i.aim_radius() as f64);\n\n        let auto_decimals = (aim_rad / speed.abs()).log10().ceil().clamp(0.0, 15.0) as usize;\n        let auto_decimals = auto_decimals + is_slow_speed as usize;\n        let max_decimals = max_decimals\n            .unwrap_or(auto_decimals + 2)\n            .at_least(min_decimals);\n        let auto_decimals = auto_decimals.clamp(min_decimals, max_decimals);\n\n        let change = ui.input_mut(|input| {\n            let mut change = 0.0;\n\n            if is_kb_editing {\n                // This deliberately doesn't listen for left and right arrow keys,\n                // because when editing, these are used to move the caret.\n                // This behavior is consistent with other editable spinner/stepper\n                // implementations, such as Chromium's (for HTML5 number input).\n                // It is also normal for such controls to go directly into edit mode\n                // when they receive keyboard focus, and some screen readers\n                // assume this behavior, so having a separate mode for incrementing\n                // and decrementing, that supports all arrow keys, would be\n                // problematic.\n                change += input.count_and_consume_key(Modifiers::NONE, Key::ArrowUp) as f64\n                    - input.count_and_consume_key(Modifiers::NONE, Key::ArrowDown) as f64;\n            }\n\n            use accesskit::Action;\n            change += input.num_accesskit_action_requests(id, Action::Increment) as f64\n                - input.num_accesskit_action_requests(id, Action::Decrement) as f64;\n\n            change\n        });\n\n        ui.input(|input| {\n            use accesskit::{Action, ActionData};\n            for request in input.accesskit_action_requests(id, Action::SetValue) {\n                if let Some(ActionData::NumericValue(new_value)) = request.data {\n                    value = new_value;\n                }\n            }\n        });\n\n        if clamp_existing_to_range {\n            value = clamp_value_to_range(value, range.clone());\n        }\n\n        if change != 0.0 {\n            value += speed * change;\n            value = emath::round_to_decimals(value, auto_decimals);\n        }\n\n        if old_value != value {\n            set(&mut get_set_value, value);\n            ui.data_mut(|data| data.remove::<String>(id));\n        }\n\n        let value_text = match custom_formatter {\n            Some(custom_formatter) => custom_formatter(value, auto_decimals..=max_decimals),\n            None => ui\n                .style()\n                .number_formatter\n                .format(value, auto_decimals..=max_decimals),\n        };\n\n        let text_style = ui.style().drag_value_text_style.clone();\n\n        if ui.memory(|mem| mem.lost_focus(id)) && !ui.input(|i| i.key_pressed(Key::Escape)) {\n            let value_text = ui.data_mut(|data| data.remove_temp::<String>(id));\n            if let Some(value_text) = value_text {\n                // We were editing the value as text last frame, but lost focus.\n                // Make sure we applied the last text value:\n                let parsed_value = parse(&custom_parser, &value_text);\n                if let Some(mut parsed_value) = parsed_value {\n                    // User edits always clamps:\n                    parsed_value = clamp_value_to_range(parsed_value, range.clone());\n                    set(&mut get_set_value, parsed_value);\n                }\n            }\n        }\n\n        let mut response = if is_kb_editing {\n            let mut value_text = ui\n                .data_mut(|data| data.remove_temp::<String>(id))\n                .unwrap_or_else(|| value_text.clone());\n            let response = ui.add(\n                TextEdit::singleline(&mut value_text)\n                    .clip_text(false)\n                    .horizontal_align(ui.layout().horizontal_align())\n                    .vertical_align(ui.layout().vertical_align())\n                    .margin(ui.spacing().button_padding)\n                    .min_size(ui.spacing().interact_size)\n                    .id(id)\n                    .desired_width(\n                        ui.spacing().interact_size.x - 2.0 * ui.spacing().button_padding.x,\n                    )\n                    .font(text_style),\n            );\n\n            // Select all text when the edit gains focus.\n            if ui.memory_mut(|mem| mem.gained_focus(id)) {\n                select_all_text(ui, id, response.id, &value_text);\n            }\n\n            let update = if update_while_editing {\n                // Update when the edit content has changed.\n                response.changed()\n            } else {\n                // Update only when the edit has lost focus.\n                response.lost_focus() && !ui.input(|i| i.key_pressed(Key::Escape))\n            };\n            if update {\n                let parsed_value = parse(&custom_parser, &value_text);\n                if let Some(mut parsed_value) = parsed_value {\n                    // User edits always clamps:\n                    parsed_value = clamp_value_to_range(parsed_value, range.clone());\n                    set(&mut get_set_value, parsed_value);\n                }\n            }\n            ui.data_mut(|data| data.insert_temp(id, value_text));\n            response\n        } else {\n            atoms.map_atoms(|atom| {\n                if atom.id == Some(atom_id) {\n                    RichText::new(value_text.clone())\n                        .text_style(text_style.clone())\n                        .into()\n                } else {\n                    atom\n                }\n            });\n            let button = Button::new(atoms)\n                .wrap_mode(TextWrapMode::Extend)\n                .sense(Sense::click_and_drag())\n                .gap(0.0)\n                .min_size(ui.spacing().interact_size); // TODO(emilk): find some more generic solution to `min_size`\n\n            let cursor_icon = if value <= *range.start() {\n                CursorIcon::ResizeEast\n            } else if value < *range.end() {\n                CursorIcon::ResizeHorizontal\n            } else {\n                CursorIcon::ResizeWest\n            };\n\n            let response = ui.add(button);\n            let mut response = response.on_hover_cursor(cursor_icon);\n\n            if ui.style().explanation_tooltips {\n                response = response.on_hover_text(format!(\n                    \"{}\\nDrag to edit or click to enter a value.\\nPress 'Shift' while dragging for better control.\",\n                    value as f32, // Show full precision value on-hover. TODO(emilk): figure out f64 vs f32\n                ));\n            }\n\n            if ui.input(|i| i.pointer.any_pressed() || i.pointer.any_released()) {\n                // Reset memory of preciely dagged value.\n                ui.data_mut(|data| data.remove::<f64>(id));\n            }\n\n            if response.clicked() {\n                ui.data_mut(|data| data.remove::<String>(id));\n                ui.memory_mut(|mem| mem.request_focus(id));\n                select_all_text(ui, id, response.id, &value_text);\n            } else if response.dragged() {\n                ui.set_cursor_icon(cursor_icon);\n\n                let mdelta = response.drag_delta();\n                let delta_points = mdelta.x - mdelta.y; // Increase to the right and up\n\n                let speed = if is_slow_speed { speed / 10.0 } else { speed };\n\n                let delta_value = delta_points as f64 * speed;\n\n                if delta_value != 0.0 {\n                    // Since we round the value being dragged, we need to store the full precision value in memory:\n                    let precise_value = ui.data_mut(|data| data.get_temp::<f64>(id));\n                    let precise_value = precise_value.unwrap_or(value);\n                    let precise_value = precise_value + delta_value;\n\n                    let aim_delta = aim_rad * speed;\n                    let rounded_new_value = emath::smart_aim::best_in_range_f64(\n                        precise_value - aim_delta,\n                        precise_value + aim_delta,\n                    );\n                    let rounded_new_value =\n                        emath::round_to_decimals(rounded_new_value, auto_decimals);\n                    // Dragging will always clamp the value to the range.\n                    let rounded_new_value = clamp_value_to_range(rounded_new_value, range.clone());\n                    set(&mut get_set_value, rounded_new_value);\n\n                    ui.data_mut(|data| data.insert_temp::<f64>(id, precise_value));\n                }\n            }\n\n            response\n        };\n\n        if get(&mut get_set_value) != old_value {\n            response.mark_changed();\n        }\n\n        response.widget_info(|| WidgetInfo::drag_value(ui.is_enabled(), value));\n\n        ui.ctx().accesskit_node_builder(response.id, |builder| {\n            use accesskit::Action;\n            // If either end of the range is unbounded, it's better\n            // to leave the corresponding AccessKit field set to None,\n            // to allow for platform-specific default behavior.\n            if range.start().is_finite() {\n                builder.set_min_numeric_value(*range.start());\n            }\n            if range.end().is_finite() {\n                builder.set_max_numeric_value(*range.end());\n            }\n            builder.set_numeric_value_step(speed);\n            builder.add_action(Action::SetValue);\n            if value < *range.end() {\n                builder.add_action(Action::Increment);\n            }\n            if value > *range.start() {\n                builder.add_action(Action::Decrement);\n            }\n            // The name field is set to the current value by the button,\n            // but we don't want it set that way on this widget type.\n            builder.clear_label();\n            // Always expose the value as a string. This makes the widget\n            // more stable to accessibility users as it switches\n            // between edit and button modes. This is particularly important\n            // for VoiceOver on macOS; if the value is not exposed as a string\n            // when the widget is in button mode, then VoiceOver speaks\n            // the value (or a percentage if the widget has a clamp range)\n            // when the widget loses focus, overriding the announcement\n            // of the newly focused widget. This is certainly a VoiceOver bug,\n            // but it's good to make our software work as well as possible\n            // with existing assistive technology. However, if the widget\n            // has a prefix and/or suffix, expose those when in button mode,\n            // just as they're exposed on the screen. This triggers the\n            // VoiceOver bug just described, but exposing all information\n            // is more important, and at least we can avoid the bug\n            // for instances of the widget with no prefix or suffix.\n            //\n            // The value is exposed as a string by the text edit widget\n            // when in edit mode.\n            if !is_kb_editing {\n                let value_text = format!(\"{prefix_text}{value_text}{suffix_text}\");\n                builder.set_value(value_text);\n            }\n        });\n\n        response\n    }\n}\n\nfn parse(custom_parser: &Option<NumParser<'_>>, value_text: &str) -> Option<f64> {\n    match &custom_parser {\n        Some(parser) => parser(value_text),\n        None => default_parser(value_text),\n    }\n}\n\n/// The default egui parser of numbers.\n///\n/// It ignored whitespaces anywhere in the input, and treats the special minus character (U+2212) as a normal minus.\nfn default_parser(text: &str) -> Option<f64> {\n    let text: String = text\n        .chars()\n        // Ignore whitespace (trailing, leading, and thousands separators):\n        .filter(|c| !c.is_whitespace())\n        // Replace special minus character with normal minus (hyphen):\n        .map(|c| if c == '−' { '-' } else { c })\n        .collect();\n\n    text.parse().ok()\n}\n\n/// Clamp the given value with careful handling of negative zero, and other corner cases.\npub(crate) fn clamp_value_to_range(x: f64, range: RangeInclusive<f64>) -> f64 {\n    let (mut min, mut max) = (*range.start(), *range.end());\n\n    if min.total_cmp(&max) == Ordering::Greater {\n        (min, max) = (max, min);\n    }\n\n    match x.total_cmp(&min) {\n        Ordering::Less | Ordering::Equal => min,\n        Ordering::Greater => match x.total_cmp(&max) {\n            Ordering::Greater | Ordering::Equal => max,\n            Ordering::Less => x,\n        },\n    }\n}\n\n/// Select all text in the `DragValue` text edit widget.\nfn select_all_text(ui: &Ui, widget_id: Id, response_id: Id, value_text: &str) {\n    let mut state = TextEdit::load_state(ui.ctx(), widget_id).unwrap_or_default();\n    state.cursor.set_char_range(Some(text::CCursorRange::two(\n        text::CCursor::default(),\n        text::CCursor::new(value_text.chars().count()),\n    )));\n    state.store(ui.ctx(), response_id);\n}\n\n#[cfg(test)]\nmod tests {\n    use super::clamp_value_to_range;\n\n    macro_rules! total_assert_eq {\n        ($a:expr, $b:expr) => {\n            assert!(\n                matches!($a.total_cmp(&$b), std::cmp::Ordering::Equal),\n                \"{} != {}\",\n                $a,\n                $b\n            );\n        };\n    }\n\n    #[test]\n    fn test_total_cmp_clamp_value_to_range() {\n        total_assert_eq!(0.0_f64, clamp_value_to_range(-0.0, 0.0..=f64::MAX));\n        total_assert_eq!(-0.0_f64, clamp_value_to_range(0.0, -1.0..=-0.0));\n        total_assert_eq!(-1.0_f64, clamp_value_to_range(-25.0, -1.0..=1.0));\n        total_assert_eq!(5.0_f64, clamp_value_to_range(5.0, -1.0..=10.0));\n        total_assert_eq!(15.0_f64, clamp_value_to_range(25.0, -1.0..=15.0));\n        total_assert_eq!(1.0_f64, clamp_value_to_range(1.0, 1.0..=10.0));\n        total_assert_eq!(10.0_f64, clamp_value_to_range(10.0, 1.0..=10.0));\n        total_assert_eq!(5.0_f64, clamp_value_to_range(5.0, 10.0..=1.0));\n        total_assert_eq!(5.0_f64, clamp_value_to_range(15.0, 5.0..=1.0));\n        total_assert_eq!(1.0_f64, clamp_value_to_range(-5.0, 5.0..=1.0));\n    }\n\n    #[test]\n    fn test_default_parser() {\n        assert_eq!(super::default_parser(\"123\"), Some(123.0));\n\n        assert_eq!(super::default_parser(\"1.23\"), Some(1.230));\n\n        assert_eq!(\n            super::default_parser(\" 1.23 \"),\n            Some(1.230),\n            \"We should handle leading and trailing spaces\"\n        );\n\n        assert_eq!(\n            super::default_parser(\"1 234 567\"),\n            Some(1_234_567.0),\n            \"We should handle thousands separators using half-space\"\n        );\n\n        assert_eq!(\n            super::default_parser(\"-1.23\"),\n            Some(-1.23),\n            \"Should handle normal hyphen as minus character\"\n        );\n        assert_eq!(\n            super::default_parser(\"−1.23\"),\n            Some(-1.23),\n            \"Should handle special minus character (https://www.compart.com/en/unicode/U+2212)\"\n        );\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/hyperlink.rs",
    "content": "use crate::{\n    CursorIcon, Label, Response, Sense, Stroke, Ui, Widget, WidgetInfo, WidgetText, WidgetType,\n    epaint, text_selection,\n};\n\nuse self::text_selection::LabelSelectionState;\n\n/// Clickable text, that looks like a hyperlink.\n///\n/// To link to a web page, use [`Hyperlink`], [`Ui::hyperlink`] or [`Ui::hyperlink_to`].\n///\n/// See also [`Ui::link`].\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// // These are equivalent:\n/// if ui.link(\"Documentation\").clicked() {\n///     // …\n/// }\n///\n/// if ui.add(egui::Link::new(\"Documentation\")).clicked() {\n///     // …\n/// }\n/// # });\n/// ```\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct Link {\n    text: WidgetText,\n}\n\nimpl Link {\n    pub fn new(text: impl Into<WidgetText>) -> Self {\n        Self { text: text.into() }\n    }\n}\n\nimpl Widget for Link {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let Self { text } = self;\n        let label = Label::new(text).sense(Sense::click());\n\n        let (galley_pos, galley, response) = label.layout_in_ui(ui);\n        response\n            .widget_info(|| WidgetInfo::labeled(WidgetType::Link, ui.is_enabled(), galley.text()));\n\n        if ui.is_rect_visible(response.rect) {\n            let color = ui.visuals().hyperlink_color;\n            let visuals = ui.style().interact(&response);\n\n            let underline = if response.hovered() || response.has_focus() {\n                Stroke::new(visuals.fg_stroke.width, color)\n            } else {\n                Stroke::NONE\n            };\n\n            let selectable = ui.style().interaction.selectable_labels;\n            if selectable {\n                LabelSelectionState::label_text_selection(\n                    ui, &response, galley_pos, galley, color, underline,\n                );\n            } else {\n                ui.painter().add(\n                    epaint::TextShape::new(galley_pos, galley, color).with_underline(underline),\n                );\n            }\n\n            if response.hovered() {\n                ui.set_cursor_icon(CursorIcon::PointingHand);\n            }\n        }\n\n        response\n    }\n}\n\n/// A clickable hyperlink, e.g. to `\"https://github.com/emilk/egui\"`.\n///\n/// See also [`Ui::hyperlink`] and [`Ui::hyperlink_to`].\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// // These are equivalent:\n/// ui.hyperlink(\"https://github.com/emilk/egui\");\n/// ui.add(egui::Hyperlink::new(\"https://github.com/emilk/egui\"));\n///\n/// // These are equivalent:\n/// ui.hyperlink_to(\"My favorite repo\", \"https://github.com/emilk/egui\");\n/// ui.add(egui::Hyperlink::from_label_and_url(\"My favorite repo\", \"https://github.com/emilk/egui\"));\n/// # });\n/// ```\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct Hyperlink {\n    url: String,\n    text: WidgetText,\n    new_tab: bool,\n}\n\nimpl Hyperlink {\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn new(url: impl ToString) -> Self {\n        let url = url.to_string();\n        Self {\n            url: url.clone(),\n            text: url.into(),\n            new_tab: false,\n        }\n    }\n\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn from_label_and_url(text: impl Into<WidgetText>, url: impl ToString) -> Self {\n        Self {\n            url: url.to_string(),\n            text: text.into(),\n            new_tab: false,\n        }\n    }\n\n    /// Always open this hyperlink in a new browser tab.\n    #[inline]\n    pub fn open_in_new_tab(mut self, new_tab: bool) -> Self {\n        self.new_tab = new_tab;\n        self\n    }\n}\n\nimpl Widget for Hyperlink {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let Self { url, text, new_tab } = self;\n\n        let response = ui.add(Link::new(text));\n\n        if response.clicked_with_open_in_background() {\n            ui.open_url(crate::OpenUrl {\n                url: url.clone(),\n                new_tab: true,\n            });\n        } else if response.clicked() {\n            ui.open_url(crate::OpenUrl {\n                url: url.clone(),\n                new_tab,\n            });\n        }\n\n        if ui.style().url_in_tooltip {\n            response.on_hover_text(url)\n        } else {\n            response\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/image.rs",
    "content": "use std::{borrow::Cow, slice::Iter, sync::Arc, time::Duration};\n\nuse emath::{Align, Float as _, GuiRounding as _, NumExt as _, Rot2};\nuse epaint::{\n    RectShape,\n    text::{LayoutJob, TextFormat, TextWrapping},\n};\n\nuse crate::{\n    Color32, Context, CornerRadius, Id, Mesh, Painter, Rect, Response, Sense, Shape, Spinner,\n    TextStyle, TextureOptions, Ui, Vec2, Widget, WidgetInfo, WidgetType,\n    load::{Bytes, SizeHint, SizedTexture, TextureLoadResult, TexturePoll},\n    pos2,\n};\n\n/// A widget which displays an image.\n///\n/// The task of actually loading the image is deferred to when the `Image` is added to the [`Ui`],\n/// and how it is loaded depends on the provided [`ImageSource`]:\n///\n/// - [`ImageSource::Uri`] will load the image using the [asynchronous loading process][`crate::load`].\n/// - [`ImageSource::Bytes`] will also load the image using the [asynchronous loading process][`crate::load`], but with lower latency.\n/// - [`ImageSource::Texture`] will use the provided texture.\n///\n/// See [`crate::load`] for more information.\n///\n/// ### Examples\n/// // Using it in a layout:\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// ui.add(\n///     egui::Image::new(egui::include_image!(\"../../assets/ferris.png\"))\n///         .corner_radius(5)\n/// );\n/// # });\n/// ```\n///\n/// // Using it just to paint:\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # let rect = egui::Rect::from_min_size(Default::default(), egui::Vec2::splat(100.0));\n/// egui::Image::new(egui::include_image!(\"../../assets/ferris.png\"))\n///     .corner_radius(5)\n///     .tint(egui::Color32::LIGHT_BLUE)\n///     .paint_at(ui, rect);\n/// # });\n/// ```\n///\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\n#[derive(Debug, Clone)]\npub struct Image<'a> {\n    source: ImageSource<'a>,\n    texture_options: TextureOptions,\n    image_options: ImageOptions,\n    sense: Sense,\n    size: ImageSize,\n    pub(crate) show_loading_spinner: Option<bool>,\n    pub(crate) alt_text: Option<String>,\n}\n\nimpl<'a> Image<'a> {\n    /// Load the image from some source.\n    pub fn new(source: impl Into<ImageSource<'a>>) -> Self {\n        fn new_mono(source: ImageSource<'_>) -> Image<'_> {\n            let size = if let ImageSource::Texture(tex) = &source {\n                // User is probably expecting their texture to have\n                // the exact size of the provided `SizedTexture`.\n                ImageSize {\n                    maintain_aspect_ratio: true,\n                    max_size: Vec2::INFINITY,\n                    fit: ImageFit::Exact(tex.size),\n                }\n            } else {\n                Default::default()\n            };\n\n            Image {\n                source,\n                texture_options: Default::default(),\n                image_options: Default::default(),\n                sense: Sense::hover(),\n                size,\n                show_loading_spinner: None,\n                alt_text: None,\n            }\n        }\n\n        new_mono(source.into())\n    }\n\n    /// Load the image from a URI.\n    ///\n    /// See [`ImageSource::Uri`].\n    pub fn from_uri(uri: impl Into<Cow<'a, str>>) -> Self {\n        Self::new(ImageSource::Uri(uri.into()))\n    }\n\n    /// Load the image from an existing texture.\n    ///\n    /// See [`ImageSource::Texture`].\n    pub fn from_texture(texture: impl Into<SizedTexture>) -> Self {\n        Self::new(ImageSource::Texture(texture.into()))\n    }\n\n    /// Load the image from some raw bytes.\n    ///\n    /// For better error messages, use the `bytes://` prefix for the URI.\n    ///\n    /// See [`ImageSource::Bytes`].\n    pub fn from_bytes(uri: impl Into<Cow<'static, str>>, bytes: impl Into<Bytes>) -> Self {\n        Self::new(ImageSource::Bytes {\n            uri: uri.into(),\n            bytes: bytes.into(),\n        })\n    }\n\n    /// Texture options used when creating the texture.\n    #[inline]\n    pub fn texture_options(mut self, texture_options: TextureOptions) -> Self {\n        self.texture_options = texture_options;\n        self\n    }\n\n    /// Set the max width of the image.\n    ///\n    /// No matter what the image is scaled to, it will never exceed this limit.\n    #[inline]\n    pub fn max_width(mut self, width: f32) -> Self {\n        self.size.max_size.x = width;\n        self\n    }\n\n    /// Set the max height of the image.\n    ///\n    /// No matter what the image is scaled to, it will never exceed this limit.\n    #[inline]\n    pub fn max_height(mut self, height: f32) -> Self {\n        self.size.max_size.y = height;\n        self\n    }\n\n    /// Set the max size of the image.\n    ///\n    /// No matter what the image is scaled to, it will never exceed this limit.\n    #[inline]\n    pub fn max_size(mut self, size: Vec2) -> Self {\n        self.size.max_size = size;\n        self\n    }\n\n    /// Whether or not the [`ImageFit`] should maintain the image's original aspect ratio.\n    #[inline]\n    pub fn maintain_aspect_ratio(mut self, value: bool) -> Self {\n        self.size.maintain_aspect_ratio = value;\n        self\n    }\n\n    /// Fit the image to its original size with some scaling.\n    ///\n    /// The texel size of the source image will be multiplied by the `scale` factor,\n    /// and then become the _ui_ size of the [`Image`].\n    ///\n    /// This will cause the image to overflow if it is larger than the available space.\n    ///\n    /// If [`Image::max_size`] is set, this is guaranteed to never exceed that limit.\n    #[inline]\n    pub fn fit_to_original_size(mut self, scale: f32) -> Self {\n        self.size.fit = ImageFit::Original { scale };\n        self\n    }\n\n    /// Fit the image to an exact size.\n    ///\n    /// If [`Image::max_size`] is set, this is guaranteed to never exceed that limit.\n    #[inline]\n    pub fn fit_to_exact_size(mut self, size: Vec2) -> Self {\n        self.size.fit = ImageFit::Exact(size);\n        self\n    }\n\n    /// Fit the image to a fraction of the available space.\n    ///\n    /// If [`Image::max_size`] is set, this is guaranteed to never exceed that limit.\n    #[inline]\n    pub fn fit_to_fraction(mut self, fraction: Vec2) -> Self {\n        self.size.fit = ImageFit::Fraction(fraction);\n        self\n    }\n\n    /// Fit the image to 100% of its available size, shrinking it if necessary.\n    ///\n    /// This is a shorthand for [`Image::fit_to_fraction`] with `1.0` for both width and height.\n    ///\n    /// If [`Image::max_size`] is set, this is guaranteed to never exceed that limit.\n    #[inline]\n    pub fn shrink_to_fit(self) -> Self {\n        self.fit_to_fraction(Vec2::new(1.0, 1.0))\n    }\n\n    /// Make the image respond to clicks and/or drags.\n    #[inline]\n    pub fn sense(mut self, sense: Sense) -> Self {\n        self.sense = sense;\n        self\n    }\n\n    /// Select UV range. Default is (0,0) in top-left, (1,1) bottom right.\n    #[inline]\n    pub fn uv(mut self, uv: impl Into<Rect>) -> Self {\n        self.image_options.uv = uv.into();\n        self\n    }\n\n    /// A solid color to put behind the image. Useful for transparent images.\n    #[inline]\n    pub fn bg_fill(mut self, bg_fill: impl Into<Color32>) -> Self {\n        self.image_options.bg_fill = bg_fill.into();\n        self\n    }\n\n    /// Multiply image color with this. Default is WHITE (no tint).\n    #[inline]\n    pub fn tint(mut self, tint: impl Into<Color32>) -> Self {\n        self.image_options.tint = tint.into();\n        self\n    }\n\n    /// Rotate the image about an origin by some angle\n    ///\n    /// Positive angle is clockwise.\n    /// Origin is a vector in normalized UV space ((0,0) in top-left, (1,1) bottom right).\n    ///\n    /// To rotate about the center you can pass `Vec2::splat(0.5)` as the origin.\n    ///\n    /// Due to limitations in the current implementation,\n    /// this will turn off rounding of the image.\n    #[inline]\n    pub fn rotate(mut self, angle: f32, origin: Vec2) -> Self {\n        self.image_options.rotation = Some((Rot2::from_angle(angle), origin));\n        self.image_options.corner_radius = CornerRadius::ZERO; // incompatible with rotation\n        self\n    }\n\n    /// Round the corners of the image.\n    ///\n    /// The default is no rounding ([`CornerRadius::ZERO`]).\n    ///\n    /// Due to limitations in the current implementation,\n    /// this will turn off any rotation of the image.\n    #[inline]\n    pub fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {\n        self.image_options.corner_radius = corner_radius.into();\n        if self.image_options.corner_radius != CornerRadius::ZERO {\n            self.image_options.rotation = None; // incompatible with rounding\n        }\n        self\n    }\n\n    /// Round the corners of the image.\n    ///\n    /// The default is no rounding ([`CornerRadius::ZERO`]).\n    ///\n    /// Due to limitations in the current implementation,\n    /// this will turn off any rotation of the image.\n    #[inline]\n    #[deprecated = \"Renamed to `corner_radius`\"]\n    pub fn rounding(self, corner_radius: impl Into<CornerRadius>) -> Self {\n        self.corner_radius(corner_radius)\n    }\n\n    /// Show a spinner when the image is loading.\n    ///\n    /// By default this uses the value of [`crate::Visuals::image_loading_spinners`].\n    #[inline]\n    pub fn show_loading_spinner(mut self, show: bool) -> Self {\n        self.show_loading_spinner = Some(show);\n        self\n    }\n\n    /// Set alt text for the image. This will be shown when the image fails to load.\n    ///\n    /// It will also be used for accessibility (e.g. read by screen readers).\n    #[inline]\n    pub fn alt_text(mut self, label: impl Into<String>) -> Self {\n        self.alt_text = Some(label.into());\n        self\n    }\n}\n\nimpl<'a, T: Into<ImageSource<'a>>> From<T> for Image<'a> {\n    fn from(value: T) -> Self {\n        Image::new(value)\n    }\n}\n\nimpl<'a> Image<'a> {\n    /// Returns the size the image will occupy in the final UI.\n    #[inline]\n    pub fn calc_size(&self, available_size: Vec2, image_source_size: Option<Vec2>) -> Vec2 {\n        let image_source_size = image_source_size.unwrap_or(Vec2::splat(24.0)); // Fallback for still-loading textures, or failure to load.\n        self.size.calc_size(available_size, image_source_size)\n    }\n\n    pub fn load_and_calc_size(&self, ui: &Ui, available_size: Vec2) -> Option<Vec2> {\n        let image_size = self.load_for_size(ui.ctx(), available_size).ok()?.size()?;\n        Some(self.size.calc_size(available_size, image_size))\n    }\n\n    #[inline]\n    pub fn size(&self) -> Option<Vec2> {\n        match &self.source {\n            ImageSource::Texture(texture) => Some(texture.size),\n            ImageSource::Uri(_) | ImageSource::Bytes { .. } => None,\n        }\n    }\n\n    /// Returns the URI of the image.\n    ///\n    /// For animated images, returns the URI without the frame number.\n    #[inline]\n    pub fn uri(&self) -> Option<&str> {\n        let uri = self.source.uri()?;\n\n        if let Ok((gif_uri, _index)) = decode_animated_image_uri(uri) {\n            Some(gif_uri)\n        } else {\n            Some(uri)\n        }\n    }\n\n    #[inline]\n    pub fn image_options(&self) -> &ImageOptions {\n        &self.image_options\n    }\n\n    #[inline]\n    pub fn source(&'a self, ctx: &Context) -> ImageSource<'a> {\n        match &self.source {\n            ImageSource::Uri(uri) if is_animated_image_uri(uri) => {\n                let frame_uri =\n                    encode_animated_image_uri(uri, animated_image_frame_index(ctx, uri));\n                ImageSource::Uri(Cow::Owned(frame_uri))\n            }\n\n            ImageSource::Bytes { uri, bytes } if are_animated_image_bytes(bytes) => {\n                let frame_uri =\n                    encode_animated_image_uri(uri, animated_image_frame_index(ctx, uri));\n                ctx.include_bytes(uri.clone(), bytes.clone());\n                ImageSource::Uri(Cow::Owned(frame_uri))\n            }\n            _ => self.source.clone(),\n        }\n    }\n\n    /// Load the image from its [`Image::source`], returning the resulting [`SizedTexture`].\n    ///\n    /// The `available_size` is used as a hint when e.g. rendering an svg.\n    ///\n    /// # Errors\n    /// May fail if they underlying [`Context::try_load_texture`] call fails.\n    pub fn load_for_size(&self, ctx: &Context, available_size: Vec2) -> TextureLoadResult {\n        let size_hint = self.size.hint(available_size, ctx.pixels_per_point());\n        self.source(ctx)\n            .clone()\n            .load(ctx, self.texture_options, size_hint)\n    }\n\n    /// Paint the image in the given rectangle.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let rect = egui::Rect::from_min_size(Default::default(), egui::Vec2::splat(100.0));\n    /// egui::Image::new(egui::include_image!(\"../../assets/ferris.png\"))\n    ///     .corner_radius(5)\n    ///     .tint(egui::Color32::LIGHT_BLUE)\n    ///     .paint_at(ui, rect);\n    /// # });\n    /// ```\n    #[inline]\n    pub fn paint_at(&self, ui: &Ui, rect: Rect) {\n        let pixels_per_point = ui.pixels_per_point();\n\n        let rect = rect.round_to_pixels(pixels_per_point);\n\n        // Load exactly the size of the rectangle we are painting to.\n        // This is important for getting crisp SVG:s.\n        let pixel_size = (pixels_per_point * rect.size()).round();\n\n        let texture = self.source(ui.ctx()).clone().load(\n            ui.ctx(),\n            self.texture_options,\n            SizeHint::Size {\n                width: pixel_size.x as _,\n                height: pixel_size.y as _,\n                maintain_aspect_ratio: false, // no - just get exactly what we asked for\n            },\n        );\n\n        paint_texture_load_result(\n            ui,\n            &texture,\n            rect,\n            self.show_loading_spinner,\n            &self.image_options,\n            self.alt_text.as_deref(),\n        );\n    }\n}\n\nimpl Widget for Image<'_> {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let tlr = self.load_for_size(ui.ctx(), ui.available_size());\n        let image_source_size = tlr.as_ref().ok().and_then(|t| t.size());\n        let ui_size = self.calc_size(ui.available_size(), image_source_size);\n\n        let (rect, response) = ui.allocate_exact_size(ui_size, self.sense);\n        response.widget_info(|| {\n            let mut info = WidgetInfo::new(WidgetType::Image);\n            info.label = self.alt_text.clone();\n            info\n        });\n        if ui.is_rect_visible(rect) {\n            paint_texture_load_result(\n                ui,\n                &tlr,\n                rect,\n                self.show_loading_spinner,\n                &self.image_options,\n                self.alt_text.as_deref(),\n            );\n        }\n        texture_load_result_response(&self.source(ui.ctx()), &tlr, response)\n    }\n}\n\n/// This type determines the constraints on how\n/// the size of an image should be calculated.\n#[derive(Debug, Clone, Copy)]\npub struct ImageSize {\n    /// Whether or not the final size should maintain the original aspect ratio.\n    ///\n    /// This setting is applied last.\n    ///\n    /// This defaults to `true`.\n    pub maintain_aspect_ratio: bool,\n\n    /// Determines the maximum size of the image.\n    ///\n    /// Defaults to `Vec2::INFINITY` (no limit).\n    pub max_size: Vec2,\n\n    /// Determines how the image should shrink/expand/stretch/etc. to fit within its allocated space.\n    ///\n    /// This setting is applied first.\n    ///\n    /// Defaults to `ImageFit::Fraction([1, 1])`\n    pub fit: ImageFit,\n}\n\n/// This type determines how the image should try to fit within the UI.\n///\n/// The final fit will be clamped to [`ImageSize::max_size`].\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum ImageFit {\n    /// Fit the image to its original srce size, scaled by some factor.\n    ///\n    /// The original size of the image is usually its texel resolution,\n    /// but for an SVG it's the point size of the SVG.\n    ///\n    /// Ignores how much space is actually available in the ui.\n    Original { scale: f32 },\n\n    /// Fit the image to a fraction of the available size.\n    Fraction(Vec2),\n\n    /// Fit the image to an exact size.\n    ///\n    /// Ignores how much space is actually available in the ui.\n    Exact(Vec2),\n}\n\nimpl ImageFit {\n    pub fn resolve(self, available_size: Vec2, image_size: Vec2) -> Vec2 {\n        match self {\n            Self::Original { scale } => image_size * scale,\n            Self::Fraction(fract) => available_size * fract,\n            Self::Exact(size) => size,\n        }\n    }\n}\n\nimpl ImageSize {\n    /// Size hint for e.g. rasterizing an svg.\n    pub fn hint(&self, available_size: Vec2, pixels_per_point: f32) -> SizeHint {\n        let Self {\n            maintain_aspect_ratio,\n            max_size,\n            fit,\n        } = *self;\n\n        let point_size = match fit {\n            ImageFit::Original { scale } => {\n                return SizeHint::Scale((pixels_per_point * scale).ord());\n            }\n            ImageFit::Fraction(fract) => available_size * fract,\n            ImageFit::Exact(size) => size,\n        };\n        let point_size = point_size.at_most(max_size);\n\n        let pixel_size = pixels_per_point * point_size;\n\n        // `inf` on an axis means \"any value\"\n        match (pixel_size.x.is_finite(), pixel_size.y.is_finite()) {\n            (true, true) => SizeHint::Size {\n                width: pixel_size.x.round() as u32,\n                height: pixel_size.y.round() as u32,\n                maintain_aspect_ratio,\n            },\n            (true, false) => SizeHint::Width(pixel_size.x.round() as u32),\n            (false, true) => SizeHint::Height(pixel_size.y.round() as u32),\n            (false, false) => SizeHint::Scale(pixels_per_point.ord()),\n        }\n    }\n\n    /// Calculate the final on-screen size in points.\n    pub fn calc_size(&self, available_size: Vec2, image_source_size: Vec2) -> Vec2 {\n        let Self {\n            maintain_aspect_ratio,\n            max_size,\n            fit,\n        } = *self;\n        match fit {\n            ImageFit::Original { scale } => {\n                let image_size = scale * image_source_size;\n                if image_size.x <= max_size.x && image_size.y <= max_size.y {\n                    image_size\n                } else {\n                    scale_to_fit(image_size, max_size, maintain_aspect_ratio)\n                }\n            }\n            ImageFit::Fraction(fract) => {\n                let scale_to_size = (available_size * fract).min(max_size);\n                scale_to_fit(image_source_size, scale_to_size, maintain_aspect_ratio)\n            }\n            ImageFit::Exact(size) => {\n                let scale_to_size = size.min(max_size);\n                scale_to_fit(image_source_size, scale_to_size, maintain_aspect_ratio)\n            }\n        }\n    }\n}\n\n// TODO(jprochazk): unit-tests\nfn scale_to_fit(image_size: Vec2, available_size: Vec2, maintain_aspect_ratio: bool) -> Vec2 {\n    if maintain_aspect_ratio {\n        let ratio_x = available_size.x / image_size.x;\n        let ratio_y = available_size.y / image_size.y;\n        let ratio = if ratio_x < ratio_y { ratio_x } else { ratio_y };\n        let ratio = if ratio.is_finite() { ratio } else { 1.0 };\n        image_size * ratio\n    } else {\n        available_size\n    }\n}\n\nimpl Default for ImageSize {\n    #[inline]\n    fn default() -> Self {\n        Self {\n            max_size: Vec2::INFINITY,\n            fit: ImageFit::Fraction(Vec2::new(1.0, 1.0)),\n            maintain_aspect_ratio: true,\n        }\n    }\n}\n\n/// This type tells the [`Ui`] how to load an image.\n///\n/// This is used by [`Image::new`] and [`Ui::image`].\n#[derive(Clone)]\npub enum ImageSource<'a> {\n    /// Load the image from a URI, e.g. `https://example.com/image.png`.\n    ///\n    /// This could be a `file://` path, `https://` url, `bytes://` identifier, or some other scheme.\n    ///\n    /// How the URI will be turned into a texture for rendering purposes is\n    /// up to the registered loaders to handle.\n    ///\n    /// See [`crate::load`] for more information.\n    Uri(Cow<'a, str>),\n\n    /// Load the image from an existing texture.\n    ///\n    /// The user is responsible for loading the texture, determining its size,\n    /// and allocating a [`crate::TextureId`] for it.\n    Texture(SizedTexture),\n\n    /// Load the image from some raw bytes.\n    ///\n    /// The [`Bytes`] may be:\n    /// - `'static`, obtained from `include_bytes!` or similar\n    /// - Anything that can be converted to `Arc<[u8]>`\n    ///\n    /// This instructs the [`Ui`] to cache the raw bytes, which are then further processed by any registered loaders.\n    ///\n    /// See also [`crate::include_image`] for an easy way to load and display static images.\n    ///\n    /// See [`crate::load`] for more information.\n    Bytes {\n        /// The unique identifier for this image, e.g. `bytes://my_logo.png`.\n        ///\n        /// You should use a proper extension (`.jpg`, `.png`, `.svg`, etc) for the image to load properly.\n        ///\n        /// Use the `bytes://` scheme for the URI for better error messages.\n        uri: Cow<'static, str>,\n\n        bytes: Bytes,\n    },\n}\n\nimpl std::fmt::Debug for ImageSource<'_> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            ImageSource::Bytes { uri, .. } | ImageSource::Uri(uri) => uri.as_ref().fmt(f),\n            ImageSource::Texture(st) => st.id.fmt(f),\n        }\n    }\n}\n\nimpl ImageSource<'_> {\n    /// Size of the texture, if known.\n    #[inline]\n    pub fn texture_size(&self) -> Option<Vec2> {\n        match self {\n            ImageSource::Texture(texture) => Some(texture.size),\n            ImageSource::Uri(_) | ImageSource::Bytes { .. } => None,\n        }\n    }\n\n    /// # Errors\n    /// Failure to load the texture.\n    pub fn load(\n        self,\n        ctx: &Context,\n        texture_options: TextureOptions,\n        size_hint: SizeHint,\n    ) -> TextureLoadResult {\n        match self {\n            Self::Texture(texture) => Ok(TexturePoll::Ready { texture }),\n            Self::Uri(uri) => ctx.try_load_texture(uri.as_ref(), texture_options, size_hint),\n            Self::Bytes { uri, bytes } => {\n                ctx.include_bytes(uri.clone(), bytes);\n                ctx.try_load_texture(uri.as_ref(), texture_options, size_hint)\n            }\n        }\n    }\n\n    /// Get the `uri` that this image was constructed from.\n    ///\n    /// This will return `None` for [`Self::Texture`].\n    pub fn uri(&self) -> Option<&str> {\n        match self {\n            ImageSource::Bytes { uri, .. } | ImageSource::Uri(uri) => Some(uri),\n            ImageSource::Texture(_) => None,\n        }\n    }\n}\n\npub fn paint_texture_load_result(\n    ui: &Ui,\n    tlr: &TextureLoadResult,\n    rect: Rect,\n    show_loading_spinner: Option<bool>,\n    options: &ImageOptions,\n    alt_text: Option<&str>,\n) {\n    match tlr {\n        Ok(TexturePoll::Ready { texture }) => {\n            paint_texture_at(ui.painter(), rect, options, texture);\n        }\n        Ok(TexturePoll::Pending { .. }) => {\n            let show_loading_spinner =\n                show_loading_spinner.unwrap_or_else(|| ui.visuals().image_loading_spinners);\n            if show_loading_spinner {\n                Spinner::new().paint_at(ui, rect);\n            }\n        }\n        Err(_) => {\n            let font_id = TextStyle::Body.resolve(ui.style());\n            let mut job = LayoutJob {\n                wrap: TextWrapping::truncate_at_width(rect.width()),\n                halign: Align::Center,\n                ..Default::default()\n            };\n            job.append(\n                \"⚠\",\n                0.0,\n                TextFormat::simple(font_id.clone(), ui.visuals().error_fg_color),\n            );\n            if let Some(alt_text) = alt_text {\n                job.append(\n                    alt_text,\n                    ui.spacing().item_spacing.x,\n                    TextFormat::simple(font_id, ui.visuals().text_color()),\n                );\n            }\n            let galley = ui.painter().layout_job(job);\n            ui.painter().galley(\n                rect.center() - Vec2::Y * galley.size().y * 0.5,\n                galley,\n                ui.visuals().text_color(),\n            );\n        }\n    }\n}\n\n/// Attach tooltips like \"Loading…\" or \"Failed loading: …\".\npub fn texture_load_result_response(\n    source: &ImageSource<'_>,\n    tlr: &TextureLoadResult,\n    response: Response,\n) -> Response {\n    match tlr {\n        Ok(TexturePoll::Ready { .. }) => response,\n        Ok(TexturePoll::Pending { .. }) => {\n            let uri = source.uri().unwrap_or(\"image\");\n            response.on_hover_text(format!(\"Loading {uri}…\"))\n        }\n        Err(err) => {\n            let uri = source.uri().unwrap_or(\"image\");\n            response.on_hover_text(format!(\"Failed loading {uri}: {err}\"))\n        }\n    }\n}\n\nimpl<'a> From<&'a str> for ImageSource<'a> {\n    #[inline]\n    fn from(value: &'a str) -> Self {\n        Self::Uri(value.into())\n    }\n}\n\nimpl<'a> From<&'a String> for ImageSource<'a> {\n    #[inline]\n    fn from(value: &'a String) -> Self {\n        Self::Uri(value.as_str().into())\n    }\n}\n\nimpl From<String> for ImageSource<'static> {\n    fn from(value: String) -> Self {\n        Self::Uri(value.into())\n    }\n}\n\nimpl<'a> From<&'a Cow<'a, str>> for ImageSource<'a> {\n    #[inline]\n    fn from(value: &'a Cow<'a, str>) -> Self {\n        Self::Uri(value.clone())\n    }\n}\n\nimpl<'a> From<Cow<'a, str>> for ImageSource<'a> {\n    #[inline]\n    fn from(value: Cow<'a, str>) -> Self {\n        Self::Uri(value)\n    }\n}\n\nimpl<T: Into<Bytes>> From<(&'static str, T)> for ImageSource<'static> {\n    #[inline]\n    fn from((uri, bytes): (&'static str, T)) -> Self {\n        Self::Bytes {\n            uri: uri.into(),\n            bytes: bytes.into(),\n        }\n    }\n}\n\nimpl<T: Into<Bytes>> From<(Cow<'static, str>, T)> for ImageSource<'static> {\n    #[inline]\n    fn from((uri, bytes): (Cow<'static, str>, T)) -> Self {\n        Self::Bytes {\n            uri,\n            bytes: bytes.into(),\n        }\n    }\n}\n\nimpl<T: Into<Bytes>> From<(String, T)> for ImageSource<'static> {\n    #[inline]\n    fn from((uri, bytes): (String, T)) -> Self {\n        Self::Bytes {\n            uri: uri.into(),\n            bytes: bytes.into(),\n        }\n    }\n}\n\nimpl<T: Into<SizedTexture>> From<T> for ImageSource<'static> {\n    fn from(value: T) -> Self {\n        Self::Texture(value.into())\n    }\n}\n\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct ImageOptions {\n    /// Select UV range. Default is (0,0) in top-left, (1,1) bottom right.\n    pub uv: Rect,\n\n    /// A solid color to put behind the image. Useful for transparent images.\n    pub bg_fill: Color32,\n\n    /// Multiply image color with this. Default is WHITE (no tint).\n    pub tint: Color32,\n\n    /// Rotate the image about an origin by some angle\n    ///\n    /// Positive angle is clockwise.\n    /// Origin is a vector in normalized UV space ((0,0) in top-left, (1,1) bottom right).\n    ///\n    /// To rotate about the center you can pass `Vec2::splat(0.5)` as the origin.\n    ///\n    /// Due to limitations in the current implementation,\n    /// this will turn off rounding of the image.\n    pub rotation: Option<(Rot2, Vec2)>,\n\n    /// Round the corners of the image.\n    ///\n    /// The default is no rounding ([`CornerRadius::ZERO`]).\n    ///\n    /// Due to limitations in the current implementation,\n    /// this will turn off any rotation of the image.\n    pub corner_radius: CornerRadius,\n}\n\nimpl Default for ImageOptions {\n    fn default() -> Self {\n        Self {\n            uv: Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)),\n            bg_fill: Default::default(),\n            tint: Color32::WHITE,\n            rotation: None,\n            corner_radius: CornerRadius::ZERO,\n        }\n    }\n}\n\npub fn paint_texture_at(\n    painter: &Painter,\n    rect: Rect,\n    options: &ImageOptions,\n    texture: &SizedTexture,\n) {\n    if options.bg_fill != Default::default() {\n        painter.add(RectShape::filled(\n            rect,\n            options.corner_radius,\n            options.bg_fill,\n        ));\n    }\n\n    match options.rotation {\n        Some((rot, origin)) => {\n            // TODO(emilk): implement this using `PathShape` (add texture support to it).\n            // This will also give us anti-aliasing of rotated images.\n            debug_assert!(\n                options.corner_radius == CornerRadius::ZERO,\n                \"Image had both rounding and rotation. Please pick only one\"\n            );\n\n            let mut mesh = Mesh::with_texture(texture.id);\n            mesh.add_rect_with_uv(rect, options.uv, options.tint);\n            mesh.rotate(rot, rect.min + origin * rect.size());\n            painter.add(Shape::mesh(mesh));\n        }\n        None => {\n            painter.add(\n                RectShape::filled(rect, options.corner_radius, options.tint)\n                    .with_texture(texture.id, options.uv),\n            );\n        }\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]\n/// Stores the durations between each frame of an animated image\npub struct FrameDurations(Arc<Vec<Duration>>);\n\nimpl FrameDurations {\n    pub fn new(durations: Vec<Duration>) -> Self {\n        Self(Arc::new(durations))\n    }\n\n    pub fn all(&self) -> Iter<'_, Duration> {\n        self.0.iter()\n    }\n}\n\n/// Animated image uris contain the uri & the frame that will be displayed\nfn encode_animated_image_uri(uri: &str, frame_index: usize) -> String {\n    format!(\"{uri}#{frame_index}\")\n}\n\n/// Extracts uri and frame index\n/// # Errors\n/// Will return `Err` if `uri` does not match pattern {uri}-{frame_index}\npub fn decode_animated_image_uri(uri: &str) -> Result<(&str, usize), String> {\n    let (uri, index) = uri\n        .rsplit_once('#')\n        .ok_or(\"Failed to find index separator '#'\")?;\n    let index: usize = index.parse().map_err(|_err| {\n        format!(\"Failed to parse animated image frame index: {index:?} is not an integer\")\n    })?;\n    Ok((uri, index))\n}\n\n/// Calculates at which frame the animated image is\nfn animated_image_frame_index(ctx: &Context, uri: &str) -> usize {\n    let now = ctx.input(|input| Duration::from_secs_f64(input.time));\n\n    let durations: Option<FrameDurations> = ctx.data(|data| data.get_temp(Id::new(uri)));\n\n    if let Some(durations) = durations {\n        let frames: Duration = durations.all().sum();\n        let pos_ms = now.as_millis() % frames.as_millis().max(1);\n\n        let mut cumulative_ms = 0;\n\n        for (index, duration) in durations.all().enumerate() {\n            cumulative_ms += duration.as_millis();\n\n            if pos_ms < cumulative_ms {\n                let ms_until_next_frame = cumulative_ms - pos_ms;\n                ctx.request_repaint_after(Duration::from_millis(ms_until_next_frame as u64));\n                return index;\n            }\n        }\n    }\n\n    0\n}\n\n/// Checks if uri is a gif file\nfn is_gif_uri(uri: &str) -> bool {\n    uri.ends_with(\".gif\") || uri.contains(\".gif#\")\n}\n\n/// Checks if bytes are gifs\npub fn has_gif_magic_header(bytes: &[u8]) -> bool {\n    bytes.starts_with(b\"GIF87a\") || bytes.starts_with(b\"GIF89a\")\n}\n\n/// Checks if uri is a webp file\nfn is_webp_uri(uri: &str) -> bool {\n    uri.ends_with(\".webp\") || uri.contains(\".webp#\")\n}\n\n/// Checks if bytes are webp\npub fn has_webp_header(bytes: &[u8]) -> bool {\n    bytes.len() >= 12 && &bytes[0..4] == b\"RIFF\" && &bytes[8..12] == b\"WEBP\"\n}\n\nfn is_animated_image_uri(uri: &str) -> bool {\n    is_gif_uri(uri) || is_webp_uri(uri)\n}\n\nfn are_animated_image_bytes(bytes: &[u8]) -> bool {\n    has_gif_magic_header(bytes) || has_webp_header(bytes)\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/image_button.rs",
    "content": "use crate::{\n    Color32, CornerRadius, Image, Rect, Response, Sense, Ui, Vec2, Widget, WidgetInfo, WidgetType,\n    widgets,\n};\n\n/// A clickable image within a frame.\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\n#[derive(Clone, Debug)]\n#[deprecated(since = \"0.33.0\", note = \"Use egui::Button::image instead\")]\npub struct ImageButton<'a> {\n    pub(crate) image: Image<'a>,\n    sense: Sense,\n    frame: bool,\n    selected: bool,\n    alt_text: Option<String>,\n}\n\n#[expect(deprecated, reason = \"Deprecated in egui 0.33.0\")]\nimpl<'a> ImageButton<'a> {\n    pub fn new(image: impl Into<Image<'a>>) -> Self {\n        Self {\n            image: image.into(),\n            sense: Sense::click(),\n            frame: true,\n            selected: false,\n            alt_text: None,\n        }\n    }\n\n    /// Select UV range. Default is (0,0) in top-left, (1,1) bottom right.\n    #[inline]\n    pub fn uv(mut self, uv: impl Into<Rect>) -> Self {\n        self.image = self.image.uv(uv);\n        self\n    }\n\n    /// Multiply image color with this. Default is WHITE (no tint).\n    #[inline]\n    pub fn tint(mut self, tint: impl Into<Color32>) -> Self {\n        self.image = self.image.tint(tint);\n        self\n    }\n\n    /// If `true`, mark this button as \"selected\".\n    #[inline]\n    pub fn selected(mut self, selected: bool) -> Self {\n        self.selected = selected;\n        self\n    }\n\n    /// Turn off the frame\n    #[inline]\n    pub fn frame(mut self, frame: bool) -> Self {\n        self.frame = frame;\n        self\n    }\n\n    /// By default, buttons senses clicks.\n    /// Change this to a drag-button with `Sense::drag()`.\n    #[inline]\n    pub fn sense(mut self, sense: Sense) -> Self {\n        self.sense = sense;\n        self\n    }\n\n    /// Set rounding for the `ImageButton`.\n    ///\n    /// If the underlying image already has rounding, this\n    /// will override that value.\n    #[inline]\n    pub fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {\n        self.image = self.image.corner_radius(corner_radius.into());\n        self\n    }\n\n    /// Set rounding for the `ImageButton`.\n    ///\n    /// If the underlying image already has rounding, this\n    /// will override that value.\n    #[inline]\n    #[deprecated = \"Renamed to `corner_radius`\"]\n    pub fn rounding(self, corner_radius: impl Into<CornerRadius>) -> Self {\n        self.corner_radius(corner_radius)\n    }\n}\n\n#[expect(deprecated, reason = \"Deprecated in egui 0.33.0\")]\nimpl Widget for ImageButton<'_> {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let padding = if self.frame {\n            // so we can see that it is a button:\n            Vec2::splat(ui.spacing().button_padding.x)\n        } else {\n            Vec2::ZERO\n        };\n\n        let available_size_for_image = ui.available_size() - 2.0 * padding;\n        let tlr = self.image.load_for_size(ui.ctx(), available_size_for_image);\n        let image_source_size = tlr.as_ref().ok().and_then(|t| t.size());\n        let image_size = self\n            .image\n            .calc_size(available_size_for_image, image_source_size);\n\n        let padded_size = image_size + 2.0 * padding;\n        let (rect, response) = ui.allocate_exact_size(padded_size, self.sense);\n        response.widget_info(|| {\n            let mut info = WidgetInfo::new(WidgetType::Button);\n            info.label = self.alt_text.clone();\n            info\n        });\n\n        if ui.is_rect_visible(rect) {\n            let (expansion, rounding, fill, stroke) = if self.selected {\n                let selection = ui.visuals().selection;\n                (\n                    Vec2::ZERO,\n                    self.image.image_options().corner_radius,\n                    selection.bg_fill,\n                    selection.stroke,\n                )\n            } else if self.frame {\n                let visuals = ui.style().interact(&response);\n                let expansion = Vec2::splat(visuals.expansion);\n                (\n                    expansion,\n                    self.image.image_options().corner_radius,\n                    visuals.weak_bg_fill,\n                    visuals.bg_stroke,\n                )\n            } else {\n                Default::default()\n            };\n\n            // Draw frame background (for transparent images):\n            ui.painter()\n                .rect_filled(rect.expand2(expansion), rounding, fill);\n\n            let image_rect = ui\n                .layout()\n                .align_size_within_rect(image_size, rect.shrink2(padding));\n            // let image_rect = image_rect.expand2(expansion); // can make it blurry, so let's not\n            let image_options = self.image.image_options().clone();\n\n            widgets::image::paint_texture_load_result(\n                ui,\n                &tlr,\n                image_rect,\n                None,\n                &image_options,\n                self.alt_text.as_deref(),\n            );\n\n            // Draw frame outline:\n            ui.painter().rect_stroke(\n                rect.expand2(expansion),\n                rounding,\n                stroke,\n                epaint::StrokeKind::Inside,\n            );\n        }\n\n        widgets::image::texture_load_result_response(&self.image.source(ui.ctx()), &tlr, response)\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/label.rs",
    "content": "use std::sync::Arc;\n\nuse crate::{\n    Align, Direction, FontSelection, Galley, Pos2, Response, Sense, Stroke, TextWrapMode, Ui,\n    Widget, WidgetInfo, WidgetText, WidgetType, epaint, pos2, text_selection::LabelSelectionState,\n};\n\n/// Static text.\n///\n/// Usually it is more convenient to use [`Ui::label`].\n///\n/// ```\n/// # use egui::TextWrapMode;\n/// # egui::__run_test_ui(|ui| {\n/// ui.label(\"Equivalent\");\n/// ui.add(egui::Label::new(\"Equivalent\"));\n/// ui.add(egui::Label::new(\"With Options\").truncate());\n/// ui.label(egui::RichText::new(\"With formatting\").underline());\n/// # });\n/// ```\n///\n/// For full control of the text you can use [`crate::text::LayoutJob`]\n/// as argument to [`Self::new`].\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct Label {\n    text: WidgetText,\n    wrap_mode: Option<TextWrapMode>,\n    sense: Option<Sense>,\n    selectable: Option<bool>,\n    halign: Option<Align>,\n    show_tooltip_when_elided: bool,\n}\n\nimpl Label {\n    pub fn new(text: impl Into<WidgetText>) -> Self {\n        Self {\n            text: text.into(),\n            wrap_mode: None,\n            sense: None,\n            selectable: None,\n            halign: None,\n            show_tooltip_when_elided: true,\n        }\n    }\n\n    pub fn text(&self) -> &str {\n        self.text.text()\n    }\n\n    /// Set the wrap mode for the text.\n    ///\n    /// By default, [`crate::Ui::wrap_mode`] will be used, which can be overridden with [`crate::Style::wrap_mode`].\n    ///\n    /// Note that any `\\n` in the text will always produce a new line.\n    #[inline]\n    pub fn wrap_mode(mut self, wrap_mode: TextWrapMode) -> Self {\n        self.wrap_mode = Some(wrap_mode);\n        self\n    }\n\n    /// Set [`Self::wrap_mode`] to [`TextWrapMode::Wrap`].\n    #[inline]\n    pub fn wrap(mut self) -> Self {\n        self.wrap_mode = Some(TextWrapMode::Wrap);\n\n        self\n    }\n\n    /// Set [`Self::wrap_mode`] to [`TextWrapMode::Truncate`].\n    #[inline]\n    pub fn truncate(mut self) -> Self {\n        self.wrap_mode = Some(TextWrapMode::Truncate);\n        self\n    }\n\n    /// Set [`Self::wrap_mode`] to [`TextWrapMode::Extend`],\n    /// disabling wrapping and truncating, and instead expanding the parent [`Ui`].\n    #[inline]\n    pub fn extend(mut self) -> Self {\n        self.wrap_mode = Some(TextWrapMode::Extend);\n        self\n    }\n\n    /// Sets the horizontal alignment of the Label to the given `Align` value.\n    #[inline]\n    pub fn halign(mut self, align: Align) -> Self {\n        self.halign = Some(align);\n        self\n    }\n\n    /// Can the user select the text with the mouse?\n    ///\n    /// Overrides [`crate::style::Interaction::selectable_labels`].\n    #[inline]\n    pub fn selectable(mut self, selectable: bool) -> Self {\n        self.selectable = Some(selectable);\n        self\n    }\n\n    /// Make the label respond to clicks and/or drags.\n    ///\n    /// By default, a label is inert and does not respond to click or drags.\n    /// By calling this you can turn the label into a button of sorts.\n    /// This will also give the label the hover-effect of a button, but without the frame.\n    ///\n    /// ```\n    /// # use egui::{Label, Sense};\n    /// # egui::__run_test_ui(|ui| {\n    /// if ui.add(Label::new(\"click me\").sense(Sense::click())).clicked() {\n    ///     /* … */\n    /// }\n    /// # });\n    /// ```\n    #[inline]\n    pub fn sense(mut self, sense: Sense) -> Self {\n        self.sense = Some(sense);\n        self\n    }\n\n    /// Show the full text when hovered, if the text was elided.\n    ///\n    /// By default, this is true.\n    ///\n    /// ```\n    /// # use egui::{Label, Sense};\n    /// # egui::__run_test_ui(|ui| {\n    /// ui.add(Label::new(\"some text\").show_tooltip_when_elided(false))\n    ///     .on_hover_text(\"completely different text\");\n    /// # });\n    /// ```\n    #[inline]\n    pub fn show_tooltip_when_elided(mut self, show: bool) -> Self {\n        self.show_tooltip_when_elided = show;\n        self\n    }\n}\n\nimpl Label {\n    /// Do layout and position the galley in the ui, without painting it or adding widget info.\n    pub fn layout_in_ui(self, ui: &mut Ui) -> (Pos2, Arc<Galley>, Response) {\n        let selectable = self\n            .selectable\n            .unwrap_or_else(|| ui.style().interaction.selectable_labels);\n\n        let mut sense = self.sense.unwrap_or_else(|| {\n            if ui.memory(|mem| mem.options.screen_reader) {\n                // We only want to focus labels if the screen reader is on.\n                Sense::focusable_noninteractive()\n            } else {\n                Sense::hover()\n            }\n        });\n\n        if selectable {\n            // On touch screens (e.g. mobile in `eframe` web), should\n            // dragging select text, or scroll the enclosing [`ScrollArea`] (if any)?\n            // Since currently copying selected text in not supported on `eframe` web,\n            // we prioritize touch-scrolling:\n            let allow_drag_to_select = ui.input(|i| !i.has_touch_screen());\n\n            let mut select_sense = if allow_drag_to_select {\n                Sense::click_and_drag()\n            } else {\n                Sense::click()\n            };\n            select_sense -= Sense::FOCUSABLE; // Don't move focus to labels with TAB key.\n\n            sense |= select_sense;\n        }\n\n        if let WidgetText::Galley(galley) = self.text {\n            // If the user said \"use this specific galley\", then just use it:\n            let (rect, response) = ui.allocate_exact_size(galley.size(), sense);\n            let pos = match galley.job.halign {\n                Align::LEFT => rect.left_top(),\n                Align::Center => rect.center_top(),\n                Align::RIGHT => rect.right_top(),\n            };\n            return (pos, galley, response);\n        }\n\n        let valign = ui.text_valign();\n        let mut layout_job = Arc::unwrap_or_clone(self.text.into_layout_job(\n            ui.style(),\n            FontSelection::Default,\n            valign,\n        ));\n\n        let available_width = ui.available_width();\n\n        let wrap_mode = self.wrap_mode.unwrap_or_else(|| ui.wrap_mode());\n        if wrap_mode == TextWrapMode::Wrap\n            && ui.layout().main_dir() == Direction::LeftToRight\n            && ui.layout().main_wrap()\n            && available_width.is_finite()\n        {\n            // On a wrapping horizontal layout we want text to start after the previous widget,\n            // then continue on the line below! This will take some extra work:\n\n            let cursor = ui.cursor();\n            let first_row_indentation = available_width - ui.available_size_before_wrap().x;\n            debug_assert!(\n                first_row_indentation.is_finite(),\n                \"first row indentation is not finite: {first_row_indentation}\"\n            );\n\n            layout_job.wrap.max_width = available_width;\n            layout_job.first_row_min_height = cursor.height();\n            layout_job.halign = Align::Min;\n            layout_job.justify = false;\n            if let Some(first_section) = layout_job.sections.first_mut() {\n                first_section.leading_space = first_row_indentation;\n            }\n            let galley = ui.fonts_mut(|fonts| fonts.layout_job(layout_job));\n\n            let pos = pos2(ui.max_rect().left(), ui.cursor().top());\n            assert!(!galley.rows.is_empty(), \"Galleys are never empty\");\n            // collect a response from many rows:\n            let rect = galley.rows[0]\n                .rect_without_leading_space()\n                .translate(pos.to_vec2());\n            let mut response = ui.allocate_rect(rect, sense);\n            response.intrinsic_size = Some(galley.intrinsic_size());\n            for placed_row in galley.rows.iter().skip(1) {\n                let rect = placed_row.rect().translate(pos.to_vec2());\n                response |= ui.allocate_rect(rect, sense);\n            }\n            (pos, galley, response)\n        } else {\n            // Apply wrap_mode, but don't overwrite anything important\n            // the user may have set manually on the layout_job:\n            match wrap_mode {\n                TextWrapMode::Extend => {\n                    layout_job.wrap.max_width = f32::INFINITY;\n                }\n                TextWrapMode::Wrap => {\n                    layout_job.wrap.max_width = available_width;\n                }\n                TextWrapMode::Truncate => {\n                    layout_job.wrap.max_width = available_width;\n                    layout_job.wrap.max_rows = 1;\n                    layout_job.wrap.break_anywhere = true;\n                }\n            }\n\n            if ui.is_grid() {\n                // TODO(emilk): remove special Grid hacks like these\n                layout_job.halign = Align::LEFT;\n                layout_job.justify = false;\n            } else {\n                layout_job.halign = self\n                    .halign\n                    .unwrap_or_else(|| ui.layout().horizontal_placement());\n                layout_job.justify = ui.layout().horizontal_justify();\n            }\n\n            let galley = ui.fonts_mut(|fonts| fonts.layout_job(layout_job));\n            let (rect, mut response) = ui.allocate_exact_size(galley.size(), sense);\n            response.intrinsic_size = Some(galley.intrinsic_size());\n            let galley_pos = match galley.job.halign {\n                Align::LEFT => rect.left_top(),\n                Align::Center => rect.center_top(),\n                Align::RIGHT => rect.right_top(),\n            };\n            (galley_pos, galley, response)\n        }\n    }\n}\n\nimpl Widget for Label {\n    fn ui(self, ui: &mut Ui) -> Response {\n        // Interactive = the uses asked to sense interaction.\n        // We DON'T want to have the color respond just because the text is selectable;\n        // the cursor is enough to communicate that.\n        let interactive = self.sense.is_some_and(|sense| sense != Sense::hover());\n\n        let selectable = self.selectable;\n        let show_tooltip_when_elided = self.show_tooltip_when_elided;\n\n        let (galley_pos, galley, mut response) = self.layout_in_ui(ui);\n        response\n            .widget_info(|| WidgetInfo::labeled(WidgetType::Label, ui.is_enabled(), galley.text()));\n\n        if ui.is_rect_visible(response.rect) {\n            if show_tooltip_when_elided && galley.elided {\n                // Keep the sections and text, but reset everything else (especially wrapping):\n                let job = crate::text::LayoutJob {\n                    sections: galley.job.sections.clone(),\n                    text: galley.job.text.clone(),\n                    ..crate::text::LayoutJob::default()\n                };\n                // Show the full (non-elided) text on hover:\n                response = response.on_hover_text(job);\n            }\n\n            let response_color = if interactive {\n                ui.style().interact(&response).text_color()\n            } else {\n                ui.style().visuals.text_color()\n            };\n\n            let underline = if response.has_focus() || response.highlighted() {\n                Stroke::new(1.0, response_color)\n            } else {\n                Stroke::NONE\n            };\n\n            let selectable = selectable.unwrap_or_else(|| ui.style().interaction.selectable_labels);\n            if selectable {\n                LabelSelectionState::label_text_selection(\n                    ui,\n                    &response,\n                    galley_pos,\n                    galley,\n                    response_color,\n                    underline,\n                );\n            } else {\n                ui.painter().add(\n                    epaint::TextShape::new(galley_pos, galley, response_color)\n                        .with_underline(underline),\n                );\n            }\n        }\n\n        response\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/mod.rs",
    "content": "//! Widgets are pieces of GUI such as [`Label`], [`Button`], [`Slider`] etc.\n//!\n//! Example widget uses:\n//! * `ui.add(Label::new(\"Text\").text_color(color::red));`\n//! * `if ui.add(Button::new(\"Click me\")).clicked() { … }`\n\nuse crate::{Response, Ui, epaint};\n\nmod button;\nmod checkbox;\npub mod color_picker;\npub(crate) mod drag_value;\nmod hyperlink;\nmod image;\nmod image_button;\nmod label;\nmod progress_bar;\nmod radio_button;\nmod selected_label;\nmod separator;\nmod slider;\nmod spinner;\npub mod text_edit;\n\n#[expect(deprecated)]\npub use self::selected_label::SelectableLabel;\n#[expect(deprecated, reason = \"Deprecated in egui 0.33.0\")]\npub use self::{\n    button::Button,\n    checkbox::Checkbox,\n    drag_value::DragValue,\n    hyperlink::{Hyperlink, Link},\n    image::{\n        FrameDurations, Image, ImageFit, ImageOptions, ImageSize, ImageSource,\n        decode_animated_image_uri, has_gif_magic_header, has_webp_header, paint_texture_at,\n    },\n    image_button::ImageButton,\n    label::Label,\n    progress_bar::ProgressBar,\n    radio_button::RadioButton,\n    separator::Separator,\n    slider::{Slider, SliderClamping, SliderOrientation},\n    spinner::Spinner,\n    text_edit::{TextBuffer, TextEdit},\n};\n\n// ----------------------------------------------------------------------------\n\n/// Anything implementing Widget can be added to a [`Ui`] with [`Ui::add`].\n///\n/// [`Button`], [`Label`], [`Slider`], etc all implement the [`Widget`] trait.\n///\n/// You only need to implement `Widget` if you care about being able to do `ui.add(your_widget);`.\n///\n/// Note that the widgets ([`Button`], [`TextEdit`] etc) are\n/// [builders](https://doc.rust-lang.org/1.0.0/style/ownership/builders.html),\n/// and not objects that hold state.\n///\n/// Tip: you can `impl Widget for &mut YourThing { }`.\n///\n/// `|ui: &mut Ui| -> Response { … }` also implements [`Widget`].\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub trait Widget {\n    /// Allocate space, interact, paint, and return a [`Response`].\n    ///\n    /// Note that this consumes `self`.\n    /// This is because most widgets ([`Button`], [`TextEdit`] etc) are\n    /// [builders](https://doc.rust-lang.org/1.0.0/style/ownership/builders.html)\n    ///\n    /// Tip: you can `impl Widget for &mut YourObject { }`.\n    fn ui(self, ui: &mut Ui) -> Response;\n}\n\n/// This enables functions that return `impl Widget`, so that you can\n/// create a widget by just returning a lambda from a function.\n///\n/// For instance: `ui.add(slider_vec2(&mut vec2));` with:\n///\n/// ```\n/// pub fn slider_vec2(value: &mut egui::Vec2) -> impl egui::Widget + '_ {\n///    move |ui: &mut egui::Ui| {\n///        ui.horizontal(|ui| {\n///            ui.add(egui::Slider::new(&mut value.x, 0.0..=1.0).text(\"x\"));\n///            ui.add(egui::Slider::new(&mut value.y, 0.0..=1.0).text(\"y\"));\n///        })\n///        .response\n///    }\n/// }\n/// ```\nimpl<F> Widget for F\nwhere\n    F: FnOnce(&mut Ui) -> Response,\n{\n    fn ui(self, ui: &mut Ui) -> Response {\n        self(ui)\n    }\n}\n\n/// Helper so that you can do e.g. `TextEdit::State::load`.\npub trait WidgetWithState {\n    type State;\n}\n\n// ----------------------------------------------------------------------------\n\n/// Show a button to reset a value to its default.\n/// The button is only enabled if the value does not already have its original value.\n///\n/// The `text` could be something like \"Reset foo\".\npub fn reset_button<T: Default + PartialEq>(ui: &mut Ui, value: &mut T, text: &str) {\n    reset_button_with(ui, value, text, T::default());\n}\n\n/// Show a button to reset a value to its default.\n/// The button is only enabled if the value does not already have its original value.\n///\n/// The `text` could be something like \"Reset foo\".\npub fn reset_button_with<T: PartialEq>(ui: &mut Ui, value: &mut T, text: &str, reset_value: T) {\n    if ui\n        .add_enabled(*value != reset_value, Button::new(text))\n        .clicked()\n    {\n        *value = reset_value;\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[deprecated = \"Use `ui.add(&mut stroke)` instead\"]\npub fn stroke_ui(ui: &mut crate::Ui, stroke: &mut epaint::Stroke, text: &str) {\n    ui.horizontal(|ui| {\n        ui.label(text);\n        ui.add(stroke);\n    });\n}\n\n/// Show a small button to switch to/from dark/light mode (globally).\npub fn global_theme_preference_switch(ui: &mut Ui) {\n    if let Some(new_theme) = ui.ctx().theme().small_toggle_button(ui) {\n        ui.ctx().set_theme(new_theme);\n    }\n}\n\n/// Show larger buttons for switching between light and dark mode (globally).\npub fn global_theme_preference_buttons(ui: &mut Ui) {\n    let mut theme_preference = ui.options(|opt| opt.theme_preference);\n    theme_preference.radio_buttons(ui);\n    ui.ctx().set_theme(theme_preference);\n}\n\n/// Show a small button to switch to/from dark/light mode (globally).\n#[deprecated = \"Use global_theme_preference_switch instead\"]\npub fn global_dark_light_mode_switch(ui: &mut Ui) {\n    global_theme_preference_switch(ui);\n}\n\n/// Show larger buttons for switching between light and dark mode (globally).\n#[deprecated = \"Use global_theme_preference_buttons instead\"]\npub fn global_dark_light_mode_buttons(ui: &mut Ui) {\n    global_theme_preference_buttons(ui);\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/progress_bar.rs",
    "content": "use crate::{\n    Color32, CornerRadius, NumExt as _, Pos2, Rect, Response, Rgba, Sense, Shape, Stroke,\n    TextStyle, TextWrapMode, Ui, Vec2, Widget, WidgetInfo, WidgetText, WidgetType, lerp, vec2,\n};\n\nenum ProgressBarText {\n    Custom(WidgetText),\n    Percentage,\n}\n\n/// A simple progress bar.\n///\n/// See also: [`crate::Spinner`].\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct ProgressBar {\n    progress: f32,\n    desired_width: Option<f32>,\n    desired_height: Option<f32>,\n    text: Option<ProgressBarText>,\n    fill: Option<Color32>,\n    animate: bool,\n    corner_radius: Option<CornerRadius>,\n}\n\nimpl ProgressBar {\n    /// Progress in the `[0, 1]` range, where `1` means \"completed\".\n    pub fn new(progress: f32) -> Self {\n        Self {\n            progress: progress.clamp(0.0, 1.0),\n            desired_width: None,\n            desired_height: None,\n            text: None,\n            fill: None,\n            animate: false,\n            corner_radius: None,\n        }\n    }\n\n    /// The desired width of the bar. Will use all horizontal space if not set.\n    #[inline]\n    pub fn desired_width(mut self, desired_width: f32) -> Self {\n        self.desired_width = Some(desired_width);\n        self\n    }\n\n    /// The desired height of the bar. Will use the default interaction size if not set.\n    #[inline]\n    pub fn desired_height(mut self, desired_height: f32) -> Self {\n        self.desired_height = Some(desired_height);\n        self\n    }\n\n    /// The fill color of the bar.\n    #[inline]\n    pub fn fill(mut self, color: Color32) -> Self {\n        self.fill = Some(color);\n        self\n    }\n\n    /// A custom text to display on the progress bar.\n    #[inline]\n    pub fn text(mut self, text: impl Into<WidgetText>) -> Self {\n        self.text = Some(ProgressBarText::Custom(text.into()));\n        self\n    }\n\n    /// Show the progress in percent on the progress bar.\n    #[inline]\n    pub fn show_percentage(mut self) -> Self {\n        self.text = Some(ProgressBarText::Percentage);\n        self\n    }\n\n    /// Whether to display a loading animation when progress `< 1`.\n    /// Note that this will cause the UI to be redrawn.\n    /// Defaults to `false`.\n    ///\n    /// If [`Self::corner_radius`] and [`Self::animate`] are used simultaneously, the animation is not\n    /// rendered, since it requires a perfect circle to render correctly. However, the UI is still\n    /// redrawn.\n    #[inline]\n    pub fn animate(mut self, animate: bool) -> Self {\n        self.animate = animate;\n        self\n    }\n\n    /// Set the rounding of the progress bar.\n    ///\n    /// If [`Self::corner_radius`] and [`Self::animate`] are used simultaneously, the animation is not\n    /// rendered, since it requires a perfect circle to render correctly. However, the UI is still\n    /// redrawn.\n    #[inline]\n    pub fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {\n        self.corner_radius = Some(corner_radius.into());\n        self\n    }\n\n    #[inline]\n    #[deprecated = \"Renamed to `corner_radius`\"]\n    pub fn rounding(self, corner_radius: impl Into<CornerRadius>) -> Self {\n        self.corner_radius(corner_radius)\n    }\n}\n\nimpl Widget for ProgressBar {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let Self {\n            progress,\n            desired_width,\n            desired_height,\n            text,\n            fill,\n            animate,\n            corner_radius,\n        } = self;\n\n        let animate = animate && progress < 1.0;\n\n        let desired_width =\n            desired_width.unwrap_or_else(|| ui.available_size_before_wrap().x.at_least(96.0));\n        let height = desired_height.unwrap_or_else(|| ui.spacing().interact_size.y);\n        let (outer_rect, response) =\n            ui.allocate_exact_size(vec2(desired_width, height), Sense::hover());\n\n        response.widget_info(|| {\n            let mut info = if let Some(ProgressBarText::Custom(text)) = &text {\n                WidgetInfo::labeled(WidgetType::ProgressIndicator, ui.is_enabled(), text.text())\n            } else {\n                WidgetInfo::new(WidgetType::ProgressIndicator)\n            };\n            info.value = Some((progress as f64 * 100.0).floor());\n\n            info\n        });\n\n        if ui.is_rect_visible(response.rect) {\n            if animate {\n                ui.request_repaint();\n            }\n\n            let visuals = ui.style().visuals.clone();\n            let has_custom_cr = corner_radius.is_some();\n            let half_height = outer_rect.height() / 2.0;\n            let corner_radius = corner_radius.unwrap_or_else(|| half_height.into());\n            ui.painter()\n                .rect_filled(outer_rect, corner_radius, visuals.extreme_bg_color);\n            let min_width =\n                2.0 * f32::max(corner_radius.sw as _, corner_radius.nw as _).at_most(half_height);\n            let filled_width = (outer_rect.width() * progress).at_least(min_width);\n            let inner_rect =\n                Rect::from_min_size(outer_rect.min, vec2(filled_width, outer_rect.height()));\n\n            let (dark, bright) = (0.7, 1.0);\n            let color_factor = if animate {\n                let time = ui.input(|i| i.time);\n                lerp(dark..=bright, time.cos().abs())\n            } else {\n                bright\n            };\n\n            ui.painter().rect_filled(\n                inner_rect,\n                corner_radius,\n                Color32::from(\n                    Rgba::from(fill.unwrap_or(visuals.selection.bg_fill)) * color_factor as f32,\n                ),\n            );\n\n            if animate && !has_custom_cr {\n                let n_points = 20;\n                let time = ui.input(|i| i.time);\n                let start_angle = time * std::f64::consts::TAU;\n                let end_angle = start_angle + 240f64.to_radians() * time.sin();\n                let circle_radius = half_height - 2.0;\n                let points: Vec<Pos2> = (0..n_points)\n                    .map(|i| {\n                        let angle = lerp(start_angle..=end_angle, i as f64 / n_points as f64);\n                        let (sin, cos) = angle.sin_cos();\n                        inner_rect.right_center()\n                            + circle_radius * vec2(cos as f32, sin as f32)\n                            + vec2(-half_height, 0.0)\n                    })\n                    .collect();\n                ui.painter()\n                    .add(Shape::line(points, Stroke::new(2.0, visuals.text_color())));\n            }\n\n            if let Some(text_kind) = text {\n                let text = match text_kind {\n                    ProgressBarText::Custom(text) => text,\n                    ProgressBarText::Percentage => {\n                        format!(\"{}%\", (progress * 100.0) as usize).into()\n                    }\n                };\n                let galley = text.into_galley(\n                    ui,\n                    Some(TextWrapMode::Extend),\n                    f32::INFINITY,\n                    TextStyle::Button,\n                );\n                let text_pos = outer_rect.left_center() - Vec2::new(0.0, galley.size().y / 2.0)\n                    + vec2(ui.spacing().item_spacing.x, 0.0);\n                let text_color = visuals\n                    .override_text_color\n                    .unwrap_or(visuals.selection.stroke.color);\n                ui.painter()\n                    .with_clip_rect(outer_rect)\n                    .galley(text_pos, galley, text_color);\n            }\n        }\n\n        response\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/radio_button.rs",
    "content": "use crate::{\n    Atom, AtomLayout, Atoms, Id, IntoAtoms, NumExt as _, Response, Sense, Ui, Vec2, Widget,\n    WidgetInfo, WidgetType, epaint,\n};\n\n/// One out of several alternatives, either selected or not.\n///\n/// Usually you'd use [`Ui::radio_value`] or [`Ui::radio`] instead.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// #[derive(PartialEq)]\n/// enum Enum { First, Second, Third }\n/// let mut my_enum = Enum::First;\n///\n/// ui.radio_value(&mut my_enum, Enum::First, \"First\");\n///\n/// // is equivalent to:\n///\n/// if ui.add(egui::RadioButton::new(my_enum == Enum::First, \"First\")).clicked() {\n///     my_enum = Enum::First\n/// }\n/// # });\n/// ```\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct RadioButton<'a> {\n    checked: bool,\n    atoms: Atoms<'a>,\n}\n\nimpl<'a> RadioButton<'a> {\n    pub fn new(checked: bool, atoms: impl IntoAtoms<'a>) -> Self {\n        Self {\n            checked,\n            atoms: atoms.into_atoms(),\n        }\n    }\n}\n\nimpl Widget for RadioButton<'_> {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let Self { checked, mut atoms } = self;\n\n        let spacing = &ui.spacing();\n        let icon_width = spacing.icon_width;\n\n        let mut min_size = Vec2::splat(spacing.interact_size.y);\n        min_size.y = min_size.y.at_least(icon_width);\n\n        // In order to center the checkbox based on min_size we set the icon height to at least min_size.y\n        let mut icon_size = Vec2::splat(icon_width);\n        icon_size.y = icon_size.y.at_least(min_size.y);\n        let rect_id = Id::new(\"egui::radio_button\");\n        atoms.push_left(Atom::custom(rect_id, icon_size));\n\n        let text = atoms.text().map(String::from);\n\n        let mut prepared = AtomLayout::new(atoms)\n            .sense(Sense::click())\n            .min_size(min_size)\n            .allocate(ui);\n\n        prepared.response.widget_info(|| {\n            WidgetInfo::selected(\n                WidgetType::RadioButton,\n                ui.is_enabled(),\n                checked,\n                text.as_deref().unwrap_or(\"\"),\n            )\n        });\n\n        if ui.is_rect_visible(prepared.response.rect) {\n            // let visuals = ui.style().interact_selectable(&response, checked); // too colorful\n            let visuals = *ui.style().interact(&prepared.response);\n\n            prepared.fallback_text_color = visuals.text_color();\n            let response = prepared.paint(ui);\n\n            if let Some(rect) = response.rect(rect_id) {\n                let (small_icon_rect, big_icon_rect) = ui.spacing().icon_rectangles(rect);\n\n                let painter = ui.painter();\n\n                painter.add(epaint::CircleShape {\n                    center: big_icon_rect.center(),\n                    radius: big_icon_rect.width() / 2.0 + visuals.expansion,\n                    fill: visuals.bg_fill,\n                    stroke: visuals.bg_stroke,\n                });\n\n                if checked {\n                    painter.add(epaint::CircleShape {\n                        center: small_icon_rect.center(),\n                        radius: small_icon_rect.width() / 3.0,\n                        fill: visuals.fg_stroke.color, // Intentional to use stroke and not fill\n                        // fill: ui.visuals().selection.stroke.color, // too much color\n                        stroke: Default::default(),\n                    });\n                }\n            }\n            response.response\n        } else {\n            prepared.response\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/selected_label.rs",
    "content": "#![expect(deprecated, clippy::new_ret_no_self)]\n\nuse crate::WidgetText;\n\n#[deprecated = \"Use `Button::selectable()` instead\"]\npub struct SelectableLabel {}\n\nimpl SelectableLabel {\n    #[deprecated = \"Use `Button::selectable()` instead\"]\n    pub fn new(selected: bool, text: impl Into<WidgetText>) -> super::Button<'static> {\n        crate::Button::selectable(selected, text)\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/separator.rs",
    "content": "use crate::{Response, Sense, Ui, Vec2, Widget, vec2, widget_style::SeparatorStyle};\n\n/// A visual separator. A horizontal or vertical line (depending on [`crate::Layout`]).\n///\n/// Usually you'd use the shorter version [`Ui::separator`].\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// // These are equivalent:\n/// ui.separator();\n/// ui.add(egui::Separator::default());\n/// # });\n/// ```\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct Separator {\n    spacing: Option<f32>,\n    grow: f32,\n    is_horizontal_line: Option<bool>,\n}\n\nimpl Default for Separator {\n    fn default() -> Self {\n        Self {\n            spacing: None,\n            grow: 0.0,\n            is_horizontal_line: None,\n        }\n    }\n}\n\nimpl Separator {\n    /// How much space we take up. The line is painted in the middle of this.\n    ///\n    /// In a vertical layout, with a horizontal Separator,\n    /// this is the height of the separator widget.\n    ///\n    /// In a horizontal layout, with a vertical Separator,\n    /// this is the width of the separator widget.\n    #[inline]\n    pub fn spacing(mut self, spacing: f32) -> Self {\n        self.spacing = Some(spacing);\n        self\n    }\n\n    /// Explicitly ask for a horizontal line.\n    ///\n    /// By default you will get a horizontal line in vertical layouts,\n    /// and a vertical line in horizontal layouts.\n    #[inline]\n    pub fn horizontal(mut self) -> Self {\n        self.is_horizontal_line = Some(true);\n        self\n    }\n\n    /// Explicitly ask for a vertical line.\n    ///\n    /// By default you will get a horizontal line in vertical layouts,\n    /// and a vertical line in horizontal layouts.\n    #[inline]\n    pub fn vertical(mut self) -> Self {\n        self.is_horizontal_line = Some(false);\n        self\n    }\n\n    /// Extend each end of the separator line by this much.\n    ///\n    /// The default is to take up the available width/height of the parent.\n    ///\n    /// This will make the line extend outside the parent ui.\n    #[inline]\n    pub fn grow(mut self, extra: f32) -> Self {\n        self.grow += extra;\n        self\n    }\n\n    /// Contract each end of the separator line by this much.\n    ///\n    /// The default is to take up the available width/height of the parent.\n    ///\n    /// This effectively adds margins to the line.\n    #[inline]\n    pub fn shrink(mut self, shrink: f32) -> Self {\n        self.grow -= shrink;\n        self\n    }\n}\n\nimpl Widget for Separator {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let Self {\n            spacing,\n            grow,\n            is_horizontal_line,\n        } = self;\n\n        // Get the widget style by reading the response from the previous pass\n        let id = ui.next_auto_id();\n        let response: Option<Response> = ui.ctx().read_response(id);\n        let state = response.map(|r| r.widget_state()).unwrap_or_default();\n        let SeparatorStyle {\n            spacing: spacing_style,\n            stroke,\n        } = ui.style().separator_style(state);\n\n        // override the spacing if not set\n        let spacing = spacing.unwrap_or(spacing_style);\n\n        let is_horizontal_line = is_horizontal_line\n            .unwrap_or_else(|| ui.is_grid() || !ui.layout().main_dir().is_horizontal());\n\n        let available_space = if ui.is_sizing_pass() {\n            Vec2::ZERO\n        } else {\n            ui.available_size_before_wrap()\n        };\n\n        let size = if is_horizontal_line {\n            vec2(available_space.x, spacing)\n        } else {\n            vec2(spacing, available_space.y)\n        };\n\n        let (rect, response) = ui.allocate_at_least(size, Sense::hover());\n\n        if ui.is_rect_visible(response.rect) {\n            let painter = ui.painter();\n            if is_horizontal_line {\n                painter.hline(\n                    (rect.left() - grow)..=(rect.right() + grow),\n                    rect.center().y,\n                    stroke,\n                );\n            } else {\n                painter.vline(\n                    rect.center().x,\n                    (rect.top() - grow)..=(rect.bottom() + grow),\n                    stroke,\n                );\n            }\n        }\n\n        response\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/slider.rs",
    "content": "#![expect(clippy::needless_pass_by_value)] // False positives with `impl ToString`\n\nuse std::ops::RangeInclusive;\n\nuse crate::{\n    Color32, DragValue, EventFilter, Key, Label, MINUS_CHAR_STR, NumExt as _, Pos2, Rangef, Rect,\n    Response, Sense, TextStyle, TextWrapMode, Ui, Vec2, Widget, WidgetInfo, WidgetText, emath,\n    epaint, lerp, pos2, remap, remap_clamp, style, style::HandleShape, vec2,\n};\n\nuse super::drag_value::clamp_value_to_range;\n\n// ----------------------------------------------------------------------------\n\ntype NumFormatter<'a> = Box<dyn 'a + Fn(f64, RangeInclusive<usize>) -> String>;\ntype NumParser<'a> = Box<dyn 'a + Fn(&str) -> Option<f64>>;\n\n// ----------------------------------------------------------------------------\n\n/// Combined into one function (rather than two) to make it easier\n/// for the borrow checker.\ntype GetSetValue<'a> = Box<dyn 'a + FnMut(Option<f64>) -> f64>;\n\nfn get(get_set_value: &mut GetSetValue<'_>) -> f64 {\n    (get_set_value)(None)\n}\n\nfn set(get_set_value: &mut GetSetValue<'_>, value: f64) {\n    (get_set_value)(Some(value));\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Clone)]\nstruct SliderSpec {\n    logarithmic: bool,\n\n    /// For logarithmic sliders, the smallest positive value we are interested in.\n    /// 1 for integer sliders, maybe 1e-6 for others.\n    smallest_positive: f64,\n\n    /// For logarithmic sliders, the largest positive value we are interested in\n    /// before the slider switches to `INFINITY`, if that is the higher end.\n    /// Default: INFINITY.\n    largest_finite: f64,\n}\n\n/// Specifies the orientation of a [`Slider`].\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum SliderOrientation {\n    Horizontal,\n    Vertical,\n}\n\n/// Specifies how values in a [`Slider`] are clamped.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum SliderClamping {\n    /// Values are not clamped.\n    ///\n    /// This means editing the value with the keyboard,\n    /// or dragging the number next to the slider will always work.\n    ///\n    /// The actual slider part is always clamped though.\n    Never,\n\n    /// Users cannot enter new values that are outside the range.\n    ///\n    /// Existing values remain intact though.\n    Edits,\n\n    /// Always clamp values, even existing ones.\n    #[default]\n    Always,\n}\n\n/// Control a number with a slider.\n///\n/// The slider range defines the values you get when pulling the slider to the far edges.\n/// By default all values are clamped to this range, even when not interacted with.\n/// You can change this behavior by passing `false` to [`Slider::clamp_to_range`].\n///\n/// The range can include any numbers, and go from low-to-high or from high-to-low.\n///\n/// The slider consists of three parts: a slider, a value display, and an optional text.\n/// The user can click the value display to edit its value. It can be turned off with `.show_value(false)`.\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # let mut my_f32: f32 = 0.0;\n/// ui.add(egui::Slider::new(&mut my_f32, 0.0..=100.0).text(\"My value\"));\n/// # });\n/// ```\n///\n/// The default [`Slider`] size is set by [`crate::style::Spacing::slider_width`].\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct Slider<'a> {\n    get_set_value: GetSetValue<'a>,\n    range: RangeInclusive<f64>,\n    spec: SliderSpec,\n    clamping: SliderClamping,\n    smart_aim: bool,\n    show_value: bool,\n    orientation: SliderOrientation,\n    prefix: String,\n    suffix: String,\n    text: WidgetText,\n\n    /// Sets the minimal step of the widget value\n    step: Option<f64>,\n\n    drag_value_speed: Option<f64>,\n    min_decimals: usize,\n    max_decimals: Option<usize>,\n    custom_formatter: Option<NumFormatter<'a>>,\n    custom_parser: Option<NumParser<'a>>,\n    trailing_fill: Option<bool>,\n    handle_shape: Option<HandleShape>,\n    update_while_editing: bool,\n}\n\nimpl<'a> Slider<'a> {\n    /// Creates a new horizontal slider.\n    ///\n    /// The `value` given will be clamped to the `range`,\n    /// unless you change this behavior with [`Self::clamping`].\n    pub fn new<Num: emath::Numeric>(value: &'a mut Num, range: RangeInclusive<Num>) -> Self {\n        let range_f64 = range.start().to_f64()..=range.end().to_f64();\n        let slf = Self::from_get_set(range_f64, move |v: Option<f64>| {\n            if let Some(v) = v {\n                *value = Num::from_f64(v);\n            }\n            value.to_f64()\n        });\n\n        if Num::INTEGRAL { slf.integer() } else { slf }\n    }\n\n    pub fn from_get_set(\n        range: RangeInclusive<f64>,\n        get_set_value: impl 'a + FnMut(Option<f64>) -> f64,\n    ) -> Self {\n        Self {\n            get_set_value: Box::new(get_set_value),\n            range,\n            spec: SliderSpec {\n                logarithmic: false,\n                smallest_positive: 1e-6,\n                largest_finite: f64::INFINITY,\n            },\n            clamping: SliderClamping::default(),\n            smart_aim: true,\n            show_value: true,\n            orientation: SliderOrientation::Horizontal,\n            prefix: Default::default(),\n            suffix: Default::default(),\n            text: Default::default(),\n            step: None,\n            drag_value_speed: None,\n            min_decimals: 0,\n            max_decimals: None,\n            custom_formatter: None,\n            custom_parser: None,\n            trailing_fill: None,\n            handle_shape: None,\n            update_while_editing: true,\n        }\n    }\n\n    /// Control whether or not the slider shows the current value.\n    /// Default: `true`.\n    #[inline]\n    pub fn show_value(mut self, show_value: bool) -> Self {\n        self.show_value = show_value;\n        self\n    }\n\n    /// Show a prefix before the number, e.g. \"x: \"\n    #[inline]\n    pub fn prefix(mut self, prefix: impl ToString) -> Self {\n        self.prefix = prefix.to_string();\n        self\n    }\n\n    /// Add a suffix to the number, this can be e.g. a unit (\"°\" or \" m\")\n    #[inline]\n    pub fn suffix(mut self, suffix: impl ToString) -> Self {\n        self.suffix = suffix.to_string();\n        self\n    }\n\n    /// Show a text next to the slider (e.g. explaining what the slider controls).\n    #[inline]\n    pub fn text(mut self, text: impl Into<WidgetText>) -> Self {\n        self.text = text.into();\n        self\n    }\n\n    #[inline]\n    pub fn text_color(mut self, text_color: Color32) -> Self {\n        self.text = self.text.color(text_color);\n        self\n    }\n\n    /// Vertical or horizontal slider? The default is horizontal.\n    #[inline]\n    pub fn orientation(mut self, orientation: SliderOrientation) -> Self {\n        self.orientation = orientation;\n        self\n    }\n\n    /// Make this a vertical slider.\n    #[inline]\n    pub fn vertical(mut self) -> Self {\n        self.orientation = SliderOrientation::Vertical;\n        self\n    }\n\n    /// Make this a logarithmic slider.\n    /// This is great for when the slider spans a huge range,\n    /// e.g. from one to a million.\n    /// The default is OFF.\n    #[inline]\n    pub fn logarithmic(mut self, logarithmic: bool) -> Self {\n        self.spec.logarithmic = logarithmic;\n        self\n    }\n\n    /// For logarithmic sliders that includes zero:\n    /// what is the smallest positive value you want to be able to select?\n    /// The default is `1` for integer sliders and `1e-6` for real sliders.\n    #[inline]\n    pub fn smallest_positive(mut self, smallest_positive: f64) -> Self {\n        self.spec.smallest_positive = smallest_positive;\n        self\n    }\n\n    /// For logarithmic sliders, the largest positive value we are interested in\n    /// before the slider switches to `INFINITY`, if that is the higher end.\n    /// Default: INFINITY.\n    #[inline]\n    pub fn largest_finite(mut self, largest_finite: f64) -> Self {\n        self.spec.largest_finite = largest_finite;\n        self\n    }\n\n    /// Controls when the values will be clamped to the range.\n    ///\n    /// ### With `.clamping(SliderClamping::Always)` (default)\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let mut my_value: f32 = 1337.0;\n    /// ui.add(egui::Slider::new(&mut my_value, 0.0..=1.0));\n    /// assert!(0.0 <= my_value && my_value <= 1.0, \"Existing value should be clamped\");\n    /// # });\n    /// ```\n    ///\n    /// ### With `.clamping(SliderClamping::Edits)`\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let mut my_value: f32 = 1337.0;\n    /// let response = ui.add(\n    ///     egui::Slider::new(&mut my_value, 0.0..=1.0)\n    ///         .clamping(egui::SliderClamping::Edits)\n    /// );\n    /// if response.dragged() {\n    ///     // The user edited the value, so it should now be clamped to the range\n    ///     assert!(0.0 <= my_value && my_value <= 1.0);\n    /// }\n    /// # });\n    /// ```\n    ///\n    /// ### With `.clamping(SliderClamping::Never)`\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// let mut my_value: f32 = 1337.0;\n    /// let response = ui.add(\n    ///     egui::Slider::new(&mut my_value, 0.0..=1.0)\n    ///         .clamping(egui::SliderClamping::Never)\n    /// );\n    /// // The user could have set the value to anything\n    /// # });\n    /// ```\n    #[inline]\n    pub fn clamping(mut self, clamping: SliderClamping) -> Self {\n        self.clamping = clamping;\n        self\n    }\n\n    #[inline]\n    #[deprecated = \"Use `slider.clamping(…) instead\"]\n    pub fn clamp_to_range(self, clamp_to_range: bool) -> Self {\n        self.clamping(if clamp_to_range {\n            SliderClamping::Always\n        } else {\n            SliderClamping::Never\n        })\n    }\n\n    /// Turn smart aim on/off. Default is ON.\n    /// There is almost no point in turning this off.\n    #[inline]\n    pub fn smart_aim(mut self, smart_aim: bool) -> Self {\n        self.smart_aim = smart_aim;\n        self\n    }\n\n    /// Sets the minimal change of the value.\n    ///\n    /// Value `0.0` effectively disables the feature. If the new value is out of range\n    /// and `clamp_to_range` is enabled, you would not have the ability to change the value.\n    ///\n    /// Default: `0.0` (disabled).\n    #[inline]\n    pub fn step_by(mut self, step: f64) -> Self {\n        self.step = if step != 0.0 { Some(step) } else { None };\n        self\n    }\n\n    /// When dragging the value, how fast does it move?\n    ///\n    /// Unit: values per point (logical pixel).\n    /// See also [`DragValue::speed`].\n    ///\n    /// By default this is the same speed as when dragging the slider,\n    /// but you can change it here to for instance have a much finer control\n    /// by dragging the slider value rather than the slider itself.\n    #[inline]\n    pub fn drag_value_speed(mut self, drag_value_speed: f64) -> Self {\n        self.drag_value_speed = Some(drag_value_speed);\n        self\n    }\n\n    // TODO(emilk): we should also have a \"min precision\".\n    /// Set a minimum number of decimals to display.\n    ///\n    /// Normally you don't need to pick a precision, as the slider will intelligently pick a precision for you.\n    /// Regardless of precision the slider will use \"smart aim\" to help the user select nice, round values.\n    #[inline]\n    pub fn min_decimals(mut self, min_decimals: usize) -> Self {\n        self.min_decimals = min_decimals;\n        self\n    }\n\n    // TODO(emilk): we should also have a \"max precision\".\n    /// Set a maximum number of decimals to display.\n    ///\n    /// Values will also be rounded to this number of decimals.\n    /// Normally you don't need to pick a precision, as the slider will intelligently pick a precision for you.\n    /// Regardless of precision the slider will use \"smart aim\" to help the user select nice, round values.\n    #[inline]\n    pub fn max_decimals(mut self, max_decimals: usize) -> Self {\n        self.max_decimals = Some(max_decimals);\n        self\n    }\n\n    #[inline]\n    pub fn max_decimals_opt(mut self, max_decimals: Option<usize>) -> Self {\n        self.max_decimals = max_decimals;\n        self\n    }\n\n    /// Set an exact number of decimals to display.\n    ///\n    /// Values will also be rounded to this number of decimals.\n    /// Normally you don't need to pick a precision, as the slider will intelligently pick a precision for you.\n    /// Regardless of precision the slider will use \"smart aim\" to help the user select nice, round values.\n    #[inline]\n    pub fn fixed_decimals(mut self, num_decimals: usize) -> Self {\n        self.min_decimals = num_decimals;\n        self.max_decimals = Some(num_decimals);\n        self\n    }\n\n    /// Display trailing color behind the slider's circle. Default is OFF.\n    ///\n    /// This setting can be enabled globally for all sliders with [`crate::Visuals::slider_trailing_fill`].\n    /// Toggling it here will override the above setting ONLY for this individual slider.\n    ///\n    /// The fill color will be taken from `selection.bg_fill` in your [`crate::Visuals`], the same as a [`crate::ProgressBar`].\n    #[inline]\n    pub fn trailing_fill(mut self, trailing_fill: bool) -> Self {\n        self.trailing_fill = Some(trailing_fill);\n        self\n    }\n\n    /// Change the shape of the slider handle\n    ///\n    /// This setting can be enabled globally for all sliders with [`crate::Visuals::handle_shape`].\n    /// Changing it here will override the above setting ONLY for this individual slider.\n    #[inline]\n    pub fn handle_shape(mut self, handle_shape: HandleShape) -> Self {\n        self.handle_shape = Some(handle_shape);\n        self\n    }\n\n    /// Set custom formatter defining how numbers are converted into text.\n    ///\n    /// A custom formatter takes a `f64` for the numeric value and a `RangeInclusive<usize>` representing\n    /// the decimal range i.e. minimum and maximum number of decimal places shown.\n    ///\n    /// The default formatter is [`crate::Style::number_formatter`].\n    ///\n    /// See also: [`Slider::custom_parser`]\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_i32: i32 = 0;\n    /// ui.add(egui::Slider::new(&mut my_i32, 0..=((60 * 60 * 24) - 1))\n    ///     .custom_formatter(|n, _| {\n    ///         let n = n as i32;\n    ///         let hours = n / (60 * 60);\n    ///         let mins = (n / 60) % 60;\n    ///         let secs = n % 60;\n    ///         format!(\"{hours:02}:{mins:02}:{secs:02}\")\n    ///     })\n    ///     .custom_parser(|s| {\n    ///         let parts: Vec<&str> = s.split(':').collect();\n    ///         if parts.len() == 3 {\n    ///             parts[0].parse::<i32>().and_then(|h| {\n    ///                 parts[1].parse::<i32>().and_then(|m| {\n    ///                     parts[2].parse::<i32>().map(|s| {\n    ///                         ((h * 60 * 60) + (m * 60) + s) as f64\n    ///                     })\n    ///                 })\n    ///             })\n    ///             .ok()\n    ///         } else {\n    ///             None\n    ///         }\n    ///     }));\n    /// # });\n    /// ```\n    pub fn custom_formatter(\n        mut self,\n        formatter: impl 'a + Fn(f64, RangeInclusive<usize>) -> String,\n    ) -> Self {\n        self.custom_formatter = Some(Box::new(formatter));\n        self\n    }\n\n    /// Set custom parser defining how the text input is parsed into a number.\n    ///\n    /// A custom parser takes an `&str` to parse into a number and returns `Some` if it was successfully parsed\n    /// or `None` otherwise.\n    ///\n    /// See also: [`Slider::custom_formatter`]\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_i32: i32 = 0;\n    /// ui.add(egui::Slider::new(&mut my_i32, 0..=((60 * 60 * 24) - 1))\n    ///     .custom_formatter(|n, _| {\n    ///         let n = n as i32;\n    ///         let hours = n / (60 * 60);\n    ///         let mins = (n / 60) % 60;\n    ///         let secs = n % 60;\n    ///         format!(\"{hours:02}:{mins:02}:{secs:02}\")\n    ///     })\n    ///     .custom_parser(|s| {\n    ///         let parts: Vec<&str> = s.split(':').collect();\n    ///         if parts.len() == 3 {\n    ///             parts[0].parse::<i32>().and_then(|h| {\n    ///                 parts[1].parse::<i32>().and_then(|m| {\n    ///                     parts[2].parse::<i32>().map(|s| {\n    ///                         ((h * 60 * 60) + (m * 60) + s) as f64\n    ///                     })\n    ///                 })\n    ///             })\n    ///             .ok()\n    ///         } else {\n    ///             None\n    ///         }\n    ///     }));\n    /// # });\n    /// ```\n    #[inline]\n    pub fn custom_parser(mut self, parser: impl 'a + Fn(&str) -> Option<f64>) -> Self {\n        self.custom_parser = Some(Box::new(parser));\n        self\n    }\n\n    /// Set `custom_formatter` and `custom_parser` to display and parse numbers as binary integers. Floating point\n    /// numbers are *not* supported.\n    ///\n    /// `min_width` specifies the minimum number of displayed digits; if the number is shorter than this, it will be\n    /// prefixed with additional 0s to match `min_width`.\n    ///\n    /// If `twos_complement` is true, negative values will be displayed as the 2's complement representation. Otherwise\n    /// they will be prefixed with a '-' sign.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `min_width` is 0.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_i32: i32 = 0;\n    /// ui.add(egui::Slider::new(&mut my_i32, -100..=100).binary(64, false));\n    /// # });\n    /// ```\n    pub fn binary(self, min_width: usize, twos_complement: bool) -> Self {\n        assert!(\n            min_width > 0,\n            \"Slider::binary: `min_width` must be greater than 0\"\n        );\n        if twos_complement {\n            self.custom_formatter(move |n, _| format!(\"{:0>min_width$b}\", n as i64))\n        } else {\n            self.custom_formatter(move |n, _| {\n                let sign = if n < 0.0 { MINUS_CHAR_STR } else { \"\" };\n                format!(\"{sign}{:0>min_width$b}\", n.abs() as i64)\n            })\n        }\n        .custom_parser(|s| i64::from_str_radix(s, 2).map(|n| n as f64).ok())\n    }\n\n    /// Set `custom_formatter` and `custom_parser` to display and parse numbers as octal integers. Floating point\n    /// numbers are *not* supported.\n    ///\n    /// `min_width` specifies the minimum number of displayed digits; if the number is shorter than this, it will be\n    /// prefixed with additional 0s to match `min_width`.\n    ///\n    /// If `twos_complement` is true, negative values will be displayed as the 2's complement representation. Otherwise\n    /// they will be prefixed with a '-' sign.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `min_width` is 0.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_i32: i32 = 0;\n    /// ui.add(egui::Slider::new(&mut my_i32, -100..=100).octal(22, false));\n    /// # });\n    /// ```\n    pub fn octal(self, min_width: usize, twos_complement: bool) -> Self {\n        assert!(\n            min_width > 0,\n            \"Slider::octal: `min_width` must be greater than 0\"\n        );\n        if twos_complement {\n            self.custom_formatter(move |n, _| format!(\"{:0>min_width$o}\", n as i64))\n        } else {\n            self.custom_formatter(move |n, _| {\n                let sign = if n < 0.0 { MINUS_CHAR_STR } else { \"\" };\n                format!(\"{sign}{:0>min_width$o}\", n.abs() as i64)\n            })\n        }\n        .custom_parser(|s| i64::from_str_radix(s, 8).map(|n| n as f64).ok())\n    }\n\n    /// Set `custom_formatter` and `custom_parser` to display and parse numbers as hexadecimal integers. Floating point\n    /// numbers are *not* supported.\n    ///\n    /// `min_width` specifies the minimum number of displayed digits; if the number is shorter than this, it will be\n    /// prefixed with additional 0s to match `min_width`.\n    ///\n    /// If `twos_complement` is true, negative values will be displayed as the 2's complement representation. Otherwise\n    /// they will be prefixed with a '-' sign.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `min_width` is 0.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_i32: i32 = 0;\n    /// ui.add(egui::Slider::new(&mut my_i32, -100..=100).hexadecimal(16, false, true));\n    /// # });\n    /// ```\n    pub fn hexadecimal(self, min_width: usize, twos_complement: bool, upper: bool) -> Self {\n        assert!(\n            min_width > 0,\n            \"Slider::hexadecimal: `min_width` must be greater than 0\"\n        );\n        match (twos_complement, upper) {\n            (true, true) => {\n                self.custom_formatter(move |n, _| format!(\"{:0>min_width$X}\", n as i64))\n            }\n            (true, false) => {\n                self.custom_formatter(move |n, _| format!(\"{:0>min_width$x}\", n as i64))\n            }\n            (false, true) => self.custom_formatter(move |n, _| {\n                let sign = if n < 0.0 { MINUS_CHAR_STR } else { \"\" };\n                format!(\"{sign}{:0>min_width$X}\", n.abs() as i64)\n            }),\n            (false, false) => self.custom_formatter(move |n, _| {\n                let sign = if n < 0.0 { MINUS_CHAR_STR } else { \"\" };\n                format!(\"{sign}{:0>min_width$x}\", n.abs() as i64)\n            }),\n        }\n        .custom_parser(|s| i64::from_str_radix(s, 16).map(|n| n as f64).ok())\n    }\n\n    /// Helper: equivalent to `self.precision(0).smallest_positive(1.0)`.\n    /// If you use one of the integer constructors (e.g. `Slider::i32`) this is called for you,\n    /// but if you want to have a slider for picking integer values in an `Slider::f64`, use this.\n    pub fn integer(self) -> Self {\n        self.fixed_decimals(0).smallest_positive(1.0).step_by(1.0)\n    }\n\n    fn get_value(&mut self) -> f64 {\n        let value = get(&mut self.get_set_value);\n        if self.clamping == SliderClamping::Always {\n            clamp_value_to_range(value, self.range.clone())\n        } else {\n            value\n        }\n    }\n\n    fn set_value(&mut self, mut value: f64) {\n        if self.clamping != SliderClamping::Never {\n            value = clamp_value_to_range(value, self.range.clone());\n        }\n\n        if let Some(step) = self.step {\n            let start = *self.range.start();\n            value = start + ((value - start) / step).round() * step;\n        }\n        if let Some(max_decimals) = self.max_decimals {\n            value = emath::round_to_decimals(value, max_decimals);\n        }\n        set(&mut self.get_set_value, value);\n    }\n\n    fn range(&self) -> RangeInclusive<f64> {\n        self.range.clone()\n    }\n\n    /// For instance, `position` is the mouse position and `position_range` is the physical location of the slider on the screen.\n    fn value_from_position(&self, position: f32, position_range: Rangef) -> f64 {\n        let normalized = remap_clamp(position, position_range, 0.0..=1.0) as f64;\n        value_from_normalized(normalized, self.range(), &self.spec)\n    }\n\n    fn position_from_value(&self, value: f64, position_range: Rangef) -> f32 {\n        let normalized = normalized_from_value(value, self.range(), &self.spec);\n        lerp(position_range, normalized as f32)\n    }\n\n    /// Update the value on each key press when text-editing the value.\n    ///\n    /// Default: `true`.\n    /// If `false`, the value will only be updated when user presses enter or deselects the value.\n    #[inline]\n    pub fn update_while_editing(mut self, update: bool) -> Self {\n        self.update_while_editing = update;\n        self\n    }\n}\n\nimpl Slider<'_> {\n    /// Just the slider, no text\n    fn allocate_slider_space(&self, ui: &mut Ui, thickness: f32) -> Response {\n        let desired_size = match self.orientation {\n            SliderOrientation::Horizontal => vec2(ui.spacing().slider_width, thickness),\n            SliderOrientation::Vertical => vec2(thickness, ui.spacing().slider_width),\n        };\n        ui.allocate_response(desired_size, Sense::drag())\n    }\n\n    /// Just the slider, no text\n    fn slider_ui(&mut self, ui: &Ui, response: &Response) {\n        let rect = &response.rect;\n        let handle_shape = self\n            .handle_shape\n            .unwrap_or_else(|| ui.style().visuals.handle_shape);\n        let position_range = self.position_range(rect, &handle_shape);\n\n        if let Some(pointer_position_2d) = response.interact_pointer_pos() {\n            let position = self.pointer_position(pointer_position_2d);\n            let new_value = if self.smart_aim {\n                let aim_radius = ui.input(|i| i.aim_radius());\n                emath::smart_aim::best_in_range_f64(\n                    self.value_from_position(position - aim_radius, position_range),\n                    self.value_from_position(position + aim_radius, position_range),\n                )\n            } else {\n                self.value_from_position(position, position_range)\n            };\n            self.set_value(new_value);\n        }\n\n        let mut decrement = 0usize;\n        let mut increment = 0usize;\n\n        if response.has_focus() {\n            ui.memory_mut(|m| {\n                m.set_focus_lock_filter(\n                    response.id,\n                    EventFilter {\n                        // pressing arrows in the orientation of the\n                        // slider should not move focus to next widget\n                        horizontal_arrows: matches!(\n                            self.orientation,\n                            SliderOrientation::Horizontal\n                        ),\n                        vertical_arrows: matches!(self.orientation, SliderOrientation::Vertical),\n                        ..Default::default()\n                    },\n                );\n            });\n\n            let (dec_key, inc_key) = match self.orientation {\n                SliderOrientation::Horizontal => (Key::ArrowLeft, Key::ArrowRight),\n                // Note that this is for moving the slider position,\n                // so up = decrement y coordinate:\n                SliderOrientation::Vertical => (Key::ArrowUp, Key::ArrowDown),\n            };\n\n            ui.input(|input| {\n                decrement += input.num_presses(dec_key);\n                increment += input.num_presses(inc_key);\n            });\n        }\n\n        ui.input(|input| {\n            use accesskit::Action;\n            decrement += input.num_accesskit_action_requests(response.id, Action::Decrement);\n            increment += input.num_accesskit_action_requests(response.id, Action::Increment);\n        });\n\n        let kb_step = increment as f32 - decrement as f32;\n\n        if kb_step != 0.0 {\n            let ui_point_per_step = 1.0; // move this many ui points for each kb_step\n            let prev_value = self.get_value();\n            let prev_position = self.position_from_value(prev_value, position_range);\n            let new_position = prev_position + ui_point_per_step * kb_step;\n            let mut new_value = match self.step {\n                Some(step) => prev_value + (kb_step as f64 * step),\n                None if self.smart_aim => {\n                    let aim_radius = 0.49 * ui_point_per_step; // Chosen so we don't include `prev_value` in the search.\n                    emath::smart_aim::best_in_range_f64(\n                        self.value_from_position(new_position - aim_radius, position_range),\n                        self.value_from_position(new_position + aim_radius, position_range),\n                    )\n                }\n                _ => self.value_from_position(new_position, position_range),\n            };\n            if let Some(max_decimals) = self.max_decimals {\n                // self.set_value rounds, so ensure we reach at the least the next breakpoint\n                // note: we give it a little bit of leeway due to floating point errors. (0.1 isn't representable in binary)\n                // 'set_value' will round it to the nearest value.\n                let min_increment = 1.0 / (10.0_f64.powi(max_decimals as i32));\n                new_value = if new_value > prev_value {\n                    f64::max(new_value, prev_value + min_increment * 1.001)\n                } else if new_value < prev_value {\n                    f64::min(new_value, prev_value - min_increment * 1.001)\n                } else {\n                    new_value\n                };\n            }\n            self.set_value(new_value);\n        }\n\n        ui.input(|input| {\n            use accesskit::{Action, ActionData};\n            for request in input.accesskit_action_requests(response.id, Action::SetValue) {\n                if let Some(ActionData::NumericValue(new_value)) = request.data {\n                    self.set_value(new_value);\n                }\n            }\n        });\n\n        // Paint it:\n        if ui.is_rect_visible(response.rect) {\n            let value = self.get_value();\n\n            let visuals = ui.style().interact(response);\n            let widget_visuals = &ui.visuals().widgets;\n            let spacing = &ui.style().spacing;\n\n            let rail_radius = (spacing.slider_rail_height / 2.0).at_least(0.0);\n            let rail_rect = self.rail_rect(rect, rail_radius);\n            let corner_radius = widget_visuals.inactive.corner_radius;\n\n            ui.painter()\n                .rect_filled(rail_rect, corner_radius, widget_visuals.inactive.bg_fill);\n\n            let position_1d = self.position_from_value(value, position_range);\n            let center = self.marker_center(position_1d, &rail_rect);\n\n            // Decide if we should add trailing fill.\n            let trailing_fill = self\n                .trailing_fill\n                .unwrap_or_else(|| ui.visuals().slider_trailing_fill);\n\n            // Paint trailing fill.\n            if trailing_fill {\n                let mut trailing_rail_rect = rail_rect;\n\n                // The trailing rect has to be drawn differently depending on the orientation.\n                match self.orientation {\n                    SliderOrientation::Horizontal => {\n                        trailing_rail_rect.max.x = center.x + corner_radius.nw as f32;\n                    }\n                    SliderOrientation::Vertical => {\n                        trailing_rail_rect.min.y = center.y - corner_radius.se as f32;\n                    }\n                }\n\n                ui.painter().rect_filled(\n                    trailing_rail_rect,\n                    corner_radius,\n                    ui.visuals().selection.bg_fill,\n                );\n            }\n\n            let radius = self.handle_radius(rect);\n\n            let handle_shape = self\n                .handle_shape\n                .unwrap_or_else(|| ui.style().visuals.handle_shape);\n            match handle_shape {\n                style::HandleShape::Circle => {\n                    ui.painter().add(epaint::CircleShape {\n                        center,\n                        radius: radius + visuals.expansion,\n                        fill: visuals.bg_fill,\n                        stroke: visuals.fg_stroke,\n                    });\n                }\n                style::HandleShape::Rect { aspect_ratio } => {\n                    let v = match self.orientation {\n                        SliderOrientation::Horizontal => Vec2::new(radius * aspect_ratio, radius),\n                        SliderOrientation::Vertical => Vec2::new(radius, radius * aspect_ratio),\n                    };\n                    let v = v + Vec2::splat(visuals.expansion);\n                    let rect = Rect::from_center_size(center, 2.0 * v);\n                    ui.painter().rect(\n                        rect,\n                        visuals.corner_radius,\n                        visuals.bg_fill,\n                        visuals.fg_stroke,\n                        epaint::StrokeKind::Inside,\n                    );\n                }\n            }\n        }\n    }\n\n    fn marker_center(&self, position_1d: f32, rail_rect: &Rect) -> Pos2 {\n        match self.orientation {\n            SliderOrientation::Horizontal => pos2(position_1d, rail_rect.center().y),\n            SliderOrientation::Vertical => pos2(rail_rect.center().x, position_1d),\n        }\n    }\n\n    fn pointer_position(&self, pointer_position_2d: Pos2) -> f32 {\n        match self.orientation {\n            SliderOrientation::Horizontal => pointer_position_2d.x,\n            SliderOrientation::Vertical => pointer_position_2d.y,\n        }\n    }\n\n    fn position_range(&self, rect: &Rect, handle_shape: &style::HandleShape) -> Rangef {\n        let handle_radius = self.handle_radius(rect);\n        let handle_radius = match handle_shape {\n            style::HandleShape::Circle => handle_radius,\n            style::HandleShape::Rect { aspect_ratio } => handle_radius * aspect_ratio,\n        };\n        match self.orientation {\n            SliderOrientation::Horizontal => rect.x_range().shrink(handle_radius),\n            // The vertical case has to be flipped because the largest slider value maps to the\n            // lowest y value (which is at the top)\n            SliderOrientation::Vertical => rect.y_range().shrink(handle_radius).flip(),\n        }\n    }\n\n    fn rail_rect(&self, rect: &Rect, radius: f32) -> Rect {\n        match self.orientation {\n            SliderOrientation::Horizontal => Rect::from_min_max(\n                pos2(rect.left(), rect.center().y - radius),\n                pos2(rect.right(), rect.center().y + radius),\n            ),\n            SliderOrientation::Vertical => Rect::from_min_max(\n                pos2(rect.center().x - radius, rect.top()),\n                pos2(rect.center().x + radius, rect.bottom()),\n            ),\n        }\n    }\n\n    fn handle_radius(&self, rect: &Rect) -> f32 {\n        let limit = match self.orientation {\n            SliderOrientation::Horizontal => rect.height(),\n            SliderOrientation::Vertical => rect.width(),\n        };\n        limit / 2.5\n    }\n\n    fn value_ui(&mut self, ui: &mut Ui, position_range: Rangef) -> Response {\n        // If [`DragValue`] is controlled from the keyboard and `step` is defined, set speed to `step`\n        let change = ui.input(|input| {\n            input.num_presses(Key::ArrowUp) as i32 + input.num_presses(Key::ArrowRight) as i32\n                - input.num_presses(Key::ArrowDown) as i32\n                - input.num_presses(Key::ArrowLeft) as i32\n        });\n\n        let any_change = change != 0;\n        let speed = if let (Some(step), true) = (self.step, any_change) {\n            // If [`DragValue`] is controlled from the keyboard and `step` is defined, set speed to `step`\n            step\n        } else {\n            self.drag_value_speed\n                .unwrap_or_else(|| self.current_gradient(position_range))\n        };\n\n        let mut value = self.get_value();\n        let response = ui.add({\n            let mut dv = DragValue::new(&mut value)\n                .speed(speed)\n                .min_decimals(self.min_decimals)\n                .max_decimals_opt(self.max_decimals)\n                .suffix(self.suffix.clone())\n                .prefix(self.prefix.clone())\n                .update_while_editing(self.update_while_editing);\n\n            match self.clamping {\n                SliderClamping::Never => {}\n                SliderClamping::Edits => {\n                    dv = dv.range(self.range.clone()).clamp_existing_to_range(false);\n                }\n                SliderClamping::Always => {\n                    dv = dv.range(self.range.clone()).clamp_existing_to_range(true);\n                }\n            }\n\n            if let Some(fmt) = &self.custom_formatter {\n                dv = dv.custom_formatter(fmt);\n            }\n            if let Some(parser) = &self.custom_parser {\n                dv = dv.custom_parser(parser);\n            }\n            dv\n        });\n        if value != self.get_value() {\n            self.set_value(value);\n        }\n        response\n    }\n\n    /// delta(value) / delta(points)\n    fn current_gradient(&mut self, position_range: Rangef) -> f64 {\n        // TODO(emilk): handle clamping\n        let value = self.get_value();\n        let value_from_pos = |position: f32| self.value_from_position(position, position_range);\n        let pos_from_value = |value: f64| self.position_from_value(value, position_range);\n        let left_value = value_from_pos(pos_from_value(value) - 0.5);\n        let right_value = value_from_pos(pos_from_value(value) + 0.5);\n        right_value - left_value\n    }\n\n    fn add_contents(&mut self, ui: &mut Ui) -> Response {\n        let old_value = self.get_value();\n\n        if self.clamping == SliderClamping::Always {\n            self.set_value(old_value);\n        }\n\n        let thickness = ui\n            .text_style_height(&TextStyle::Body)\n            .at_least(ui.spacing().interact_size.y);\n        let mut response = self.allocate_slider_space(ui, thickness);\n        self.slider_ui(ui, &response);\n\n        let value = self.get_value();\n        if value != old_value {\n            response.mark_changed();\n        }\n        response.widget_info(|| WidgetInfo::slider(ui.is_enabled(), value, self.text.text()));\n\n        ui.ctx().accesskit_node_builder(response.id, |builder| {\n            use accesskit::Action;\n            builder.set_min_numeric_value(*self.range.start());\n            builder.set_max_numeric_value(*self.range.end());\n            if let Some(step) = self.step {\n                builder.set_numeric_value_step(step);\n            }\n            builder.add_action(Action::SetValue);\n\n            let clamp_range = if self.clamping == SliderClamping::Never {\n                f64::NEG_INFINITY..=f64::INFINITY\n            } else {\n                self.range()\n            };\n            if value < *clamp_range.end() {\n                builder.add_action(Action::Increment);\n            }\n            if value > *clamp_range.start() {\n                builder.add_action(Action::Decrement);\n            }\n        });\n\n        let slider_response = response.clone();\n\n        let value_response = if self.show_value {\n            let handle_shape = self\n                .handle_shape\n                .unwrap_or_else(|| ui.style().visuals.handle_shape);\n            let position_range = self.position_range(&response.rect, &handle_shape);\n            let value_response = self.value_ui(ui, position_range);\n            if value_response.gained_focus()\n                || value_response.has_focus()\n                || value_response.lost_focus()\n            {\n                // Use the [`DragValue`] id as the id of the whole widget,\n                // so that the focus events work as expected.\n                response = value_response.union(response);\n            } else {\n                // Use the slider id as the id for the whole widget\n                response = response.union(value_response.clone());\n            }\n            Some(value_response)\n        } else {\n            None\n        };\n\n        if !self.text.is_empty() {\n            let label_response =\n                ui.add(Label::new(self.text.clone()).wrap_mode(TextWrapMode::Extend));\n            // The slider already has an accessibility label via widget info,\n            // but sometimes it's useful for a screen reader to know\n            // that a piece of text is a label for another widget,\n            // e.g. so the text itself can be excluded from navigation.\n            slider_response.labelled_by(label_response.id);\n            if let Some(value_response) = value_response {\n                value_response.labelled_by(label_response.id);\n            }\n        }\n\n        response\n    }\n}\n\nimpl Widget for Slider<'_> {\n    fn ui(mut self, ui: &mut Ui) -> Response {\n        let inner_response = match self.orientation {\n            SliderOrientation::Horizontal => ui.horizontal(|ui| self.add_contents(ui)),\n            SliderOrientation::Vertical => ui.vertical(|ui| self.add_contents(ui)),\n        };\n\n        inner_response.inner | inner_response.response\n    }\n}\n\n// ----------------------------------------------------------------------------\n// Helpers for converting slider range to/from normalized [0-1] range.\n// Always clamps.\n// Logarithmic sliders are allowed to include zero and infinity,\n// even though mathematically it doesn't make sense.\n\nconst INFINITY: f64 = f64::INFINITY;\n\n/// When the user asks for an infinitely large range (e.g. logarithmic from zero),\n/// give a scale that this many orders of magnitude in size.\nconst INF_RANGE_MAGNITUDE: f64 = 10.0;\n\nfn value_from_normalized(normalized: f64, range: RangeInclusive<f64>, spec: &SliderSpec) -> f64 {\n    let (min, max) = (*range.start(), *range.end());\n\n    if min.is_nan() || max.is_nan() {\n        f64::NAN\n    } else if min == max {\n        min\n    } else if min > max {\n        value_from_normalized(1.0 - normalized, max..=min, spec)\n    } else if normalized <= 0.0 {\n        min\n    } else if normalized >= 1.0 {\n        max\n    } else if spec.logarithmic {\n        if max <= 0.0 {\n            // non-positive range\n            -value_from_normalized(normalized, -min..=-max, spec)\n        } else if 0.0 <= min {\n            let (min_log, max_log) = range_log10(min, max, spec);\n            let log = lerp(min_log..=max_log, normalized);\n            10.0_f64.powf(log)\n        } else {\n            assert!(\n                min < 0.0 && 0.0 < max,\n                \"min should be negative and max positive, but got min={min} and max={max}\"\n            );\n            let zero_cutoff = logarithmic_zero_cutoff(min, max);\n            if normalized < zero_cutoff {\n                // negative\n                value_from_normalized(\n                    remap(normalized, 0.0..=zero_cutoff, 0.0..=1.0),\n                    min..=0.0,\n                    spec,\n                )\n            } else {\n                // positive\n                value_from_normalized(\n                    remap(normalized, zero_cutoff..=1.0, 0.0..=1.0),\n                    0.0..=max,\n                    spec,\n                )\n            }\n        }\n    } else {\n        debug_assert!(\n            min.is_finite() && max.is_finite(),\n            \"You should use a logarithmic range\"\n        );\n        lerp(range, normalized.clamp(0.0, 1.0))\n    }\n}\n\nfn normalized_from_value(value: f64, range: RangeInclusive<f64>, spec: &SliderSpec) -> f64 {\n    let (min, max) = (*range.start(), *range.end());\n\n    if min.is_nan() || max.is_nan() {\n        f64::NAN\n    } else if min == max {\n        0.5 // empty range, show center of slider\n    } else if min > max {\n        1.0 - normalized_from_value(value, max..=min, spec)\n    } else if value <= min {\n        0.0\n    } else if value >= max {\n        1.0\n    } else if spec.logarithmic {\n        if max <= 0.0 {\n            // non-positive range\n            normalized_from_value(-value, -min..=-max, spec)\n        } else if 0.0 <= min {\n            let (min_log, max_log) = range_log10(min, max, spec);\n            let value_log = value.log10();\n            remap_clamp(value_log, min_log..=max_log, 0.0..=1.0)\n        } else {\n            assert!(\n                min < 0.0 && 0.0 < max,\n                \"min should be negative and max positive, but got min={min} and max={max}\"\n            );\n            let zero_cutoff = logarithmic_zero_cutoff(min, max);\n            if value < 0.0 {\n                // negative\n                remap(\n                    normalized_from_value(value, min..=0.0, spec),\n                    0.0..=1.0,\n                    0.0..=zero_cutoff,\n                )\n            } else {\n                // positive side\n                remap(\n                    normalized_from_value(value, 0.0..=max, spec),\n                    0.0..=1.0,\n                    zero_cutoff..=1.0,\n                )\n            }\n        }\n    } else {\n        debug_assert!(\n            min.is_finite() && max.is_finite(),\n            \"You should use a logarithmic range\"\n        );\n        remap_clamp(value, range, 0.0..=1.0)\n    }\n}\n\nfn range_log10(min: f64, max: f64, spec: &SliderSpec) -> (f64, f64) {\n    assert!(spec.logarithmic, \"spec must be logarithmic\");\n    assert!(\n        min <= max,\n        \"min must be less than or equal to max, but was min={min} and max={max}\"\n    );\n\n    if min == 0.0 && max == INFINITY {\n        (spec.smallest_positive.log10(), INF_RANGE_MAGNITUDE)\n    } else if min == 0.0 {\n        if spec.smallest_positive < max {\n            (spec.smallest_positive.log10(), max.log10())\n        } else {\n            (max.log10() - INF_RANGE_MAGNITUDE, max.log10())\n        }\n    } else if max == INFINITY {\n        if min < spec.largest_finite {\n            (min.log10(), spec.largest_finite.log10())\n        } else {\n            (min.log10(), min.log10() + INF_RANGE_MAGNITUDE)\n        }\n    } else {\n        (min.log10(), max.log10())\n    }\n}\n\n/// where to put the zero cutoff for logarithmic sliders\n/// that crosses zero ?\nfn logarithmic_zero_cutoff(min: f64, max: f64) -> f64 {\n    assert!(\n        min < 0.0 && 0.0 < max,\n        \"min must be negative and max positive, but got min={min} and max={max}\"\n    );\n\n    let min_magnitude = if min == -INFINITY {\n        INF_RANGE_MAGNITUDE\n    } else {\n        min.abs().log10().abs()\n    };\n    let max_magnitude = if max == INFINITY {\n        INF_RANGE_MAGNITUDE\n    } else {\n        max.log10().abs()\n    };\n\n    let cutoff = min_magnitude / (min_magnitude + max_magnitude);\n    debug_assert!(\n        0.0 <= cutoff && cutoff <= 1.0,\n        \"Bad cutoff {cutoff:?} for min {min:?} and max {max:?}\"\n    );\n    cutoff\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/spinner.rs",
    "content": "use epaint::{Color32, Pos2, Rect, Shape, Stroke, emath::lerp, vec2};\n\nuse crate::{Response, Sense, Ui, Widget, WidgetInfo, WidgetType};\n\n/// A spinner widget used to indicate loading.\n///\n/// See also: [`crate::ProgressBar`].\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\n#[derive(Default)]\npub struct Spinner {\n    /// Uses the style's `interact_size` if `None`.\n    size: Option<f32>,\n    color: Option<Color32>,\n}\n\nimpl Spinner {\n    /// Create a new spinner that uses the style's `interact_size` unless changed.\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Sets the spinner's size. The size sets both the height and width, as the spinner is always\n    /// square. If the size isn't set explicitly, the active style's `interact_size` is used.\n    #[inline]\n    pub fn size(mut self, size: f32) -> Self {\n        self.size = Some(size);\n        self\n    }\n\n    /// Sets the spinner's color.\n    #[inline]\n    pub fn color(mut self, color: impl Into<Color32>) -> Self {\n        self.color = Some(color.into());\n        self\n    }\n\n    /// Paint the spinner in the given rectangle.\n    pub fn paint_at(&self, ui: &Ui, rect: Rect) {\n        if ui.is_rect_visible(rect) {\n            ui.request_repaint(); // because it is animated\n\n            let color = self\n                .color\n                .unwrap_or_else(|| ui.visuals().strong_text_color());\n            let radius = (rect.height().min(rect.width()) / 2.0) - 2.0;\n            let n_points = (radius.round() as u32).clamp(8, 128);\n            let time = ui.input(|i| i.time);\n            let start_angle = time * std::f64::consts::TAU;\n            let end_angle = start_angle + 240f64.to_radians() * time.sin();\n            let points: Vec<Pos2> = (0..n_points)\n                .map(|i| {\n                    let angle = lerp(start_angle..=end_angle, i as f64 / n_points as f64);\n                    let (sin, cos) = angle.sin_cos();\n                    rect.center() + radius * vec2(cos as f32, sin as f32)\n                })\n                .collect();\n            ui.painter()\n                .add(Shape::line(points, Stroke::new(3.0, color)));\n        }\n    }\n}\n\nimpl Widget for Spinner {\n    fn ui(self, ui: &mut Ui) -> Response {\n        let size = self\n            .size\n            .unwrap_or_else(|| ui.style().spacing.interact_size.y);\n        let (rect, response) = ui.allocate_exact_size(vec2(size, size), Sense::hover());\n        response.widget_info(|| WidgetInfo::new(WidgetType::ProgressIndicator));\n        self.paint_at(ui, rect);\n\n        response\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/text_edit/builder.rs",
    "content": "use std::sync::Arc;\n\nuse emath::{Rect, TSTransform};\nuse epaint::{\n    StrokeKind,\n    text::{Galley, LayoutJob, cursor::CCursor},\n};\n\nuse crate::{\n    Align, Align2, Color32, Context, CursorIcon, Event, EventFilter, FontSelection, Id, ImeEvent,\n    Key, KeyboardShortcut, Margin, Modifiers, NumExt as _, Response, Sense, Shape, TextBuffer,\n    TextStyle, TextWrapMode, Ui, Vec2, Widget, WidgetInfo, WidgetText, WidgetWithState, epaint,\n    os::OperatingSystem,\n    output::OutputEvent,\n    response, text_selection,\n    text_selection::{CCursorRange, text_cursor_state::cursor_rect, visuals::paint_text_selection},\n    vec2,\n};\n\nuse super::{TextEditOutput, TextEditState};\n\ntype LayouterFn<'t> = &'t mut dyn FnMut(&Ui, &dyn TextBuffer, f32) -> Arc<Galley>;\n\n/// A text region that the user can edit the contents of.\n///\n/// See also [`Ui::text_edit_singleline`] and [`Ui::text_edit_multiline`].\n///\n/// Example:\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # let mut my_string = String::new();\n/// let response = ui.add(egui::TextEdit::singleline(&mut my_string));\n/// if response.changed() {\n///     // …\n/// }\n/// if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {\n///     // …\n/// }\n/// # });\n/// ```\n///\n/// To fill an [`Ui`] with a [`TextEdit`] use [`Ui::add_sized`]:\n///\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # let mut my_string = String::new();\n/// ui.add_sized(ui.available_size(), egui::TextEdit::multiline(&mut my_string));\n/// # });\n/// ```\n///\n///\n/// You can also use [`TextEdit`] to show text that can be selected, but not edited.\n/// To do so, pass in a `&mut` reference to a `&str`, for instance:\n///\n/// ```\n/// fn selectable_text(ui: &mut egui::Ui, mut text: &str) {\n///     ui.add(egui::TextEdit::multiline(&mut text));\n/// }\n/// ```\n///\n/// ## Advanced usage\n/// See [`TextEdit::show`].\n///\n/// ## Other\n/// The background color of a [`crate::TextEdit`] is [`crate::Visuals::text_edit_bg_color`] or can be set with [`crate::TextEdit::background_color`].\n#[must_use = \"You should put this widget in a ui with `ui.add(widget);`\"]\npub struct TextEdit<'t> {\n    text: &'t mut dyn TextBuffer,\n    hint_text: WidgetText,\n    hint_text_font: Option<FontSelection>,\n    id: Option<Id>,\n    id_salt: Option<Id>,\n    font_selection: FontSelection,\n    text_color: Option<Color32>,\n    layouter: Option<LayouterFn<'t>>,\n    password: bool,\n    frame: bool,\n    margin: Margin,\n    multiline: bool,\n    interactive: bool,\n    desired_width: Option<f32>,\n    desired_height_rows: usize,\n    event_filter: EventFilter,\n    cursor_at_end: bool,\n    min_size: Vec2,\n    align: Align2,\n    clip_text: bool,\n    char_limit: usize,\n    return_key: Option<KeyboardShortcut>,\n    background_color: Option<Color32>,\n}\n\nimpl WidgetWithState for TextEdit<'_> {\n    type State = TextEditState;\n}\n\nimpl TextEdit<'_> {\n    pub fn load_state(ctx: &Context, id: Id) -> Option<TextEditState> {\n        TextEditState::load(ctx, id)\n    }\n\n    pub fn store_state(ctx: &Context, id: Id, state: TextEditState) {\n        state.store(ctx, id);\n    }\n}\n\nimpl<'t> TextEdit<'t> {\n    /// No newlines (`\\n`) allowed. Pressing enter key will result in the [`TextEdit`] losing focus (`response.lost_focus`).\n    pub fn singleline(text: &'t mut dyn TextBuffer) -> Self {\n        Self {\n            desired_height_rows: 1,\n            multiline: false,\n            clip_text: true,\n            ..Self::multiline(text)\n        }\n    }\n\n    /// A [`TextEdit`] for multiple lines. Pressing enter key will create a new line by default (can be changed with [`return_key`](TextEdit::return_key)).\n    pub fn multiline(text: &'t mut dyn TextBuffer) -> Self {\n        Self {\n            text,\n            hint_text: Default::default(),\n            hint_text_font: None,\n            id: None,\n            id_salt: None,\n            font_selection: Default::default(),\n            text_color: None,\n            layouter: None,\n            password: false,\n            frame: true,\n            margin: Margin::symmetric(4, 2),\n            multiline: true,\n            interactive: true,\n            desired_width: None,\n            desired_height_rows: 4,\n            event_filter: EventFilter {\n                // moving the cursor is really important\n                horizontal_arrows: true,\n                vertical_arrows: true,\n                tab: false, // tab is used to change focus, not to insert a tab character\n                ..Default::default()\n            },\n            cursor_at_end: true,\n            min_size: Vec2::ZERO,\n            align: Align2::LEFT_TOP,\n            clip_text: false,\n            char_limit: usize::MAX,\n            return_key: Some(KeyboardShortcut::new(Modifiers::NONE, Key::Enter)),\n            background_color: None,\n        }\n    }\n\n    /// Build a [`TextEdit`] focused on code editing.\n    /// By default it comes with:\n    /// - monospaced font\n    /// - focus lock (tab will insert a tab character instead of moving focus)\n    pub fn code_editor(self) -> Self {\n        self.font(TextStyle::Monospace).lock_focus(true)\n    }\n\n    /// Use if you want to set an explicit [`Id`] for this widget.\n    #[inline]\n    pub fn id(mut self, id: Id) -> Self {\n        self.id = Some(id);\n        self\n    }\n\n    /// A source for the unique [`Id`], e.g. `.id_source(\"second_text_edit_field\")` or `.id_source(loop_index)`.\n    #[inline]\n    pub fn id_source(self, id_salt: impl std::hash::Hash) -> Self {\n        self.id_salt(id_salt)\n    }\n\n    /// A source for the unique [`Id`], e.g. `.id_salt(\"second_text_edit_field\")` or `.id_salt(loop_index)`.\n    #[inline]\n    pub fn id_salt(mut self, id_salt: impl std::hash::Hash) -> Self {\n        self.id_salt = Some(Id::new(id_salt));\n        self\n    }\n\n    /// Show a faint hint text when the text field is empty.\n    ///\n    /// If the hint text needs to be persisted even when the text field has input,\n    /// the following workaround can be used:\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_string = String::new();\n    /// # use egui::{ Color32, FontId };\n    /// let text_edit = egui::TextEdit::multiline(&mut my_string)\n    ///     .desired_width(f32::INFINITY);\n    /// let output = text_edit.show(ui);\n    /// let painter = ui.painter_at(output.response.rect);\n    /// let text_color = Color32::from_rgba_premultiplied(100, 100, 100, 100);\n    /// let galley = painter.layout(\n    ///     String::from(\"Enter text\"),\n    ///     FontId::default(),\n    ///     text_color,\n    ///     f32::INFINITY\n    /// );\n    /// painter.galley(output.galley_pos, galley, text_color);\n    /// # });\n    /// ```\n    #[inline]\n    pub fn hint_text(mut self, hint_text: impl Into<WidgetText>) -> Self {\n        self.hint_text = hint_text.into();\n        self\n    }\n\n    /// Set the background color of the [`TextEdit`]. The default is [`crate::Visuals::text_edit_bg_color`].\n    // TODO(bircni): remove this once #3284 is implemented\n    #[inline]\n    pub fn background_color(mut self, color: Color32) -> Self {\n        self.background_color = Some(color);\n        self\n    }\n\n    /// Set a specific style for the hint text.\n    #[inline]\n    pub fn hint_text_font(mut self, hint_text_font: impl Into<FontSelection>) -> Self {\n        self.hint_text_font = Some(hint_text_font.into());\n        self\n    }\n\n    /// If true, hide the letters from view and prevent copying from the field.\n    #[inline]\n    pub fn password(mut self, password: bool) -> Self {\n        self.password = password;\n        self\n    }\n\n    /// Pick a [`crate::FontId`] or [`TextStyle`].\n    #[inline]\n    pub fn font(mut self, font_selection: impl Into<FontSelection>) -> Self {\n        self.font_selection = font_selection.into();\n        self\n    }\n\n    #[inline]\n    pub fn text_color(mut self, text_color: Color32) -> Self {\n        self.text_color = Some(text_color);\n        self\n    }\n\n    #[inline]\n    pub fn text_color_opt(mut self, text_color: Option<Color32>) -> Self {\n        self.text_color = text_color;\n        self\n    }\n\n    /// Override how text is being shown inside the [`TextEdit`].\n    ///\n    /// This can be used to implement things like syntax highlighting.\n    ///\n    /// This function will be called at least once per frame,\n    /// so it is strongly suggested that you cache the results of any syntax highlighter\n    /// so as not to waste CPU highlighting the same string every frame.\n    ///\n    /// The arguments is the enclosing [`Ui`] (so you can access e.g. [`Context::fonts`]),\n    /// the text and the wrap width.\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_code = String::new();\n    /// # fn my_memoized_highlighter(s: &str) -> egui::text::LayoutJob { Default::default() }\n    /// let mut layouter = |ui: &egui::Ui, buf: &dyn egui::TextBuffer, wrap_width: f32| {\n    ///     let mut layout_job: egui::text::LayoutJob = my_memoized_highlighter(buf.as_str());\n    ///     layout_job.wrap.max_width = wrap_width;\n    ///     ui.fonts_mut(|f| f.layout_job(layout_job))\n    /// };\n    /// ui.add(egui::TextEdit::multiline(&mut my_code).layouter(&mut layouter));\n    /// # });\n    /// ```\n    #[inline]\n    pub fn layouter(\n        mut self,\n        layouter: &'t mut dyn FnMut(&Ui, &dyn TextBuffer, f32) -> Arc<Galley>,\n    ) -> Self {\n        self.layouter = Some(layouter);\n\n        self\n    }\n\n    /// Default is `true`. If set to `false` then you cannot interact with the text (neither edit or select it).\n    ///\n    /// Consider using [`Ui::add_enabled`] instead to also give the [`TextEdit`] a greyed out look.\n    #[inline]\n    pub fn interactive(mut self, interactive: bool) -> Self {\n        self.interactive = interactive;\n        self\n    }\n\n    /// Default is `true`. If set to `false` there will be no frame showing that this is editable text!\n    #[inline]\n    pub fn frame(mut self, frame: bool) -> Self {\n        self.frame = frame;\n        self\n    }\n\n    /// Set margin of text. Default is `Margin::symmetric(4.0, 2.0)`\n    #[inline]\n    pub fn margin(mut self, margin: impl Into<Margin>) -> Self {\n        self.margin = margin.into();\n        self\n    }\n\n    /// Set to 0.0 to keep as small as possible.\n    /// Set to [`f32::INFINITY`] to take up all available space (i.e. disable automatic word wrap).\n    #[inline]\n    pub fn desired_width(mut self, desired_width: f32) -> Self {\n        self.desired_width = Some(desired_width);\n        self\n    }\n\n    /// Set the number of rows to show by default.\n    /// The default for singleline text is `1`.\n    /// The default for multiline text is `4`.\n    #[inline]\n    pub fn desired_rows(mut self, desired_height_rows: usize) -> Self {\n        self.desired_height_rows = desired_height_rows;\n        self\n    }\n\n    /// When `false` (default), pressing TAB will move focus\n    /// to the next widget.\n    ///\n    /// When `true`, the widget will keep the focus and pressing TAB\n    /// will insert the `'\\t'` character.\n    #[inline]\n    pub fn lock_focus(mut self, tab_will_indent: bool) -> Self {\n        self.event_filter.tab = tab_will_indent;\n        self\n    }\n\n    /// When `true` (default), the cursor will initially be placed at the end of the text.\n    ///\n    /// When `false`, the cursor will initially be placed at the beginning of the text.\n    #[inline]\n    pub fn cursor_at_end(mut self, b: bool) -> Self {\n        self.cursor_at_end = b;\n        self\n    }\n\n    /// When `true` (default), overflowing text will be clipped.\n    ///\n    /// When `false`, widget width will expand to make all text visible.\n    ///\n    /// This only works for singleline [`TextEdit`].\n    #[inline]\n    pub fn clip_text(mut self, b: bool) -> Self {\n        // always show everything in multiline\n        if !self.multiline {\n            self.clip_text = b;\n        }\n        self\n    }\n\n    /// Sets the limit for the amount of characters can be entered\n    ///\n    /// This only works for singleline [`TextEdit`]\n    #[inline]\n    pub fn char_limit(mut self, limit: usize) -> Self {\n        self.char_limit = limit;\n        self\n    }\n\n    /// Set the horizontal align of the inner text.\n    #[inline]\n    pub fn horizontal_align(mut self, align: Align) -> Self {\n        self.align.0[0] = align;\n        self\n    }\n\n    /// Set the vertical align of the inner text.\n    #[inline]\n    pub fn vertical_align(mut self, align: Align) -> Self {\n        self.align.0[1] = align;\n        self\n    }\n\n    /// Set the minimum size of the [`TextEdit`].\n    #[inline]\n    pub fn min_size(mut self, min_size: Vec2) -> Self {\n        self.min_size = min_size;\n        self\n    }\n\n    /// Set the return key combination.\n    ///\n    /// This combination will cause a newline on multiline,\n    /// whereas on singleline it will cause the widget to lose focus.\n    ///\n    /// This combination is optional and can be disabled by passing [`None`] into this function.\n    #[inline]\n    pub fn return_key(mut self, return_key: impl Into<Option<KeyboardShortcut>>) -> Self {\n        self.return_key = return_key.into();\n        self\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nimpl Widget for TextEdit<'_> {\n    fn ui(self, ui: &mut Ui) -> Response {\n        self.show(ui).response\n    }\n}\n\nimpl TextEdit<'_> {\n    /// Show the [`TextEdit`], returning a rich [`TextEditOutput`].\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// # let mut my_string = String::new();\n    /// let output = egui::TextEdit::singleline(&mut my_string).show(ui);\n    /// if let Some(text_cursor_range) = output.cursor_range {\n    ///     use egui::TextBuffer as _;\n    ///     let selected_chars = text_cursor_range.as_sorted_char_range();\n    ///     let selected_text = my_string.char_range(selected_chars);\n    ///     ui.label(\"Selected text: \");\n    ///     ui.monospace(selected_text);\n    /// }\n    /// # });\n    /// ```\n    pub fn show(self, ui: &mut Ui) -> TextEditOutput {\n        let is_mutable = self.text.is_mutable();\n        let frame = self.frame;\n        let where_to_put_background = ui.painter().add(Shape::Noop);\n        let background_color = self\n            .background_color\n            .unwrap_or_else(|| ui.visuals().text_edit_bg_color());\n        let output = self.show_content(ui);\n\n        if frame {\n            let visuals = ui.style().interact(&output.response);\n            let frame_rect = output.response.rect.expand(visuals.expansion);\n            let shape = if is_mutable {\n                if output.response.has_focus() {\n                    epaint::RectShape::new(\n                        frame_rect,\n                        visuals.corner_radius,\n                        background_color,\n                        ui.visuals().selection.stroke,\n                        StrokeKind::Inside,\n                    )\n                } else {\n                    epaint::RectShape::new(\n                        frame_rect,\n                        visuals.corner_radius,\n                        background_color,\n                        visuals.bg_stroke, // TODO(emilk): we want to show something here, or a text-edit field doesn't \"pop\".\n                        StrokeKind::Inside,\n                    )\n                }\n            } else {\n                let visuals = &ui.style().visuals.widgets.inactive;\n                epaint::RectShape::stroke(\n                    frame_rect,\n                    visuals.corner_radius,\n                    visuals.bg_stroke, // TODO(emilk): we want to show something here, or a text-edit field doesn't \"pop\".\n                    StrokeKind::Inside,\n                )\n            };\n\n            ui.painter().set(where_to_put_background, shape);\n        }\n\n        output\n    }\n\n    fn show_content(self, ui: &mut Ui) -> TextEditOutput {\n        let TextEdit {\n            text,\n            hint_text,\n            hint_text_font,\n            id,\n            id_salt,\n            font_selection,\n            text_color,\n            layouter,\n            password,\n            frame: _,\n            margin,\n            multiline,\n            interactive,\n            desired_width,\n            desired_height_rows,\n            event_filter,\n            cursor_at_end,\n            min_size,\n            align,\n            clip_text,\n            char_limit,\n            return_key,\n            background_color: _,\n        } = self;\n\n        let text_color = text_color\n            .or_else(|| ui.visuals().override_text_color)\n            // .unwrap_or_else(|| ui.style().interact(&response).text_color()); // too bright\n            .unwrap_or_else(|| ui.visuals().widgets.inactive.text_color());\n\n        let prev_text = text.as_str().to_owned();\n        let hint_text_str = hint_text.text().to_owned();\n\n        let font_id = font_selection.resolve(ui.style());\n        let row_height = ui.fonts_mut(|f| f.row_height(&font_id));\n        const MIN_WIDTH: f32 = 24.0; // Never make a [`TextEdit`] more narrow than this.\n        let available_width = (ui.available_width() - margin.sum().x).at_least(MIN_WIDTH);\n        let desired_width = desired_width.unwrap_or_else(|| ui.spacing().text_edit_width);\n        let wrap_width = if ui.layout().horizontal_justify() {\n            available_width\n        } else {\n            desired_width.min(available_width)\n        };\n\n        let font_id_clone = font_id.clone();\n        let mut default_layouter = move |ui: &Ui, text: &dyn TextBuffer, wrap_width: f32| {\n            let text = mask_if_password(password, text.as_str());\n            let layout_job = if multiline {\n                LayoutJob::simple(text, font_id_clone.clone(), text_color, wrap_width)\n            } else {\n                LayoutJob::simple_singleline(text, font_id_clone.clone(), text_color)\n            };\n            ui.fonts_mut(|f| f.layout_job(layout_job))\n        };\n\n        let layouter = layouter.unwrap_or(&mut default_layouter);\n\n        let mut galley = layouter(ui, text, wrap_width);\n\n        let desired_inner_width = if clip_text {\n            wrap_width // visual clipping with scroll in singleline input.\n        } else {\n            galley.size().x.max(wrap_width)\n        };\n        let desired_height = (desired_height_rows.at_least(1) as f32) * row_height;\n        let desired_inner_size = vec2(desired_inner_width, galley.size().y.max(desired_height));\n        let desired_outer_size = (desired_inner_size + margin.sum()).at_least(min_size);\n        let (auto_id, outer_rect) = ui.allocate_space(desired_outer_size);\n        let rect = outer_rect - margin; // inner rect (excluding frame/margin).\n\n        let id = id.unwrap_or_else(|| {\n            if let Some(id_salt) = id_salt {\n                ui.make_persistent_id(id_salt)\n            } else {\n                auto_id // Since we are only storing the cursor a persistent Id is not super important\n            }\n        });\n        let mut state = TextEditState::load(ui.ctx(), id).unwrap_or_default();\n\n        // On touch screens (e.g. mobile in `eframe` web), should\n        // dragging select text, or scroll the enclosing [`ScrollArea`] (if any)?\n        // Since currently copying selected text in not supported on `eframe` web,\n        // we prioritize touch-scrolling:\n        let allow_drag_to_select =\n            ui.input(|i| !i.has_touch_screen()) || ui.memory(|mem| mem.has_focus(id));\n\n        let sense = if interactive {\n            if allow_drag_to_select {\n                Sense::click_and_drag()\n            } else {\n                Sense::click()\n            }\n        } else {\n            Sense::hover()\n        };\n        let mut response = ui.interact(outer_rect, id, sense);\n        response.intrinsic_size = Some(Vec2::new(desired_width, desired_outer_size.y));\n\n        // Don't sent `OutputEvent::Clicked` when a user presses the space bar\n        response.flags -= response::Flags::FAKE_PRIMARY_CLICKED;\n        let text_clip_rect = rect;\n        let painter = ui.painter_at(text_clip_rect.expand(1.0)); // expand to avoid clipping cursor\n\n        if interactive && let Some(pointer_pos) = response.interact_pointer_pos() {\n            if response.hovered() && text.is_mutable() {\n                ui.output_mut(|o| o.mutable_text_under_cursor = true);\n            }\n\n            // TODO(emilk): drag selected text to either move or clone (ctrl on windows, alt on mac)\n\n            let cursor_at_pointer =\n                galley.cursor_from_pos(pointer_pos - rect.min + state.text_offset);\n\n            if ui.visuals().text_cursor.preview\n                && response.hovered()\n                && ui.input(|i| i.pointer.is_moving())\n            {\n                // text cursor preview:\n                let cursor_rect = TSTransform::from_translation(rect.min.to_vec2())\n                    * cursor_rect(&galley, &cursor_at_pointer, row_height);\n                text_selection::visuals::paint_cursor_end(&painter, ui.visuals(), cursor_rect);\n            }\n\n            let is_being_dragged = ui.ctx().is_being_dragged(response.id);\n            let did_interact = state.cursor.pointer_interaction(\n                ui,\n                &response,\n                cursor_at_pointer,\n                &galley,\n                is_being_dragged,\n            );\n\n            if did_interact || response.clicked() {\n                ui.memory_mut(|mem| mem.request_focus(response.id));\n\n                state.last_interaction_time = ui.input(|i| i.time);\n            }\n        }\n\n        if interactive && response.hovered() {\n            ui.set_cursor_icon(CursorIcon::Text);\n        }\n\n        let mut cursor_range = None;\n        let prev_cursor_range = state.cursor.range(&galley);\n        if interactive && ui.memory(|mem| mem.has_focus(id)) {\n            ui.memory_mut(|mem| mem.set_focus_lock_filter(id, event_filter));\n\n            let default_cursor_range = if cursor_at_end {\n                CCursorRange::one(galley.end())\n            } else {\n                CCursorRange::default()\n            };\n\n            let (changed, new_cursor_range) = events(\n                ui,\n                &mut state,\n                text,\n                &mut galley,\n                layouter,\n                id,\n                wrap_width,\n                multiline,\n                password,\n                default_cursor_range,\n                char_limit,\n                event_filter,\n                return_key,\n            );\n\n            if changed {\n                response.mark_changed();\n            }\n            cursor_range = Some(new_cursor_range);\n        }\n\n        let mut galley_pos = align\n            .align_size_within_rect(galley.size(), rect)\n            .intersect(rect) // limit pos to the response rect area\n            .min;\n        let align_offset = rect.left_top() - galley_pos;\n\n        // Visual clipping for singleline text editor with text larger than width\n        if clip_text && align_offset.x == 0.0 {\n            let cursor_pos = match (cursor_range, ui.memory(|mem| mem.has_focus(id))) {\n                (Some(cursor_range), true) => galley.pos_from_cursor(cursor_range.primary).min.x,\n                _ => 0.0,\n            };\n\n            let mut offset_x = state.text_offset.x;\n            let visible_range = offset_x..=offset_x + desired_inner_size.x;\n\n            if !visible_range.contains(&cursor_pos) {\n                if cursor_pos < *visible_range.start() {\n                    offset_x = cursor_pos;\n                } else {\n                    offset_x = cursor_pos - desired_inner_size.x;\n                }\n            }\n\n            offset_x = offset_x\n                .at_most(galley.size().x - desired_inner_size.x)\n                .at_least(0.0);\n\n            state.text_offset = vec2(offset_x, align_offset.y);\n            galley_pos -= vec2(offset_x, 0.0);\n        } else {\n            state.text_offset = align_offset;\n        }\n\n        let selection_changed = if let (Some(cursor_range), Some(prev_cursor_range)) =\n            (cursor_range, prev_cursor_range)\n        {\n            prev_cursor_range != cursor_range\n        } else {\n            false\n        };\n\n        if ui.is_rect_visible(rect) {\n            if text.as_str().is_empty() && !hint_text.is_empty() {\n                let hint_text_color = ui.visuals().weak_text_color();\n                let hint_text_font_id = hint_text_font.unwrap_or_else(|| font_id.into());\n                let galley = if multiline {\n                    hint_text.into_galley(\n                        ui,\n                        Some(TextWrapMode::Wrap),\n                        desired_inner_size.x,\n                        hint_text_font_id,\n                    )\n                } else {\n                    hint_text.into_galley(\n                        ui,\n                        Some(TextWrapMode::Extend),\n                        f32::INFINITY,\n                        hint_text_font_id,\n                    )\n                };\n                let galley_pos = align\n                    .align_size_within_rect(galley.size(), rect)\n                    .intersect(rect)\n                    .min;\n                painter.galley(galley_pos, galley, hint_text_color);\n            }\n\n            let has_focus = ui.memory(|mem| mem.has_focus(id));\n\n            if has_focus && let Some(cursor_range) = state.cursor.range(&galley) {\n                // Add text selection rectangles to the galley:\n                paint_text_selection(&mut galley, ui.visuals(), &cursor_range, None);\n            }\n\n            // Allocate additional space if edits were made this frame that changed the size. This is important so that,\n            // if there's a ScrollArea, it can properly scroll to the cursor.\n            // Condition `!clip_text` is important to avoid breaking layout for `TextEdit::singleline` (PR #5640)\n            if !clip_text\n                && let extra_size = galley.size() - rect.size()\n                && (extra_size.x > 0.0 || extra_size.y > 0.0)\n            {\n                match ui.layout().main_dir() {\n                    crate::Direction::LeftToRight | crate::Direction::TopDown => {\n                        ui.allocate_rect(\n                            Rect::from_min_size(outer_rect.max, extra_size),\n                            Sense::hover(),\n                        );\n                    }\n                    crate::Direction::RightToLeft => {\n                        ui.allocate_rect(\n                            Rect::from_min_size(\n                                emath::pos2(outer_rect.min.x - extra_size.x, outer_rect.max.y),\n                                extra_size,\n                            ),\n                            Sense::hover(),\n                        );\n                    }\n                    crate::Direction::BottomUp => {\n                        ui.allocate_rect(\n                            Rect::from_min_size(\n                                emath::pos2(outer_rect.min.x, outer_rect.max.y - extra_size.y),\n                                extra_size,\n                            ),\n                            Sense::hover(),\n                        );\n                    }\n                }\n            } else {\n                // Avoid an ID shift during this pass if the textedit grow\n                ui.skip_ahead_auto_ids(1);\n            }\n\n            painter.galley(galley_pos, Arc::clone(&galley), text_color);\n\n            if has_focus && let Some(cursor_range) = state.cursor.range(&galley) {\n                let primary_cursor_rect = cursor_rect(&galley, &cursor_range.primary, row_height)\n                    .translate(galley_pos.to_vec2());\n\n                if response.changed() || selection_changed {\n                    // Scroll to keep primary cursor in view:\n                    ui.scroll_to_rect(primary_cursor_rect + margin, None);\n                }\n\n                if text.is_mutable() && interactive {\n                    let now = ui.input(|i| i.time);\n                    if response.changed() || selection_changed {\n                        state.last_interaction_time = now;\n                    }\n\n                    // Only show (and blink) cursor if the egui viewport has focus.\n                    // This is for two reasons:\n                    // * Don't give the impression that the user can type into a window without focus\n                    // * Don't repaint the ui because of a blinking cursor in an app that is not in focus\n                    let viewport_has_focus = ui.input(|i| i.focused);\n                    if viewport_has_focus {\n                        text_selection::visuals::paint_text_cursor(\n                            ui,\n                            &painter,\n                            primary_cursor_rect,\n                            now - state.last_interaction_time,\n                        );\n                    }\n\n                    // Set IME output (in screen coords) when text is editable and visible\n                    let to_global = ui\n                        .ctx()\n                        .layer_transform_to_global(ui.layer_id())\n                        .unwrap_or_default();\n\n                    ui.ctx().output_mut(|o| {\n                        o.ime = Some(crate::output::IMEOutput {\n                            rect: to_global * rect,\n                            cursor_rect: to_global * primary_cursor_rect,\n                        });\n                    });\n                }\n            }\n        }\n\n        // Ensures correct IME behavior when the text input area gains or loses focus.\n        if state.ime_enabled && (response.gained_focus() || response.lost_focus()) {\n            state.ime_enabled = false;\n            if let Some(mut ccursor_range) = state.cursor.char_range() {\n                ccursor_range.secondary.index = ccursor_range.primary.index;\n                state.cursor.set_char_range(Some(ccursor_range));\n            }\n            ui.input_mut(|i| i.events.retain(|e| !matches!(e, Event::Ime(_))));\n        }\n\n        state.clone().store(ui.ctx(), id);\n\n        if response.changed() {\n            response.widget_info(|| {\n                WidgetInfo::text_edit(\n                    ui.is_enabled(),\n                    mask_if_password(password, prev_text.as_str()),\n                    mask_if_password(password, text.as_str()),\n                    hint_text_str.as_str(),\n                )\n            });\n        } else if selection_changed && let Some(cursor_range) = cursor_range {\n            let char_range = cursor_range.primary.index..=cursor_range.secondary.index;\n            let info = WidgetInfo::text_selection_changed(\n                ui.is_enabled(),\n                char_range,\n                mask_if_password(password, text.as_str()),\n            );\n            response.output_event(OutputEvent::TextSelectionChanged(info));\n        } else {\n            response.widget_info(|| {\n                WidgetInfo::text_edit(\n                    ui.is_enabled(),\n                    mask_if_password(password, prev_text.as_str()),\n                    mask_if_password(password, text.as_str()),\n                    hint_text_str.as_str(),\n                )\n            });\n        }\n\n        {\n            let role = if password {\n                accesskit::Role::PasswordInput\n            } else if multiline {\n                accesskit::Role::MultilineTextInput\n            } else {\n                accesskit::Role::TextInput\n            };\n\n            crate::text_selection::accesskit_text::update_accesskit_for_text_widget(\n                ui.ctx(),\n                id,\n                cursor_range,\n                role,\n                TSTransform::from_translation(galley_pos.to_vec2()),\n                &galley,\n            );\n        }\n\n        TextEditOutput {\n            response,\n            galley,\n            galley_pos,\n            text_clip_rect,\n            state,\n            cursor_range,\n        }\n    }\n}\n\nfn mask_if_password(is_password: bool, text: &str) -> String {\n    fn mask_password(text: &str) -> String {\n        std::iter::repeat_n(\n            epaint::text::PASSWORD_REPLACEMENT_CHAR,\n            text.chars().count(),\n        )\n        .collect::<String>()\n    }\n\n    if is_password {\n        mask_password(text)\n    } else {\n        text.to_owned()\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Check for (keyboard) events to edit the cursor and/or text.\n#[expect(clippy::too_many_arguments)]\nfn events(\n    ui: &crate::Ui,\n    state: &mut TextEditState,\n    text: &mut dyn TextBuffer,\n    galley: &mut Arc<Galley>,\n    layouter: &mut dyn FnMut(&Ui, &dyn TextBuffer, f32) -> Arc<Galley>,\n    id: Id,\n    wrap_width: f32,\n    multiline: bool,\n    password: bool,\n    default_cursor_range: CCursorRange,\n    char_limit: usize,\n    event_filter: EventFilter,\n    return_key: Option<KeyboardShortcut>,\n) -> (bool, CCursorRange) {\n    let os = ui.ctx().os();\n\n    let mut cursor_range = state.cursor.range(galley).unwrap_or(default_cursor_range);\n\n    // We feed state to the undoer both before and after handling input\n    // so that the undoer creates automatic saves even when there are no events for a while.\n    state.undoer.lock().feed_state(\n        ui.input(|i| i.time),\n        &(cursor_range, text.as_str().to_owned()),\n    );\n\n    let copy_if_not_password = |ui: &Ui, text: String| {\n        if !password {\n            ui.copy_text(text);\n        }\n    };\n\n    let mut any_change = false;\n\n    let mut events = ui.input(|i| i.filtered_events(&event_filter));\n\n    if state.ime_enabled {\n        remove_ime_incompatible_events(&mut events);\n        // Process IME events first:\n        events.sort_by_key(|e| !matches!(e, Event::Ime(_)));\n    }\n\n    for event in &events {\n        let did_mutate_text = match event {\n            // First handle events that only changes the selection cursor, not the text:\n            event if cursor_range.on_event(os, event, galley, id) => None,\n\n            Event::Copy => {\n                if !cursor_range.is_empty() {\n                    copy_if_not_password(ui, cursor_range.slice_str(text.as_str()).to_owned());\n                }\n                None\n            }\n            Event::Cut => {\n                if cursor_range.is_empty() {\n                    None\n                } else {\n                    copy_if_not_password(ui, cursor_range.slice_str(text.as_str()).to_owned());\n                    Some(CCursorRange::one(text.delete_selected(&cursor_range)))\n                }\n            }\n            Event::Paste(text_to_insert) => {\n                if !text_to_insert.is_empty() {\n                    let mut ccursor = text.delete_selected(&cursor_range);\n                    if multiline {\n                        text.insert_text_at(&mut ccursor, text_to_insert, char_limit);\n                    } else {\n                        let single_line = text_to_insert.replace(['\\r', '\\n'], \" \");\n                        text.insert_text_at(&mut ccursor, &single_line, char_limit);\n                    }\n\n                    Some(CCursorRange::one(ccursor))\n                } else {\n                    None\n                }\n            }\n            Event::Text(text_to_insert) => {\n                // Newlines are handled by `Key::Enter`.\n                if !text_to_insert.is_empty() && text_to_insert != \"\\n\" && text_to_insert != \"\\r\" {\n                    let mut ccursor = text.delete_selected(&cursor_range);\n\n                    text.insert_text_at(&mut ccursor, text_to_insert, char_limit);\n\n                    Some(CCursorRange::one(ccursor))\n                } else {\n                    None\n                }\n            }\n            Event::Key {\n                key: Key::Tab,\n                pressed: true,\n                modifiers,\n                ..\n            } if multiline => {\n                let mut ccursor = text.delete_selected(&cursor_range);\n                if modifiers.shift {\n                    // TODO(emilk): support removing indentation over a selection?\n                    text.decrease_indentation(&mut ccursor);\n                } else {\n                    text.insert_text_at(&mut ccursor, \"\\t\", char_limit);\n                }\n                Some(CCursorRange::one(ccursor))\n            }\n            Event::Key {\n                key,\n                pressed: true,\n                modifiers,\n                ..\n            } if return_key.is_some_and(|return_key| {\n                *key == return_key.logical_key && modifiers.matches_logically(return_key.modifiers)\n            }) =>\n            {\n                if multiline {\n                    let mut ccursor = text.delete_selected(&cursor_range);\n                    text.insert_text_at(&mut ccursor, \"\\n\", char_limit);\n                    // TODO(emilk): if code editor, auto-indent by same leading tabs, + one if the lines end on an opening bracket\n                    Some(CCursorRange::one(ccursor))\n                } else {\n                    ui.memory_mut(|mem| mem.surrender_focus(id)); // End input with enter\n                    break;\n                }\n            }\n\n            Event::Key {\n                key,\n                pressed: true,\n                modifiers,\n                ..\n            } if (modifiers.matches_logically(Modifiers::COMMAND) && *key == Key::Y)\n                || (modifiers.matches_logically(Modifiers::SHIFT | Modifiers::COMMAND)\n                    && *key == Key::Z) =>\n            {\n                if let Some((redo_ccursor_range, redo_txt)) = state\n                    .undoer\n                    .lock()\n                    .redo(&(cursor_range, text.as_str().to_owned()))\n                {\n                    text.replace_with(redo_txt);\n                    Some(*redo_ccursor_range)\n                } else {\n                    None\n                }\n            }\n\n            Event::Key {\n                key: Key::Z,\n                pressed: true,\n                modifiers,\n                ..\n            } if modifiers.matches_logically(Modifiers::COMMAND) => {\n                if let Some((undo_ccursor_range, undo_txt)) = state\n                    .undoer\n                    .lock()\n                    .undo(&(cursor_range, text.as_str().to_owned()))\n                {\n                    text.replace_with(undo_txt);\n                    Some(*undo_ccursor_range)\n                } else {\n                    None\n                }\n            }\n\n            Event::Key {\n                modifiers,\n                key,\n                pressed: true,\n                ..\n            } => check_for_mutating_key_press(os, &cursor_range, text, galley, modifiers, *key),\n\n            Event::Ime(ime_event) => {\n                /// Both `ImeEvent::Preedit(\"\")` and `ImeEvent::Commit(\"\")`\n                /// might be emitted from different integrations to signify that\n                /// the current IME composition should be cleared.\n                ///\n                /// Example integrations where only `ImeEvent::Preedit(\"\")` of\n                /// those two events is emitted when the last character is\n                /// deleted with a backspace:\n                /// - `egui-winit` on macOS 15.7.3.\n                /// - `egui-winit` on Debian13 with gnome48 and wayland.\n                ///\n                /// An example integration where only `ImeEvent::Commit(\"\")` of\n                /// those two events is emitted when the last character is\n                /// deleted with a backspace:\n                /// - `eframe`'s web integration on Safari 26.2 (on macOS\n                ///   15.7.3).\n                ///\n                /// ## Note\n                ///\n                /// The term “pre-edit string” is used by X11 and Wayland, and\n                /// we use “pre-edit text” and “pre-edit range” here in the\n                /// same manner.\n                /// See: <https://wayland.app/protocols/input-method-unstable-v2>\n                ///\n                /// We previously referred to “pre-edit text” as “prediction”,\n                /// which is not standard and can mean different things.\n                fn clear_preedit_text(\n                    text: &mut dyn TextBuffer,\n                    preedit_range: &CCursorRange,\n                ) -> CCursor {\n                    text.delete_selected(preedit_range)\n                }\n\n                match ime_event {\n                    ImeEvent::Enabled => {\n                        state.ime_enabled = true;\n                        state.ime_cursor_range = cursor_range;\n                        None\n                    }\n                    ImeEvent::Preedit(preedit_text) => {\n                        if preedit_text == \"\\n\" || preedit_text == \"\\r\" {\n                            None\n                        } else {\n                            let mut ccursor = clear_preedit_text(text, &cursor_range);\n\n                            let start_cursor = ccursor;\n                            if !preedit_text.is_empty() {\n                                text.insert_text_at(&mut ccursor, preedit_text, char_limit);\n                            }\n                            state.ime_cursor_range = cursor_range;\n                            Some(CCursorRange::two(start_cursor, ccursor))\n                        }\n                    }\n                    ImeEvent::Commit(commit_text) => {\n                        if commit_text == \"\\n\" || commit_text == \"\\r\" {\n                            None\n                        } else {\n                            state.ime_enabled = false;\n\n                            let mut ccursor = clear_preedit_text(text, &cursor_range);\n\n                            if !commit_text.is_empty()\n                                && cursor_range.secondary.index\n                                    == state.ime_cursor_range.secondary.index\n                            {\n                                text.insert_text_at(&mut ccursor, commit_text, char_limit);\n                            }\n\n                            Some(CCursorRange::one(ccursor))\n                        }\n                    }\n                    ImeEvent::Disabled => {\n                        state.ime_enabled = false;\n                        None\n                    }\n                }\n            }\n\n            _ => None,\n        };\n\n        if let Some(new_ccursor_range) = did_mutate_text {\n            any_change = true;\n\n            // Layout again to avoid frame delay, and to keep `text` and `galley` in sync.\n            *galley = layouter(ui, text, wrap_width);\n\n            // Set cursor_range using new galley:\n            cursor_range = new_ccursor_range;\n        }\n    }\n\n    state.cursor.set_char_range(Some(cursor_range));\n\n    state.undoer.lock().feed_state(\n        ui.input(|i| i.time),\n        &(cursor_range, text.as_str().to_owned()),\n    );\n\n    (any_change, cursor_range)\n}\n\n// ----------------------------------------------------------------------------\n\nfn remove_ime_incompatible_events(events: &mut Vec<Event>) {\n    // Remove key events which cause problems while 'IME' is being used.\n    // See https://github.com/emilk/egui/pull/4509\n    events.retain(|event| {\n        !matches!(\n            event,\n            Event::Key { repeat: true, .. }\n                | Event::Key {\n                    key: Key::Backspace\n                        | Key::ArrowUp\n                        | Key::ArrowDown\n                        | Key::ArrowLeft\n                        | Key::ArrowRight,\n                    ..\n                }\n        )\n    });\n}\n\n// ----------------------------------------------------------------------------\n\n/// Returns `Some(new_cursor)` if we did mutate `text`.\nfn check_for_mutating_key_press(\n    os: OperatingSystem,\n    cursor_range: &CCursorRange,\n    text: &mut dyn TextBuffer,\n    galley: &Galley,\n    modifiers: &Modifiers,\n    key: Key,\n) -> Option<CCursorRange> {\n    match key {\n        Key::Backspace => {\n            let ccursor = if modifiers.mac_cmd {\n                text.delete_paragraph_before_cursor(galley, cursor_range)\n            } else if let Some(cursor) = cursor_range.single() {\n                if modifiers.alt || modifiers.ctrl {\n                    // alt on mac, ctrl on windows\n                    text.delete_previous_word(cursor)\n                } else {\n                    text.delete_previous_char(cursor)\n                }\n            } else {\n                text.delete_selected(cursor_range)\n            };\n            Some(CCursorRange::one(ccursor))\n        }\n\n        Key::Delete if !modifiers.shift || os != OperatingSystem::Windows => {\n            let ccursor = if modifiers.mac_cmd {\n                text.delete_paragraph_after_cursor(galley, cursor_range)\n            } else if let Some(cursor) = cursor_range.single() {\n                if modifiers.alt || modifiers.ctrl {\n                    // alt on mac, ctrl on windows\n                    text.delete_next_word(cursor)\n                } else {\n                    text.delete_next_char(cursor)\n                }\n            } else {\n                text.delete_selected(cursor_range)\n            };\n            let ccursor = CCursor {\n                prefer_next_row: true,\n                ..ccursor\n            };\n            Some(CCursorRange::one(ccursor))\n        }\n\n        Key::H if modifiers.ctrl => {\n            let ccursor = text.delete_previous_char(cursor_range.primary);\n            Some(CCursorRange::one(ccursor))\n        }\n\n        Key::K if modifiers.ctrl => {\n            let ccursor = text.delete_paragraph_after_cursor(galley, cursor_range);\n            Some(CCursorRange::one(ccursor))\n        }\n\n        Key::U if modifiers.ctrl => {\n            let ccursor = text.delete_paragraph_before_cursor(galley, cursor_range);\n            Some(CCursorRange::one(ccursor))\n        }\n\n        Key::W if modifiers.ctrl => {\n            let ccursor = if let Some(cursor) = cursor_range.single() {\n                text.delete_previous_word(cursor)\n            } else {\n                text.delete_selected(cursor_range)\n            };\n            Some(CCursorRange::one(ccursor))\n        }\n\n        _ => None,\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/text_edit/mod.rs",
    "content": "mod builder;\nmod output;\nmod state;\nmod text_buffer;\n\npub use {\n    crate::text_selection::TextCursorState, builder::TextEdit, output::TextEditOutput,\n    state::TextEditState, text_buffer::TextBuffer,\n};\n"
  },
  {
    "path": "crates/egui/src/widgets/text_edit/output.rs",
    "content": "use std::sync::Arc;\n\nuse crate::text::CCursorRange;\n\n/// The output from a [`TextEdit`](crate::TextEdit).\npub struct TextEditOutput {\n    /// The interaction response.\n    pub response: crate::Response,\n\n    /// How the text was displayed.\n    pub galley: Arc<crate::Galley>,\n\n    /// Where the text in [`Self::galley`] ended up on the screen.\n    pub galley_pos: crate::Pos2,\n\n    /// The text was clipped to this rectangle when painted.\n    pub text_clip_rect: crate::Rect,\n\n    /// The state we stored after the run.\n    pub state: super::TextEditState,\n\n    /// Where the text cursor is.\n    pub cursor_range: Option<CCursorRange>,\n}\n\n// TODO(emilk): add `output.paint` and `output.store` and split out that code from `TextEdit::show`.\n"
  },
  {
    "path": "crates/egui/src/widgets/text_edit/state.rs",
    "content": "use std::sync::Arc;\n\nuse crate::mutex::Mutex;\n\nuse crate::{\n    Context, Id, Vec2,\n    text_selection::{CCursorRange, TextCursorState},\n};\n\npub type TextEditUndoer = crate::util::undoer::Undoer<(CCursorRange, String)>;\n\n/// The text edit state stored between frames.\n///\n/// Attention: You also need to `store` the updated state.\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// # let mut text = String::new();\n/// use egui::text::{CCursor, CCursorRange};\n///\n/// let mut output = egui::TextEdit::singleline(&mut text).show(ui);\n///\n/// // Create a new selection range\n/// let min = CCursor::new(0);\n/// let max = CCursor::new(0);\n/// let new_range = CCursorRange::two(min, max);\n///\n/// // Update the state\n/// output.state.cursor.set_char_range(Some(new_range));\n/// // Store the updated state\n/// output.state.store(ui.ctx(), output.response.id);\n/// # });\n/// ```\n#[derive(Clone, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct TextEditState {\n    /// Controls the text selection.\n    pub cursor: TextCursorState,\n\n    /// Wrapped in Arc for cheaper clones.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) undoer: Arc<Mutex<TextEditUndoer>>,\n\n    // If IME candidate window is shown on this text edit.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) ime_enabled: bool,\n\n    // cursor range for IME candidate.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) ime_cursor_range: CCursorRange,\n\n    // Text offset within the widget area.\n    // Used for sensing and singleline text clipping.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) text_offset: Vec2,\n\n    /// When did the user last press a key or click on the `TextEdit`.\n    /// Used to pause the cursor animation when typing.\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) last_interaction_time: f64,\n}\n\nimpl TextEditState {\n    pub fn load(ctx: &Context, id: Id) -> Option<Self> {\n        ctx.data_mut(|d| d.get_persisted(id))\n    }\n\n    pub fn store(self, ctx: &Context, id: Id) {\n        ctx.data_mut(|d| d.insert_persisted(id, self));\n    }\n\n    pub fn undoer(&self) -> TextEditUndoer {\n        self.undoer.lock().clone()\n    }\n\n    #[expect(clippy::needless_pass_by_ref_mut)] // Intentionally hide interiority of mutability\n    pub fn set_undoer(&mut self, undoer: TextEditUndoer) {\n        *self.undoer.lock() = undoer;\n    }\n\n    pub fn clear_undoer(&mut self) {\n        self.set_undoer(TextEditUndoer::default());\n    }\n}\n"
  },
  {
    "path": "crates/egui/src/widgets/text_edit/text_buffer.rs",
    "content": "use std::{borrow::Cow, ops::Range};\n\nuse epaint::{\n    Galley,\n    text::{TAB_SIZE, cursor::CCursor},\n};\n\nuse crate::{\n    text::CCursorRange,\n    text_selection::text_cursor_state::{\n        byte_index_from_char_index, ccursor_next_word, ccursor_previous_word,\n        char_index_from_byte_index, find_line_start, slice_char_range,\n    },\n};\n\n/// Trait constraining what types [`crate::TextEdit`] may use as\n/// an underlying buffer.\n///\n/// Most likely you will use a [`String`] which implements [`TextBuffer`].\npub trait TextBuffer {\n    /// Can this text be edited?\n    fn is_mutable(&self) -> bool;\n\n    /// Returns this buffer as a `str`.\n    fn as_str(&self) -> &str;\n\n    /// Inserts text `text` into this buffer at character index `char_index`.\n    ///\n    /// # Notes\n    /// `char_index` is a *character index*, not a byte index.\n    ///\n    /// # Return\n    /// Returns how many *characters* were successfully inserted\n    fn insert_text(&mut self, text: &str, char_index: usize) -> usize;\n\n    /// Deletes a range of text `char_range` from this buffer.\n    ///\n    /// # Notes\n    /// `char_range` is a *character range*, not a byte range.\n    fn delete_char_range(&mut self, char_range: Range<usize>);\n\n    /// Reads the given character range.\n    fn char_range(&self, char_range: Range<usize>) -> &str {\n        slice_char_range(self.as_str(), char_range)\n    }\n\n    fn byte_index_from_char_index(&self, char_index: usize) -> usize {\n        byte_index_from_char_index(self.as_str(), char_index)\n    }\n\n    fn char_index_from_byte_index(&self, char_index: usize) -> usize {\n        char_index_from_byte_index(self.as_str(), char_index)\n    }\n\n    /// Clears all characters in this buffer\n    fn clear(&mut self) {\n        self.delete_char_range(0..self.as_str().len());\n    }\n\n    /// Replaces all contents of this string with `text`\n    fn replace_with(&mut self, text: &str) {\n        self.clear();\n        self.insert_text(text, 0);\n    }\n\n    /// Clears all characters in this buffer and returns a string of the contents.\n    fn take(&mut self) -> String {\n        let s = self.as_str().to_owned();\n        self.clear();\n        s\n    }\n\n    fn insert_text_at(&mut self, ccursor: &mut CCursor, text_to_insert: &str, char_limit: usize) {\n        if char_limit < usize::MAX {\n            let mut new_string = text_to_insert;\n            // Avoid subtract with overflow panic\n            let cutoff = char_limit.saturating_sub(self.as_str().chars().count());\n\n            new_string = match new_string.char_indices().nth(cutoff) {\n                None => new_string,\n                Some((idx, _)) => &new_string[..idx],\n            };\n\n            ccursor.index += self.insert_text(new_string, ccursor.index);\n        } else {\n            ccursor.index += self.insert_text(text_to_insert, ccursor.index);\n        }\n    }\n\n    fn decrease_indentation(&mut self, ccursor: &mut CCursor) {\n        let line_start = find_line_start(self.as_str(), *ccursor);\n\n        let remove_len = if self.as_str().chars().nth(line_start.index) == Some('\\t') {\n            Some(1)\n        } else if self\n            .as_str()\n            .chars()\n            .skip(line_start.index)\n            .take(TAB_SIZE)\n            .all(|c| c == ' ')\n        {\n            Some(TAB_SIZE)\n        } else {\n            None\n        };\n\n        if let Some(len) = remove_len {\n            self.delete_char_range(line_start.index..(line_start.index + len));\n            if *ccursor != line_start {\n                *ccursor -= len;\n            }\n        }\n    }\n\n    fn delete_selected(&mut self, cursor_range: &CCursorRange) -> CCursor {\n        let [min, max] = cursor_range.sorted_cursors();\n        self.delete_selected_ccursor_range([min, max])\n    }\n\n    fn delete_selected_ccursor_range(&mut self, [min, max]: [CCursor; 2]) -> CCursor {\n        self.delete_char_range(min.index..max.index);\n        CCursor {\n            index: min.index,\n            prefer_next_row: true,\n        }\n    }\n\n    fn delete_previous_char(&mut self, ccursor: CCursor) -> CCursor {\n        if ccursor.index > 0 {\n            let max_ccursor = ccursor;\n            let min_ccursor = max_ccursor - 1;\n            self.delete_selected_ccursor_range([min_ccursor, max_ccursor])\n        } else {\n            ccursor\n        }\n    }\n\n    fn delete_next_char(&mut self, ccursor: CCursor) -> CCursor {\n        self.delete_selected_ccursor_range([ccursor, ccursor + 1])\n    }\n\n    fn delete_previous_word(&mut self, max_ccursor: CCursor) -> CCursor {\n        let min_ccursor = ccursor_previous_word(self.as_str(), max_ccursor);\n        self.delete_selected_ccursor_range([min_ccursor, max_ccursor])\n    }\n\n    fn delete_next_word(&mut self, min_ccursor: CCursor) -> CCursor {\n        let max_ccursor = ccursor_next_word(self.as_str(), min_ccursor);\n        self.delete_selected_ccursor_range([min_ccursor, max_ccursor])\n    }\n\n    fn delete_paragraph_before_cursor(\n        &mut self,\n        galley: &Galley,\n        cursor_range: &CCursorRange,\n    ) -> CCursor {\n        let [min, max] = cursor_range.sorted_cursors();\n        let min = galley.cursor_begin_of_paragraph(&min);\n        if min == max {\n            self.delete_previous_char(min)\n        } else {\n            self.delete_selected(&CCursorRange::two(min, max))\n        }\n    }\n\n    fn delete_paragraph_after_cursor(\n        &mut self,\n        galley: &Galley,\n        cursor_range: &CCursorRange,\n    ) -> CCursor {\n        let [min, max] = cursor_range.sorted_cursors();\n        let max = galley.cursor_end_of_paragraph(&max);\n        if min == max {\n            self.delete_next_char(min)\n        } else {\n            self.delete_selected(&CCursorRange::two(min, max))\n        }\n    }\n\n    /// Returns a unique identifier for the implementing type.\n    ///\n    /// This is useful for downcasting from this trait to the implementing type.\n    /// Here is an example usage:\n    /// ```\n    /// use egui::TextBuffer;\n    /// use std::any::TypeId;\n    ///\n    /// struct ExampleBuffer {}\n    ///\n    /// impl TextBuffer for ExampleBuffer {\n    ///     fn is_mutable(&self) -> bool { unimplemented!() }\n    ///     fn as_str(&self) -> &str { unimplemented!() }\n    ///     fn insert_text(&mut self, text: &str, char_index: usize) -> usize { unimplemented!() }\n    ///     fn delete_char_range(&mut self, char_range: std::ops::Range<usize>) { unimplemented!() }\n    ///\n    ///     // Implement it like the following:\n    ///     fn type_id(&self) -> TypeId {\n    ///         TypeId::of::<Self>()\n    ///     }\n    /// }\n    ///\n    /// // Example downcast:\n    /// pub fn downcast_example(buffer: &dyn TextBuffer) -> Option<&ExampleBuffer> {\n    ///     if buffer.type_id() == TypeId::of::<ExampleBuffer>() {\n    ///         unsafe { Some(&*(buffer as *const dyn TextBuffer as *const ExampleBuffer)) }\n    ///     } else {\n    ///         None\n    ///     }\n    /// }\n    /// ```\n    fn type_id(&self) -> std::any::TypeId;\n}\n\nimpl TextBuffer for String {\n    fn is_mutable(&self) -> bool {\n        true\n    }\n\n    fn as_str(&self) -> &str {\n        self.as_ref()\n    }\n\n    fn insert_text(&mut self, text: &str, char_index: usize) -> usize {\n        // Get the byte index from the character index\n        let byte_idx = byte_index_from_char_index(self.as_str(), char_index);\n\n        // Then insert the string\n        self.insert_str(byte_idx, text);\n\n        text.chars().count()\n    }\n\n    fn delete_char_range(&mut self, char_range: Range<usize>) {\n        assert!(\n            char_range.start <= char_range.end,\n            \"start must be <= end, but got {char_range:?}\"\n        );\n\n        // Get both byte indices\n        let byte_start = byte_index_from_char_index(self.as_str(), char_range.start);\n        let byte_end = byte_index_from_char_index(self.as_str(), char_range.end);\n\n        // Then drain all characters within this range\n        self.drain(byte_start..byte_end);\n    }\n\n    fn clear(&mut self) {\n        self.clear();\n    }\n\n    fn replace_with(&mut self, text: &str) {\n        text.clone_into(self);\n    }\n\n    fn take(&mut self) -> String {\n        std::mem::take(self)\n    }\n\n    fn type_id(&self) -> std::any::TypeId {\n        std::any::TypeId::of::<Self>()\n    }\n}\n\nimpl TextBuffer for Cow<'_, str> {\n    fn is_mutable(&self) -> bool {\n        true\n    }\n\n    fn as_str(&self) -> &str {\n        self.as_ref()\n    }\n\n    fn insert_text(&mut self, text: &str, char_index: usize) -> usize {\n        <String as TextBuffer>::insert_text(self.to_mut(), text, char_index)\n    }\n\n    fn delete_char_range(&mut self, char_range: Range<usize>) {\n        <String as TextBuffer>::delete_char_range(self.to_mut(), char_range);\n    }\n\n    fn clear(&mut self) {\n        <String as TextBuffer>::clear(self.to_mut());\n    }\n\n    fn replace_with(&mut self, text: &str) {\n        *self = Cow::Owned(text.to_owned());\n    }\n\n    fn take(&mut self) -> String {\n        std::mem::take(self).into_owned()\n    }\n\n    fn type_id(&self) -> std::any::TypeId {\n        std::any::TypeId::of::<Cow<'_, str>>()\n    }\n}\n\n/// Immutable view of a `&str`!\nimpl TextBuffer for &str {\n    fn is_mutable(&self) -> bool {\n        false\n    }\n\n    fn as_str(&self) -> &str {\n        self\n    }\n\n    fn insert_text(&mut self, _text: &str, _ch_idx: usize) -> usize {\n        0\n    }\n\n    fn delete_char_range(&mut self, _ch_range: Range<usize>) {}\n\n    fn type_id(&self) -> std::any::TypeId {\n        std::any::TypeId::of::<&str>()\n    }\n}\n"
  },
  {
    "path": "crates/egui-wgpu/CHANGELOG.md",
    "content": "# Changelog for egui-wgpu\nAll notable changes to the `egui-wgpu` integration will be noted in this file.\n\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n## 0.33.3 - 2025-12-11\nNothing new\n\n\n## 0.33.2 - 2025-11-13\n* Fix jittering during window resize on MacOS for WGPU/Metal [#7641](https://github.com/emilk/egui/pull/7641) by [@aspcartman](https://github.com/aspcartman)\n\n\n## 0.33.0 - 2025-10-09\n### 🔧 Changed\n* Update wgpu to 26 and wasm-bindgen to 0.2.100 [#7540](https://github.com/emilk/egui/pull/7540) by [@Kumpelinus](https://github.com/Kumpelinus)\n* Warn if `DYLD_LIBRARY_PATH` is set and we find no wgpu adapter [#7572](https://github.com/emilk/egui/pull/7572) by [@emilk](https://github.com/emilk)\n* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)\n* Update wgpu to 27.0.0 [#7580](https://github.com/emilk/egui/pull/7580) by [@Wumpf](https://github.com/Wumpf)\n* Create `egui_wgpu::RendererOptions` [#7601](https://github.com/emilk/egui/pull/7601) by [@emilk](https://github.com/emilk)\n* Use software texture filtering in kittest [#7602](https://github.com/emilk/egui/pull/7602) by [@emilk](https://github.com/emilk)\n\n\n## 0.32.3 - 2025-09-12\nNothing new\n\n\n## 0.32.2 - 2025-09-04\nNothing new\n\n\n## 0.32.1 - 2025-08-15\n* Enable wgpu default features in eframe / egui_wgpu default features [#7344](https://github.com/emilk/egui/pull/7344) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n\n## 0.32.0 - 2025-07-10\n* Update to wgpu 25 [#6744](https://github.com/emilk/egui/pull/6744) by [@torokati44](https://github.com/torokati44)\n* Free textures after submitting queue instead of before with wgpu renderer on Web [#7291](https://github.com/emilk/egui/pull/7291) by [@Wumpf](https://github.com/Wumpf)\n* Improve texture filtering by doing it in gamma space [#7311](https://github.com/emilk/egui/pull/7311) by [@emilk](https://github.com/emilk)\n\n\n## 0.31.1 - 2025-03-05\nNothing new\n\n\n## 0.31.0 - 2025-02-04\n* Upgrade to wgpu 24 [#5610](https://github.com/emilk/egui/pull/5610) by [@torokati44](https://github.com/torokati44)\n* Extend `WgpuSetup`, `egui_kittest` now prefers software rasterizers for testing [#5506](https://github.com/emilk/egui/pull/5506) by [@Wumpf](https://github.com/Wumpf)\n* Wgpu resources are no longer wrapped in `Arc` (since they are now `Clone`) [#5612](https://github.com/emilk/egui/pull/5612) by [@Wumpf](https://github.com/Wumpf)\n\n\n## 0.30.0 - 2024-12-16\n* Fix docs.rs build [#5204](https://github.com/emilk/egui/pull/5204) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Free textures after submitting queue instead of before with wgpu renderer [#5226](https://github.com/emilk/egui/pull/5226) by [@Rusty-Cube](https://github.com/Rusty-Cube)\n* Add option to initialize with existing wgpu instance/adapter/device/queue [#5319](https://github.com/emilk/egui/pull/5319) by [@ArthurBrussee](https://github.com/ArthurBrussee)\n* Updare to `wgpu` 23.0.0 and `wasm-bindgen` to 0.2.95 [#5330](https://github.com/emilk/egui/pull/5330) by [@torokati44](https://github.com/torokati44)\n* Support wgpu-tracing with same mechanism as wgpu examples [#5450](https://github.com/emilk/egui/pull/5450) by [@EriKWDev](https://github.com/EriKWDev)\n\n\n## 0.29.1 - 2024-10-01\nNothing new\n\n\n## 0.29.0 - 2024-09-26 - `wgpu` 22.0\n### ⭐ Added\n* Add opt-out `fragile-send-sync-non-atomic-wasm` feature for wgpu [#5098](https://github.com/emilk/egui/pull/5098) by [@9SMTM6](https://github.com/9SMTM6)\n\n### 🔧 Changed\n* Upgrade to wgpu 22.0.0 [#4847](https://github.com/emilk/egui/pull/4847) by [@KeKsBoTer](https://github.com/KeKsBoTer)\n* Introduce dithering to reduce banding [#4497](https://github.com/emilk/egui/pull/4497) by [@jwagner](https://github.com/jwagner)\n* Ensure that `WgpuConfiguration` is `Send + Sync` [#4803](https://github.com/emilk/egui/pull/4803) by [@murl-digital](https://github.com/murl-digital)\n* Wgpu render pass on paint callback has now `'static` lifetime [#5149](https://github.com/emilk/egui/pull/5149) by [@Wumpf](https://github.com/Wumpf)\n\n### 🐛 Fixed\n* Update sampler along with texture on wgpu backend [#5122](https://github.com/emilk/egui/pull/5122) by [@valadaptive](https://github.com/valadaptive)\n\n\n## 0.28.1 - 2024-07-05\nNothing new\n\n\n## 0.28.0 - 2024-07-03\n* Update to wgpu 0.20 [#4433](https://github.com/emilk/egui/pull/4433) by [@KeKsBoTer](https://github.com/KeKsBoTer)\n* Fix doclinks in egui-wgpu docs [#4677](https://github.com/emilk/egui/pull/4677) by [@emilk](https://github.com/emilk)\n\n\n## 0.27.2 - 2024-04-02\n* Nothing new\n\n\n## 0.27.1 - 2024-03-29\n* Nothing new\n\n\n## 0.27.0 - 2024-03-26\n* Improve panic message in egui-wgpu when failing to create buffers [#3986](https://github.com/emilk/egui/pull/3986)\n\n\n## 0.26.2 - 2024-02-14\n* Nothing new\n\n\n## 0.26.1 - 2024-02-11\n* Improve panic message in egui-wgpu when failing to create buffers [#3986](https://github.com/emilk/egui/pull/3986)\n\n\n## 0.26.0 - 2024-02-05\n* Update wgpu to 0.19 [#3824](https://github.com/emilk/egui/pull/3824)\n* Add `WgpuConfiguration::desired_maximum_frame_latency` [#3874](https://github.com/emilk/egui/pull/3874)\n* Disable the default features of `wgpu` [#3875](https://github.com/emilk/egui/pull/3875)\n* If WebGPU fails, re-try adapter creation with WebGL [#3895](https://github.com/emilk/egui/pull/3895) (thanks [@Wumpf](https://github.com/Wumpf)!)\n* Delay call to `get_current_texture` (possible small performance win) [#3914](https://github.com/emilk/egui/pull/3914)\n* Add `x11` and `wayland` features [#3909](https://github.com/emilk/egui/pull/3909) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Pass `ScreenDescriptor` to `egui_wgpu::CallbackTrait::prepare` [#3960](https://github.com/emilk/egui/pull/3960) (thanks [@StratusFearMe21](https://github.com/StratusFearMe21)!)\n* Make `egui_wgpu::renderer` a private module [#3979](https://github.com/emilk/egui/pull/3979)\n\n\n## 0.25.0 - 2024-01-08\n* Only call wgpu paint callback if viewport is positive [#3778](https://github.com/emilk/egui/pull/3778) (thanks [@msparkles](https://github.com/msparkles)!)\n\n\n## 0.24.1 - 2023-11-30\n* Add a few `puffin` profile scopes\n\n\n## 0.24.0 - 2023-11-23\n* Updated to wgpu 0.18 [#3505](https://github.com/emilk/egui/pull/3505) (thanks [@Wumpf](https://github.com/Wumpf)!)\n* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)\n* Properly clamp and round viewport values, preventing rare warnings [#3604](https://github.com/emilk/egui/pull/3604) (thanks [@Wumpf](https://github.com/Wumpf)!)\n\n\n## 0.23.0 - 2023-09-27\n* Update to `wgpu` 0.17.0 [#3170](https://github.com/emilk/egui/pull/3170) (thanks [@Aaron1011](https://github.com/Aaron1011)!)\n* Improved wgpu callbacks [#3253](https://github.com/emilk/egui/pull/3253) (thanks [@Wumpf](https://github.com/Wumpf)!)\n* Fix depth texture init with multisampling [#3207](https://github.com/emilk/egui/pull/3207) (thanks [@mauliu](https://github.com/mauliu)!)\n* Fix panic on wgpu GL backend due to new screenshot capability [#3078](https://github.com/emilk/egui/pull/3078) (thanks [@amfaber](https://github.com/amfaber)!)\n\n\n## 0.22.0 - 2023-05-23\n* Update to wgpu 0.16 [#2884](https://github.com/emilk/egui/pull/2884) (thanks [@niklaskorz](https://github.com/niklaskorz)!)\n* Device configuration is now dependent on adapter [#2951](https://github.com/emilk/egui/pull/2951) (thanks [@Wumpf](https://github.com/Wumpf)!)\n* Expose `wgpu::Adapter` via `RenderState` [#2954](https://github.com/emilk/egui/pull/2954) (thanks [@Wumpf](https://github.com/Wumpf)!)\n* Add `read_screen_rgba` to the egui-wgpu `Painter`, to allow for capturing the current frame when using wgpu. Used in conjunction with `Frame::request_screenshot` [#2676](https://github.com/emilk/egui/pull/2676)\n* Improve performance of `update_buffers` [#2820](https://github.com/emilk/egui/pull/2820) (thanks [@Wumpf](https://github.com/Wumpf)!)\n* Added support for multisampling (MSAA) [#2878](https://github.com/emilk/egui/pull/2878) (thanks [@PPakalns](https://github.com/PPakalns)!)\n\n\n## 0.21.0 - 2023-02-08\n* Update to `wgpu` 0.15 ([#2629](https://github.com/emilk/egui/pull/2629))\n* Return `Err` instead of panic if we can't find a device ([#2428](https://github.com/emilk/egui/pull/2428)).\n* `winit::Painter::set_window` is now `async` ([#2434](https://github.com/emilk/egui/pull/2434)).\n* `egui-wgpu` now only depends on `epaint` instead of the entire `egui` ([#2438](https://github.com/emilk/egui/pull/2438)).\n* `winit::Painter` now supports transparent backbuffer ([#2684](https://github.com/emilk/egui/pull/2684)).\n\n\n## 0.20.0 - 2022-12-08 - web support\n* Renamed `RenderPass` to `Renderer`.\n* Renamed `RenderPass::execute` to `RenderPass::render`.\n* Renamed `RenderPass::execute_with_renderpass` to `Renderer::render` (replacing existing `Renderer::render`)\n* Reexported `Renderer`.\n* You can now use `egui-wgpu` on web, using WebGL ([#2107](https://github.com/emilk/egui/pull/2107)).\n* `Renderer` no longer handles pass creation and depth buffer creation ([#2136](https://github.com/emilk/egui/pull/2136))\n* `PrepareCallback` now passes `wgpu::CommandEncoder` ([#2136](https://github.com/emilk/egui/pull/2136))\n* `PrepareCallback` can now returns `wgpu::CommandBuffer` that are bundled into a single `wgpu::Queue::submit` call ([#2230](https://github.com/emilk/egui/pull/2230))\n* Only a single vertex & index buffer is now created and resized when necessary (previously, vertex/index buffers were allocated for every mesh) ([#2148](https://github.com/emilk/egui/pull/2148)).\n* `Renderer::update_texture` no longer creates a new `wgpu::Sampler` with every new texture ([#2198](https://github.com/emilk/egui/pull/2198))\n* `Painter`'s instance/device/adapter/surface creation is now configurable via `WgpuConfiguration` ([#2207](https://github.com/emilk/egui/pull/2207))\n* Fix panic on using a depth buffer ([#2316](https://github.com/emilk/egui/pull/2316))\n\n\n## 0.19.0 - 2022-08-20\n* Enables deferred render + surface state initialization for Android ([#1634](https://github.com/emilk/egui/pull/1634)).\n* Make `RenderPass` `Send` and `Sync` ([#1883](https://github.com/emilk/egui/pull/1883)).\n\n\n## 0.18.0 - 2022-05-15\nFirst published version since moving the code into the `egui` repository from <https://github.com/LU15W1R7H/eww>.\n"
  },
  {
    "path": "crates/egui-wgpu/Cargo.toml",
    "content": "[package]\nname = \"egui-wgpu\"\nversion.workspace = true\ndescription = \"Bindings for using egui natively using the wgpu library\"\nauthors = [\n  \"Nils Hasenbanck <nils@hasenbanck.de>\",\n  \"embotech <opensource@embotech.com>\",\n  \"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\",\n]\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui/tree/main/crates/egui-wgpu\"\nlicense.workspace = true\nreadme = \"README.md\"\nrepository = \"https://github.com/emilk/egui/tree/main/crates/egui-wgpu\"\ncategories = [\"gui\", \"game-development\"]\nkeywords = [\"wgpu\", \"egui\", \"gui\", \"gamedev\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"**/*.wgsl\", \"Cargo.toml\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[features]\ndefault = [\"fragile-send-sync-non-atomic-wasm\", \"macos-window-resize-jitter-fix\", \"wgpu/default\"]\n\n## Enables the `capture` module for capturing screenshots.\ncapture = [\"dep:egui\"]\n\n## Enable [`winit`](https://docs.rs/winit) integration. On Linux, requires either `wayland` or `x11`\nwinit = [\"dep:winit\", \"winit/rwh_06\", \"dep:egui\", \"capture\"]\n\n## Enables Wayland support for winit.\nwayland = [\"winit?/wayland\"]\n\n## Enables x11 support for winit.\nx11 = [\"winit?/x11\"]\n\n## Make the renderer `Sync` on wasm, exploiting that by default wasm isn't multithreaded.\n## It may make code easier, especially when targeting both native and web.\n## On native most wgpu objects are send and sync, on the web they are not (by nature of the WebGPU specification).\n## This is not supported in [multithreaded WASM](https://gpuweb.github.io/gpuweb/explainer/#multithreading-transfer).\n## Thus that usage is guarded against with compiler errors in wgpu.\nfragile-send-sync-non-atomic-wasm = [\"wgpu/fragile-send-sync-non-atomic-wasm\"]\n\n## Enables `present_with_transaction` surface flag temporary during window resize on MacOS.\nmacos-window-resize-jitter-fix = [\"wgpu/metal\"]\n\n[dependencies]\nepaint = { workspace = true, default-features = false, features = [\"bytemuck\"] }\n\nahash.workspace = true\nbytemuck.workspace = true\ndocument-features.workspace = true\nlog.workspace = true\nprofiling.workspace = true\nthiserror.workspace = true\ntype-map.workspace = true\nweb-time.workspace = true\nwgpu = { workspace = true, features = [\"wgsl\"] }\n\n# Optional dependencies:\n\negui = { workspace = true, optional = true, default-features = false }\nwinit = { workspace = true, optional = true, default-features = false }\n"
  },
  {
    "path": "crates/egui-wgpu/README.md",
    "content": "# egui-wgpu\n\n[![Latest version](https://img.shields.io/crates/v/egui-wgpu.svg)](https://crates.io/crates/egui-wgpu)\n[![Documentation](https://docs.rs/egui-wgpu/badge.svg)](https://docs.rs/egui-wgpu)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\nThis crates provides bindings between [`egui`](https://github.com/emilk/egui) and [wgpu](https://crates.io/crates/wgpu).\n\nThis was originally hosted at https://github.com/hasenbanck/egui_wgpu_backend\n"
  },
  {
    "path": "crates/egui-wgpu/src/capture.rs",
    "content": "use egui::{UserData, ViewportId};\nuse epaint::ColorImage;\nuse std::sync::{Arc, mpsc};\nuse wgpu::{BindGroupLayout, MultisampleState, StoreOp};\n\n/// A texture and a buffer for reading the rendered frame back to the cpu.\n///\n/// The texture is required since [`wgpu::TextureUsages::COPY_SRC`] is not an allowed\n/// flag for the surface texture on all platforms. This means that anytime we want to\n/// capture the frame, we first render it to this texture, and then we can copy it to\n/// both the surface texture (via a render pass) and the buffer (via a texture to buffer copy),\n/// from where we can pull it back\n/// to the cpu.\npub struct CaptureState {\n    padding: BufferPadding,\n    pub texture: wgpu::Texture,\n    pipeline: wgpu::RenderPipeline,\n    bind_group: wgpu::BindGroup,\n}\n\npub type CaptureReceiver = mpsc::Receiver<(ViewportId, Vec<UserData>, ColorImage)>;\npub type CaptureSender = mpsc::Sender<(ViewportId, Vec<UserData>, ColorImage)>;\npub use mpsc::channel as capture_channel;\n\nimpl CaptureState {\n    pub fn new(device: &wgpu::Device, surface_texture: &wgpu::Texture) -> Self {\n        let shader = device.create_shader_module(wgpu::include_wgsl!(\"texture_copy.wgsl\"));\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"texture_copy\"),\n            layout: None,\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: Some(\"vs_main\"),\n                compilation_options: Default::default(),\n                buffers: &[],\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                compilation_options: Default::default(),\n                targets: &[Some(surface_texture.format().into())],\n            }),\n            primitive: wgpu::PrimitiveState {\n                topology: wgpu::PrimitiveTopology::TriangleList,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let bind_group_layout = pipeline.get_bind_group_layout(0);\n\n        let (texture, padding, bind_group) =\n            Self::create_texture(device, surface_texture, &bind_group_layout);\n\n        Self {\n            padding,\n            texture,\n            pipeline,\n            bind_group,\n        }\n    }\n\n    fn create_texture(\n        device: &wgpu::Device,\n        surface_texture: &wgpu::Texture,\n        layout: &BindGroupLayout,\n    ) -> (wgpu::Texture, BufferPadding, wgpu::BindGroup) {\n        let texture = device.create_texture(&wgpu::TextureDescriptor {\n            label: Some(\"egui_screen_capture_texture\"),\n            size: surface_texture.size(),\n            mip_level_count: surface_texture.mip_level_count(),\n            sample_count: surface_texture.sample_count(),\n            dimension: surface_texture.dimension(),\n            format: surface_texture.format(),\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT\n                | wgpu::TextureUsages::TEXTURE_BINDING\n                | wgpu::TextureUsages::COPY_SRC,\n            view_formats: &[],\n        });\n\n        let padding = BufferPadding::new(surface_texture.width());\n\n        let view = texture.create_view(&Default::default());\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: wgpu::BindingResource::TextureView(&view),\n            }],\n            label: None,\n        });\n\n        (texture, padding, bind_group)\n    }\n\n    /// Updates the [`CaptureState`] if the size of the surface texture has changed\n    pub fn update(&mut self, device: &wgpu::Device, texture: &wgpu::Texture) {\n        if self.texture.size() != texture.size() {\n            let (new_texture, padding, bind_group) =\n                Self::create_texture(device, texture, &self.pipeline.get_bind_group_layout(0));\n            self.texture = new_texture;\n            self.padding = padding;\n            self.bind_group = bind_group;\n        }\n    }\n\n    /// Handles copying from the [`CaptureState`] texture to the surface texture and the buffer.\n    /// Pass the returned buffer to [`CaptureState::read_screen_rgba`] to read the data back to the cpu.\n    pub fn copy_textures(\n        &mut self,\n        device: &wgpu::Device,\n        output_frame: &wgpu::SurfaceTexture,\n        encoder: &mut wgpu::CommandEncoder,\n    ) -> wgpu::Buffer {\n        debug_assert_eq!(\n            self.texture.size(),\n            output_frame.texture.size(),\n            \"Texture sizes must match, `CaptureState::update` was probably not called\"\n        );\n\n        // It would be more efficient to reuse the Buffer, e.g. via some kind of ring buffer, but\n        // for most screenshot use cases this should be fine. When taking many screenshots (e.g. for a video)\n        // it might make sense to revisit this and implement a more efficient solution.\n        #[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm\n        let buffer = device.create_buffer(&wgpu::BufferDescriptor {\n            label: Some(\"egui_screen_capture_buffer\"),\n            size: (self.padding.padded_bytes_per_row * self.texture.height()) as u64,\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,\n            mapped_at_creation: false,\n        });\n        let padding = self.padding;\n        let tex = &mut self.texture;\n\n        let tex_extent = tex.size();\n\n        encoder.copy_texture_to_buffer(\n            tex.as_image_copy(),\n            wgpu::TexelCopyBufferInfo {\n                buffer: &buffer,\n                layout: wgpu::TexelCopyBufferLayout {\n                    offset: 0,\n                    bytes_per_row: Some(padding.padded_bytes_per_row),\n                    rows_per_image: None,\n                },\n            },\n            tex_extent,\n        );\n\n        let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n            label: Some(\"texture_copy\"),\n            color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                view: &output_frame.texture.create_view(&Default::default()),\n                resolve_target: None,\n                ops: wgpu::Operations {\n                    load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),\n                    store: StoreOp::Store,\n                },\n                depth_slice: None,\n            })],\n            depth_stencil_attachment: None,\n            occlusion_query_set: None,\n            timestamp_writes: None,\n            multiview_mask: None,\n        });\n\n        pass.set_pipeline(&self.pipeline);\n        pass.set_bind_group(0, &self.bind_group, &[]);\n        pass.draw(0..3, 0..1);\n\n        buffer\n    }\n\n    /// Handles copying from the [`CaptureState`] texture to the surface texture and the cpu\n    /// This function is non-blocking and will send the data to the given sender when it's ready.\n    /// Pass in the buffer returned from [`CaptureState::copy_textures`].\n    /// Make sure to call this after the encoder has been submitted.\n    pub fn read_screen_rgba(\n        &self,\n        ctx: egui::Context,\n        buffer: wgpu::Buffer,\n        data: Vec<UserData>,\n        tx: CaptureSender,\n        viewport_id: ViewportId,\n    ) {\n        #[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm\n        let buffer = Arc::new(buffer);\n        let buffer_clone = Arc::clone(&buffer);\n        let buffer_slice = buffer_clone.slice(..);\n        let format = self.texture.format();\n        let tex_extent = self.texture.size();\n        let padding = self.padding;\n        let to_rgba = match format {\n            wgpu::TextureFormat::Rgba8Unorm => [0, 1, 2, 3],\n            wgpu::TextureFormat::Bgra8Unorm => [2, 1, 0, 3],\n            _ => {\n                log::error!(\n                    \"Screen can't be captured unless the surface format is Rgba8Unorm or Bgra8Unorm. Current surface format is {format:?}\"\n                );\n                return;\n            }\n        };\n        buffer_slice.map_async(wgpu::MapMode::Read, move |result| {\n            if let Err(err) = result {\n                log::error!(\"Failed to map buffer for reading: {err}\");\n                return;\n            }\n            let buffer_slice = buffer.slice(..);\n\n            let mut pixels = Vec::with_capacity((tex_extent.width * tex_extent.height) as usize);\n            for padded_row in buffer_slice\n                .get_mapped_range()\n                .chunks(padding.padded_bytes_per_row as usize)\n            {\n                let row = &padded_row[..padding.unpadded_bytes_per_row as usize];\n                for color in row.chunks(4) {\n                    pixels.push(epaint::Color32::from_rgba_premultiplied(\n                        color[to_rgba[0]],\n                        color[to_rgba[1]],\n                        color[to_rgba[2]],\n                        color[to_rgba[3]],\n                    ));\n                }\n            }\n            buffer.unmap();\n\n            tx.send((\n                viewport_id,\n                data,\n                ColorImage::new(\n                    [tex_extent.width as usize, tex_extent.height as usize],\n                    pixels,\n                ),\n            ))\n            .ok();\n            ctx.request_repaint();\n        });\n    }\n}\n\n#[derive(Copy, Clone)]\nstruct BufferPadding {\n    unpadded_bytes_per_row: u32,\n    padded_bytes_per_row: u32,\n}\n\nimpl BufferPadding {\n    fn new(width: u32) -> Self {\n        let bytes_per_pixel = std::mem::size_of::<u32>() as u32;\n        let unpadded_bytes_per_row = width * bytes_per_pixel;\n        let padded_bytes_per_row =\n            wgpu::util::align_to(unpadded_bytes_per_row, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);\n        Self {\n            unpadded_bytes_per_row,\n            padded_bytes_per_row,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui-wgpu/src/egui.wgsl",
    "content": "// Vertex shader bindings\n\nstruct VertexOutput {\n    @location(0) tex_coord: vec2<f32>,\n    @location(1) color: vec4<f32>, // gamma 0-1\n    @builtin(position) position: vec4<f32>,\n};\n\nstruct Locals {\n    screen_size: vec2<f32>,\n\n    /// 1 if dithering is enabled, 0 otherwise\n    dithering: u32,\n\n    /// 1 to do manual filtering for more predictable kittest snapshot images.\n    /// See also https://github.com/emilk/egui/issues/5295\n    predictable_texture_filtering: u32,\n};\n@group(0) @binding(0) var<uniform> r_locals: Locals;\n\n\n// -----------------------------------------------\n// Adapted from\n// https://www.shadertoy.com/view/llVGzG\n// Originally presented in:\n// Jimenez 2014, \"Next Generation Post-Processing in Call of Duty\"\n//\n// A good overview can be found in\n// https://blog.demofox.org/2022/01/01/interleaved-gradient-noise-a-different-kind-of-low-discrepancy-sequence/\n// via https://github.com/rerun-io/rerun/\nfn interleaved_gradient_noise(n: vec2<f32>) -> f32 {\n    let f = 0.06711056 * n.x + 0.00583715 * n.y;\n    return fract(52.9829189 * fract(f));\n}\n\nfn dither_interleaved(rgb: vec3<f32>, levels: f32, frag_coord: vec4<f32>) -> vec3<f32> {\n    var noise = interleaved_gradient_noise(frag_coord.xy);\n    // scale down the noise slightly to ensure flat colors aren't getting dithered\n    noise = (noise - 0.5) * 0.95;\n    return rgb + noise / (levels - 1.0);\n}\n\n// 0-1 linear  from  0-1 sRGB gamma\nfn linear_from_gamma_rgb(srgb: vec3<f32>) -> vec3<f32> {\n    let cutoff = srgb < vec3<f32>(0.04045);\n    let lower = srgb / vec3<f32>(12.92);\n    let higher = pow((srgb + vec3<f32>(0.055)) / vec3<f32>(1.055), vec3<f32>(2.4));\n    return select(higher, lower, cutoff);\n}\n\n// 0-1 sRGB gamma  from  0-1 linear\nfn gamma_from_linear_rgb(rgb: vec3<f32>) -> vec3<f32> {\n    let cutoff = rgb < vec3<f32>(0.0031308);\n    let lower = rgb * vec3<f32>(12.92);\n    let higher = vec3<f32>(1.055) * pow(rgb, vec3<f32>(1.0 / 2.4)) - vec3<f32>(0.055);\n    return select(higher, lower, cutoff);\n}\n\n// 0-1 sRGBA gamma  from  0-1 linear\nfn gamma_from_linear_rgba(linear_rgba: vec4<f32>) -> vec4<f32> {\n    return vec4<f32>(gamma_from_linear_rgb(linear_rgba.rgb), linear_rgba.a);\n}\n\n// [u8; 4] SRGB as u32 -> [r, g, b, a] in 0.-1\nfn unpack_color(color: u32) -> vec4<f32> {\n    return vec4<f32>(\n        f32(color & 255u),\n        f32((color >> 8u) & 255u),\n        f32((color >> 16u) & 255u),\n        f32((color >> 24u) & 255u),\n    ) / 255.0;\n}\n\nfn position_from_screen(screen_pos: vec2<f32>) -> vec4<f32> {\n    return vec4<f32>(\n        2.0 * screen_pos.x / r_locals.screen_size.x - 1.0,\n        1.0 - 2.0 * screen_pos.y / r_locals.screen_size.y,\n        0.0,\n        1.0,\n    );\n}\n\n@vertex\nfn vs_main(\n    @location(0) a_pos: vec2<f32>,\n    @location(1) a_tex_coord: vec2<f32>,\n    @location(2) a_color: u32,\n) -> VertexOutput {\n    var out: VertexOutput;\n    out.tex_coord = a_tex_coord;\n    out.color = unpack_color(a_color);\n    out.position = position_from_screen(a_pos);\n    return out;\n}\n\n// Fragment shader bindings\n\n@group(1) @binding(0) var r_tex_color: texture_2d<f32>;\n@group(1) @binding(1) var r_tex_sampler: sampler;\n\nfn sample_texture(in: VertexOutput) -> vec4<f32> {\n    if r_locals.predictable_texture_filtering == 0 {\n        // Hardware filtering: fast, but varies across GPUs and drivers.\n        return textureSample(r_tex_color, r_tex_sampler, in.tex_coord);\n    } else {\n        // Manual bilinear filtering with four taps at pixel centers using textureLoad\n        let texture_size = vec2<i32>(textureDimensions(r_tex_color, 0));\n        let texture_size_f = vec2<f32>(texture_size);\n        let pixel_coord = in.tex_coord * texture_size_f - 0.5;\n        let pixel_fract = fract(pixel_coord);\n        let pixel_floor = vec2<i32>(floor(pixel_coord));\n\n        // Manual texture clamping\n        let max_coord = texture_size - vec2<i32>(1, 1);\n        let p00 = clamp(pixel_floor + vec2<i32>(0, 0), vec2<i32>(0, 0), max_coord);\n        let p10 = clamp(pixel_floor + vec2<i32>(1, 0), vec2<i32>(0, 0), max_coord);\n        let p01 = clamp(pixel_floor + vec2<i32>(0, 1), vec2<i32>(0, 0), max_coord);\n        let p11 = clamp(pixel_floor + vec2<i32>(1, 1), vec2<i32>(0, 0), max_coord);\n\n        // Load at pixel centers\n        let tl = textureLoad(r_tex_color, p00, 0);\n        let tr = textureLoad(r_tex_color, p10, 0);\n        let bl = textureLoad(r_tex_color, p01, 0);\n        let br = textureLoad(r_tex_color, p11, 0);\n\n        // Manual bilinear interpolation\n        let top = mix(tl, tr, pixel_fract.x);\n        let bottom = mix(bl, br, pixel_fract.x);\n        return mix(top, bottom, pixel_fract.y);\n    }\n}\n\n@fragment\nfn fs_main_linear_framebuffer(in: VertexOutput) -> @location(0) vec4<f32> {\n    // We expect \"normal\" textures that are NOT sRGB-aware.\n    let tex_gamma = sample_texture(in);\n    var out_color_gamma = in.color * tex_gamma;\n    // Dither the float color down to eight bits to reduce banding.\n    // This step is optional for egui backends.\n    // Note that dithering is performed on the gamma encoded values,\n    // because this function is used together with a srgb converting target.\n    if r_locals.dithering == 1 {\n        let out_color_gamma_rgb = dither_interleaved(out_color_gamma.rgb, 256.0, in.position);\n        out_color_gamma = vec4<f32>(out_color_gamma_rgb, out_color_gamma.a);\n    }\n    let out_color_linear = linear_from_gamma_rgb(out_color_gamma.rgb);\n    return vec4<f32>(out_color_linear, out_color_gamma.a);\n}\n\n@fragment\nfn fs_main_gamma_framebuffer(in: VertexOutput) -> @location(0) vec4<f32> {\n    // We expect \"normal\" textures that are NOT sRGB-aware.\n    let tex_gamma = sample_texture(in);\n    var out_color_gamma = in.color * tex_gamma;\n    // Dither the float color down to eight bits to reduce banding.\n    // This step is optional for egui backends.\n    if r_locals.dithering == 1 {\n        let out_color_gamma_rgb = dither_interleaved(out_color_gamma.rgb, 256.0, in.position);\n        out_color_gamma = vec4<f32>(out_color_gamma_rgb, out_color_gamma.a);\n    }\n    return out_color_gamma;\n}\n"
  },
  {
    "path": "crates/egui-wgpu/src/lib.rs",
    "content": "//! This crates provides bindings between [`egui`](https://github.com/emilk/egui) and [wgpu](https://crates.io/crates/wgpu).\n//!\n//! If you're targeting WebGL you also need to turn on the\n//! `webgl` feature of the `wgpu` crate:\n//!\n//! ```toml\n//! # Enable both WebGL and WebGPU backends on web.\n//! wgpu = { version = \"*\", features = [\"webgpu\", \"webgl\"] }\n//! ```\n//!\n//! You can control whether WebGL or WebGPU will be picked at runtime by configuring\n//! [`WgpuConfiguration::wgpu_setup`].\n//! The default is to prefer WebGPU and fall back on WebGL.\n//!\n//! ## Feature flags\n#![doc = document_features::document_features!()]\n//!\n\npub use wgpu;\n\n/// Low-level painting of [`egui`](https://github.com/emilk/egui) on [`wgpu`].\nmod renderer;\n\nmod setup;\n\npub use renderer::*;\npub use setup::{NativeAdapterSelectorMethod, WgpuSetup, WgpuSetupCreateNew, WgpuSetupExisting};\n\n/// Helpers for capturing screenshots of the UI.\n#[cfg(feature = \"capture\")]\npub mod capture;\n\n/// Module for painting [`egui`](https://github.com/emilk/egui) with [`wgpu`] on [`winit`].\n#[cfg(feature = \"winit\")]\npub mod winit;\n\nuse std::sync::Arc;\n\nuse epaint::mutex::RwLock;\n\n/// An error produced by egui-wgpu.\n#[derive(thiserror::Error, Debug)]\npub enum WgpuError {\n    #[error(transparent)]\n    RequestAdapterError(#[from] wgpu::RequestAdapterError),\n\n    #[error(\"Adapter selection failed: {0}\")]\n    CustomNativeAdapterSelectionError(String),\n\n    #[error(\"There was no valid format for the surface at all.\")]\n    NoSurfaceFormatsAvailable,\n\n    #[error(transparent)]\n    RequestDeviceError(#[from] wgpu::RequestDeviceError),\n\n    #[error(transparent)]\n    CreateSurfaceError(#[from] wgpu::CreateSurfaceError),\n\n    #[cfg(feature = \"winit\")]\n    #[error(transparent)]\n    HandleError(#[from] ::winit::raw_window_handle::HandleError),\n}\n\n/// Access to the render state for egui.\n#[derive(Clone)]\npub struct RenderState {\n    /// Wgpu adapter used for rendering.\n    pub adapter: wgpu::Adapter,\n\n    /// All the available adapters.\n    ///\n    /// This is not available on web.\n    /// On web, we always select WebGPU is available, then fall back to WebGL if not.\n    #[cfg(not(target_arch = \"wasm32\"))]\n    pub available_adapters: Vec<wgpu::Adapter>,\n\n    /// Wgpu device used for rendering, created from the adapter.\n    pub device: wgpu::Device,\n\n    /// Wgpu queue used for rendering, created from the adapter.\n    pub queue: wgpu::Queue,\n\n    /// The target texture format used for presenting to the window.\n    pub target_format: wgpu::TextureFormat,\n\n    /// Egui renderer responsible for drawing the UI.\n    pub renderer: Arc<RwLock<Renderer>>,\n}\n\nasync fn request_adapter(\n    instance: &wgpu::Instance,\n    power_preference: wgpu::PowerPreference,\n    compatible_surface: Option<&wgpu::Surface<'_>>,\n    available_adapters: &[wgpu::Adapter],\n) -> Result<wgpu::Adapter, WgpuError> {\n    profiling::function_scope!();\n\n    let adapter = instance\n        .request_adapter(&wgpu::RequestAdapterOptions {\n            power_preference,\n            compatible_surface,\n            // We don't expose this as an option right now since it's fairly rarely useful:\n            // * only has an effect on native\n            // * fails if there's no software rasterizer available\n            // * can achieve the same with `native_adapter_selector`\n            force_fallback_adapter: false,\n        })\n        .await\n        .inspect_err(|_err| {\n            if cfg!(target_arch = \"wasm32\") {\n                // Nothing to add here\n            } else if available_adapters.is_empty() {\n                if std::env::var(\"DYLD_LIBRARY_PATH\").is_ok() {\n                    // DYLD_LIBRARY_PATH can sometimes lead to loading dylibs that cause\n                    // us to find zero adapters. Very strange.\n                    // I don't want to debug this again.\n                    // See https://github.com/rerun-io/rerun/issues/11351 for more\n                    log::warn!(\n                        \"No wgpu adapter found. This could be because DYLD_LIBRARY_PATH causes dylibs to be loaded that interfere with Metal device creation. Try restarting with DYLD_LIBRARY_PATH=''\"\n                    );\n                } else {\n                    log::info!(\"No wgpu adapter found\");\n                }\n            } else if available_adapters.len() == 1 {\n                log::info!(\n                    \"The only available wgpu adapter was not suitable: {}\",\n                    adapter_info_summary(&available_adapters[0].get_info())\n                );\n            } else {\n                log::info!(\n                    \"No suitable wgpu adapter found out of the {} available ones: {}\",\n                    available_adapters.len(),\n                    describe_adapters(available_adapters)\n                );\n            }\n        })?;\n\n    if cfg!(target_arch = \"wasm32\") {\n        log::debug!(\n            \"Picked wgpu adapter: {}\",\n            adapter_info_summary(&adapter.get_info())\n        );\n    } else {\n        // native:\n        if available_adapters.len() == 1 {\n            log::debug!(\n                \"Picked the only available wgpu adapter: {}\",\n                adapter_info_summary(&adapter.get_info())\n            );\n        } else {\n            log::info!(\n                \"There were {} available wgpu adapters: {}\",\n                available_adapters.len(),\n                describe_adapters(available_adapters)\n            );\n            log::debug!(\n                \"Picked wgpu adapter: {}\",\n                adapter_info_summary(&adapter.get_info())\n            );\n        }\n    }\n\n    Ok(adapter)\n}\n\nimpl RenderState {\n    /// Creates a new [`RenderState`], containing everything needed for drawing egui with wgpu.\n    ///\n    /// # Errors\n    /// Wgpu initialization may fail due to incompatible hardware or driver for a given config.\n    pub async fn create(\n        config: &WgpuConfiguration,\n        instance: &wgpu::Instance,\n        compatible_surface: Option<&wgpu::Surface<'static>>,\n        options: RendererOptions,\n    ) -> Result<Self, WgpuError> {\n        profiling::scope!(\"RenderState::create\"); // async yield give bad names using `profile_function`\n\n        // This is always an empty list on web.\n        #[cfg(not(target_arch = \"wasm32\"))]\n        let available_adapters = {\n            let backends = if let WgpuSetup::CreateNew(create_new) = &config.wgpu_setup {\n                create_new.instance_descriptor.backends\n            } else {\n                wgpu::Backends::all()\n            };\n\n            instance.enumerate_adapters(backends).await\n        };\n\n        let (adapter, device, queue) = match config.wgpu_setup.clone() {\n            WgpuSetup::CreateNew(WgpuSetupCreateNew {\n                instance_descriptor: _,\n                power_preference,\n                native_adapter_selector: _native_adapter_selector,\n                device_descriptor,\n            }) => {\n                let adapter = {\n                    #[cfg(target_arch = \"wasm32\")]\n                    {\n                        request_adapter(instance, power_preference, compatible_surface, &[]).await\n                    }\n                    #[cfg(not(target_arch = \"wasm32\"))]\n                    if let Some(native_adapter_selector) = _native_adapter_selector {\n                        native_adapter_selector(&available_adapters, compatible_surface)\n                            .map_err(WgpuError::CustomNativeAdapterSelectionError)\n                    } else {\n                        request_adapter(\n                            instance,\n                            power_preference,\n                            compatible_surface,\n                            &available_adapters,\n                        )\n                        .await\n                    }\n                }?;\n\n                let (device, queue) = {\n                    profiling::scope!(\"request_device\");\n                    adapter\n                        .request_device(&(*device_descriptor)(&adapter))\n                        .await?\n                };\n\n                (adapter, device, queue)\n            }\n            WgpuSetup::Existing(WgpuSetupExisting {\n                instance: _,\n                adapter,\n                device,\n                queue,\n            }) => (adapter, device, queue),\n        };\n\n        let surface_formats = {\n            profiling::scope!(\"get_capabilities\");\n            compatible_surface.map_or_else(\n                || vec![wgpu::TextureFormat::Rgba8Unorm],\n                |s| s.get_capabilities(&adapter).formats,\n            )\n        };\n        let target_format = crate::preferred_framebuffer_format(&surface_formats)?;\n\n        let renderer = Renderer::new(&device, target_format, options);\n\n        // On wasm, depending on feature flags, wgpu objects may or may not implement sync.\n        // It doesn't make sense to switch to Rc for that special usecase, so simply disable the lint.\n        #[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm\n        Ok(Self {\n            adapter,\n            #[cfg(not(target_arch = \"wasm32\"))]\n            available_adapters,\n            device,\n            queue,\n            target_format,\n            renderer: Arc::new(RwLock::new(renderer)),\n        })\n    }\n}\n\nfn describe_adapters(adapters: &[wgpu::Adapter]) -> String {\n    if adapters.is_empty() {\n        \"(none)\".to_owned()\n    } else if adapters.len() == 1 {\n        adapter_info_summary(&adapters[0].get_info())\n    } else {\n        adapters\n            .iter()\n            .map(|a| format!(\"{{{}}}\", adapter_info_summary(&a.get_info())))\n            .collect::<Vec<_>>()\n            .join(\", \")\n    }\n}\n\n/// Specifies which action should be taken as consequence of a [`wgpu::SurfaceError`]\npub enum SurfaceErrorAction {\n    /// Do nothing and skip the current frame.\n    SkipFrame,\n\n    /// Instructs egui to recreate the surface, then skip the current frame.\n    RecreateSurface,\n}\n\n/// Configuration for using wgpu with eframe or the egui-wgpu winit feature.\n#[derive(Clone)]\npub struct WgpuConfiguration {\n    /// Present mode used for the primary surface.\n    pub present_mode: wgpu::PresentMode,\n\n    /// Desired maximum number of frames that the presentation engine should queue in advance.\n    ///\n    /// Use `1` for low-latency, and `2` for high-throughput.\n    ///\n    /// See [`wgpu::SurfaceConfiguration::desired_maximum_frame_latency`] for details.\n    ///\n    /// `None` = `wgpu` default.\n    pub desired_maximum_frame_latency: Option<u32>,\n\n    /// How to create the wgpu adapter & device\n    pub wgpu_setup: WgpuSetup,\n\n    /// Callback for surface errors.\n    pub on_surface_error: Arc<dyn Fn(wgpu::SurfaceError) -> SurfaceErrorAction + Send + Sync>,\n}\n\n#[test]\nfn wgpu_config_impl_send_sync() {\n    fn assert_send_sync<T: Send + Sync>() {}\n    assert_send_sync::<WgpuConfiguration>();\n}\n\nimpl std::fmt::Debug for WgpuConfiguration {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let Self {\n            present_mode,\n            desired_maximum_frame_latency,\n            wgpu_setup,\n            on_surface_error: _,\n        } = self;\n        f.debug_struct(\"WgpuConfiguration\")\n            .field(\"present_mode\", &present_mode)\n            .field(\n                \"desired_maximum_frame_latency\",\n                &desired_maximum_frame_latency,\n            )\n            .field(\"wgpu_setup\", &wgpu_setup)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl Default for WgpuConfiguration {\n    fn default() -> Self {\n        Self {\n            present_mode: wgpu::PresentMode::AutoVsync,\n            desired_maximum_frame_latency: None,\n            wgpu_setup: Default::default(),\n            on_surface_error: Arc::new(|err| {\n                if err == wgpu::SurfaceError::Outdated {\n                    // This error occurs when the app is minimized on Windows.\n                    // Silently return here to prevent spamming the console with:\n                    // \"The underlying surface has changed, and therefore the swap chain must be updated\"\n                } else {\n                    log::warn!(\"Dropped frame with error: {err}\");\n                }\n                SurfaceErrorAction::SkipFrame\n            }),\n        }\n    }\n}\n\n/// Find the framebuffer format that egui prefers\n///\n/// # Errors\n/// Returns [`WgpuError::NoSurfaceFormatsAvailable`] if the given list of formats is empty.\npub fn preferred_framebuffer_format(\n    formats: &[wgpu::TextureFormat],\n) -> Result<wgpu::TextureFormat, WgpuError> {\n    for &format in formats {\n        if matches!(\n            format,\n            wgpu::TextureFormat::Rgba8Unorm | wgpu::TextureFormat::Bgra8Unorm\n        ) {\n            return Ok(format);\n        }\n    }\n\n    formats\n        .first()\n        .copied()\n        .ok_or(WgpuError::NoSurfaceFormatsAvailable)\n}\n\n/// Take's epi's depth/stencil bits and returns the corresponding wgpu format.\npub fn depth_format_from_bits(depth_buffer: u8, stencil_buffer: u8) -> Option<wgpu::TextureFormat> {\n    match (depth_buffer, stencil_buffer) {\n        (0, 8) => Some(wgpu::TextureFormat::Stencil8),\n        (16, 0) => Some(wgpu::TextureFormat::Depth16Unorm),\n        (24, 0) => Some(wgpu::TextureFormat::Depth24Plus),\n        (24, 8) => Some(wgpu::TextureFormat::Depth24PlusStencil8),\n        (32, 0) => Some(wgpu::TextureFormat::Depth32Float),\n        (32, 8) => Some(wgpu::TextureFormat::Depth32FloatStencil8),\n        _ => None,\n    }\n}\n\n// ---------------------------------------------------------------------------\n\n/// A human-readable summary about an adapter\npub fn adapter_info_summary(info: &wgpu::AdapterInfo) -> String {\n    let wgpu::AdapterInfo {\n        name,\n        vendor,\n        device,\n        device_type,\n        driver,\n        driver_info,\n        backend,\n        device_pci_bus_id,\n        subgroup_min_size,\n        subgroup_max_size,\n        transient_saves_memory,\n    } = &info;\n\n    // Example values:\n    // > name: \"llvmpipe (LLVM 16.0.6, 256 bits)\", device_type: Cpu, backend: Vulkan, driver: \"llvmpipe\", driver_info: \"Mesa 23.1.6-arch1.4 (LLVM 16.0.6)\"\n    // > name: \"Apple M1 Pro\", device_type: IntegratedGpu, backend: Metal, driver: \"\", driver_info: \"\"\n    // > name: \"ANGLE (Apple, Apple M1 Pro, OpenGL 4.1)\", device_type: IntegratedGpu, backend: Gl, driver: \"\", driver_info: \"\"\n\n    let mut summary = format!(\"backend: {backend:?}, device_type: {device_type:?}\");\n\n    if !name.is_empty() {\n        summary += &format!(\", name: {name:?}\");\n    }\n    if !driver.is_empty() {\n        summary += &format!(\", driver: {driver:?}\");\n    }\n    if !driver_info.is_empty() {\n        summary += &format!(\", driver_info: {driver_info:?}\");\n    }\n    if *vendor != 0 {\n        #[cfg(not(target_arch = \"wasm32\"))]\n        {\n            summary += &format!(\", vendor: {} (0x{vendor:04X})\", parse_vendor_id(*vendor));\n        }\n        #[cfg(target_arch = \"wasm32\")]\n        {\n            summary += &format!(\", vendor: 0x{vendor:04X}\");\n        }\n    }\n    if *device != 0 {\n        summary += &format!(\", device: 0x{device:02X}\");\n    }\n    if !device_pci_bus_id.is_empty() {\n        summary += &format!(\", pci_bus_id: {device_pci_bus_id:?}\");\n    }\n    if *subgroup_min_size != 0 || *subgroup_max_size != 0 {\n        summary += &format!(\", subgroup_size: {subgroup_min_size}..={subgroup_max_size}\");\n    }\n    summary += &format!(\", transient_saves_memory: {transient_saves_memory}\");\n\n    summary\n}\n\n/// Tries to parse the adapter's vendor ID to a human-readable string.\n#[cfg(not(target_arch = \"wasm32\"))]\npub fn parse_vendor_id(vendor_id: u32) -> &'static str {\n    match vendor_id {\n        wgpu::hal::auxil::db::amd::VENDOR => \"AMD\",\n        wgpu::hal::auxil::db::apple::VENDOR => \"Apple\",\n        wgpu::hal::auxil::db::arm::VENDOR => \"ARM\",\n        wgpu::hal::auxil::db::broadcom::VENDOR => \"Broadcom\",\n        wgpu::hal::auxil::db::imgtec::VENDOR => \"Imagination Technologies\",\n        wgpu::hal::auxil::db::intel::VENDOR => \"Intel\",\n        wgpu::hal::auxil::db::mesa::VENDOR => \"Mesa\",\n        wgpu::hal::auxil::db::nvidia::VENDOR => \"NVIDIA\",\n        wgpu::hal::auxil::db::qualcomm::VENDOR => \"Qualcomm\",\n        _ => \"Unknown\",\n    }\n}\n"
  },
  {
    "path": "crates/egui-wgpu/src/renderer.rs",
    "content": "#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps\n\nuse std::{borrow::Cow, num::NonZeroU64, ops::Range};\n\nuse ahash::HashMap;\nuse bytemuck::Zeroable as _;\nuse epaint::{PaintCallbackInfo, Primitive, Vertex, emath::NumExt as _};\n\nuse wgpu::util::DeviceExt as _;\n\n// Only implements Send + Sync on wasm32 in order to allow storing wgpu resources on the type map.\n#[cfg(not(all(\n    target_arch = \"wasm32\",\n    not(feature = \"fragile-send-sync-non-atomic-wasm\"),\n)))]\n/// You can use this for storage when implementing [`CallbackTrait`].\npub type CallbackResources = type_map::concurrent::TypeMap;\n#[cfg(all(\n    target_arch = \"wasm32\",\n    not(feature = \"fragile-send-sync-non-atomic-wasm\"),\n))]\n/// You can use this for storage when implementing [`CallbackTrait`].\npub type CallbackResources = type_map::TypeMap;\n\n/// You can use this to do custom [`wgpu`] rendering in an egui app.\n///\n/// Implement [`CallbackTrait`] and call [`Callback::new_paint_callback`].\n///\n/// This can be turned into a [`epaint::PaintCallback`] and [`epaint::Shape`].\npub struct Callback(Box<dyn CallbackTrait>);\n\nimpl Callback {\n    /// Creates a new [`epaint::PaintCallback`] from a callback trait instance.\n    pub fn new_paint_callback(\n        rect: epaint::emath::Rect,\n        callback: impl CallbackTrait + 'static,\n    ) -> epaint::PaintCallback {\n        epaint::PaintCallback {\n            rect,\n            callback: std::sync::Arc::new(Self(Box::new(callback))),\n        }\n    }\n}\n\n/// A callback trait that can be used to compose an [`epaint::PaintCallback`] via [`Callback`]\n/// for custom WGPU rendering.\n///\n/// Callbacks in [`Renderer`] are done in three steps:\n/// * [`CallbackTrait::prepare`]: called for all registered callbacks before the main egui render pass.\n/// * [`CallbackTrait::finish_prepare`]: called for all registered callbacks after all callbacks finished calling prepare.\n/// * [`CallbackTrait::paint`]: called for all registered callbacks during the main egui render pass.\n///\n/// Each callback has access to an instance of [`CallbackResources`] that is stored in the [`Renderer`].\n/// This can be used to store wgpu resources that need to be accessed during the [`CallbackTrait::paint`] step.\n///\n/// The callbacks implementing [`CallbackTrait`] itself must always be Send + Sync, but resources stored in\n/// [`Renderer::callback_resources`] are not required to implement Send + Sync when building for wasm.\n/// (this is because wgpu stores references to the JS heap in most of its resources which can not be shared with other threads).\n///\n///\n/// # Command submission\n///\n/// ## Command Encoder\n///\n/// The passed-in [`wgpu::CommandEncoder`] is egui's and can be used directly to register\n/// wgpu commands for simple use cases.\n/// This allows reusing the same [`wgpu::CommandEncoder`] for all callbacks and egui\n/// rendering itself.\n///\n/// ## Command Buffers\n///\n/// For more complicated use cases, one can also return a list of arbitrary\n/// [`wgpu::CommandBuffer`]s and have complete control over how they get created and fed.\n/// In particular, this gives an opportunity to parallelize command registration and\n/// prevents a faulty callback from poisoning the main wgpu pipeline.\n///\n/// When using eframe, the main egui command buffer, as well as all user-defined\n/// command buffers returned by this function, are guaranteed to all be submitted\n/// at once in a single call.\n///\n/// Command Buffers returned by [`CallbackTrait::finish_prepare`] will always be issued *after*\n/// those returned by [`CallbackTrait::prepare`].\n/// Order within command buffers returned by [`CallbackTrait::prepare`] is dependent\n/// on the order the respective [`epaint::Shape::Callback`]s were submitted in.\n///\n/// # Example\n///\n/// See the [`custom3d_wgpu`](https://github.com/emilk/egui/blob/main/crates/egui_demo_app/src/apps/custom3d_wgpu.rs) demo source for a detailed usage example.\npub trait CallbackTrait: Send + Sync {\n    fn prepare(\n        &self,\n        _device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n        _screen_descriptor: &ScreenDescriptor,\n        _egui_encoder: &mut wgpu::CommandEncoder,\n        _callback_resources: &mut CallbackResources,\n    ) -> Vec<wgpu::CommandBuffer> {\n        Vec::new()\n    }\n\n    /// Called after all [`CallbackTrait::prepare`] calls are done.\n    fn finish_prepare(\n        &self,\n        _device: &wgpu::Device,\n        _queue: &wgpu::Queue,\n        _egui_encoder: &mut wgpu::CommandEncoder,\n        _callback_resources: &mut CallbackResources,\n    ) -> Vec<wgpu::CommandBuffer> {\n        Vec::new()\n    }\n\n    /// Called after all [`CallbackTrait::finish_prepare`] calls are done.\n    ///\n    /// It is given access to the [`wgpu::RenderPass`] so that it can issue draw commands\n    /// into the same [`wgpu::RenderPass`] that is used for all other egui elements.\n    fn paint(\n        &self,\n        info: PaintCallbackInfo,\n        render_pass: &mut wgpu::RenderPass<'static>,\n        callback_resources: &CallbackResources,\n    );\n}\n\n/// Information about the screen used for rendering.\npub struct ScreenDescriptor {\n    /// Size of the window in physical pixels.\n    pub size_in_pixels: [u32; 2],\n\n    /// High-DPI scale factor (pixels per point).\n    pub pixels_per_point: f32,\n}\n\nimpl ScreenDescriptor {\n    /// size in \"logical\" points\n    fn screen_size_in_points(&self) -> [f32; 2] {\n        [\n            self.size_in_pixels[0] as f32 / self.pixels_per_point,\n            self.size_in_pixels[1] as f32 / self.pixels_per_point,\n        ]\n    }\n}\n\n/// Uniform buffer used when rendering.\n#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]\n#[repr(C)]\nstruct UniformBuffer {\n    screen_size_in_points: [f32; 2],\n    dithering: u32,\n\n    /// 1 to do manual filtering for more predictable kittest snapshot images.\n    ///\n    /// See also <https://github.com/emilk/egui/issues/5295>.\n    predictable_texture_filtering: u32,\n}\n\nstruct SlicedBuffer {\n    buffer: wgpu::Buffer,\n    slices: Vec<Range<usize>>,\n    capacity: wgpu::BufferAddress,\n}\n\npub struct Texture {\n    /// The texture may be None if the `TextureId` is just a handle to a user-provided bind-group.\n    pub texture: Option<wgpu::Texture>,\n\n    /// Bindgroup for the texture + sampler.\n    pub bind_group: wgpu::BindGroup,\n\n    /// Options describing the sampler used in the bind group. This may be None if the `TextureId`\n    /// is just a handle to a user-provided bind-group.\n    pub options: Option<epaint::textures::TextureOptions>,\n}\n\n/// Ways to configure [`Renderer`] during creation.\n#[derive(Clone, Copy, Debug)]\npub struct RendererOptions {\n    /// Set the level of the multisampling anti-aliasing (MSAA).\n    ///\n    /// Must be a power-of-two. Higher = more smooth 3D.\n    ///\n    /// A value of `0` or `1` turns it off (default).\n    ///\n    /// `egui` already performs anti-aliasing via \"feathering\"\n    /// (controlled by [`egui::epaint::TessellationOptions`]),\n    /// but if you are embedding 3D in egui you may want to turn on multisampling.\n    pub msaa_samples: u32,\n\n    /// What format to use for the depth and stencil buffers,\n    /// e.g. [`wgpu::TextureFormat::Depth32FloatStencil8`].\n    ///\n    /// egui doesn't need depth/stencil, so the default value is `None` (no depth or stancil buffers).\n    pub depth_stencil_format: Option<wgpu::TextureFormat>,\n\n    /// Controls whether to apply dithering to minimize banding artifacts.\n    ///\n    /// Dithering assumes an sRGB output and thus will apply noise to any input value that lies between\n    /// two 8bit values after applying the sRGB OETF function, i.e. if it's not a whole 8bit value in \"gamma space\".\n    /// This means that only inputs from texture interpolation and vertex colors should be affected in practice.\n    ///\n    /// Defaults to true.\n    pub dithering: bool,\n\n    /// Perform texture filtering in software?\n    ///\n    /// This is useful when you want predictable rendering across\n    /// different hardware, e.g. for kittest snapshots.\n    ///\n    /// Default is `false`.\n    ///\n    /// See also <https://github.com/emilk/egui/issues/5295>.\n    pub predictable_texture_filtering: bool,\n}\n\nimpl RendererOptions {\n    /// Set options that produce the most predicatable output.\n    ///\n    /// Useful for image snapshot tests.\n    pub const PREDICTABLE: Self = Self {\n        msaa_samples: 1,\n        depth_stencil_format: None,\n        dithering: false,\n        predictable_texture_filtering: true,\n    };\n}\n\nimpl Default for RendererOptions {\n    fn default() -> Self {\n        Self {\n            msaa_samples: 0,\n            depth_stencil_format: None,\n            dithering: true,\n            predictable_texture_filtering: false,\n        }\n    }\n}\n\n/// Renderer for a egui based GUI.\npub struct Renderer {\n    pipeline: wgpu::RenderPipeline,\n\n    index_buffer: SlicedBuffer,\n    vertex_buffer: SlicedBuffer,\n\n    uniform_buffer: wgpu::Buffer,\n    previous_uniform_buffer_content: UniformBuffer,\n    uniform_bind_group: wgpu::BindGroup,\n    texture_bind_group_layout: wgpu::BindGroupLayout,\n\n    /// Map of egui texture IDs to textures and their associated bindgroups (texture view +\n    /// sampler). The texture may be None if the `TextureId` is just a handle to a user-provided\n    /// sampler.\n    textures: HashMap<epaint::TextureId, Texture>,\n    next_user_texture_id: u64,\n    samplers: HashMap<epaint::textures::TextureOptions, wgpu::Sampler>,\n\n    options: RendererOptions,\n\n    /// Storage for resources shared with all invocations of [`CallbackTrait`]'s methods.\n    ///\n    /// See also [`CallbackTrait`].\n    pub callback_resources: CallbackResources,\n}\n\nimpl Renderer {\n    /// Creates a renderer for a egui UI.\n    ///\n    /// `output_color_format` should preferably be [`wgpu::TextureFormat::Rgba8Unorm`] or\n    /// [`wgpu::TextureFormat::Bgra8Unorm`], i.e. in gamma-space.\n    pub fn new(\n        device: &wgpu::Device,\n        output_color_format: wgpu::TextureFormat,\n        options: RendererOptions,\n    ) -> Self {\n        profiling::function_scope!();\n\n        let shader = wgpu::ShaderModuleDescriptor {\n            label: Some(\"egui\"),\n            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(\"egui.wgsl\"))),\n        };\n        let module = {\n            profiling::scope!(\"create_shader_module\");\n            device.create_shader_module(shader)\n        };\n\n        let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"egui_uniform_buffer\"),\n            contents: bytemuck::cast_slice(&[UniformBuffer {\n                screen_size_in_points: [0.0, 0.0],\n                dithering: u32::from(options.dithering),\n                predictable_texture_filtering: u32::from(options.predictable_texture_filtering),\n            }]),\n            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,\n        });\n\n        let uniform_bind_group_layout = {\n            profiling::scope!(\"create_bind_group_layout\");\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                label: Some(\"egui_uniform_bind_group_layout\"),\n                entries: &[wgpu::BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,\n                    ty: wgpu::BindingType::Buffer {\n                        has_dynamic_offset: false,\n                        min_binding_size: NonZeroU64::new(std::mem::size_of::<UniformBuffer>() as _),\n                        ty: wgpu::BufferBindingType::Uniform,\n                    },\n                    count: None,\n                }],\n            })\n        };\n\n        let uniform_bind_group = {\n            profiling::scope!(\"create_bind_group\");\n            device.create_bind_group(&wgpu::BindGroupDescriptor {\n                label: Some(\"egui_uniform_bind_group\"),\n                layout: &uniform_bind_group_layout,\n                entries: &[wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {\n                        buffer: &uniform_buffer,\n                        offset: 0,\n                        size: None,\n                    }),\n                }],\n            })\n        };\n\n        let texture_bind_group_layout = {\n            profiling::scope!(\"create_bind_group_layout\");\n            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                label: Some(\"egui_texture_bind_group_layout\"),\n                entries: &[\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 0,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Texture {\n                            multisampled: false,\n                            sample_type: wgpu::TextureSampleType::Float { filterable: true },\n                            view_dimension: wgpu::TextureViewDimension::D2,\n                        },\n                        count: None,\n                    },\n                    wgpu::BindGroupLayoutEntry {\n                        binding: 1,\n                        visibility: wgpu::ShaderStages::FRAGMENT,\n                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),\n                        count: None,\n                    },\n                ],\n            })\n        };\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"egui_pipeline_layout\"),\n            bind_group_layouts: &[&uniform_bind_group_layout, &texture_bind_group_layout],\n            immediate_size: 0,\n        });\n\n        let depth_stencil = options\n            .depth_stencil_format\n            .map(|format| wgpu::DepthStencilState {\n                format,\n                depth_write_enabled: false,\n                depth_compare: wgpu::CompareFunction::Always,\n                stencil: wgpu::StencilState::default(),\n                bias: wgpu::DepthBiasState::default(),\n            });\n\n        let pipeline = {\n            profiling::scope!(\"create_render_pipeline\");\n            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n                label: Some(\"egui_pipeline\"),\n                layout: Some(&pipeline_layout),\n                vertex: wgpu::VertexState {\n                    entry_point: Some(\"vs_main\"),\n                    module: &module,\n                    buffers: &[wgpu::VertexBufferLayout {\n                        array_stride: 5 * 4,\n                        step_mode: wgpu::VertexStepMode::Vertex,\n                        // 0: vec2 position\n                        // 1: vec2 texture coordinates\n                        // 2: uint color\n                        attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Uint32],\n                    }],\n                    compilation_options: wgpu::PipelineCompilationOptions::default()\n                },\n                primitive: wgpu::PrimitiveState {\n                    topology: wgpu::PrimitiveTopology::TriangleList,\n                    unclipped_depth: false,\n                    conservative: false,\n                    cull_mode: None,\n                    front_face: wgpu::FrontFace::default(),\n                    polygon_mode: wgpu::PolygonMode::default(),\n                    strip_index_format: None,\n                },\n                depth_stencil,\n                multisample: wgpu::MultisampleState {\n                    alpha_to_coverage_enabled: false,\n                    count: options.msaa_samples.max(1),\n                    mask: !0,\n                },\n\n                fragment: Some(wgpu::FragmentState {\n                    module: &module,\n                    entry_point: Some(if output_color_format.is_srgb() {\n                        log::warn!(\"Detected a linear (sRGBA aware) framebuffer {output_color_format:?}. egui prefers Rgba8Unorm or Bgra8Unorm\");\n                        \"fs_main_linear_framebuffer\"\n                    } else {\n                        \"fs_main_gamma_framebuffer\" // this is what we prefer\n                    }),\n                    targets: &[Some(wgpu::ColorTargetState {\n                        format: output_color_format,\n                        blend: Some(wgpu::BlendState {\n                            color: wgpu::BlendComponent {\n                                src_factor: wgpu::BlendFactor::One,\n                                dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,\n                                operation: wgpu::BlendOperation::Add,\n                            },\n                            alpha: wgpu::BlendComponent {\n                                src_factor: wgpu::BlendFactor::OneMinusDstAlpha,\n                                dst_factor: wgpu::BlendFactor::One,\n                                operation: wgpu::BlendOperation::Add,\n                            },\n                        }),\n                        write_mask: wgpu::ColorWrites::ALL,\n                    })],\n                    compilation_options: wgpu::PipelineCompilationOptions::default()\n                }),\n                multiview_mask: None,\n                cache: None,\n            }\n        )\n        };\n\n        const VERTEX_BUFFER_START_CAPACITY: wgpu::BufferAddress =\n            (std::mem::size_of::<Vertex>() * 1024) as _;\n        const INDEX_BUFFER_START_CAPACITY: wgpu::BufferAddress =\n            (std::mem::size_of::<u32>() * 1024 * 3) as _;\n\n        Self {\n            pipeline,\n            vertex_buffer: SlicedBuffer {\n                buffer: create_vertex_buffer(device, VERTEX_BUFFER_START_CAPACITY),\n                slices: Vec::with_capacity(64),\n                capacity: VERTEX_BUFFER_START_CAPACITY,\n            },\n            index_buffer: SlicedBuffer {\n                buffer: create_index_buffer(device, INDEX_BUFFER_START_CAPACITY),\n                slices: Vec::with_capacity(64),\n                capacity: INDEX_BUFFER_START_CAPACITY,\n            },\n            uniform_buffer,\n            // Buffers on wgpu are zero initialized, so this is indeed its current state!\n            previous_uniform_buffer_content: UniformBuffer::zeroed(),\n            uniform_bind_group,\n            texture_bind_group_layout,\n            textures: HashMap::default(),\n            next_user_texture_id: 0,\n            samplers: HashMap::default(),\n            options,\n            callback_resources: CallbackResources::default(),\n        }\n    }\n\n    /// Executes the egui renderer onto an existing wgpu renderpass.\n    ///\n    /// Note that the lifetime of `render_pass` is `'static` which requires a call to [`wgpu::RenderPass::forget_lifetime`].\n    /// This allows users to pass resources that live outside of the callback resources to the render pass.\n    /// The render pass internally keeps all referenced resources alive as long as necessary.\n    /// The only consequence of `forget_lifetime` is that any operation on the parent encoder will cause a runtime error\n    /// instead of a compile time error.\n    pub fn render(\n        &self,\n        render_pass: &mut wgpu::RenderPass<'static>,\n        paint_jobs: &[epaint::ClippedPrimitive],\n        screen_descriptor: &ScreenDescriptor,\n    ) {\n        profiling::function_scope!();\n\n        let pixels_per_point = screen_descriptor.pixels_per_point;\n        let size_in_pixels = screen_descriptor.size_in_pixels;\n\n        // Whether or not we need to reset the render pass because a paint callback has just\n        // run.\n        let mut needs_reset = true;\n\n        let mut index_buffer_slices = self.index_buffer.slices.iter();\n        let mut vertex_buffer_slices = self.vertex_buffer.slices.iter();\n\n        for epaint::ClippedPrimitive {\n            clip_rect,\n            primitive,\n        } in paint_jobs\n        {\n            if needs_reset {\n                render_pass.set_viewport(\n                    0.0,\n                    0.0,\n                    size_in_pixels[0] as f32,\n                    size_in_pixels[1] as f32,\n                    0.0,\n                    1.0,\n                );\n                render_pass.set_pipeline(&self.pipeline);\n                render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);\n                needs_reset = false;\n            }\n\n            {\n                let rect = ScissorRect::new(clip_rect, pixels_per_point, size_in_pixels);\n\n                if rect.width == 0 || rect.height == 0 {\n                    // Skip rendering zero-sized clip areas.\n                    if let Primitive::Mesh(_) = primitive {\n                        // If this is a mesh, we need to advance the index and vertex buffer iterators:\n                        index_buffer_slices.next().unwrap();\n                        vertex_buffer_slices.next().unwrap();\n                    }\n                    continue;\n                }\n\n                render_pass.set_scissor_rect(rect.x, rect.y, rect.width, rect.height);\n            }\n\n            match primitive {\n                Primitive::Mesh(mesh) => {\n                    let index_buffer_slice = index_buffer_slices.next().unwrap();\n                    let vertex_buffer_slice = vertex_buffer_slices.next().unwrap();\n\n                    if let Some(Texture { bind_group, .. }) = self.textures.get(&mesh.texture_id) {\n                        render_pass.set_bind_group(1, bind_group, &[]);\n                        render_pass.set_index_buffer(\n                            self.index_buffer.buffer.slice(\n                                index_buffer_slice.start as u64..index_buffer_slice.end as u64,\n                            ),\n                            wgpu::IndexFormat::Uint32,\n                        );\n                        render_pass.set_vertex_buffer(\n                            0,\n                            self.vertex_buffer.buffer.slice(\n                                vertex_buffer_slice.start as u64..vertex_buffer_slice.end as u64,\n                            ),\n                        );\n                        render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);\n                    } else {\n                        log::warn!(\"Missing texture: {:?}\", mesh.texture_id);\n                    }\n                }\n                Primitive::Callback(callback) => {\n                    let Some(cbfn) = callback.callback.downcast_ref::<Callback>() else {\n                        // We already warned in the `prepare` callback\n                        continue;\n                    };\n\n                    let info = PaintCallbackInfo {\n                        viewport: callback.rect,\n                        clip_rect: *clip_rect,\n                        pixels_per_point,\n                        screen_size_px: size_in_pixels,\n                    };\n\n                    let viewport_px = info.viewport_in_pixels();\n                    if viewport_px.width_px > 0 && viewport_px.height_px > 0 {\n                        profiling::scope!(\"callback\");\n\n                        needs_reset = true;\n\n                        // We're setting a default viewport for the render pass as a\n                        // courtesy for the user, so that they don't have to think about\n                        // it in the simple case where they just want to fill the whole\n                        // paint area.\n                        //\n                        // The user still has the possibility of setting their own custom\n                        // viewport during the paint callback, effectively overriding this\n                        // one.\n                        render_pass.set_viewport(\n                            viewport_px.left_px as f32,\n                            viewport_px.top_px as f32,\n                            viewport_px.width_px as f32,\n                            viewport_px.height_px as f32,\n                            0.0,\n                            1.0,\n                        );\n\n                        cbfn.0.paint(info, render_pass, &self.callback_resources);\n                    }\n                }\n            }\n        }\n\n        render_pass.set_scissor_rect(0, 0, size_in_pixels[0], size_in_pixels[1]);\n    }\n\n    /// Should be called before [`Self::render`].\n    pub fn update_texture(\n        &mut self,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n        id: epaint::TextureId,\n        image_delta: &epaint::ImageDelta,\n    ) {\n        profiling::function_scope!();\n\n        let width = image_delta.image.width() as u32;\n        let height = image_delta.image.height() as u32;\n\n        let size = wgpu::Extent3d {\n            width,\n            height,\n            depth_or_array_layers: 1,\n        };\n\n        let data_color32 = match &image_delta.image {\n            epaint::ImageData::Color(image) => {\n                assert_eq!(\n                    width as usize * height as usize,\n                    image.pixels.len(),\n                    \"Mismatch between texture size and texel count\"\n                );\n                Cow::Borrowed(&image.pixels)\n            }\n        };\n        let data_bytes: &[u8] = bytemuck::cast_slice(data_color32.as_slice());\n\n        let queue_write_data_to_texture = |texture, origin| {\n            profiling::scope!(\"write_texture\");\n            queue.write_texture(\n                wgpu::TexelCopyTextureInfo {\n                    texture,\n                    mip_level: 0,\n                    origin,\n                    aspect: wgpu::TextureAspect::All,\n                },\n                data_bytes,\n                wgpu::TexelCopyBufferLayout {\n                    offset: 0,\n                    bytes_per_row: Some(4 * width),\n                    rows_per_image: Some(height),\n                },\n                size,\n            );\n        };\n\n        // Use same label for all resources associated with this texture id (no point in retyping the type)\n        let label_str = format!(\"egui_texid_{id:?}\");\n        let label = Some(label_str.as_str());\n\n        let (texture, origin, bind_group) = if let Some(pos) = image_delta.pos {\n            // update the existing texture\n            let Texture {\n                texture,\n                bind_group,\n                options,\n            } = self\n                .textures\n                .remove(&id)\n                .expect(\"Tried to update a texture that has not been allocated yet.\");\n            let texture = texture.expect(\"Tried to update user texture.\");\n            let options = options.expect(\"Tried to update user texture.\");\n            let origin = wgpu::Origin3d {\n                x: pos[0] as u32,\n                y: pos[1] as u32,\n                z: 0,\n            };\n\n            (\n                texture,\n                origin,\n                // If the TextureOptions are the same as the previous ones, we can reuse the bind group. Otherwise we\n                // have to recreate it.\n                if image_delta.options == options {\n                    Some(bind_group)\n                } else {\n                    None\n                },\n            )\n        } else {\n            // allocate a new texture\n            let texture = {\n                profiling::scope!(\"create_texture\");\n                device.create_texture(&wgpu::TextureDescriptor {\n                    label,\n                    size,\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    dimension: wgpu::TextureDimension::D2,\n                    format: wgpu::TextureFormat::Rgba8Unorm,\n                    usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,\n                    view_formats: &[wgpu::TextureFormat::Rgba8Unorm],\n                })\n            };\n            let origin = wgpu::Origin3d::ZERO;\n            (texture, origin, None)\n        };\n\n        let bind_group = bind_group.unwrap_or_else(|| {\n            let sampler = self\n                .samplers\n                .entry(image_delta.options)\n                .or_insert_with(|| create_sampler(image_delta.options, device));\n            device.create_bind_group(&wgpu::BindGroupDescriptor {\n                label,\n                layout: &self.texture_bind_group_layout,\n                entries: &[\n                    wgpu::BindGroupEntry {\n                        binding: 0,\n                        resource: wgpu::BindingResource::TextureView(\n                            &texture.create_view(&wgpu::TextureViewDescriptor::default()),\n                        ),\n                    },\n                    wgpu::BindGroupEntry {\n                        binding: 1,\n                        resource: wgpu::BindingResource::Sampler(sampler),\n                    },\n                ],\n            })\n        });\n\n        queue_write_data_to_texture(&texture, origin);\n        self.textures.insert(\n            id,\n            Texture {\n                texture: Some(texture),\n                bind_group,\n                options: Some(image_delta.options),\n            },\n        );\n    }\n\n    pub fn free_texture(&mut self, id: &epaint::TextureId) {\n        if let Some(texture) = self.textures.remove(id).and_then(|t| t.texture) {\n            texture.destroy();\n        }\n    }\n\n    /// Get the WGPU texture and bind group associated to a texture that has been allocated by egui.\n    ///\n    /// This could be used by custom paint hooks to render images that have been added through\n    /// [`epaint::Context::load_texture`](https://docs.rs/egui/latest/egui/struct.Context.html#method.load_texture).\n    pub fn texture(&self, id: &epaint::TextureId) -> Option<&Texture> {\n        self.textures.get(id)\n    }\n\n    /// Registers a [`wgpu::Texture`] with a [`epaint::TextureId`].\n    ///\n    /// This enables the application to reference the texture inside an image ui element.\n    /// This effectively enables off-screen rendering inside the egui UI. Texture must have\n    /// the texture format [`wgpu::TextureFormat::Rgba8Unorm`].\n    pub fn register_native_texture(\n        &mut self,\n        device: &wgpu::Device,\n        texture: &wgpu::TextureView,\n        texture_filter: wgpu::FilterMode,\n    ) -> epaint::TextureId {\n        self.register_native_texture_with_sampler_options(\n            device,\n            texture,\n            wgpu::SamplerDescriptor {\n                label: Some(format!(\"egui_user_image_{}\", self.next_user_texture_id).as_str()),\n                mag_filter: texture_filter,\n                min_filter: texture_filter,\n                ..Default::default()\n            },\n        )\n    }\n\n    /// Registers a [`wgpu::Texture`] with an existing [`epaint::TextureId`].\n    ///\n    /// This enables applications to reuse [`epaint::TextureId`]s.\n    pub fn update_egui_texture_from_wgpu_texture(\n        &mut self,\n        device: &wgpu::Device,\n        texture: &wgpu::TextureView,\n        texture_filter: wgpu::FilterMode,\n        id: epaint::TextureId,\n    ) {\n        self.update_egui_texture_from_wgpu_texture_with_sampler_options(\n            device,\n            texture,\n            wgpu::SamplerDescriptor {\n                label: Some(format!(\"egui_user_image_{}\", self.next_user_texture_id).as_str()),\n                mag_filter: texture_filter,\n                min_filter: texture_filter,\n                ..Default::default()\n            },\n            id,\n        );\n    }\n\n    /// Registers a [`wgpu::Texture`] with a [`epaint::TextureId`] while also accepting custom\n    /// [`wgpu::SamplerDescriptor`] options.\n    ///\n    /// This allows applications to specify individual minification/magnification filters as well as\n    /// custom mipmap and tiling options.\n    ///\n    /// The texture must have the format [`wgpu::TextureFormat::Rgba8Unorm`].\n    /// Any compare function supplied in the [`wgpu::SamplerDescriptor`] will be ignored.\n    #[expect(clippy::needless_pass_by_value)] // false positive\n    pub fn register_native_texture_with_sampler_options(\n        &mut self,\n        device: &wgpu::Device,\n        texture: &wgpu::TextureView,\n        sampler_descriptor: wgpu::SamplerDescriptor<'_>,\n    ) -> epaint::TextureId {\n        profiling::function_scope!();\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            compare: None,\n            ..sampler_descriptor\n        });\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: Some(format!(\"egui_user_image_{}\", self.next_user_texture_id).as_str()),\n            layout: &self.texture_bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(texture),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n\n        let id = epaint::TextureId::User(self.next_user_texture_id);\n        self.textures.insert(\n            id,\n            Texture {\n                texture: None,\n                bind_group,\n                options: None,\n            },\n        );\n        self.next_user_texture_id += 1;\n\n        id\n    }\n\n    /// Registers a [`wgpu::Texture`] with an existing [`epaint::TextureId`] while also accepting custom\n    /// [`wgpu::SamplerDescriptor`] options.\n    ///\n    /// This allows applications to reuse [`epaint::TextureId`]s created with custom sampler options.\n    #[expect(clippy::needless_pass_by_value)] // false positive\n    pub fn update_egui_texture_from_wgpu_texture_with_sampler_options(\n        &mut self,\n        device: &wgpu::Device,\n        texture: &wgpu::TextureView,\n        sampler_descriptor: wgpu::SamplerDescriptor<'_>,\n        id: epaint::TextureId,\n    ) {\n        profiling::function_scope!();\n\n        let Texture {\n            bind_group: user_texture_binding,\n            ..\n        } = self\n            .textures\n            .get_mut(&id)\n            .expect(\"Tried to update a texture that has not been allocated yet.\");\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            compare: None,\n            ..sampler_descriptor\n        });\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: Some(format!(\"egui_user_image_{}\", self.next_user_texture_id).as_str()),\n            layout: &self.texture_bind_group_layout,\n            entries: &[\n                wgpu::BindGroupEntry {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(texture),\n                },\n                wgpu::BindGroupEntry {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n\n        *user_texture_binding = bind_group;\n    }\n\n    /// Uploads the uniform, vertex and index data used by the renderer.\n    /// Should be called before [`Self::render`].\n    ///\n    /// Returns all user-defined command buffers gathered from [`CallbackTrait::prepare`] & [`CallbackTrait::finish_prepare`] callbacks.\n    pub fn update_buffers(\n        &mut self,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n        encoder: &mut wgpu::CommandEncoder,\n        paint_jobs: &[epaint::ClippedPrimitive],\n        screen_descriptor: &ScreenDescriptor,\n    ) -> Vec<wgpu::CommandBuffer> {\n        profiling::function_scope!();\n\n        let screen_size_in_points = screen_descriptor.screen_size_in_points();\n\n        let uniform_buffer_content = UniformBuffer {\n            screen_size_in_points,\n            dithering: u32::from(self.options.dithering),\n            predictable_texture_filtering: u32::from(self.options.predictable_texture_filtering),\n        };\n        if uniform_buffer_content != self.previous_uniform_buffer_content {\n            profiling::scope!(\"update uniforms\");\n            queue.write_buffer(\n                &self.uniform_buffer,\n                0,\n                bytemuck::cast_slice(&[uniform_buffer_content]),\n            );\n            self.previous_uniform_buffer_content = uniform_buffer_content;\n        }\n\n        // Determine how many vertices & indices need to be rendered, and gather prepare callbacks\n        let mut callbacks = Vec::new();\n        let (vertex_count, index_count) = {\n            profiling::scope!(\"count_vertices_indices\");\n            paint_jobs.iter().fold((0, 0), |acc, clipped_primitive| {\n                match &clipped_primitive.primitive {\n                    Primitive::Mesh(mesh) => {\n                        (acc.0 + mesh.vertices.len(), acc.1 + mesh.indices.len())\n                    }\n                    Primitive::Callback(callback) => {\n                        if let Some(c) = callback.callback.downcast_ref::<Callback>() {\n                            callbacks.push(c.0.as_ref());\n                        } else {\n                            log::warn!(\"Unknown paint callback: expected `egui_wgpu::Callback`\");\n                        }\n                        acc\n                    }\n                }\n            })\n        };\n\n        if index_count > 0 {\n            profiling::scope!(\"indices\", index_count.to_string().as_str());\n\n            self.index_buffer.slices.clear();\n\n            let required_index_buffer_size = (std::mem::size_of::<u32>() * index_count) as u64;\n            if self.index_buffer.capacity < required_index_buffer_size {\n                // Resize index buffer if needed.\n                self.index_buffer.capacity =\n                    (self.index_buffer.capacity * 2).at_least(required_index_buffer_size);\n                self.index_buffer.buffer = create_index_buffer(device, self.index_buffer.capacity);\n            }\n\n            let index_buffer_staging = queue.write_buffer_with(\n                &self.index_buffer.buffer,\n                0,\n                NonZeroU64::new(required_index_buffer_size).unwrap(),\n            );\n\n            let Some(mut index_buffer_staging) = index_buffer_staging else {\n                panic!(\n                    \"Failed to create staging buffer for index data. Index count: {index_count}. Required index buffer size: {required_index_buffer_size}. Actual size {} and capacity: {} (bytes)\",\n                    self.index_buffer.buffer.size(),\n                    self.index_buffer.capacity\n                );\n            };\n\n            let mut index_offset = 0;\n            for epaint::ClippedPrimitive { primitive, .. } in paint_jobs {\n                match primitive {\n                    Primitive::Mesh(mesh) => {\n                        let size = mesh.indices.len() * std::mem::size_of::<u32>();\n                        let slice = index_offset..(size + index_offset);\n                        index_buffer_staging[slice.clone()]\n                            .copy_from_slice(bytemuck::cast_slice(&mesh.indices));\n                        self.index_buffer.slices.push(slice);\n                        index_offset += size;\n                    }\n                    Primitive::Callback(_) => {}\n                }\n            }\n        }\n        if vertex_count > 0 {\n            profiling::scope!(\"vertices\", vertex_count.to_string().as_str());\n\n            self.vertex_buffer.slices.clear();\n\n            let required_vertex_buffer_size = (std::mem::size_of::<Vertex>() * vertex_count) as u64;\n            if self.vertex_buffer.capacity < required_vertex_buffer_size {\n                // Resize vertex buffer if needed.\n                self.vertex_buffer.capacity =\n                    (self.vertex_buffer.capacity * 2).at_least(required_vertex_buffer_size);\n                self.vertex_buffer.buffer =\n                    create_vertex_buffer(device, self.vertex_buffer.capacity);\n            }\n\n            let vertex_buffer_staging = queue.write_buffer_with(\n                &self.vertex_buffer.buffer,\n                0,\n                NonZeroU64::new(required_vertex_buffer_size).unwrap(),\n            );\n\n            let Some(mut vertex_buffer_staging) = vertex_buffer_staging else {\n                panic!(\n                    \"Failed to create staging buffer for vertex data. Vertex count: {vertex_count}. Required vertex buffer size: {required_vertex_buffer_size}. Actual size {} and capacity: {} (bytes)\",\n                    self.vertex_buffer.buffer.size(),\n                    self.vertex_buffer.capacity\n                );\n            };\n\n            let mut vertex_offset = 0;\n            for epaint::ClippedPrimitive { primitive, .. } in paint_jobs {\n                match primitive {\n                    Primitive::Mesh(mesh) => {\n                        let size = mesh.vertices.len() * std::mem::size_of::<Vertex>();\n                        let slice = vertex_offset..(size + vertex_offset);\n                        vertex_buffer_staging[slice.clone()]\n                            .copy_from_slice(bytemuck::cast_slice(&mesh.vertices));\n                        self.vertex_buffer.slices.push(slice);\n                        vertex_offset += size;\n                    }\n                    Primitive::Callback(_) => {}\n                }\n            }\n        }\n\n        let mut user_cmd_bufs = Vec::new();\n        {\n            profiling::scope!(\"prepare callbacks\");\n            for callback in &callbacks {\n                user_cmd_bufs.extend(callback.prepare(\n                    device,\n                    queue,\n                    screen_descriptor,\n                    encoder,\n                    &mut self.callback_resources,\n                ));\n            }\n        }\n        {\n            profiling::scope!(\"finish prepare callbacks\");\n            for callback in &callbacks {\n                user_cmd_bufs.extend(callback.finish_prepare(\n                    device,\n                    queue,\n                    encoder,\n                    &mut self.callback_resources,\n                ));\n            }\n        }\n\n        user_cmd_bufs\n    }\n}\n\nfn create_sampler(\n    options: epaint::textures::TextureOptions,\n    device: &wgpu::Device,\n) -> wgpu::Sampler {\n    let mag_filter = match options.magnification {\n        epaint::textures::TextureFilter::Nearest => wgpu::FilterMode::Nearest,\n        epaint::textures::TextureFilter::Linear => wgpu::FilterMode::Linear,\n    };\n    let min_filter = match options.minification {\n        epaint::textures::TextureFilter::Nearest => wgpu::FilterMode::Nearest,\n        epaint::textures::TextureFilter::Linear => wgpu::FilterMode::Linear,\n    };\n    let address_mode = match options.wrap_mode {\n        epaint::textures::TextureWrapMode::ClampToEdge => wgpu::AddressMode::ClampToEdge,\n        epaint::textures::TextureWrapMode::Repeat => wgpu::AddressMode::Repeat,\n        epaint::textures::TextureWrapMode::MirroredRepeat => wgpu::AddressMode::MirrorRepeat,\n    };\n    device.create_sampler(&wgpu::SamplerDescriptor {\n        label: Some(&format!(\n            \"egui sampler (mag: {mag_filter:?}, min {min_filter:?})\"\n        )),\n        mag_filter,\n        min_filter,\n        address_mode_u: address_mode,\n        address_mode_v: address_mode,\n        ..Default::default()\n    })\n}\n\nfn create_vertex_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer {\n    profiling::function_scope!();\n    device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"egui_vertex_buffer\"),\n        usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,\n        size,\n        mapped_at_creation: false,\n    })\n}\n\nfn create_index_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer {\n    profiling::function_scope!();\n    device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"egui_index_buffer\"),\n        usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,\n        size,\n        mapped_at_creation: false,\n    })\n}\n\n/// A Rect in physical pixel space, used for setting clipping rectangles.\nstruct ScissorRect {\n    x: u32,\n    y: u32,\n    width: u32,\n    height: u32,\n}\n\nimpl ScissorRect {\n    fn new(clip_rect: &epaint::Rect, pixels_per_point: f32, target_size: [u32; 2]) -> Self {\n        // Transform clip rect to physical pixels:\n        let clip_min_x = pixels_per_point * clip_rect.min.x;\n        let clip_min_y = pixels_per_point * clip_rect.min.y;\n        let clip_max_x = pixels_per_point * clip_rect.max.x;\n        let clip_max_y = pixels_per_point * clip_rect.max.y;\n\n        // Round to integer:\n        let clip_min_x = clip_min_x.round() as u32;\n        let clip_min_y = clip_min_y.round() as u32;\n        let clip_max_x = clip_max_x.round() as u32;\n        let clip_max_y = clip_max_y.round() as u32;\n\n        // Clamp:\n        let clip_min_x = clip_min_x.clamp(0, target_size[0]);\n        let clip_min_y = clip_min_y.clamp(0, target_size[1]);\n        let clip_max_x = clip_max_x.clamp(clip_min_x, target_size[0]);\n        let clip_max_y = clip_max_y.clamp(clip_min_y, target_size[1]);\n\n        Self {\n            x: clip_min_x,\n            y: clip_min_y,\n            width: clip_max_x - clip_min_x,\n            height: clip_max_y - clip_min_y,\n        }\n    }\n}\n\n// Look at the feature flag for an explanation.\n#[cfg(not(all(\n    target_arch = \"wasm32\",\n    not(feature = \"fragile-send-sync-non-atomic-wasm\"),\n)))]\n#[test]\nfn renderer_impl_send_sync() {\n    fn assert_send_sync<T: Send + Sync>() {}\n    assert_send_sync::<Renderer>();\n}\n"
  },
  {
    "path": "crates/egui-wgpu/src/setup.rs",
    "content": "use std::sync::Arc;\n\n#[derive(Clone)]\npub enum WgpuSetup {\n    /// Construct a wgpu setup using some predefined settings & heuristics.\n    /// This is the default option. You can customize most behaviours overriding the\n    /// supported backends, power preferences, and device description.\n    ///\n    /// By default can also be configured with various environment variables:\n    /// * `WGPU_BACKEND`: `vulkan`, `dx12`, `metal`, `opengl`, `webgpu`\n    /// * `WGPU_POWER_PREF`: `low`, `high` or `none`\n    /// * `WGPU_TRACE`: Path to a file to output a wgpu trace file.\n    ///\n    /// Each instance flag also comes with an environment variable (for details see [`wgpu::InstanceFlags`]):\n    /// * `WGPU_VALIDATION`: Enables validation (enabled by default in debug builds).\n    /// * `WGPU_DEBUG`: Generate debug information in shaders and objects  (enabled by default in debug builds).\n    /// * `WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER`: Whether wgpu should expose adapters that run on top of non-compliant adapters.\n    /// * `WGPU_GPU_BASED_VALIDATION`: Enable GPU-based validation.\n    CreateNew(WgpuSetupCreateNew),\n\n    /// Run on an existing wgpu setup.\n    Existing(WgpuSetupExisting),\n}\n\nimpl Default for WgpuSetup {\n    fn default() -> Self {\n        Self::CreateNew(WgpuSetupCreateNew::default())\n    }\n}\n\nimpl std::fmt::Debug for WgpuSetup {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::CreateNew(create_new) => f\n                .debug_tuple(\"WgpuSetup::CreateNew\")\n                .field(create_new)\n                .finish(),\n            Self::Existing { .. } => f.debug_tuple(\"WgpuSetup::Existing\").finish(),\n        }\n    }\n}\n\nimpl WgpuSetup {\n    /// Creates a new [`wgpu::Instance`] or clones the existing one.\n    ///\n    /// Does *not* store the wgpu instance, so calling this repeatedly may\n    /// create a new instance every time!\n    pub async fn new_instance(&self) -> wgpu::Instance {\n        match self {\n            Self::CreateNew(create_new) => {\n                #[allow(clippy::allow_attributes, unused_mut)]\n                let mut backends = create_new.instance_descriptor.backends;\n\n                // Don't try WebGPU if we're not in a secure context.\n                #[cfg(target_arch = \"wasm32\")]\n                if backends.contains(wgpu::Backends::BROWSER_WEBGPU) {\n                    let is_secure_context =\n                        wgpu::web_sys::window().is_some_and(|w| w.is_secure_context());\n                    if !is_secure_context {\n                        log::info!(\n                            \"WebGPU is only available in secure contexts, i.e. on HTTPS and on localhost.\"\n                        );\n                        backends.remove(wgpu::Backends::BROWSER_WEBGPU);\n                    }\n                }\n\n                log::debug!(\"Creating wgpu instance with backends {backends:?}\");\n                wgpu::util::new_instance_with_webgpu_detection(&create_new.instance_descriptor)\n                    .await\n            }\n            Self::Existing(existing) => existing.instance.clone(),\n        }\n    }\n}\n\nimpl From<WgpuSetupCreateNew> for WgpuSetup {\n    fn from(create_new: WgpuSetupCreateNew) -> Self {\n        Self::CreateNew(create_new)\n    }\n}\n\nimpl From<WgpuSetupExisting> for WgpuSetup {\n    fn from(existing: WgpuSetupExisting) -> Self {\n        Self::Existing(existing)\n    }\n}\n\n/// Method for selecting an adapter on native.\n///\n/// This can be used for fully custom adapter selection.\n/// If available, `wgpu::Surface` is passed to allow checking for surface compatibility.\npub type NativeAdapterSelectorMethod = Arc<\n    dyn Fn(&[wgpu::Adapter], Option<&wgpu::Surface<'_>>) -> Result<wgpu::Adapter, String>\n        + Send\n        + Sync,\n>;\n\n/// Configuration for creating a new wgpu setup.\n///\n/// Used for [`WgpuSetup::CreateNew`].\npub struct WgpuSetupCreateNew {\n    /// Instance descriptor for creating a wgpu instance.\n    ///\n    /// The most important field is [`wgpu::InstanceDescriptor::backends`], which\n    /// controls which backends are supported (wgpu will pick one of these).\n    /// If you only want to support WebGL (and not WebGPU),\n    /// you can set this to [`wgpu::Backends::GL`].\n    /// By default on web, WebGPU will be used if available.\n    /// WebGL will only be used as a fallback,\n    /// and only if you have enabled the `webgl` feature of crate `wgpu`.\n    pub instance_descriptor: wgpu::InstanceDescriptor,\n\n    /// Power preference for the adapter if [`Self::native_adapter_selector`] is not set or targeting web.\n    pub power_preference: wgpu::PowerPreference,\n\n    /// Optional selector for native adapters.\n    ///\n    /// This field has no effect when targeting web!\n    /// Otherwise, if set [`Self::power_preference`] is ignored and the adapter is instead selected by this method.\n    /// Note that [`Self::instance_descriptor`]'s [`wgpu::InstanceDescriptor::backends`]\n    /// are still used to filter the adapter enumeration in the first place.\n    ///\n    /// Defaults to `None`.\n    pub native_adapter_selector: Option<NativeAdapterSelectorMethod>,\n\n    /// Configuration passed on device request, given an adapter\n    pub device_descriptor:\n        Arc<dyn Fn(&wgpu::Adapter) -> wgpu::DeviceDescriptor<'static> + Send + Sync>,\n}\n\nimpl Clone for WgpuSetupCreateNew {\n    fn clone(&self) -> Self {\n        Self {\n            instance_descriptor: self.instance_descriptor.clone(),\n            power_preference: self.power_preference,\n            native_adapter_selector: self.native_adapter_selector.clone(),\n            device_descriptor: Arc::clone(&self.device_descriptor),\n        }\n    }\n}\n\nimpl std::fmt::Debug for WgpuSetupCreateNew {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"WgpuSetupCreateNew\")\n            .field(\"instance_descriptor\", &self.instance_descriptor)\n            .field(\"power_preference\", &self.power_preference)\n            .field(\n                \"native_adapter_selector\",\n                &self.native_adapter_selector.is_some(),\n            )\n            .finish()\n    }\n}\n\nimpl Default for WgpuSetupCreateNew {\n    fn default() -> Self {\n        Self {\n            instance_descriptor: wgpu::InstanceDescriptor {\n                // Add GL backend, primarily because WebGPU is not stable enough yet.\n                // (note however, that the GL backend needs to be opted-in via the wgpu feature flag \"webgl\")\n                backends: wgpu::Backends::from_env()\n                    .unwrap_or(wgpu::Backends::PRIMARY | wgpu::Backends::GL),\n                flags: wgpu::InstanceFlags::from_build_config().with_env(),\n                backend_options: wgpu::BackendOptions::from_env_or_default(),\n                memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),\n            },\n\n            power_preference: wgpu::PowerPreference::from_env()\n                .unwrap_or(wgpu::PowerPreference::HighPerformance),\n\n            native_adapter_selector: None,\n\n            device_descriptor: Arc::new(|adapter| {\n                let base_limits = if adapter.get_info().backend == wgpu::Backend::Gl {\n                    wgpu::Limits::downlevel_webgl2_defaults()\n                } else {\n                    wgpu::Limits::default()\n                };\n\n                wgpu::DeviceDescriptor {\n                    label: Some(\"egui wgpu device\"),\n                    required_limits: wgpu::Limits {\n                        // When using a depth buffer, we have to be able to create a texture\n                        // large enough for the entire surface, and we want to support 4k+ displays.\n                        max_texture_dimension_2d: 8192,\n                        ..base_limits\n                    },\n                    ..Default::default()\n                }\n            }),\n        }\n    }\n}\n\n/// Configuration for using an existing wgpu setup.\n///\n/// Used for [`WgpuSetup::Existing`].\n#[derive(Clone)]\npub struct WgpuSetupExisting {\n    pub instance: wgpu::Instance,\n    pub adapter: wgpu::Adapter,\n    pub device: wgpu::Device,\n    pub queue: wgpu::Queue,\n}\n"
  },
  {
    "path": "crates/egui-wgpu/src/texture_copy.wgsl",
    "content": "struct VertexOutput {\n    @builtin(position) position: vec4<f32>,\n};\n\nvar<private> positions: array<vec2f, 3> = array<vec2f, 3>(\n    vec2f(-1.0, -3.0),\n    vec2f(-1.0, 1.0),\n    vec2f(3.0, 1.0)\n);\n\n// meant to be called with 3 vertex indices: 0, 1, 2\n// draws one large triangle over the clip space like this:\n// (the asterisks represent the clip space bounds)\n//-1,1           1,1\n// ---------------------------------\n// |              *              .\n// |              *           .\n// |              *        .\n// |              *      .\n// |              *    .\n// |              * .\n// |***************\n// |            . 1,-1\n// |          .\n// |       .\n// |     .\n// |   .\n// |.\n@vertex\nfn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var result: VertexOutput;\n    result.position = vec4f(positions[vertex_index], 0.0, 1.0);\n    return result;\n}\n\n@group(0)\n@binding(0)\nvar r_color: texture_2d<f32>;\n\n@fragment\nfn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {\n    return textureLoad(r_color, vec2i(vertex.position.xy), 0);\n}\n"
  },
  {
    "path": "crates/egui-wgpu/src/winit.rs",
    "content": "#![expect(clippy::missing_errors_doc)]\n#![expect(clippy::undocumented_unsafe_blocks)]\n#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps\n#![expect(unsafe_code)]\n\nuse crate::{RenderState, SurfaceErrorAction, WgpuConfiguration, renderer};\nuse crate::{\n    RendererOptions,\n    capture::{CaptureReceiver, CaptureSender, CaptureState, capture_channel},\n};\nuse egui::{Context, Event, UserData, ViewportId, ViewportIdMap, ViewportIdSet};\nuse std::{num::NonZeroU32, sync::Arc};\n\nstruct SurfaceState {\n    surface: wgpu::Surface<'static>,\n    alpha_mode: wgpu::CompositeAlphaMode,\n    width: u32,\n    height: u32,\n    resizing: bool,\n}\n\n/// Everything you need to paint egui with [`wgpu`] on [`winit`].\n///\n/// Alternatively you can use [`crate::Renderer`] directly.\n///\n/// NOTE: all egui viewports share the same painter.\npub struct Painter {\n    context: Context,\n    configuration: WgpuConfiguration,\n    options: RendererOptions,\n    support_transparent_backbuffer: bool,\n    screen_capture_state: Option<CaptureState>,\n\n    instance: wgpu::Instance,\n    render_state: Option<RenderState>,\n\n    // Per viewport/window:\n    depth_texture_view: ViewportIdMap<wgpu::TextureView>,\n    msaa_texture_view: ViewportIdMap<wgpu::TextureView>,\n    surfaces: ViewportIdMap<SurfaceState>,\n    capture_tx: CaptureSender,\n    capture_rx: CaptureReceiver,\n}\n\nimpl Painter {\n    /// Manages [`wgpu`] state, including surface state, required to render egui.\n    ///\n    /// Only the [`wgpu::Instance`] is initialized here. Device selection and the initialization\n    /// of render + surface state is deferred until the painter is given its first window target\n    /// via [`set_window()`](Self::set_window). (Ensuring that a device that's compatible with the\n    /// native window is chosen)\n    ///\n    /// Before calling [`paint_and_update_textures()`](Self::paint_and_update_textures) a\n    /// [`wgpu::Surface`] must be initialized (and corresponding render state) by calling\n    /// [`set_window()`](Self::set_window) once you have\n    /// a [`winit::window::Window`] with a valid `.raw_window_handle()`\n    /// associated.\n    pub async fn new(\n        context: Context,\n        configuration: WgpuConfiguration,\n        support_transparent_backbuffer: bool,\n        options: RendererOptions,\n    ) -> Self {\n        let (capture_tx, capture_rx) = capture_channel();\n        let instance = configuration.wgpu_setup.new_instance().await;\n\n        Self {\n            context,\n            configuration,\n            options,\n            support_transparent_backbuffer,\n            screen_capture_state: None,\n\n            instance,\n            render_state: None,\n\n            depth_texture_view: Default::default(),\n            surfaces: Default::default(),\n            msaa_texture_view: Default::default(),\n\n            capture_tx,\n            capture_rx,\n        }\n    }\n\n    /// Get the [`RenderState`].\n    ///\n    /// Will return [`None`] if the render state has not been initialized yet.\n    pub fn render_state(&self) -> Option<RenderState> {\n        self.render_state.clone()\n    }\n\n    fn configure_surface(\n        surface_state: &SurfaceState,\n        render_state: &RenderState,\n        config: &WgpuConfiguration,\n    ) {\n        profiling::function_scope!();\n\n        let width = surface_state.width;\n        let height = surface_state.height;\n\n        let mut surf_config = wgpu::SurfaceConfiguration {\n            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n            format: render_state.target_format,\n            present_mode: config.present_mode,\n            alpha_mode: surface_state.alpha_mode,\n            view_formats: vec![render_state.target_format],\n            ..surface_state\n                .surface\n                .get_default_config(&render_state.adapter, width, height)\n                .expect(\"The surface isn't supported by this adapter\")\n        };\n\n        if let Some(desired_maximum_frame_latency) = config.desired_maximum_frame_latency {\n            surf_config.desired_maximum_frame_latency = desired_maximum_frame_latency;\n        }\n\n        surface_state\n            .surface\n            .configure(&render_state.device, &surf_config);\n    }\n\n    /// Updates (or clears) the [`winit::window::Window`] associated with the [`Painter`]\n    ///\n    /// This creates a [`wgpu::Surface`] for the given Window (as well as initializing render\n    /// state if needed) that is used for egui rendering.\n    ///\n    /// This must be called before trying to render via\n    /// [`paint_and_update_textures`](Self::paint_and_update_textures)\n    ///\n    /// # Portability\n    ///\n    /// _In particular it's important to note that on Android a it's only possible to create\n    /// a window surface between `Resumed` and `Paused` lifecycle events, and Winit will panic on\n    /// attempts to query the raw window handle while paused._\n    ///\n    /// On Android [`set_window`](Self::set_window) should be called with `Some(window)` for each\n    /// `Resumed` event and `None` for each `Paused` event. Currently, on all other platforms\n    /// [`set_window`](Self::set_window) may be called with `Some(window)` as soon as you have a\n    /// valid [`winit::window::Window`].\n    ///\n    /// # Errors\n    /// If the provided wgpu configuration does not match an available device.\n    pub async fn set_window(\n        &mut self,\n        viewport_id: ViewportId,\n        window: Option<Arc<winit::window::Window>>,\n    ) -> Result<(), crate::WgpuError> {\n        profiling::scope!(\"Painter::set_window\"); // profile_function gives bad names for async functions\n\n        if let Some(window) = window {\n            let size = window.inner_size();\n            if !self.surfaces.contains_key(&viewport_id) {\n                let surface = self.instance.create_surface(window)?;\n                self.add_surface(surface, viewport_id, size).await?;\n            }\n        } else {\n            log::warn!(\"No window - clearing all surfaces\");\n            self.surfaces.clear();\n        }\n        Ok(())\n    }\n\n    /// Updates (or clears) the [`winit::window::Window`] associated with the [`Painter`] without taking ownership of the window.\n    ///\n    /// Like [`set_window`](Self::set_window) except:\n    ///\n    /// # Safety\n    /// The user is responsible for ensuring that the window is alive for as long as it is set.\n    pub async unsafe fn set_window_unsafe(\n        &mut self,\n        viewport_id: ViewportId,\n        window: Option<&winit::window::Window>,\n    ) -> Result<(), crate::WgpuError> {\n        profiling::scope!(\"Painter::set_window_unsafe\"); // profile_function gives bad names for async functions\n\n        if let Some(window) = window {\n            let size = window.inner_size();\n            if !self.surfaces.contains_key(&viewport_id) {\n                let surface = unsafe {\n                    self.instance\n                        .create_surface_unsafe(wgpu::SurfaceTargetUnsafe::from_window(&window)?)?\n                };\n                self.add_surface(surface, viewport_id, size).await?;\n            }\n        } else {\n            log::warn!(\"No window - clearing all surfaces\");\n            self.surfaces.clear();\n        }\n        Ok(())\n    }\n\n    async fn add_surface(\n        &mut self,\n        surface: wgpu::Surface<'static>,\n        viewport_id: ViewportId,\n        size: winit::dpi::PhysicalSize<u32>,\n    ) -> Result<(), crate::WgpuError> {\n        let render_state = if let Some(render_state) = &self.render_state {\n            render_state\n        } else {\n            let render_state = RenderState::create(\n                &self.configuration,\n                &self.instance,\n                Some(&surface),\n                self.options,\n            )\n            .await?;\n            self.render_state.get_or_insert(render_state)\n        };\n        let alpha_mode = if self.support_transparent_backbuffer {\n            let supported_alpha_modes = surface.get_capabilities(&render_state.adapter).alpha_modes;\n\n            // Prefer pre multiplied over post multiplied!\n            if supported_alpha_modes.contains(&wgpu::CompositeAlphaMode::PreMultiplied) {\n                wgpu::CompositeAlphaMode::PreMultiplied\n            } else if supported_alpha_modes.contains(&wgpu::CompositeAlphaMode::PostMultiplied) {\n                wgpu::CompositeAlphaMode::PostMultiplied\n            } else {\n                log::warn!(\n                    \"Transparent window was requested, but the active wgpu surface does not support a `CompositeAlphaMode` with transparency.\"\n                );\n                wgpu::CompositeAlphaMode::Auto\n            }\n        } else {\n            wgpu::CompositeAlphaMode::Auto\n        };\n        self.surfaces.insert(\n            viewport_id,\n            SurfaceState {\n                surface,\n                width: size.width,\n                height: size.height,\n                alpha_mode,\n                resizing: false,\n            },\n        );\n        let Some(width) = NonZeroU32::new(size.width) else {\n            log::debug!(\"The window width was zero; skipping generate textures\");\n            return Ok(());\n        };\n        let Some(height) = NonZeroU32::new(size.height) else {\n            log::debug!(\"The window height was zero; skipping generate textures\");\n            return Ok(());\n        };\n        self.resize_and_generate_depth_texture_view_and_msaa_view(viewport_id, width, height);\n        Ok(())\n    }\n\n    /// Returns the maximum texture dimension supported if known\n    ///\n    /// This API will only return a known dimension after `set_window()` has been called\n    /// at least once, since the underlying device and render state are initialized lazily\n    /// once we have a window (that may determine the choice of adapter/device).\n    pub fn max_texture_side(&self) -> Option<usize> {\n        self.render_state\n            .as_ref()\n            .map(|rs| rs.device.limits().max_texture_dimension_2d as usize)\n    }\n\n    fn resize_and_generate_depth_texture_view_and_msaa_view(\n        &mut self,\n        viewport_id: ViewportId,\n        width_in_pixels: NonZeroU32,\n        height_in_pixels: NonZeroU32,\n    ) {\n        profiling::function_scope!();\n\n        let width = width_in_pixels.get();\n        let height = height_in_pixels.get();\n\n        let render_state = self.render_state.as_ref().unwrap();\n        let surface_state = self.surfaces.get_mut(&viewport_id).unwrap();\n\n        surface_state.width = width;\n        surface_state.height = height;\n\n        Self::configure_surface(surface_state, render_state, &self.configuration);\n\n        if let Some(depth_format) = self.options.depth_stencil_format {\n            self.depth_texture_view.insert(\n                viewport_id,\n                render_state\n                    .device\n                    .create_texture(&wgpu::TextureDescriptor {\n                        label: Some(\"egui_depth_texture\"),\n                        size: wgpu::Extent3d {\n                            width,\n                            height,\n                            depth_or_array_layers: 1,\n                        },\n                        mip_level_count: 1,\n                        sample_count: self.options.msaa_samples.max(1),\n                        dimension: wgpu::TextureDimension::D2,\n                        format: depth_format,\n                        usage: wgpu::TextureUsages::RENDER_ATTACHMENT\n                            | wgpu::TextureUsages::TEXTURE_BINDING,\n                        view_formats: &[depth_format],\n                    })\n                    .create_view(&wgpu::TextureViewDescriptor::default()),\n            );\n        }\n\n        if let Some(render_state) = (self.options.msaa_samples > 1)\n            .then_some(self.render_state.as_ref())\n            .flatten()\n        {\n            let texture_format = render_state.target_format;\n            self.msaa_texture_view.insert(\n                viewport_id,\n                render_state\n                    .device\n                    .create_texture(&wgpu::TextureDescriptor {\n                        label: Some(\"egui_msaa_texture\"),\n                        size: wgpu::Extent3d {\n                            width,\n                            height,\n                            depth_or_array_layers: 1,\n                        },\n                        mip_level_count: 1,\n                        sample_count: self.options.msaa_samples.max(1),\n                        dimension: wgpu::TextureDimension::D2,\n                        format: texture_format,\n                        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,\n                        view_formats: &[texture_format],\n                    })\n                    .create_view(&wgpu::TextureViewDescriptor::default()),\n            );\n        }\n    }\n\n    /// Handles changes of the resizing state.\n    ///\n    /// Should be called prior to the first [`Painter::on_window_resized`] call and after the last in\n    /// the chain. Used to apply platform-specific logic, e.g. OSX Metal window resize jitter fix.\n    pub fn on_window_resize_state_change(&mut self, viewport_id: ViewportId, resizing: bool) {\n        profiling::function_scope!();\n\n        let Some(state) = self.surfaces.get_mut(&viewport_id) else {\n            return;\n        };\n        if state.resizing == resizing {\n            if resizing {\n                log::debug!(\n                    \"Painter::on_window_resize_state_change() redundant call while resizing\"\n                );\n            } else {\n                log::debug!(\n                    \"Painter::on_window_resize_state_change() redundant call after resizing\"\n                );\n            }\n            return;\n        }\n\n        // Resizing is a bit tricky on macOS.\n        // It requires enabling [\"present_with_transaction\"](https://developer.apple.com/documentation/quartzcore/cametallayer/presentswithtransaction)\n        // flag to avoid jittering during the resize. Even though resize jittering on macOS\n        // is common across rendering backends, the solution for wgpu/metal is known.\n        //\n        // See https://github.com/emilk/egui/issues/903\n        #[cfg(all(target_os = \"macos\", feature = \"macos-window-resize-jitter-fix\"))]\n        {\n            // SAFETY: The cast is checked with if condition. If the used backend is not metal\n            // it gracefully fails.\n            unsafe {\n                if let Some(hal_surface) = state.surface.as_hal::<wgpu::hal::api::Metal>() {\n                    hal_surface\n                        .render_layer()\n                        .lock()\n                        .set_presents_with_transaction(resizing);\n\n                    Self::configure_surface(\n                        state,\n                        self.render_state.as_ref().unwrap(),\n                        &self.configuration,\n                    );\n                }\n            }\n        }\n\n        state.resizing = resizing;\n    }\n\n    pub fn on_window_resized(\n        &mut self,\n        viewport_id: ViewportId,\n        width_in_pixels: NonZeroU32,\n        height_in_pixels: NonZeroU32,\n    ) {\n        profiling::function_scope!();\n\n        if self.surfaces.contains_key(&viewport_id) {\n            self.resize_and_generate_depth_texture_view_and_msaa_view(\n                viewport_id,\n                width_in_pixels,\n                height_in_pixels,\n            );\n        } else {\n            log::warn!(\n                \"Ignoring window resize notification with no surface created via Painter::set_window()\"\n            );\n        }\n    }\n\n    /// Returns two things:\n    ///\n    /// The approximate number of seconds spent on vsync-waiting (if any),\n    /// and the captures captured screenshot if it was requested.\n    ///\n    /// If `capture_data` isn't empty, a screenshot will be captured.\n    pub fn paint_and_update_textures(\n        &mut self,\n        viewport_id: ViewportId,\n        pixels_per_point: f32,\n        clear_color: [f32; 4],\n        clipped_primitives: &[epaint::ClippedPrimitive],\n        textures_delta: &epaint::textures::TexturesDelta,\n        capture_data: Vec<UserData>,\n    ) -> f32 {\n        profiling::function_scope!();\n\n        /// Guard to ensure that commands are always submitted to the renderer queue\n        /// so that calls to [`write_buffer()`](https://docs.rs/wgpu/latest/wgpu/struct.Queue.html#method.write_buffer)\n        /// are completed even if we take a codepath which doesn't submit commands and avoids\n        /// internal buffers growing indefinitely.\n        ///\n        /// This may happen, for example, if no output frame is resolved.\n        /// See <https://github.com/emilk/egui/pull/7928> for full context.\n        struct RendererQueueGuard<'q> {\n            queue: &'q wgpu::Queue,\n            commands_submitted: bool,\n        }\n\n        impl Drop for RendererQueueGuard<'_> {\n            fn drop(&mut self) {\n                // Only submit an empty command buffer array if no commands were\n                // explicitly submitted.\n                if !self.commands_submitted {\n                    self.queue.submit([]);\n                }\n            }\n        }\n\n        let capture = !capture_data.is_empty();\n        let mut vsync_sec = 0.0;\n\n        let Some(render_state) = self.render_state.as_mut() else {\n            return vsync_sec;\n        };\n\n        let mut render_queue_guard = RendererQueueGuard {\n            queue: &render_state.queue,\n            commands_submitted: false,\n        };\n\n        let Some(surface_state) = self.surfaces.get(&viewport_id) else {\n            return vsync_sec;\n        };\n\n        let mut encoder =\n            render_state\n                .device\n                .create_command_encoder(&wgpu::CommandEncoderDescriptor {\n                    label: Some(\"encoder\"),\n                });\n\n        // Upload all resources for the GPU.\n        let screen_descriptor = renderer::ScreenDescriptor {\n            size_in_pixels: [surface_state.width, surface_state.height],\n            pixels_per_point,\n        };\n\n        let user_cmd_bufs = {\n            let mut renderer = render_state.renderer.write();\n            for (id, image_delta) in &textures_delta.set {\n                renderer.update_texture(\n                    &render_state.device,\n                    &render_state.queue,\n                    *id,\n                    image_delta,\n                );\n            }\n\n            renderer.update_buffers(\n                &render_state.device,\n                &render_state.queue,\n                &mut encoder,\n                clipped_primitives,\n                &screen_descriptor,\n            )\n        };\n\n        let output_frame = {\n            profiling::scope!(\"get_current_texture\");\n            // This is what vsync-waiting happens on my Mac.\n            let start = web_time::Instant::now();\n            let output_frame = surface_state.surface.get_current_texture();\n            vsync_sec += start.elapsed().as_secs_f32();\n            output_frame\n        };\n\n        let output_frame = match output_frame {\n            Ok(frame) => frame,\n            Err(err) => match (*self.configuration.on_surface_error)(err) {\n                SurfaceErrorAction::RecreateSurface => {\n                    Self::configure_surface(surface_state, render_state, &self.configuration);\n                    return vsync_sec;\n                }\n                SurfaceErrorAction::SkipFrame => {\n                    return vsync_sec;\n                }\n            },\n        };\n\n        let mut capture_buffer = None;\n        {\n            let renderer = render_state.renderer.read();\n\n            let target_texture = if capture {\n                let capture_state = self.screen_capture_state.get_or_insert_with(|| {\n                    CaptureState::new(&render_state.device, &output_frame.texture)\n                });\n                capture_state.update(&render_state.device, &output_frame.texture);\n\n                &capture_state.texture\n            } else {\n                &output_frame.texture\n            };\n            let target_view = target_texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n            let (view, resolve_target) = (self.options.msaa_samples > 1)\n                .then_some(self.msaa_texture_view.get(&viewport_id))\n                .flatten()\n                .map_or((&target_view, None), |texture_view| {\n                    (texture_view, Some(&target_view))\n                });\n\n            let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {\n                label: Some(\"egui_render\"),\n                color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                    view,\n                    resolve_target,\n                    ops: wgpu::Operations {\n                        load: wgpu::LoadOp::Clear(wgpu::Color {\n                            r: clear_color[0] as f64,\n                            g: clear_color[1] as f64,\n                            b: clear_color[2] as f64,\n                            a: clear_color[3] as f64,\n                        }),\n                        store: wgpu::StoreOp::Store,\n                    },\n                    depth_slice: None,\n                })],\n                depth_stencil_attachment: self.depth_texture_view.get(&viewport_id).map(|view| {\n                    wgpu::RenderPassDepthStencilAttachment {\n                        view,\n                        depth_ops: self\n                            .options\n                            .depth_stencil_format\n                            .is_some_and(|depth_stencil_format| {\n                                depth_stencil_format.has_depth_aspect()\n                            })\n                            .then_some(wgpu::Operations {\n                                load: wgpu::LoadOp::Clear(1.0),\n                                // It is very unlikely that the depth buffer is needed after egui finished rendering\n                                // so no need to store it. (this can improve performance on tiling GPUs like mobile chips or Apple Silicon)\n                                store: wgpu::StoreOp::Discard,\n                            }),\n                        stencil_ops: self\n                            .options\n                            .depth_stencil_format\n                            .is_some_and(|depth_stencil_format| {\n                                depth_stencil_format.has_stencil_aspect()\n                            })\n                            .then_some(wgpu::Operations {\n                                load: wgpu::LoadOp::Clear(0),\n                                store: wgpu::StoreOp::Discard,\n                            }),\n                    }\n                }),\n                timestamp_writes: None,\n                occlusion_query_set: None,\n                multiview_mask: None,\n            });\n\n            // Forgetting the pass' lifetime means that we are no longer compile-time protected from\n            // runtime errors caused by accessing the parent encoder before the render pass is dropped.\n            // Since we don't pass it on to the renderer, we should be perfectly safe against this mistake here!\n            renderer.render(\n                &mut render_pass.forget_lifetime(),\n                clipped_primitives,\n                &screen_descriptor,\n            );\n\n            if capture && let Some(capture_state) = &mut self.screen_capture_state {\n                capture_buffer = Some(capture_state.copy_textures(\n                    &render_state.device,\n                    &output_frame,\n                    &mut encoder,\n                ));\n            }\n        }\n\n        let encoded = {\n            profiling::scope!(\"CommandEncoder::finish\");\n            encoder.finish()\n        };\n\n        // Submit the commands: both the main buffer and user-defined ones.\n        {\n            profiling::scope!(\"Queue::submit\");\n            // wgpu doesn't document where vsync can happen. Maybe here?\n            let start = web_time::Instant::now();\n            render_state\n                .queue\n                .submit(user_cmd_bufs.into_iter().chain([encoded]));\n            vsync_sec += start.elapsed().as_secs_f32();\n        };\n\n        // Ensure that the queue guard does not do unnecessary work when dropped\n        render_queue_guard.commands_submitted = true;\n\n        // Free textures marked for destruction **after** queue submit since they might still be used in the current frame.\n        // Calling `wgpu::Texture::destroy` on a texture that is still in use would invalidate the command buffer(s) it is used in.\n        // However, once we called `wgpu::Queue::submit`, it is up for wgpu to determine how long the underlying gpu resource has to live.\n        {\n            let mut renderer = render_state.renderer.write();\n            for id in &textures_delta.free {\n                renderer.free_texture(id);\n            }\n        }\n\n        if let Some(capture_buffer) = capture_buffer\n            && let Some(screen_capture_state) = &mut self.screen_capture_state\n        {\n            screen_capture_state.read_screen_rgba(\n                self.context.clone(),\n                capture_buffer,\n                capture_data,\n                self.capture_tx.clone(),\n                viewport_id,\n            );\n        }\n\n        {\n            profiling::scope!(\"present\");\n            // wgpu doesn't document where vsync can happen. Maybe here?\n            let start = web_time::Instant::now();\n            output_frame.present();\n            vsync_sec += start.elapsed().as_secs_f32();\n        }\n\n        vsync_sec\n    }\n\n    /// Call this at the beginning of each frame to receive the requested screenshots.\n    pub fn handle_screenshots(&self, events: &mut Vec<Event>) {\n        for (viewport_id, user_data, screenshot) in self.capture_rx.try_iter() {\n            let screenshot = Arc::new(screenshot);\n            for data in user_data {\n                events.push(Event::Screenshot {\n                    viewport_id,\n                    user_data: data,\n                    image: Arc::clone(&screenshot),\n                });\n            }\n        }\n    }\n\n    pub fn gc_viewports(&mut self, active_viewports: &ViewportIdSet) {\n        self.surfaces.retain(|id, _| active_viewports.contains(id));\n        self.depth_texture_view\n            .retain(|id, _| active_viewports.contains(id));\n        self.msaa_texture_view\n            .retain(|id, _| active_viewports.contains(id));\n    }\n\n    #[expect(clippy::needless_pass_by_ref_mut, clippy::unused_self)]\n    pub fn destroy(&mut self) {\n        // TODO(emilk): something here?\n    }\n}\n"
  },
  {
    "path": "crates/egui-winit/CHANGELOG.md",
    "content": "# Changelog for egui-winit\nAll notable changes to the `egui-winit` integration will be noted in this file.\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n## 0.33.3 - 2025-12-11\nNothing new\n\n\n## 0.33.2 - 2025-11-13\n* Don't enable `arboard` on iOS [#7663](https://github.com/emilk/egui/pull/7663) by [@irh](https://github.com/irh)\n\n\n## 0.33.0 - 2025-10-09\n### ⭐ Added\n* Add rotation gesture support for trackpad sources [#7453](https://github.com/emilk/egui/pull/7453) by [@thatcomputerguy0101](https://github.com/thatcomputerguy0101)\n* Add support for the safe area on iOS [#7578](https://github.com/emilk/egui/pull/7578) by [@irh](https://github.com/irh)\n\n### 🔧 Changed\n* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)\n* Create `egui_wgpu::RendererOptions` [#7601](https://github.com/emilk/egui/pull/7601) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Fix build error in egui-winit with profiling enabled [#7557](https://github.com/emilk/egui/pull/7557) by [@torokati44](https://github.com/torokati44)\n* Properly end winit event loop [#7565](https://github.com/emilk/egui/pull/7565) by [@tye-exe](https://github.com/tye-exe)\n* Fix eframe window not being focused on mac on startup [#7593](https://github.com/emilk/egui/pull/7593) by [@emilk](https://github.com/emilk)\n\n\n## 0.32.3 - 2025-09-12\nNothing new\n\n\n## 0.32.2 - 2025-09-04\nNothing new\n\n\n## 0.32.1 - 2025-08-15\n* Update to winit 0.30.12 [#7420](https://github.com/emilk/egui/pull/7420) by [@emilk](https://github.com/emilk)\n\n\n## 0.32.0 - 2025-07-10\n* Mark all keys as released if the app loses focus [#5743](https://github.com/emilk/egui/pull/5743) by [@emilk](https://github.com/emilk)\n* Fix text input on Android [#5759](https://github.com/emilk/egui/pull/5759) by [@StratusFearMe21](https://github.com/StratusFearMe21)\n* Add macOS-specific `has_shadow` and `with_has_shadow` to ViewportBuilder [#6850](https://github.com/emilk/egui/pull/6850) by [@gaelanmcmillan](https://github.com/gaelanmcmillan)\n* Support for back-button on Android [#7073](https://github.com/emilk/egui/pull/7073) by [@ardocrat](https://github.com/ardocrat)\n* Fix incorrect window sizes for non-resizable windows on Wayland [#7103](https://github.com/emilk/egui/pull/7103) by [@GoldsteinE](https://github.com/GoldsteinE)\n\n\n## 0.31.1 - 2025-03-05\nNothing new\n\n\n## 0.31.0 - 2025-02-04\n* Re-enable IME support on Linux [#5198](https://github.com/emilk/egui/pull/5198) by [@YgorSouza](https://github.com/YgorSouza)\n* Update to winit 0.30.7 [#5516](https://github.com/emilk/egui/pull/5516) by [@emilk](https://github.com/emilk)\n\n\n## 0.30.0 - 2024-12-16\n* iOS: Support putting UI next to the dynamic island [#5211](https://github.com/emilk/egui/pull/5211) by [@frederik-uni](https://github.com/frederik-uni)\n* Remove implicit `accesskit_winit` feature [#5316](https://github.com/emilk/egui/pull/5316) by [@waywardmonkeys](https://github.com/waywardmonkeys)\n\n\n## 0.29.1 - 2024-10-01 - Fix backspace/arrow keys on X11\n* Linux: Disable IME to fix backspace/arrow keys [#5188](https://github.com/emilk/egui/pull/5188) by [@emilk](https://github.com/emilk)\n\n\n## 0.29.0 - 2024-09-26 - `winit` 0.30\n* Upgrade to `winit` 0.30 [#4849](https://github.com/emilk/egui/pull/4849) [#4939](https://github.com/emilk/egui/pull/4939) by [@ArthurBrussee](https://github.com/ArthurBrussee)\n* Fix: Backspace not working after IME input [#4912](https://github.com/emilk/egui/pull/4912) by [@rustbasic](https://github.com/rustbasic)\n\n\n## 0.28.1 - 2024-07-05\nNothing new\n\n\n## 0.28.0 - 2024-07-03\n* Update `webbrowser` to `v1.0.0` [#4394](https://github.com/emilk/egui/pull/4394) by [@torokati44](https://github.com/torokati44)\n* Emit physical key presses when a non-Latin layout is active [#4461](https://github.com/emilk/egui/pull/4461) by [@TicClick](https://github.com/TicClick)\n* IME for chinese [#4436](https://github.com/emilk/egui/pull/4436) by [@rustbasic](https://github.com/rustbasic)\n* Fix: Window position creeps between executions on scaled monitors [#4443](https://github.com/emilk/egui/pull/4443) by [@avery-radmacher](https://github.com/avery-radmacher)\n* Ignore synthetic key presses [#4514](https://github.com/emilk/egui/pull/4514) by [@hut](https://github.com/hut)\n\n\n## 0.27.2 - 2024-04-02\n* Fix continuous repaint on Wayland when TextEdit is focused or IME output is set [#4269](https://github.com/emilk/egui/pull/4269) (thanks [@white-axe](https://github.com/white-axe)!)\n\n\n## 0.27.1 - 2024-03-29\n* Nothing new\n\n\n## 0.27.0 - 2024-03-26\n* Update memoffset to 0.9.0, arboard to 3.3.1, and remove egui_glow's needless dependency on pure_glow's deps  [#4036](https://github.com/emilk/egui/pull/4036) (thanks [@Nopey](https://github.com/Nopey)!)\n* Don't clear modifier state on focus change [#4157](https://github.com/emilk/egui/pull/4157) (thanks [@ming08108](https://github.com/ming08108)!)\n\n\n## 0.26.2 - 2024-02-14\n* Update memoffset to 0.9.0, arboard to 3.3.1, and remove egui_glow's needless dependency on pure_glow's deps  [#4036](https://github.com/emilk/egui/pull/4036) (thanks [@Nopey](https://github.com/Nopey)!)\n\n\n## 0.26.1 - 2024-02-11\n* Nothing new\n\n\n## 0.26.0 - 2024-02-05\n* Don't consume clipboard shortcuts [#3812](https://github.com/emilk/egui/pull/3812) (thanks [@Dinnerbone](https://github.com/Dinnerbone)!)\n* Make the `clipboard_text` and `allow_ime` state public [#3724](https://github.com/emilk/egui/pull/3724) (thanks [@tosti007](https://github.com/tosti007)!)\n\n\n## 0.25.0 - 2024-01-08\n* Update to winit 0.29 [#3649](https://github.com/emilk/egui/pull/3649) (thanks [@fornwall](https://github.com/fornwall)!)\n* Fix: Let `accesskit` process window events [#3733](https://github.com/emilk/egui/pull/3733) (thanks [@DataTriny](https://github.com/DataTriny)!)\n* Simplify `egui_winit::State` [#3678](https://github.com/emilk/egui/pull/3678)\n\n\n## 0.24.1 - 2023-11-30\n* Don't treat `WindowEvent::CloseRequested` as consumed [#3627](https://github.com/emilk/egui/pull/3627) (thanks [@Aaron1011](https://github.com/Aaron1011)!)\n* Fix windowing problems when using the `x11` feature on Linux [#3643](https://github.com/emilk/egui/pull/3643)\n\n\n## 0.24.0 - 2023-11-23\n* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)\n* Some breaking changes required for multi-viewport support\n\n\n## 0.23.0 - 2023-09-27\n* Only show on-screen-keyboard and IME when editing text [#3362](https://github.com/emilk/egui/pull/3362) (thanks [@Barugon](https://github.com/Barugon)!)\n* Replace `instant` with `web_time` [#3296](https://github.com/emilk/egui/pull/3296)\n* Allow users to opt-out of default `winit` features [#3228](https://github.com/emilk/egui/pull/3228)\n* Recognize numpad enter/plus/minus [#3285](https://github.com/emilk/egui/pull/3285)\n\n\n## 0.22.0 - 2023-05-23\n* Only use `wasm-bindgen` feature for `instant` when building for wasm32 [#2808](https://github.com/emilk/egui/pull/2808) (thanks [@gferon](https://github.com/gferon)!)\n* Fix unsafe API of `Clipboard::new` [#2765](https://github.com/emilk/egui/pull/2765) (thanks [@dhardy](https://github.com/dhardy)!)\n* Remove `android-activity` dependency + add `Activity` backend features [#2863](https://github.com/emilk/egui/pull/2863) (thanks [@rib](https://github.com/rib)!)\n* Use `RawDisplayHandle` for smithay clipboard init [#2914](https://github.com/emilk/egui/pull/2914) (thanks [@lunixbochs](https://github.com/lunixbochs)!)\n* Clear all keys and modifies on focus change [#2933](https://github.com/emilk/egui/pull/2933)\n* Support Wasm target [#2949](https://github.com/emilk/egui/pull/2949) (thanks [@jinleili](https://github.com/jinleili)!)\n* Fix unsafe API: remove `State::new_with_wayland_display`; change `Clipboard::new` to take `&EventLoopWindowTarget<T>`\n\n\n## 0.21.1 - 2023-02-12\n* Fixed crash when window position is in an invalid state, which could happen e.g. due to changes in monitor size or DPI ([#2722](https://github.com/emilk/egui/issues/2722)).\n\n\n## 0.21.0 - 2023-02-08\n* Fixed persistence of native window position on Windows OS ([#2583](https://github.com/emilk/egui/issues/2583)).\n* Update to `winit` 0.28, adding support for mac trackpad zoom ([#2654](https://github.com/emilk/egui/pull/2654)).\n* Remove the `screen_reader` feature. Use the `accesskit` feature flag instead ([#2669](https://github.com/emilk/egui/pull/2669)).\n* Fix bug where the cursor could get stuck using the wrong icon.\n\n\n## 0.20.1 - 2022-12-11\n* Fix [docs.rs](https://docs.rs/egui-winit) build ([#2420](https://github.com/emilk/egui/pull/2420)).\n\n\n## 0.20.0 - 2022-12-08\n* The default features of the `winit` crate are not enabled if the default features of `egui-winit` are disabled too ([#1971](https://github.com/emilk/egui/pull/1971)).\n* Added new feature `wayland` which enables Wayland support ([#1971](https://github.com/emilk/egui/pull/1971)).\n* Don't repaint when just moving window ([#1980](https://github.com/emilk/egui/pull/1980)).\n* Added optional integration with [AccessKit](https://accesskit.dev/) for implementing platform accessibility APIs ([#2294](https://github.com/emilk/egui/pull/2294)).\n\n## 0.19.0 - 2022-08-20\n* MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)).\n* Fixed clipboard on Wayland ([#1613](https://github.com/emilk/egui/pull/1613)).\n* Allow deferred render + surface state initialization for Android ([#1634](https://github.com/emilk/egui/pull/1634)).\n* Fixed window position persistence ([#1745](https://github.com/emilk/egui/pull/1745)).\n* Fixed mouse cursor change on Linux ([#1747](https://github.com/emilk/egui/pull/1747)).\n* Use the new `RawInput::has_focus` field to indicate whether the window has the keyboard focus ([#1859](https://github.com/emilk/egui/pull/1859)).\n\n\n## 0.18.0 - 2022-04-30\n* Reexport `egui` crate\n* MSRV (Minimum Supported Rust Version) is now `1.60.0` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Added new feature `puffin` to add [`puffin profiler`](https://github.com/EmbarkStudios/puffin) scopes ([#1483](https://github.com/emilk/egui/pull/1483)).\n* Renamed the feature `convert_bytemuck` to `bytemuck` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Renamed the feature `serialize` to `serde` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Removed the features `dark-light` and `persistence` ([#1542](https://github.com/emilk/egui/pull/1542)).\n\n\n## 0.17.0 - 2022-02-22\n* Fixed horizontal scrolling direction on Linux.\n* Replaced `std::time::Instant` with `instant::Instant` for WebAssembly compatibility ([#1023](https://github.com/emilk/egui/pull/1023))\n* Automatically detect and apply dark or light mode from system ([#1045](https://github.com/emilk/egui/pull/1045)).\n* Fixed `enable_drag` on Windows OS ([#1108](https://github.com/emilk/egui/pull/1108)).\n* Shift-scroll will now result in horizontal scrolling on all platforms ([#1136](https://github.com/emilk/egui/pull/1136)).\n* Require knowledge about max texture side (e.g. `GL_MAX_TEXTURE_SIZE`)) ([#1154](https://github.com/emilk/egui/pull/1154)).\n\n\n## 0.16.0 - 2021-12-29\n* Added helper `EpiIntegration` ([#871](https://github.com/emilk/egui/pull/871)).\n* Fixed shift key getting stuck enabled with the X11 option `shift:both_capslock` enabled ([#849](https://github.com/emilk/egui/pull/849)).\n* Removed `State::is_quit_event` and `State::is_quit_shortcut` ([#881](https://github.com/emilk/egui/pull/881)).\n* Updated `winit` to 0.26 ([#930](https://github.com/emilk/egui/pull/930)).\n\n\n## 0.15.0 - 2021-10-24\nFirst stand-alone release. Previously part of `egui_glium`.\n"
  },
  {
    "path": "crates/egui-winit/Cargo.toml",
    "content": "[package]\nname = \"egui-winit\"\nversion.workspace = true\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\ndescription = \"Bindings for using egui with winit\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui/tree/main/crates/egui-winit\"\nlicense.workspace = true\nreadme = \"README.md\"\nrepository = \"https://github.com/emilk/egui/tree/main/crates/egui-winit\"\ncategories = [\"gui\", \"game-development\"]\nkeywords = [\"winit\", \"egui\", \"gui\", \"gamedev\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"Cargo.toml\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[features]\ndefault = [\"clipboard\", \"links\", \"wayland\", \"winit/default\", \"x11\"]\n\n## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).\naccesskit = [\"dep:accesskit_winit\"]\n\n# Allow crates to choose an android-activity backend via Winit\n# - It's important that most applications should not have to depend on android-activity directly, and can\n#   rely on Winit to pull in a suitable version (unlike most Rust crates, any version conflicts won't link)\n# - It's also important that we don't impose an android-activity backend by taking this choice away from applications.\n## Enable the `game-activity` backend via Winit on Android\nandroid-game-activity = [\"winit/android-game-activity\"]\n## Enable the `native-activity` backend via Winit on Android\nandroid-native-activity = [\"winit/android-native-activity\"]\n\n## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`egui::epaint::Vertex`], [`egui::Vec2`] etc to `&[u8]`.\nbytemuck = [\"egui/bytemuck\", \"dep:bytemuck\"]\n\n## Enable cut/copy/paste to OS clipboard.\n## If disabled a clipboard will be simulated so you can still copy/paste within the egui app.\nclipboard = [\"arboard\", \"bytemuck\", \"smithay-clipboard\"]\n\n## Enable opening links in a browser when an egui hyperlink is clicked.\nlinks = [\"webbrowser\"]\n\n## Allow serialization of [`WindowSettings`] using [`serde`](https://docs.rs/serde).\nserde = [\"egui/serde\", \"dep:serde\"]\n\n## Enables Wayland support.\nwayland = [\"winit/wayland\", \"bytemuck\"]\n\n## Enables compiling for x11.\nx11 = [\"winit/x11\", \"bytemuck\"]\n\n[dependencies]\negui = { workspace = true, default-features = false }\n\nlog.workspace = true\nprofiling.workspace = true\nraw-window-handle.workspace = true\nweb-time.workspace = true\nwinit = { workspace = true, default-features = false }\n\n#! ### Optional dependencies\n\n# feature accesskit\naccesskit_winit = { workspace = true, optional = true }\n\nbytemuck = { workspace = true, optional = true }\n\n## Enable this when generating docs.\ndocument-features = { workspace = true, optional = true }\n\nserde = { workspace = true, optional = true }\nwebbrowser = { workspace = true, optional = true }\n\n[target.'cfg(target_os = \"ios\")'.dependencies]\nobjc2.workspace = true\nobjc2-foundation = { workspace = true, features = [\"std\", \"NSThread\"] }\nobjc2-ui-kit = { workspace = true, features = [\n  \"std\",\n  \"UIApplication\",\n  \"UIGeometry\",\n  \"UIResponder\",\n  \"UIScene\",\n  \"UISceneDefinitions\",\n  \"UIView\",\n  \"UIWindow\",\n  \"UIWindowScene\",\n] }\n\n[target.'cfg(any(target_os=\"linux\", target_os=\"dragonfly\", target_os=\"freebsd\", target_os=\"netbsd\", target_os=\"openbsd\"))'.dependencies]\nsmithay-clipboard = { workspace = true, optional = true }\n\n# The wayland-cursor normally selected doesn't properly enable all the features it uses\n# and thus doesn't compile as it is used in egui-winit. This is fixed upstream, so force\n# a slightly newer version. Remove this when winit upgrades past this version.\nwayland-cursor = { workspace = true, optional = true }\n\n[target.'cfg(not(target_os = \"android\"))'.dependencies]\narboard = { workspace = true, optional = true, features = [\"image-data\"] }\n"
  },
  {
    "path": "crates/egui-winit/README.md",
    "content": "# egui-winit\n\n[![Latest version](https://img.shields.io/crates/v/egui-winit.svg)](https://crates.io/crates/egui-winit)\n[![Documentation](https://docs.rs/egui-winit/badge.svg)](https://docs.rs/egui-winit)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\nThis crates provides bindings between [`egui`](https://github.com/emilk/egui) and [`winit`](https://crates.io/crates/winit).\n\nThe library translates winit events to egui, handled copy/paste, updates the cursor, open links clicked in egui, etc.\n"
  },
  {
    "path": "crates/egui-winit/src/clipboard.rs",
    "content": "use raw_window_handle::RawDisplayHandle;\n\n/// Handles interfacing with the OS clipboard.\n///\n/// If the \"clipboard\" feature is off, or we cannot connect to the OS clipboard,\n/// then a fallback clipboard that just works within the same app is used instead.\npub struct Clipboard {\n    #[cfg(all(\n        not(any(target_os = \"android\", target_os = \"ios\")),\n        feature = \"arboard\",\n    ))]\n    arboard: Option<arboard::Clipboard>,\n\n    #[cfg(all(\n        any(\n            target_os = \"linux\",\n            target_os = \"dragonfly\",\n            target_os = \"freebsd\",\n            target_os = \"netbsd\",\n            target_os = \"openbsd\"\n        ),\n        feature = \"smithay-clipboard\"\n    ))]\n    smithay: Option<smithay_clipboard::Clipboard>,\n\n    /// Fallback manual clipboard.\n    clipboard: String,\n}\n\nimpl Clipboard {\n    /// Construct a new instance\n    pub fn new(_raw_display_handle: Option<RawDisplayHandle>) -> Self {\n        Self {\n            #[cfg(all(\n                not(any(target_os = \"android\", target_os = \"ios\")),\n                feature = \"arboard\",\n            ))]\n            arboard: init_arboard(),\n\n            #[cfg(all(\n                any(\n                    target_os = \"linux\",\n                    target_os = \"dragonfly\",\n                    target_os = \"freebsd\",\n                    target_os = \"netbsd\",\n                    target_os = \"openbsd\"\n                ),\n                feature = \"smithay-clipboard\"\n            ))]\n            smithay: init_smithay_clipboard(_raw_display_handle),\n\n            clipboard: Default::default(),\n        }\n    }\n\n    pub fn get(&mut self) -> Option<String> {\n        #[cfg(all(\n            any(\n                target_os = \"linux\",\n                target_os = \"dragonfly\",\n                target_os = \"freebsd\",\n                target_os = \"netbsd\",\n                target_os = \"openbsd\"\n            ),\n            feature = \"smithay-clipboard\"\n        ))]\n        if let Some(clipboard) = &mut self.smithay {\n            return match clipboard.load() {\n                Ok(text) => Some(text),\n                Err(err) => {\n                    log::error!(\"smithay paste error: {err}\");\n                    None\n                }\n            };\n        }\n\n        #[cfg(all(\n            not(any(target_os = \"android\", target_os = \"ios\")),\n            feature = \"arboard\",\n        ))]\n        if let Some(clipboard) = &mut self.arboard {\n            return match clipboard.get_text() {\n                Ok(text) => Some(text),\n                Err(err) => {\n                    log::error!(\"arboard paste error: {err}\");\n                    None\n                }\n            };\n        }\n\n        Some(self.clipboard.clone())\n    }\n\n    pub fn set_text(&mut self, text: String) {\n        #[cfg(all(\n            any(\n                target_os = \"linux\",\n                target_os = \"dragonfly\",\n                target_os = \"freebsd\",\n                target_os = \"netbsd\",\n                target_os = \"openbsd\"\n            ),\n            feature = \"smithay-clipboard\"\n        ))]\n        if let Some(clipboard) = &mut self.smithay {\n            clipboard.store(text);\n            return;\n        }\n\n        #[cfg(all(\n            not(any(target_os = \"android\", target_os = \"ios\")),\n            feature = \"arboard\",\n        ))]\n        if let Some(clipboard) = &mut self.arboard {\n            if let Err(err) = clipboard.set_text(text) {\n                log::error!(\"arboard copy/cut error: {err}\");\n            }\n            return;\n        }\n\n        self.clipboard = text;\n    }\n\n    pub fn set_image(&mut self, image: &egui::ColorImage) {\n        #[cfg(all(\n            not(any(target_os = \"android\", target_os = \"ios\")),\n            feature = \"arboard\",\n        ))]\n        if let Some(clipboard) = &mut self.arboard {\n            if let Err(err) = clipboard.set_image(arboard::ImageData {\n                width: image.width(),\n                height: image.height(),\n                bytes: std::borrow::Cow::Borrowed(bytemuck::cast_slice(&image.pixels)),\n            }) {\n                log::error!(\"arboard copy/cut error: {err}\");\n            }\n            log::debug!(\"Copied image to clipboard\");\n            return;\n        }\n\n        log::error!(\n            \"Copying images is not supported. Enable the 'clipboard' feature of `egui-winit` to enable it.\"\n        );\n        _ = image;\n    }\n}\n\n#[cfg(all(\n    not(any(target_os = \"android\", target_os = \"ios\")),\n    feature = \"arboard\",\n))]\nfn init_arboard() -> Option<arboard::Clipboard> {\n    profiling::function_scope!();\n\n    log::trace!(\"Initializing arboard clipboard…\");\n    match arboard::Clipboard::new() {\n        Ok(clipboard) => Some(clipboard),\n        Err(err) => {\n            log::warn!(\"Failed to initialize arboard clipboard: {err}\");\n            None\n        }\n    }\n}\n\n#[cfg(all(\n    any(\n        target_os = \"linux\",\n        target_os = \"dragonfly\",\n        target_os = \"freebsd\",\n        target_os = \"netbsd\",\n        target_os = \"openbsd\"\n    ),\n    feature = \"smithay-clipboard\"\n))]\nfn init_smithay_clipboard(\n    raw_display_handle: Option<RawDisplayHandle>,\n) -> Option<smithay_clipboard::Clipboard> {\n    #![expect(clippy::undocumented_unsafe_blocks)]\n\n    profiling::function_scope!();\n\n    if let Some(RawDisplayHandle::Wayland(display)) = raw_display_handle {\n        log::trace!(\"Initializing smithay clipboard…\");\n        #[expect(unsafe_code)]\n        Some(unsafe { smithay_clipboard::Clipboard::new(display.display.as_ptr()) })\n    } else {\n        #[cfg(feature = \"wayland\")]\n        log::debug!(\"Cannot init smithay clipboard without a Wayland display handle\");\n        #[cfg(not(feature = \"wayland\"))]\n        log::debug!(\n            \"Cannot init smithay clipboard: the 'wayland' feature of 'egui-winit' is not enabled\"\n        );\n        None\n    }\n}\n"
  },
  {
    "path": "crates/egui-winit/src/lib.rs",
    "content": "//! [`egui`] bindings for [`winit`](https://github.com/rust-windowing/winit).\n//!\n//! The library translates winit events to egui, handled copy/paste,\n//! updates the cursor, open links clicked in egui, etc.\n//!\n//! ## Feature flags\n#![cfg_attr(feature = \"document-features\", doc = document_features::document_features!())]\n//!\n\n#![expect(clippy::manual_range_contains)]\n\n#[cfg(feature = \"accesskit\")]\npub use accesskit_winit;\npub use egui;\n#[cfg(feature = \"accesskit\")]\nuse egui::accesskit;\nuse egui::{Pos2, Rect, Theme, Vec2, ViewportBuilder, ViewportCommand, ViewportId, ViewportInfo};\npub use winit;\n\npub mod clipboard;\nmod safe_area;\nmod window_settings;\n\npub use window_settings::WindowSettings;\n\nuse raw_window_handle::HasDisplayHandle;\n\nuse winit::{\n    dpi::{PhysicalPosition, PhysicalSize},\n    event::ElementState,\n    event_loop::ActiveEventLoop,\n    window::{CursorGrabMode, Window, WindowButtons, WindowLevel},\n};\n\npub fn screen_size_in_pixels(window: &Window) -> egui::Vec2 {\n    let size = if cfg!(target_os = \"ios\") {\n        // `outer_size` Includes the area behind the \"dynamic island\".\n        // It is up to the eframe user to make sure the dynamic island doesn't cover anything important.\n        // That will be easier once https://github.com/rust-windowing/winit/pull/3890 lands\n        window.outer_size()\n    } else {\n        window.inner_size()\n    };\n    egui::vec2(size.width as f32, size.height as f32)\n}\n\n/// Calculate the `pixels_per_point` for a given window, given the current egui zoom factor\npub fn pixels_per_point(egui_ctx: &egui::Context, window: &Window) -> f32 {\n    let native_pixels_per_point = window.scale_factor() as f32;\n    let egui_zoom_factor = egui_ctx.zoom_factor();\n    egui_zoom_factor * native_pixels_per_point\n}\n\n// ----------------------------------------------------------------------------\n\n#[must_use]\n#[derive(Clone, Copy, Debug, Default)]\npub struct EventResponse {\n    /// If true, egui consumed this event, i.e. wants exclusive use of this event\n    /// (e.g. a mouse click on an egui window, or entering text into a text field).\n    ///\n    /// For instance, if you use egui for a game, you should only\n    /// pass on the events to your game when [`Self::consumed`] is `false`.\n    ///\n    /// Note that egui uses `tab` to move focus between elements, so this will always be `true` for tabs.\n    pub consumed: bool,\n\n    /// Do we need an egui refresh because of this event?\n    pub repaint: bool,\n}\n\n// ----------------------------------------------------------------------------\n\n/// Handles the integration between egui and a winit Window.\n///\n/// Instantiate one of these per viewport/window.\npub struct State {\n    /// Shared clone.\n    egui_ctx: egui::Context,\n\n    viewport_id: ViewportId,\n    start_time: web_time::Instant,\n    egui_input: egui::RawInput,\n    pointer_pos_in_points: Option<egui::Pos2>,\n    any_pointer_button_down: bool,\n    current_cursor_icon: Option<egui::CursorIcon>,\n\n    clipboard: clipboard::Clipboard,\n\n    /// If `true`, mouse inputs will be treated as touches.\n    /// Useful for debugging touch support in egui.\n    ///\n    /// Creates duplicate touches, if real touch inputs are coming.\n    simulate_touch_screen: bool,\n\n    /// Is Some(…) when a touch is being translated to a pointer.\n    ///\n    /// Only one touch will be interpreted as pointer at any time.\n    pointer_touch_id: Option<u64>,\n\n    /// track ime state\n    has_sent_ime_enabled: bool,\n\n    #[cfg(feature = \"accesskit\")]\n    pub accesskit: Option<accesskit_winit::Adapter>,\n\n    allow_ime: bool,\n    ime_rect_px: Option<egui::Rect>,\n}\n\nimpl State {\n    /// Construct a new instance\n    pub fn new(\n        egui_ctx: egui::Context,\n        viewport_id: ViewportId,\n        display_target: &dyn HasDisplayHandle,\n        native_pixels_per_point: Option<f32>,\n        theme: Option<winit::window::Theme>,\n        max_texture_side: Option<usize>,\n    ) -> Self {\n        profiling::function_scope!();\n\n        let egui_input = egui::RawInput {\n            focused: false, // winit will tell us when we have focus\n            ..Default::default()\n        };\n\n        let mut slf = Self {\n            egui_ctx,\n            viewport_id,\n            start_time: web_time::Instant::now(),\n            egui_input,\n            pointer_pos_in_points: None,\n            any_pointer_button_down: false,\n            current_cursor_icon: None,\n\n            clipboard: clipboard::Clipboard::new(\n                display_target.display_handle().ok().map(|h| h.as_raw()),\n            ),\n\n            simulate_touch_screen: false,\n            pointer_touch_id: None,\n\n            has_sent_ime_enabled: false,\n\n            #[cfg(feature = \"accesskit\")]\n            accesskit: None,\n\n            allow_ime: false,\n            ime_rect_px: None,\n        };\n\n        slf.egui_input\n            .viewports\n            .entry(ViewportId::ROOT)\n            .or_default()\n            .native_pixels_per_point = native_pixels_per_point;\n        slf.egui_input.system_theme = theme.map(to_egui_theme);\n\n        if let Some(max_texture_side) = max_texture_side {\n            slf.set_max_texture_side(max_texture_side);\n        }\n        slf\n    }\n\n    #[cfg(feature = \"accesskit\")]\n    pub fn init_accesskit<T: From<accesskit_winit::Event> + Send>(\n        &mut self,\n        event_loop: &ActiveEventLoop,\n        window: &Window,\n        event_loop_proxy: winit::event_loop::EventLoopProxy<T>,\n    ) {\n        profiling::function_scope!();\n\n        self.accesskit = Some(accesskit_winit::Adapter::with_event_loop_proxy(\n            event_loop,\n            window,\n            event_loop_proxy,\n        ));\n    }\n\n    /// Call this once a graphics context has been created to update the maximum texture dimensions\n    /// that egui will use.\n    pub fn set_max_texture_side(&mut self, max_texture_side: usize) {\n        self.egui_input.max_texture_side = Some(max_texture_side);\n    }\n\n    /// Fetches text from the clipboard and returns it.\n    pub fn clipboard_text(&mut self) -> Option<String> {\n        self.clipboard.get()\n    }\n\n    /// Places the text onto the clipboard.\n    pub fn set_clipboard_text(&mut self, text: String) {\n        self.clipboard.set_text(text);\n    }\n\n    /// Returns [`false`] or the last value that [`Window::set_ime_allowed()`] was called with, used for debouncing.\n    pub fn allow_ime(&self) -> bool {\n        self.allow_ime\n    }\n\n    /// Set the last value that [`Window::set_ime_allowed()`] was called with.\n    pub fn set_allow_ime(&mut self, allow: bool) {\n        self.allow_ime = allow;\n    }\n\n    #[inline]\n    pub fn egui_ctx(&self) -> &egui::Context {\n        &self.egui_ctx\n    }\n\n    /// The current input state.\n    /// This is changed by [`Self::on_window_event`] and cleared by [`Self::take_egui_input`].\n    #[inline]\n    pub fn egui_input(&self) -> &egui::RawInput {\n        &self.egui_input\n    }\n\n    /// The current input state.\n    /// This is changed by [`Self::on_window_event`] and cleared by [`Self::take_egui_input`].\n    #[inline]\n    pub fn egui_input_mut(&mut self) -> &mut egui::RawInput {\n        &mut self.egui_input\n    }\n\n    /// Prepare for a new frame by extracting the accumulated input,\n    ///\n    /// as well as setting [the time](egui::RawInput::time) and [screen rectangle](egui::RawInput::screen_rect).\n    ///\n    /// You need to set [`egui::RawInput::viewports`] yourself though.\n    /// Use [`update_viewport_info`] to update the info for each\n    /// viewport.\n    pub fn take_egui_input(&mut self, window: &Window) -> egui::RawInput {\n        profiling::function_scope!();\n\n        self.egui_input.time = Some(self.start_time.elapsed().as_secs_f64());\n\n        // On Windows, a minimized window will have 0 width and height.\n        // See: https://github.com/rust-windowing/winit/issues/208\n        // This solves an issue where egui window positions would be changed when minimizing on Windows.\n        let screen_size_in_pixels = screen_size_in_pixels(window);\n        let screen_size_in_points =\n            screen_size_in_pixels / pixels_per_point(&self.egui_ctx, window);\n\n        self.egui_input.screen_rect = (screen_size_in_points.x > 0.0\n            && screen_size_in_points.y > 0.0)\n            .then(|| Rect::from_min_size(Pos2::ZERO, screen_size_in_points));\n\n        // Tell egui which viewport is now active:\n        self.egui_input.viewport_id = self.viewport_id;\n\n        self.egui_input\n            .viewports\n            .entry(self.viewport_id)\n            .or_default()\n            .native_pixels_per_point = Some(window.scale_factor() as f32);\n\n        self.egui_input.take()\n    }\n\n    /// Call this when there is a new event.\n    ///\n    /// The result can be found in [`Self::egui_input`] and be extracted with [`Self::take_egui_input`].\n    pub fn on_window_event(\n        &mut self,\n        window: &Window,\n        event: &winit::event::WindowEvent,\n    ) -> EventResponse {\n        profiling::function_scope!(short_window_event_description(event));\n\n        #[cfg(feature = \"accesskit\")]\n        if let Some(accesskit) = self.accesskit.as_mut() {\n            accesskit.process_event(window, event);\n        }\n\n        use winit::event::WindowEvent;\n\n        #[cfg(target_os = \"ios\")]\n        match &event {\n            WindowEvent::Resized(_)\n            | WindowEvent::ScaleFactorChanged { .. }\n            | WindowEvent::Focused(true)\n            | WindowEvent::Occluded(false) => {\n                // Once winit v0.31 has been released this can be reworked to get the safe area from\n                // `Window::safe_area`, and updated from a new event which is being discussed in\n                // https://github.com/rust-windowing/winit/issues/3911.\n                self.egui_input_mut().safe_area_insets = Some(safe_area::get_safe_area_insets());\n            }\n            _ => {}\n        }\n\n        match event {\n            WindowEvent::ScaleFactorChanged { scale_factor, .. } => {\n                let native_pixels_per_point = *scale_factor as f32;\n\n                self.egui_input\n                    .viewports\n                    .entry(self.viewport_id)\n                    .or_default()\n                    .native_pixels_per_point = Some(native_pixels_per_point);\n\n                EventResponse {\n                    repaint: true,\n                    consumed: false,\n                }\n            }\n            WindowEvent::MouseInput { state, button, .. } => {\n                self.on_mouse_button_input(*state, *button);\n                EventResponse {\n                    repaint: true,\n                    consumed: self.egui_ctx.egui_wants_pointer_input(),\n                }\n            }\n            WindowEvent::MouseWheel { delta, phase, .. } => {\n                self.on_mouse_wheel(window, *delta, *phase);\n                EventResponse {\n                    repaint: true,\n                    consumed: self.egui_ctx.egui_wants_pointer_input(),\n                }\n            }\n            WindowEvent::CursorMoved { position, .. } => {\n                self.on_cursor_moved(window, *position);\n                EventResponse {\n                    repaint: true,\n                    consumed: self.egui_ctx.egui_is_using_pointer(),\n                }\n            }\n            WindowEvent::CursorLeft { .. } => {\n                self.pointer_pos_in_points = None;\n                self.egui_input.events.push(egui::Event::PointerGone);\n                EventResponse {\n                    repaint: true,\n                    consumed: false,\n                }\n            }\n            // WindowEvent::TouchpadPressure {device_id, pressure, stage, ..  } => {} // TODO(emilk)\n            WindowEvent::Touch(touch) => {\n                self.on_touch(window, touch);\n                let consumed = match touch.phase {\n                    winit::event::TouchPhase::Started\n                    | winit::event::TouchPhase::Ended\n                    | winit::event::TouchPhase::Cancelled => {\n                        self.egui_ctx.egui_wants_pointer_input()\n                    }\n                    winit::event::TouchPhase::Moved => self.egui_ctx.egui_is_using_pointer(),\n                };\n                EventResponse {\n                    repaint: true,\n                    consumed,\n                }\n            }\n\n            WindowEvent::Ime(ime) => {\n                self.on_ime(ime);\n\n                EventResponse {\n                    repaint: true,\n                    consumed: self.egui_ctx.egui_wants_keyboard_input(),\n                }\n            }\n            WindowEvent::KeyboardInput {\n                event,\n                is_synthetic,\n                ..\n            } => {\n                // Winit generates fake \"synthetic\" KeyboardInput events when the focus\n                // is changed to the window, or away from it. Synthetic key presses\n                // represent no real key presses and should be ignored.\n                // See https://github.com/rust-windowing/winit/issues/3543\n                if *is_synthetic && event.state == ElementState::Pressed {\n                    EventResponse {\n                        repaint: true,\n                        consumed: false,\n                    }\n                } else {\n                    self.on_keyboard_input(event);\n\n                    // When pressing the Tab key, egui focuses the first focusable element, hence Tab always consumes.\n                    let consumed = self.egui_ctx.egui_wants_keyboard_input()\n                        || event.logical_key\n                            == winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab);\n                    EventResponse {\n                        repaint: true,\n                        consumed,\n                    }\n                }\n            }\n            WindowEvent::Focused(focused) => {\n                let focused = if cfg!(target_os = \"macos\") {\n                    // TODO(emilk): remove this work-around once we update winit\n                    // https://github.com/rust-windowing/winit/issues/4371\n                    // https://github.com/emilk/egui/issues/7588\n                    window.has_focus()\n                } else {\n                    *focused\n                };\n\n                self.egui_input.focused = focused;\n                self.egui_input\n                    .events\n                    .push(egui::Event::WindowFocused(focused));\n                EventResponse {\n                    repaint: true,\n                    consumed: false,\n                }\n            }\n            WindowEvent::ThemeChanged(winit_theme) => {\n                self.egui_input.system_theme = Some(to_egui_theme(*winit_theme));\n                EventResponse {\n                    repaint: true,\n                    consumed: false,\n                }\n            }\n            WindowEvent::HoveredFile(path) => {\n                self.egui_input.hovered_files.push(egui::HoveredFile {\n                    path: Some(path.clone()),\n                    ..Default::default()\n                });\n                EventResponse {\n                    repaint: true,\n                    consumed: false,\n                }\n            }\n            WindowEvent::HoveredFileCancelled => {\n                self.egui_input.hovered_files.clear();\n                EventResponse {\n                    repaint: true,\n                    consumed: false,\n                }\n            }\n            WindowEvent::DroppedFile(path) => {\n                self.egui_input.hovered_files.clear();\n                self.egui_input.dropped_files.push(egui::DroppedFile {\n                    path: Some(path.clone()),\n                    ..Default::default()\n                });\n                EventResponse {\n                    repaint: true,\n                    consumed: false,\n                }\n            }\n            WindowEvent::ModifiersChanged(state) => {\n                let state = state.state();\n\n                let alt = state.alt_key();\n                let ctrl = state.control_key();\n                let shift = state.shift_key();\n                let super_ = state.super_key();\n\n                self.egui_input.modifiers.alt = alt;\n                self.egui_input.modifiers.ctrl = ctrl;\n                self.egui_input.modifiers.shift = shift;\n                self.egui_input.modifiers.mac_cmd = cfg!(target_os = \"macos\") && super_;\n                self.egui_input.modifiers.command = if cfg!(target_os = \"macos\") {\n                    super_\n                } else {\n                    ctrl\n                };\n\n                EventResponse {\n                    repaint: true,\n                    consumed: false,\n                }\n            }\n\n            // Things that may require repaint:\n            WindowEvent::RedrawRequested\n            | WindowEvent::CursorEntered { .. }\n            | WindowEvent::Destroyed\n            | WindowEvent::Occluded(_)\n            | WindowEvent::Resized(_)\n            | WindowEvent::Moved(_)\n            | WindowEvent::TouchpadPressure { .. }\n            | WindowEvent::CloseRequested => EventResponse {\n                repaint: true,\n                consumed: false,\n            },\n\n            // Things we completely ignore:\n            WindowEvent::ActivationTokenDone { .. }\n            | WindowEvent::AxisMotion { .. }\n            | WindowEvent::DoubleTapGesture { .. } => EventResponse {\n                repaint: false,\n                consumed: false,\n            },\n\n            WindowEvent::PinchGesture { delta, .. } => {\n                // Positive delta values indicate magnification (zooming in).\n                // Negative delta values indicate shrinking (zooming out).\n                let zoom_factor = (*delta as f32).exp();\n                self.egui_input.events.push(egui::Event::Zoom(zoom_factor));\n                EventResponse {\n                    repaint: true,\n                    consumed: self.egui_ctx.egui_wants_pointer_input(),\n                }\n            }\n\n            WindowEvent::RotationGesture { delta, .. } => {\n                // Positive delta values indicate counterclockwise rotation\n                // Negative delta values indicate clockwise rotation\n                // This is opposite of egui's sign convention for angles\n                self.egui_input\n                    .events\n                    .push(egui::Event::Rotate(-delta.to_radians()));\n                EventResponse {\n                    repaint: true,\n                    consumed: self.egui_ctx.egui_wants_pointer_input(),\n                }\n            }\n\n            WindowEvent::PanGesture { delta, phase, .. } => {\n                let pixels_per_point = pixels_per_point(&self.egui_ctx, window);\n\n                self.egui_input.events.push(egui::Event::MouseWheel {\n                    unit: egui::MouseWheelUnit::Point,\n                    delta: Vec2::new(delta.x, delta.y) / pixels_per_point,\n                    phase: to_egui_touch_phase(*phase),\n                    modifiers: self.egui_input.modifiers,\n                });\n                EventResponse {\n                    repaint: true,\n                    consumed: self.egui_ctx.egui_wants_pointer_input(),\n                }\n            }\n        }\n    }\n\n    /// ## NOTE\n    ///\n    /// on Mac even Cmd-C is pressed during ime, a `c` is pushed to Preedit.\n    /// So no need to check `is_mac_cmd`.\n    ///\n    /// ### How events are emitted by [`winit`] across different setups in various situations\n    ///\n    /// This is done by uncommenting the code block at the top of this method\n    /// and checking console outputs.\n    ///\n    /// winit version: 0.30.12.\n    ///\n    /// #### Setups\n    ///\n    /// - `a-macos15-apple_shuangpin`: macOS 15.7.3 `aarch64`, IME: builtin Chinese Shuangpin - Simplified. (Demo app shows: renderer: `wgpu`, backend: `Metal`.)\n    /// - `b-debian13_gnome48_wayland-fcitx5_shuangpin`: Debian 13 `aarch64`, Gnome 48, Wayland, IME: Fcitx5 with fcitx5-chinese-addons's Shuangpin. (Demo app shows: renderer: `wgpu`, backend: `Gl`.)\n    /// - `c-windows11-ms_pinyin`: Windows11 23H2 `x86_64`, IME: builtin Microsoft Pinyin. (Demo app shows: renderer: `wgpu`, backend: `Vulkan` & `Dx12`, others: `Dx12` & `Gl`.)\n    ///\n    /// #### Situation: pressed space to select the first candidate \"测试\"\n    ///\n    /// | Setup                                       | Events in Order                                                                                                                  |\n    /// | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |\n    /// | a-macos15-apple_shuangpin                   | `Preedit(\"\", None)` -> `Commit(\"测试\")`                                                                                          |\n    /// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Preedit(\"\", None)` -> `Commit(\"测试\")` -> `Preedit(\"\", Some(0, 0))` -> `Preedit(\"\", None)` (duplicate until `TextEdit` blurred) |\n    /// | c-windows11-ms_pinyin                       | `Preedit(\"测试\", Some(…))` -> `Preedit(\"\", None)` -> `Commit(\"测试\")` -> `Disabled`                                              |\n    ///\n    /// #### Situation: pressed backspace to delete the last character in the composition\n    ///\n    /// | Setup                                       | Events in Order                                                                       |\n    /// | a-macos15-apple_shuangpin                   | `Preedit(\"\", None)`                                                                   |\n    /// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Preedit(\"\", Some(0, 0))` -> `Preedit(\"\", None)` (duplicate until `TextEdit` blurred) |\n    /// | c-windows11-ms_pinyin                       | `Preedit(\"\", Some(0, 0))` -> `Preedit(\"\", None)` -> `Commit(\"\")` -> `Disabled`        |\n    ///\n    /// #### Situation: clicked somewhere else while there is an active composition with the pre-edit text \"ce\"\n    ///\n    /// | Setup                                       | Events in Order                                                                                   |\n    /// | ------------------------------------------- | ------------------------------------------------------------------------------------------------- |\n    /// | a-macos15-apple_shuangpin                   | nothing emitted                                                                                   |\n    /// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Preedit(\"\", Some(0, 0))` (duplicate) -> `Preedit(\"\", None)` (duplicate until `TextEdit` blurred) |\n    /// | c-windows11-ms_pinyin                       | nothing emitted                                                                                   |\n    fn on_ime(&mut self, ime: &winit::event::Ime) {\n        // // code for inspecting ime events emitted by winit:\n        // {\n        //     static LAST_IME: std::sync::Mutex<Option<winit::event::Ime>> =\n        //         std::sync::Mutex::new(None);\n        //     static IS_LAST_DUPLICATE: std::sync::atomic::AtomicBool =\n        //         std::sync::atomic::AtomicBool::new(false);\n        //     let mut last_ime_guard = LAST_IME.lock().unwrap();\n        //     if { last_ime_guard.as_ref().cloned() }.as_ref() != Some(ime) {\n        //         println!(\"IME={ime:?}\");\n        //         *last_ime_guard = Some(ime.clone());\n        //         IS_LAST_DUPLICATE.store(false, std::sync::atomic::Ordering::Relaxed);\n        //     } else if !IS_LAST_DUPLICATE.load(std::sync::atomic::Ordering::Relaxed) {\n        //         println!(\"IME=(duplicate)\");\n        //         IS_LAST_DUPLICATE.store(true, std::sync::atomic::Ordering::Relaxed);\n        //     }\n        // }\n\n        match ime {\n            winit::event::Ime::Enabled => {\n                if cfg!(target_os = \"linux\") {\n                    // This event means different things in X11 and Wayland, but we can just\n                    // ignore it and enable IME on the preedit event.\n                    // See <https://github.com/rust-windowing/winit/issues/2498>\n                } else {\n                    self.ime_event_enable();\n                }\n            }\n            winit::event::Ime::Preedit(text, Some(_cursor)) => {\n                self.ime_event_enable();\n                self.egui_input\n                    .events\n                    .push(egui::Event::Ime(egui::ImeEvent::Preedit(text.clone())));\n            }\n            winit::event::Ime::Commit(text) => {\n                self.egui_input\n                    .events\n                    .push(egui::Event::Ime(egui::ImeEvent::Commit(text.clone())));\n                self.ime_event_disable();\n            }\n            winit::event::Ime::Disabled => {\n                self.ime_event_disable();\n            }\n            winit::event::Ime::Preedit(_, None) => {\n                if cfg!(target_os = \"macos\") {\n                    // On macOS, when the user presses backspace to delete the\n                    // last character in an IME composition, `winit` only emits\n                    // `winit::event::Ime::Preedit(\"\", None)` without a\n                    // preceding `winit::event::Ime::Preedit(\"\", Some(0, 0))`.\n                    //\n                    // The current implementation of `egui::TextEdit` relies on\n                    // receiving an `egui::ImeEvent::Preedit(\"\")` to remove the\n                    // last character in the composition in this case, so we\n                    // emit it here.\n                    //\n                    // This is guarded to macOS-only, as applying it on other\n                    // platforms is unnecessary and can cause undesired\n                    // behavior.\n                    // See: https://github.com/emilk/egui/pull/7973\n                    self.egui_input\n                        .events\n                        .push(egui::Event::Ime(egui::ImeEvent::Preedit(String::new())));\n                }\n\n                self.ime_event_disable();\n            }\n        }\n    }\n\n    pub fn ime_event_enable(&mut self) {\n        if !self.has_sent_ime_enabled {\n            self.egui_input\n                .events\n                .push(egui::Event::Ime(egui::ImeEvent::Enabled));\n            self.has_sent_ime_enabled = true;\n        }\n    }\n\n    pub fn ime_event_disable(&mut self) {\n        self.egui_input\n            .events\n            .push(egui::Event::Ime(egui::ImeEvent::Disabled));\n        self.has_sent_ime_enabled = false;\n    }\n\n    /// Returns `true` if the event was sent to egui.\n    pub fn on_mouse_motion(&mut self, delta: (f64, f64)) -> bool {\n        if !self.is_pointer_in_window() && !self.any_pointer_button_down {\n            return false;\n        }\n\n        self.egui_input.events.push(egui::Event::MouseMoved(Vec2 {\n            x: delta.0 as f32,\n            y: delta.1 as f32,\n        }));\n        true\n    }\n\n    /// Returns `true` when the pointer is currently inside the window.\n    pub fn is_pointer_in_window(&self) -> bool {\n        self.pointer_pos_in_points.is_some()\n    }\n\n    /// Returns `true` if any pointer button is currently held down.\n    pub fn is_any_pointer_button_down(&self) -> bool {\n        self.any_pointer_button_down\n    }\n\n    /// Call this when there is a new [`accesskit::ActionRequest`].\n    ///\n    /// The result can be found in [`Self::egui_input`] and be extracted with [`Self::take_egui_input`].\n    #[cfg(feature = \"accesskit\")]\n    pub fn on_accesskit_action_request(&mut self, request: accesskit::ActionRequest) {\n        self.egui_input\n            .events\n            .push(egui::Event::AccessKitActionRequest(request));\n    }\n\n    fn on_mouse_button_input(\n        &mut self,\n        state: winit::event::ElementState,\n        button: winit::event::MouseButton,\n    ) {\n        if let Some(pos) = self.pointer_pos_in_points\n            && let Some(button) = translate_mouse_button(button)\n        {\n            let pressed = state == winit::event::ElementState::Pressed;\n\n            self.egui_input.events.push(egui::Event::PointerButton {\n                pos,\n                button,\n                pressed,\n                modifiers: self.egui_input.modifiers,\n            });\n\n            if self.simulate_touch_screen {\n                if pressed {\n                    self.any_pointer_button_down = true;\n\n                    self.egui_input.events.push(egui::Event::Touch {\n                        device_id: egui::TouchDeviceId(0),\n                        id: egui::TouchId(0),\n                        phase: egui::TouchPhase::Start,\n                        pos,\n                        force: None,\n                    });\n                } else {\n                    self.any_pointer_button_down = false;\n\n                    self.egui_input.events.push(egui::Event::PointerGone);\n\n                    self.egui_input.events.push(egui::Event::Touch {\n                        device_id: egui::TouchDeviceId(0),\n                        id: egui::TouchId(0),\n                        phase: egui::TouchPhase::End,\n                        pos,\n                        force: None,\n                    });\n                }\n            }\n        }\n    }\n\n    fn on_cursor_moved(\n        &mut self,\n        window: &Window,\n        pos_in_pixels: winit::dpi::PhysicalPosition<f64>,\n    ) {\n        let pixels_per_point = pixels_per_point(&self.egui_ctx, window);\n\n        let pos_in_points = egui::pos2(\n            pos_in_pixels.x as f32 / pixels_per_point,\n            pos_in_pixels.y as f32 / pixels_per_point,\n        );\n        self.pointer_pos_in_points = Some(pos_in_points);\n\n        if self.simulate_touch_screen {\n            if self.any_pointer_button_down {\n                self.egui_input\n                    .events\n                    .push(egui::Event::PointerMoved(pos_in_points));\n\n                self.egui_input.events.push(egui::Event::Touch {\n                    device_id: egui::TouchDeviceId(0),\n                    id: egui::TouchId(0),\n                    phase: egui::TouchPhase::Move,\n                    pos: pos_in_points,\n                    force: None,\n                });\n            }\n        } else {\n            self.egui_input\n                .events\n                .push(egui::Event::PointerMoved(pos_in_points));\n        }\n    }\n\n    fn on_touch(&mut self, window: &Window, touch: &winit::event::Touch) {\n        let pixels_per_point = pixels_per_point(&self.egui_ctx, window);\n\n        // Emit touch event\n        self.egui_input.events.push(egui::Event::Touch {\n            device_id: egui::TouchDeviceId(egui::epaint::util::hash(touch.device_id)),\n            id: egui::TouchId::from(touch.id),\n            phase: to_egui_touch_phase(touch.phase),\n            pos: egui::pos2(\n                touch.location.x as f32 / pixels_per_point,\n                touch.location.y as f32 / pixels_per_point,\n            ),\n            force: match touch.force {\n                Some(winit::event::Force::Normalized(force)) => Some(force as f32),\n                Some(winit::event::Force::Calibrated {\n                    force,\n                    max_possible_force,\n                    ..\n                }) => Some((force / max_possible_force) as f32),\n                None => None,\n            },\n        });\n        // If we're not yet translating a touch or we're translating this very\n        // touch …\n        if self.pointer_touch_id.is_none() || self.pointer_touch_id.unwrap_or_default() == touch.id\n        {\n            // … emit PointerButton resp. PointerMoved events to emulate mouse\n            match touch.phase {\n                winit::event::TouchPhase::Started => {\n                    self.pointer_touch_id = Some(touch.id);\n                    // First move the pointer to the right location\n                    self.on_cursor_moved(window, touch.location);\n                    self.on_mouse_button_input(\n                        winit::event::ElementState::Pressed,\n                        winit::event::MouseButton::Left,\n                    );\n                }\n                winit::event::TouchPhase::Moved => {\n                    self.on_cursor_moved(window, touch.location);\n                }\n                winit::event::TouchPhase::Ended => {\n                    self.pointer_touch_id = None;\n                    self.on_mouse_button_input(\n                        winit::event::ElementState::Released,\n                        winit::event::MouseButton::Left,\n                    );\n                    // The pointer should vanish completely to not get any\n                    // hover effects\n                    self.pointer_pos_in_points = None;\n                    self.egui_input.events.push(egui::Event::PointerGone);\n                }\n                winit::event::TouchPhase::Cancelled => {\n                    self.pointer_touch_id = None;\n                    self.pointer_pos_in_points = None;\n                    self.egui_input.events.push(egui::Event::PointerGone);\n                }\n            }\n        }\n    }\n\n    fn on_mouse_wheel(\n        &mut self,\n        window: &Window,\n        delta: winit::event::MouseScrollDelta,\n        phase: winit::event::TouchPhase,\n    ) {\n        let pixels_per_point = pixels_per_point(&self.egui_ctx, window);\n\n        {\n            let (unit, delta) = match delta {\n                winit::event::MouseScrollDelta::LineDelta(x, y) => {\n                    (egui::MouseWheelUnit::Line, egui::vec2(x, y))\n                }\n                winit::event::MouseScrollDelta::PixelDelta(winit::dpi::PhysicalPosition {\n                    x,\n                    y,\n                }) => (\n                    egui::MouseWheelUnit::Point,\n                    egui::vec2(x as f32, y as f32) / pixels_per_point,\n                ),\n            };\n            let phase = to_egui_touch_phase(phase);\n            let modifiers = self.egui_input.modifiers;\n            self.egui_input.events.push(egui::Event::MouseWheel {\n                unit,\n                delta,\n                phase,\n                modifiers,\n            });\n        }\n    }\n\n    fn on_keyboard_input(&mut self, event: &winit::event::KeyEvent) {\n        let winit::event::KeyEvent {\n            // Represents the position of a key independent of the currently active layout.\n            //\n            // It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode).\n            // The most prevalent use case for this is games. For example the default keys for the player\n            // to move around might be the W, A, S, and D keys on a US layout. The position of these keys\n            // is more important than their label, so they should map to Z, Q, S, and D on an \"AZERTY\"\n            // layout. (This value is `KeyCode::KeyW` for the Z key on an AZERTY layout.)\n            physical_key,\n\n            // Represents the results of a keymap, i.e. what character a certain key press represents.\n            // When telling users \"Press Ctrl-F to find\", this is where we should\n            // look for the \"F\" key, because they may have a dvorak layout on\n            // a qwerty keyboard, and so the logical \"F\" character may not be located on the physical `KeyCode::KeyF` position.\n            logical_key: winit_logical_key,\n\n            text,\n\n            state,\n\n            location: _, // e.g. is it on the numpad?\n            repeat: _,   // egui will figure this out for us\n            ..\n        } = event;\n\n        let pressed = *state == winit::event::ElementState::Pressed;\n\n        let physical_key = if let winit::keyboard::PhysicalKey::Code(keycode) = *physical_key {\n            key_from_key_code(keycode)\n        } else {\n            None\n        };\n\n        let logical_key = key_from_winit_key(winit_logical_key);\n\n        // Helpful logging to enable when adding new key support\n        log::trace!(\n            \"logical {:?} -> {:?},  physical {:?} -> {:?}\",\n            event.logical_key,\n            logical_key,\n            event.physical_key,\n            physical_key\n        );\n\n        // \"Logical OR physical key\" is a fallback mechanism for keyboard layouts without Latin characters: it lets them\n        // emit events as if the corresponding keys from the Latin layout were pressed. In this case, clipboard shortcuts\n        // are mapped to the physical keys that normally contain C, X, V, etc.\n        // See also: https://github.com/emilk/egui/issues/3653\n        if let Some(active_key) = logical_key.or(physical_key) {\n            if pressed {\n                if is_cut_command(self.egui_input.modifiers, active_key) {\n                    self.egui_input.events.push(egui::Event::Cut);\n                    return;\n                } else if is_copy_command(self.egui_input.modifiers, active_key) {\n                    self.egui_input.events.push(egui::Event::Copy);\n                    return;\n                } else if is_paste_command(self.egui_input.modifiers, active_key) {\n                    if let Some(contents) = self.clipboard.get() {\n                        let contents = contents.replace(\"\\r\\n\", \"\\n\");\n                        if !contents.is_empty() {\n                            self.egui_input.events.push(egui::Event::Paste(contents));\n                        }\n                    }\n                    return;\n                }\n            }\n\n            self.egui_input.events.push(egui::Event::Key {\n                key: active_key,\n                physical_key,\n                pressed,\n                repeat: false, // egui will fill this in for us!\n                modifiers: self.egui_input.modifiers,\n            });\n        }\n\n        if let Some(text) = text\n            .as_ref()\n            .map(|t| t.as_str())\n            .or_else(|| winit_logical_key.to_text())\n        {\n            // Make sure there is text, and that it is not control characters\n            // (e.g. delete is sent as \"\\u{f728}\" on macOS).\n            if !text.is_empty() && text.chars().all(is_printable_char) {\n                // On some platforms we get here when the user presses Cmd-C (copy), ctrl-W, etc.\n                // We need to ignore these characters that are side-effects of commands.\n                // Also make sure the key is pressed (not released). On Linux, text might\n                // contain some data even when the key is released.\n                let is_cmd = self.egui_input.modifiers.ctrl\n                    || self.egui_input.modifiers.command\n                    || self.egui_input.modifiers.mac_cmd;\n                if pressed && !is_cmd {\n                    self.egui_input\n                        .events\n                        .push(egui::Event::Text(text.to_owned()));\n                }\n            }\n        }\n    }\n\n    /// Call with the output given by `egui`.\n    ///\n    /// This will, if needed:\n    /// * update the cursor\n    /// * copy text to the clipboard\n    /// * open any clicked urls\n    /// * update the IME\n    /// *\n    pub fn handle_platform_output(\n        &mut self,\n        window: &Window,\n        platform_output: egui::PlatformOutput,\n    ) {\n        profiling::function_scope!();\n\n        let egui::PlatformOutput {\n            commands,\n            cursor_icon,\n            events: _,                    // handled elsewhere\n            mutable_text_under_cursor: _, // only used in eframe web\n            ime,\n            accesskit_update,\n            num_completed_passes: _,    // `egui::Context::run` handles this\n            request_discard_reasons: _, // `egui::Context::run` handles this\n        } = platform_output;\n\n        for command in commands {\n            match command {\n                egui::OutputCommand::CopyText(text) => {\n                    self.clipboard.set_text(text);\n                }\n                egui::OutputCommand::CopyImage(image) => {\n                    self.clipboard.set_image(&image);\n                }\n                egui::OutputCommand::OpenUrl(open_url) => {\n                    open_url_in_browser(&open_url.url);\n                }\n            }\n        }\n\n        self.set_cursor_icon(window, cursor_icon);\n\n        let allow_ime = ime.is_some();\n        if self.allow_ime != allow_ime {\n            self.allow_ime = allow_ime;\n            profiling::scope!(\"set_ime_allowed\");\n            window.set_ime_allowed(allow_ime);\n        }\n\n        if let Some(ime) = ime {\n            let pixels_per_point = pixels_per_point(&self.egui_ctx, window);\n            let ime_rect_px = pixels_per_point * ime.rect;\n            if self.ime_rect_px != Some(ime_rect_px)\n                || self.egui_ctx.input(|i| !i.events.is_empty())\n            {\n                self.ime_rect_px = Some(ime_rect_px);\n                profiling::scope!(\"set_ime_cursor_area\");\n                window.set_ime_cursor_area(\n                    winit::dpi::PhysicalPosition {\n                        x: ime_rect_px.min.x,\n                        y: ime_rect_px.min.y,\n                    },\n                    winit::dpi::PhysicalSize {\n                        width: ime_rect_px.width(),\n                        height: ime_rect_px.height(),\n                    },\n                );\n            }\n        } else {\n            self.ime_rect_px = None;\n        }\n\n        #[cfg(feature = \"accesskit\")]\n        if let Some(accesskit) = self.accesskit.as_mut()\n            && let Some(update) = accesskit_update\n        {\n            profiling::scope!(\"accesskit\");\n            accesskit.update_if_active(|| update);\n        }\n\n        #[cfg(not(feature = \"accesskit\"))]\n        let _ = accesskit_update;\n    }\n\n    fn set_cursor_icon(&mut self, window: &Window, cursor_icon: egui::CursorIcon) {\n        if self.current_cursor_icon == Some(cursor_icon) {\n            // Prevent flickering near frame boundary when Windows OS tries to control cursor icon for window resizing.\n            // On other platforms: just early-out to save CPU.\n            return;\n        }\n\n        let is_pointer_in_window = self.pointer_pos_in_points.is_some();\n        if is_pointer_in_window {\n            self.current_cursor_icon = Some(cursor_icon);\n\n            if let Some(winit_cursor_icon) = translate_cursor(cursor_icon) {\n                window.set_cursor_visible(true);\n                window.set_cursor(winit_cursor_icon);\n            } else {\n                window.set_cursor_visible(false);\n            }\n        } else {\n            // Remember to set the cursor again once the cursor returns to the screen:\n            self.current_cursor_icon = None;\n        }\n    }\n}\n\nfn to_egui_touch_phase(phase: winit::event::TouchPhase) -> egui::TouchPhase {\n    match phase {\n        winit::event::TouchPhase::Started => egui::TouchPhase::Start,\n        winit::event::TouchPhase::Moved => egui::TouchPhase::Move,\n        winit::event::TouchPhase::Ended => egui::TouchPhase::End,\n        winit::event::TouchPhase::Cancelled => egui::TouchPhase::Cancel,\n    }\n}\n\nfn to_egui_theme(theme: winit::window::Theme) -> Theme {\n    match theme {\n        winit::window::Theme::Dark => Theme::Dark,\n        winit::window::Theme::Light => Theme::Light,\n    }\n}\n\npub fn inner_rect_in_points(window: &Window, pixels_per_point: f32) -> Option<Rect> {\n    let inner_pos_px = window.inner_position().ok()?;\n    let inner_pos_px = egui::pos2(inner_pos_px.x as f32, inner_pos_px.y as f32);\n\n    let inner_size_px = window.inner_size();\n    let inner_size_px = egui::vec2(inner_size_px.width as f32, inner_size_px.height as f32);\n\n    let inner_rect_px = egui::Rect::from_min_size(inner_pos_px, inner_size_px);\n\n    Some(inner_rect_px / pixels_per_point)\n}\n\npub fn outer_rect_in_points(window: &Window, pixels_per_point: f32) -> Option<Rect> {\n    let outer_pos_px = window.outer_position().ok()?;\n    let outer_pos_px = egui::pos2(outer_pos_px.x as f32, outer_pos_px.y as f32);\n\n    let outer_size_px = window.outer_size();\n    let outer_size_px = egui::vec2(outer_size_px.width as f32, outer_size_px.height as f32);\n\n    let outer_rect_px = egui::Rect::from_min_size(outer_pos_px, outer_size_px);\n\n    Some(outer_rect_px / pixels_per_point)\n}\n\n/// Update the given viewport info with the current state of the window.\n///\n/// Call before [`State::take_egui_input`].\n///\n/// If this is called right after window creation, `is_init` should be `true`, otherwise `false`.\npub fn update_viewport_info(\n    viewport_info: &mut ViewportInfo,\n    egui_ctx: &egui::Context,\n    window: &Window,\n    is_init: bool,\n) {\n    profiling::function_scope!();\n    let pixels_per_point = pixels_per_point(egui_ctx, window);\n\n    let has_a_position = match window.is_minimized() {\n        Some(true) => false,\n        Some(false) | None => true,\n    };\n\n    let inner_rect = if has_a_position {\n        inner_rect_in_points(window, pixels_per_point)\n    } else {\n        None\n    };\n\n    let outer_rect = if has_a_position {\n        outer_rect_in_points(window, pixels_per_point)\n    } else {\n        None\n    };\n\n    let monitor_size = {\n        profiling::scope!(\"monitor_size\");\n        if let Some(monitor) = window.current_monitor() {\n            let size = monitor.size().to_logical::<f32>(pixels_per_point.into());\n            Some(egui::vec2(size.width, size.height))\n        } else {\n            None\n        }\n    };\n\n    viewport_info.title = Some(window.title());\n    viewport_info.native_pixels_per_point = Some(window.scale_factor() as f32);\n\n    viewport_info.monitor_size = monitor_size;\n    viewport_info.inner_rect = inner_rect;\n    viewport_info.outer_rect = outer_rect;\n\n    if is_init || !cfg!(target_os = \"macos\") {\n        // Asking for minimized/maximized state at runtime leads to a deadlock on Mac when running\n        // `cargo run -p custom_window_frame`.\n        // See https://github.com/emilk/egui/issues/3494\n        viewport_info.maximized = Some(window.is_maximized());\n        viewport_info.minimized = Some(window.is_minimized().unwrap_or(false));\n    }\n\n    viewport_info.fullscreen = Some(window.fullscreen().is_some());\n    viewport_info.focused = Some(window.has_focus());\n}\n\nfn open_url_in_browser(_url: &str) {\n    #[cfg(feature = \"webbrowser\")]\n    if let Err(err) = webbrowser::open(_url) {\n        log::warn!(\"Failed to open url: {err}\");\n    }\n\n    #[cfg(not(feature = \"webbrowser\"))]\n    {\n        log::warn!(\"Cannot open url - feature \\\"links\\\" not enabled.\");\n    }\n}\n\n/// Winit sends special keys (backspace, delete, F1, …) as characters.\n/// Ignore those.\n/// We also ignore '\\r', '\\n', '\\t'.\n/// Newlines are handled by the `Key::Enter` event.\nfn is_printable_char(chr: char) -> bool {\n    let is_in_private_use_area = '\\u{e000}' <= chr && chr <= '\\u{f8ff}'\n        || '\\u{f0000}' <= chr && chr <= '\\u{ffffd}'\n        || '\\u{100000}' <= chr && chr <= '\\u{10fffd}';\n\n    !is_in_private_use_area && !chr.is_ascii_control()\n}\n\nfn is_cut_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool {\n    keycode == egui::Key::Cut\n        || (modifiers.command && keycode == egui::Key::X)\n        || (cfg!(target_os = \"windows\") && modifiers.shift && keycode == egui::Key::Delete)\n}\n\nfn is_copy_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool {\n    keycode == egui::Key::Copy\n        || (modifiers.command && keycode == egui::Key::C)\n        || (cfg!(target_os = \"windows\") && modifiers.ctrl && keycode == egui::Key::Insert)\n}\n\nfn is_paste_command(modifiers: egui::Modifiers, keycode: egui::Key) -> bool {\n    keycode == egui::Key::Paste\n        || (modifiers.command && keycode == egui::Key::V)\n        || (cfg!(target_os = \"windows\") && modifiers.shift && keycode == egui::Key::Insert)\n}\n\nfn translate_mouse_button(button: winit::event::MouseButton) -> Option<egui::PointerButton> {\n    match button {\n        winit::event::MouseButton::Left => Some(egui::PointerButton::Primary),\n        winit::event::MouseButton::Right => Some(egui::PointerButton::Secondary),\n        winit::event::MouseButton::Middle => Some(egui::PointerButton::Middle),\n        winit::event::MouseButton::Back => Some(egui::PointerButton::Extra1),\n        winit::event::MouseButton::Forward => Some(egui::PointerButton::Extra2),\n        winit::event::MouseButton::Other(_) => None,\n    }\n}\n\nfn key_from_winit_key(key: &winit::keyboard::Key) -> Option<egui::Key> {\n    match key {\n        winit::keyboard::Key::Named(named_key) => key_from_named_key(*named_key),\n        winit::keyboard::Key::Character(str) => egui::Key::from_name(str.as_str()),\n        winit::keyboard::Key::Unidentified(_) | winit::keyboard::Key::Dead(_) => None,\n    }\n}\n\nfn key_from_named_key(named_key: winit::keyboard::NamedKey) -> Option<egui::Key> {\n    use egui::Key;\n    use winit::keyboard::NamedKey;\n\n    Some(match named_key {\n        NamedKey::Enter => Key::Enter,\n        NamedKey::Tab => Key::Tab,\n        NamedKey::ArrowDown => Key::ArrowDown,\n        NamedKey::ArrowLeft => Key::ArrowLeft,\n        NamedKey::ArrowRight => Key::ArrowRight,\n        NamedKey::ArrowUp => Key::ArrowUp,\n        NamedKey::End => Key::End,\n        NamedKey::Home => Key::Home,\n        NamedKey::PageDown => Key::PageDown,\n        NamedKey::PageUp => Key::PageUp,\n        NamedKey::Backspace => Key::Backspace,\n        NamedKey::Delete => Key::Delete,\n        NamedKey::Insert => Key::Insert,\n        NamedKey::Escape => Key::Escape,\n        NamedKey::Cut => Key::Cut,\n        NamedKey::Copy => Key::Copy,\n        NamedKey::Paste => Key::Paste,\n\n        NamedKey::Space => Key::Space,\n\n        NamedKey::F1 => Key::F1,\n        NamedKey::F2 => Key::F2,\n        NamedKey::F3 => Key::F3,\n        NamedKey::F4 => Key::F4,\n        NamedKey::F5 => Key::F5,\n        NamedKey::F6 => Key::F6,\n        NamedKey::F7 => Key::F7,\n        NamedKey::F8 => Key::F8,\n        NamedKey::F9 => Key::F9,\n        NamedKey::F10 => Key::F10,\n        NamedKey::F11 => Key::F11,\n        NamedKey::F12 => Key::F12,\n        NamedKey::F13 => Key::F13,\n        NamedKey::F14 => Key::F14,\n        NamedKey::F15 => Key::F15,\n        NamedKey::F16 => Key::F16,\n        NamedKey::F17 => Key::F17,\n        NamedKey::F18 => Key::F18,\n        NamedKey::F19 => Key::F19,\n        NamedKey::F20 => Key::F20,\n        NamedKey::F21 => Key::F21,\n        NamedKey::F22 => Key::F22,\n        NamedKey::F23 => Key::F23,\n        NamedKey::F24 => Key::F24,\n        NamedKey::F25 => Key::F25,\n        NamedKey::F26 => Key::F26,\n        NamedKey::F27 => Key::F27,\n        NamedKey::F28 => Key::F28,\n        NamedKey::F29 => Key::F29,\n        NamedKey::F30 => Key::F30,\n        NamedKey::F31 => Key::F31,\n        NamedKey::F32 => Key::F32,\n        NamedKey::F33 => Key::F33,\n        NamedKey::F34 => Key::F34,\n        NamedKey::F35 => Key::F35,\n\n        NamedKey::BrowserBack => Key::BrowserBack,\n        _ => {\n            log::trace!(\"Unknown key: {named_key:?}\");\n            return None;\n        }\n    })\n}\n\nfn key_from_key_code(key: winit::keyboard::KeyCode) -> Option<egui::Key> {\n    use egui::Key;\n    use winit::keyboard::KeyCode;\n\n    Some(match key {\n        KeyCode::ArrowDown => Key::ArrowDown,\n        KeyCode::ArrowLeft => Key::ArrowLeft,\n        KeyCode::ArrowRight => Key::ArrowRight,\n        KeyCode::ArrowUp => Key::ArrowUp,\n\n        KeyCode::Escape => Key::Escape,\n        KeyCode::Tab => Key::Tab,\n        KeyCode::Backspace => Key::Backspace,\n        KeyCode::Enter | KeyCode::NumpadEnter => Key::Enter,\n\n        KeyCode::Insert => Key::Insert,\n        KeyCode::Delete => Key::Delete,\n        KeyCode::Home => Key::Home,\n        KeyCode::End => Key::End,\n        KeyCode::PageUp => Key::PageUp,\n        KeyCode::PageDown => Key::PageDown,\n\n        // Punctuation\n        KeyCode::Space => Key::Space,\n        KeyCode::Comma => Key::Comma,\n        KeyCode::Period => Key::Period,\n        // KeyCode::Colon => Key::Colon, // NOTE: there is no physical colon key on an american keyboard\n        KeyCode::Semicolon => Key::Semicolon,\n        KeyCode::Backslash => Key::Backslash,\n        KeyCode::Slash | KeyCode::NumpadDivide => Key::Slash,\n        KeyCode::BracketLeft => Key::OpenBracket,\n        KeyCode::BracketRight => Key::CloseBracket,\n        KeyCode::Backquote => Key::Backtick,\n        KeyCode::Quote => Key::Quote,\n\n        KeyCode::Cut => Key::Cut,\n        KeyCode::Copy => Key::Copy,\n        KeyCode::Paste => Key::Paste,\n        KeyCode::Minus | KeyCode::NumpadSubtract => Key::Minus,\n        KeyCode::NumpadAdd => Key::Plus,\n        KeyCode::Equal => Key::Equals,\n\n        KeyCode::Digit0 | KeyCode::Numpad0 => Key::Num0,\n        KeyCode::Digit1 | KeyCode::Numpad1 => Key::Num1,\n        KeyCode::Digit2 | KeyCode::Numpad2 => Key::Num2,\n        KeyCode::Digit3 | KeyCode::Numpad3 => Key::Num3,\n        KeyCode::Digit4 | KeyCode::Numpad4 => Key::Num4,\n        KeyCode::Digit5 | KeyCode::Numpad5 => Key::Num5,\n        KeyCode::Digit6 | KeyCode::Numpad6 => Key::Num6,\n        KeyCode::Digit7 | KeyCode::Numpad7 => Key::Num7,\n        KeyCode::Digit8 | KeyCode::Numpad8 => Key::Num8,\n        KeyCode::Digit9 | KeyCode::Numpad9 => Key::Num9,\n\n        KeyCode::KeyA => Key::A,\n        KeyCode::KeyB => Key::B,\n        KeyCode::KeyC => Key::C,\n        KeyCode::KeyD => Key::D,\n        KeyCode::KeyE => Key::E,\n        KeyCode::KeyF => Key::F,\n        KeyCode::KeyG => Key::G,\n        KeyCode::KeyH => Key::H,\n        KeyCode::KeyI => Key::I,\n        KeyCode::KeyJ => Key::J,\n        KeyCode::KeyK => Key::K,\n        KeyCode::KeyL => Key::L,\n        KeyCode::KeyM => Key::M,\n        KeyCode::KeyN => Key::N,\n        KeyCode::KeyO => Key::O,\n        KeyCode::KeyP => Key::P,\n        KeyCode::KeyQ => Key::Q,\n        KeyCode::KeyR => Key::R,\n        KeyCode::KeyS => Key::S,\n        KeyCode::KeyT => Key::T,\n        KeyCode::KeyU => Key::U,\n        KeyCode::KeyV => Key::V,\n        KeyCode::KeyW => Key::W,\n        KeyCode::KeyX => Key::X,\n        KeyCode::KeyY => Key::Y,\n        KeyCode::KeyZ => Key::Z,\n\n        KeyCode::F1 => Key::F1,\n        KeyCode::F2 => Key::F2,\n        KeyCode::F3 => Key::F3,\n        KeyCode::F4 => Key::F4,\n        KeyCode::F5 => Key::F5,\n        KeyCode::F6 => Key::F6,\n        KeyCode::F7 => Key::F7,\n        KeyCode::F8 => Key::F8,\n        KeyCode::F9 => Key::F9,\n        KeyCode::F10 => Key::F10,\n        KeyCode::F11 => Key::F11,\n        KeyCode::F12 => Key::F12,\n        KeyCode::F13 => Key::F13,\n        KeyCode::F14 => Key::F14,\n        KeyCode::F15 => Key::F15,\n        KeyCode::F16 => Key::F16,\n        KeyCode::F17 => Key::F17,\n        KeyCode::F18 => Key::F18,\n        KeyCode::F19 => Key::F19,\n        KeyCode::F20 => Key::F20,\n        KeyCode::F21 => Key::F21,\n        KeyCode::F22 => Key::F22,\n        KeyCode::F23 => Key::F23,\n        KeyCode::F24 => Key::F24,\n        KeyCode::F25 => Key::F25,\n        KeyCode::F26 => Key::F26,\n        KeyCode::F27 => Key::F27,\n        KeyCode::F28 => Key::F28,\n        KeyCode::F29 => Key::F29,\n        KeyCode::F30 => Key::F30,\n        KeyCode::F31 => Key::F31,\n        KeyCode::F32 => Key::F32,\n        KeyCode::F33 => Key::F33,\n        KeyCode::F34 => Key::F34,\n        KeyCode::F35 => Key::F35,\n\n        _ => {\n            return None;\n        }\n    })\n}\n\nfn translate_cursor(cursor_icon: egui::CursorIcon) -> Option<winit::window::CursorIcon> {\n    match cursor_icon {\n        egui::CursorIcon::None => None,\n\n        egui::CursorIcon::Alias => Some(winit::window::CursorIcon::Alias),\n        egui::CursorIcon::AllScroll => Some(winit::window::CursorIcon::AllScroll),\n        egui::CursorIcon::Cell => Some(winit::window::CursorIcon::Cell),\n        egui::CursorIcon::ContextMenu => Some(winit::window::CursorIcon::ContextMenu),\n        egui::CursorIcon::Copy => Some(winit::window::CursorIcon::Copy),\n        egui::CursorIcon::Crosshair => Some(winit::window::CursorIcon::Crosshair),\n        egui::CursorIcon::Default => Some(winit::window::CursorIcon::Default),\n        egui::CursorIcon::Grab => Some(winit::window::CursorIcon::Grab),\n        egui::CursorIcon::Grabbing => Some(winit::window::CursorIcon::Grabbing),\n        egui::CursorIcon::Help => Some(winit::window::CursorIcon::Help),\n        egui::CursorIcon::Move => Some(winit::window::CursorIcon::Move),\n        egui::CursorIcon::NoDrop => Some(winit::window::CursorIcon::NoDrop),\n        egui::CursorIcon::NotAllowed => Some(winit::window::CursorIcon::NotAllowed),\n        egui::CursorIcon::PointingHand => Some(winit::window::CursorIcon::Pointer),\n        egui::CursorIcon::Progress => Some(winit::window::CursorIcon::Progress),\n\n        egui::CursorIcon::ResizeHorizontal => Some(winit::window::CursorIcon::EwResize),\n        egui::CursorIcon::ResizeNeSw => Some(winit::window::CursorIcon::NeswResize),\n        egui::CursorIcon::ResizeNwSe => Some(winit::window::CursorIcon::NwseResize),\n        egui::CursorIcon::ResizeVertical => Some(winit::window::CursorIcon::NsResize),\n\n        egui::CursorIcon::ResizeEast => Some(winit::window::CursorIcon::EResize),\n        egui::CursorIcon::ResizeSouthEast => Some(winit::window::CursorIcon::SeResize),\n        egui::CursorIcon::ResizeSouth => Some(winit::window::CursorIcon::SResize),\n        egui::CursorIcon::ResizeSouthWest => Some(winit::window::CursorIcon::SwResize),\n        egui::CursorIcon::ResizeWest => Some(winit::window::CursorIcon::WResize),\n        egui::CursorIcon::ResizeNorthWest => Some(winit::window::CursorIcon::NwResize),\n        egui::CursorIcon::ResizeNorth => Some(winit::window::CursorIcon::NResize),\n        egui::CursorIcon::ResizeNorthEast => Some(winit::window::CursorIcon::NeResize),\n        egui::CursorIcon::ResizeColumn => Some(winit::window::CursorIcon::ColResize),\n        egui::CursorIcon::ResizeRow => Some(winit::window::CursorIcon::RowResize),\n\n        egui::CursorIcon::Text => Some(winit::window::CursorIcon::Text),\n        egui::CursorIcon::VerticalText => Some(winit::window::CursorIcon::VerticalText),\n        egui::CursorIcon::Wait => Some(winit::window::CursorIcon::Wait),\n        egui::CursorIcon::ZoomIn => Some(winit::window::CursorIcon::ZoomIn),\n        egui::CursorIcon::ZoomOut => Some(winit::window::CursorIcon::ZoomOut),\n    }\n}\n\n// Helpers for egui Viewports\n// ---------------------------------------------------------------------------\n#[derive(PartialEq, Eq, Hash, Debug)]\npub enum ActionRequested {\n    Screenshot(egui::UserData),\n    Cut,\n    Copy,\n    Paste,\n}\n\npub fn process_viewport_commands(\n    egui_ctx: &egui::Context,\n    info: &mut ViewportInfo,\n    commands: impl IntoIterator<Item = ViewportCommand>,\n    window: &Window,\n    actions_requested: &mut Vec<ActionRequested>,\n) {\n    for command in commands {\n        process_viewport_command(egui_ctx, window, command, info, actions_requested);\n    }\n}\n\nfn process_viewport_command(\n    egui_ctx: &egui::Context,\n    window: &Window,\n    command: ViewportCommand,\n    info: &mut ViewportInfo,\n    actions_requested: &mut Vec<ActionRequested>,\n) {\n    profiling::function_scope!(&format!(\"{command:?}\"));\n\n    use winit::window::ResizeDirection;\n\n    log::trace!(\"Processing ViewportCommand::{command:?}\");\n\n    let pixels_per_point = pixels_per_point(egui_ctx, window);\n\n    match command {\n        ViewportCommand::Close => {\n            info.events.push(egui::ViewportEvent::Close);\n        }\n        ViewportCommand::CancelClose => {\n            // Need to be handled elsewhere\n        }\n        ViewportCommand::StartDrag => {\n            // If `.has_focus()` is not checked on x11 the input will be permanently taken until the app is killed!\n            if window.has_focus()\n                && let Err(err) = window.drag_window()\n            {\n                log::warn!(\"{command:?}: {err}\");\n            }\n        }\n        ViewportCommand::InnerSize(size) => {\n            let width_px = pixels_per_point * size.x.max(1.0);\n            let height_px = pixels_per_point * size.y.max(1.0);\n            let requested_size = PhysicalSize::new(width_px, height_px);\n            if let Some(_returned_inner_size) = window.request_inner_size(requested_size) {\n                // On platforms where the size is entirely controlled by the user the\n                // applied size will be returned immediately, resize event in such case\n                // may not be generated.\n                // e.g. Linux\n\n                // On platforms where resizing is disallowed by the windowing system, the current\n                // inner size is returned immediately, and the user one is ignored.\n                // e.g. Android, iOS, …\n\n                // However, comparing the results is prone to numerical errors\n                // because the linux backend converts physical to logical and back again.\n                // So let's just assume it worked:\n\n                info.inner_rect = inner_rect_in_points(window, pixels_per_point);\n                info.outer_rect = outer_rect_in_points(window, pixels_per_point);\n            } else {\n                // e.g. macOS, Windows\n                // The request went to the display system,\n                // and the actual size will be delivered later with the [`WindowEvent::Resized`].\n            }\n        }\n        ViewportCommand::BeginResize(direction) => {\n            if let Err(err) = window.drag_resize_window(match direction {\n                egui::viewport::ResizeDirection::North => ResizeDirection::North,\n                egui::viewport::ResizeDirection::South => ResizeDirection::South,\n                egui::viewport::ResizeDirection::East => ResizeDirection::East,\n                egui::viewport::ResizeDirection::West => ResizeDirection::West,\n                egui::viewport::ResizeDirection::NorthEast => ResizeDirection::NorthEast,\n                egui::viewport::ResizeDirection::SouthEast => ResizeDirection::SouthEast,\n                egui::viewport::ResizeDirection::NorthWest => ResizeDirection::NorthWest,\n                egui::viewport::ResizeDirection::SouthWest => ResizeDirection::SouthWest,\n            }) {\n                log::warn!(\"{command:?}: {err}\");\n            }\n        }\n        ViewportCommand::Title(title) => {\n            window.set_title(&title);\n        }\n        ViewportCommand::Transparent(v) => window.set_transparent(v),\n        ViewportCommand::Visible(v) => window.set_visible(v),\n        ViewportCommand::OuterPosition(pos) => {\n            window.set_outer_position(PhysicalPosition::new(\n                pixels_per_point * pos.x,\n                pixels_per_point * pos.y,\n            ));\n        }\n        ViewportCommand::MinInnerSize(s) => {\n            window.set_min_inner_size((s.is_finite() && s != Vec2::ZERO).then_some(\n                PhysicalSize::new(pixels_per_point * s.x, pixels_per_point * s.y),\n            ));\n        }\n        ViewportCommand::MaxInnerSize(s) => {\n            window.set_max_inner_size((s.is_finite() && s != Vec2::INFINITY).then_some(\n                PhysicalSize::new(pixels_per_point * s.x, pixels_per_point * s.y),\n            ));\n        }\n        ViewportCommand::ResizeIncrements(s) => {\n            window.set_resize_increments(\n                s.map(|s| PhysicalSize::new(pixels_per_point * s.x, pixels_per_point * s.y)),\n            );\n        }\n        ViewportCommand::Resizable(v) => window.set_resizable(v),\n        ViewportCommand::EnableButtons {\n            close,\n            minimized,\n            maximize,\n        } => window.set_enabled_buttons(\n            if close {\n                WindowButtons::CLOSE\n            } else {\n                WindowButtons::empty()\n            } | if minimized {\n                WindowButtons::MINIMIZE\n            } else {\n                WindowButtons::empty()\n            } | if maximize {\n                WindowButtons::MAXIMIZE\n            } else {\n                WindowButtons::empty()\n            },\n        ),\n        ViewportCommand::Minimized(v) => {\n            window.set_minimized(v);\n            info.minimized = Some(v);\n        }\n        ViewportCommand::Maximized(v) => {\n            window.set_maximized(v);\n            info.maximized = Some(v);\n        }\n        ViewportCommand::Fullscreen(v) => {\n            window.set_fullscreen(v.then_some(winit::window::Fullscreen::Borderless(None)));\n        }\n        ViewportCommand::Decorations(v) => window.set_decorations(v),\n        ViewportCommand::WindowLevel(l) => window.set_window_level(match l {\n            egui::viewport::WindowLevel::AlwaysOnBottom => WindowLevel::AlwaysOnBottom,\n            egui::viewport::WindowLevel::AlwaysOnTop => WindowLevel::AlwaysOnTop,\n            egui::viewport::WindowLevel::Normal => WindowLevel::Normal,\n        }),\n        ViewportCommand::Icon(icon) => {\n            let winit_icon = icon.and_then(|icon| to_winit_icon(&icon));\n            window.set_window_icon(winit_icon);\n        }\n        ViewportCommand::IMERect(rect) => {\n            window.set_ime_cursor_area(\n                PhysicalPosition::new(pixels_per_point * rect.min.x, pixels_per_point * rect.min.y),\n                PhysicalSize::new(\n                    pixels_per_point * rect.size().x,\n                    pixels_per_point * rect.size().y,\n                ),\n            );\n        }\n        ViewportCommand::IMEAllowed(v) => window.set_ime_allowed(v),\n        ViewportCommand::IMEPurpose(p) => window.set_ime_purpose(match p {\n            egui::viewport::IMEPurpose::Password => winit::window::ImePurpose::Password,\n            egui::viewport::IMEPurpose::Terminal => winit::window::ImePurpose::Terminal,\n            egui::viewport::IMEPurpose::Normal => winit::window::ImePurpose::Normal,\n        }),\n        ViewportCommand::Focus => {\n            if !window.has_focus() {\n                window.focus_window();\n            }\n        }\n        ViewportCommand::RequestUserAttention(a) => {\n            window.request_user_attention(match a {\n                egui::UserAttentionType::Reset => None,\n                egui::UserAttentionType::Critical => {\n                    Some(winit::window::UserAttentionType::Critical)\n                }\n                egui::UserAttentionType::Informational => {\n                    Some(winit::window::UserAttentionType::Informational)\n                }\n            });\n        }\n        ViewportCommand::SetTheme(t) => window.set_theme(match t {\n            egui::SystemTheme::Light => Some(winit::window::Theme::Light),\n            egui::SystemTheme::Dark => Some(winit::window::Theme::Dark),\n            egui::SystemTheme::SystemDefault => None,\n        }),\n        ViewportCommand::ContentProtected(v) => window.set_content_protected(v),\n        ViewportCommand::CursorPosition(pos) => {\n            if let Err(err) = window.set_cursor_position(PhysicalPosition::new(\n                pixels_per_point * pos.x,\n                pixels_per_point * pos.y,\n            )) {\n                log::warn!(\"{command:?}: {err}\");\n            }\n        }\n        ViewportCommand::CursorGrab(o) => {\n            if let Err(err) = window.set_cursor_grab(match o {\n                egui::viewport::CursorGrab::None => CursorGrabMode::None,\n                egui::viewport::CursorGrab::Confined => CursorGrabMode::Confined,\n                egui::viewport::CursorGrab::Locked => CursorGrabMode::Locked,\n            }) {\n                log::warn!(\"{command:?}: {err}\");\n            }\n        }\n        ViewportCommand::CursorVisible(v) => window.set_cursor_visible(v),\n        ViewportCommand::MousePassthrough(passthrough) => {\n            if let Err(err) = window.set_cursor_hittest(!passthrough) {\n                log::warn!(\"{command:?}: {err}\");\n            }\n        }\n        ViewportCommand::Screenshot(user_data) => {\n            actions_requested.push(ActionRequested::Screenshot(user_data));\n        }\n        ViewportCommand::RequestCut => {\n            actions_requested.push(ActionRequested::Cut);\n        }\n        ViewportCommand::RequestCopy => {\n            actions_requested.push(ActionRequested::Copy);\n        }\n        ViewportCommand::RequestPaste => {\n            actions_requested.push(ActionRequested::Paste);\n        }\n    }\n}\n\n/// Build and intitlaize a window.\n///\n/// Wrapper around `create_winit_window_builder` and `apply_viewport_builder_to_window`.\n///\n/// # Errors\n/// Possible causes of error include denied permission, incompatible system, and lack of memory.\npub fn create_window(\n    egui_ctx: &egui::Context,\n    event_loop: &ActiveEventLoop,\n    viewport_builder: &ViewportBuilder,\n) -> Result<Window, winit::error::OsError> {\n    profiling::function_scope!();\n\n    let window_attributes = create_winit_window_attributes(egui_ctx, viewport_builder.clone());\n    let window = event_loop.create_window(window_attributes)?;\n    apply_viewport_builder_to_window(egui_ctx, &window, viewport_builder);\n    Ok(window)\n}\n\npub fn create_winit_window_attributes(\n    egui_ctx: &egui::Context,\n    viewport_builder: ViewportBuilder,\n) -> winit::window::WindowAttributes {\n    profiling::function_scope!();\n\n    let ViewportBuilder {\n        title,\n        position,\n        inner_size,\n        min_inner_size,\n        max_inner_size,\n        fullscreen,\n        maximized,\n        resizable,\n        transparent,\n        decorations,\n        icon,\n        active,\n        visible,\n        close_button,\n        minimize_button,\n        maximize_button,\n        window_level,\n\n        // macOS:\n        fullsize_content_view: _fullsize_content_view,\n        movable_by_window_background: _movable_by_window_background,\n        title_shown: _title_shown,\n        titlebar_buttons_shown: _titlebar_buttons_shown,\n        titlebar_shown: _titlebar_shown,\n        has_shadow: _has_shadow,\n\n        // Windows:\n        drag_and_drop: _drag_and_drop,\n        taskbar: _taskbar,\n\n        // wayland:\n        app_id: _app_id,\n\n        // x11\n        window_type: _window_type,\n        override_redirect: _override_redirect,\n\n        mouse_passthrough: _, // handled in `apply_viewport_builder_to_window`\n        clamp_size_to_monitor_size: _, // Handled in `viewport_builder` in `epi_integration.rs`\n    } = viewport_builder;\n\n    let mut window_attributes = winit::window::WindowAttributes::default()\n        .with_title(title.unwrap_or_else(|| \"egui window\".to_owned()))\n        .with_transparent(transparent.unwrap_or(false))\n        .with_decorations(decorations.unwrap_or(true))\n        .with_resizable(resizable.unwrap_or(true))\n        .with_visible(visible.unwrap_or(true))\n        .with_maximized(if cfg!(target_os = \"ios\") {\n            true\n        } else {\n            maximized.unwrap_or(false)\n        })\n        .with_window_level(match window_level.unwrap_or_default() {\n            egui::viewport::WindowLevel::AlwaysOnBottom => WindowLevel::AlwaysOnBottom,\n            egui::viewport::WindowLevel::AlwaysOnTop => WindowLevel::AlwaysOnTop,\n            egui::viewport::WindowLevel::Normal => WindowLevel::Normal,\n        })\n        .with_fullscreen(\n            fullscreen.and_then(|e| e.then_some(winit::window::Fullscreen::Borderless(None))),\n        )\n        .with_enabled_buttons({\n            let mut buttons = WindowButtons::empty();\n            if minimize_button.unwrap_or(true) {\n                buttons |= WindowButtons::MINIMIZE;\n            }\n            if maximize_button.unwrap_or(true) {\n                buttons |= WindowButtons::MAXIMIZE;\n            }\n            if close_button.unwrap_or(true) {\n                buttons |= WindowButtons::CLOSE;\n            }\n            buttons\n        })\n        .with_active(active.unwrap_or(true));\n\n    // Here and below: we create `LogicalSize` / `LogicalPosition` taking\n    // zoom factor into account. We don't have a good way to get physical size here,\n    // and trying to do it anyway leads to weird bugs on Wayland, see:\n    // https://github.com/emilk/egui/issues/7095#issuecomment-2920545377\n    // https://github.com/rust-windowing/winit/issues/4266\n    #[expect(\n        clippy::disallowed_types,\n        reason = \"zoom factor is manually accounted for\"\n    )]\n    #[cfg(not(target_os = \"ios\"))]\n    {\n        use winit::dpi::{LogicalPosition, LogicalSize};\n        let zoom_factor = egui_ctx.zoom_factor();\n\n        if let Some(size) = inner_size {\n            window_attributes = window_attributes\n                .with_inner_size(LogicalSize::new(zoom_factor * size.x, zoom_factor * size.y));\n        }\n\n        if let Some(size) = min_inner_size {\n            window_attributes = window_attributes\n                .with_min_inner_size(LogicalSize::new(zoom_factor * size.x, zoom_factor * size.y));\n        }\n\n        if let Some(size) = max_inner_size {\n            window_attributes = window_attributes\n                .with_max_inner_size(LogicalSize::new(zoom_factor * size.x, zoom_factor * size.y));\n        }\n\n        if let Some(pos) = position {\n            window_attributes = window_attributes.with_position(LogicalPosition::new(\n                zoom_factor * pos.x,\n                zoom_factor * pos.y,\n            ));\n        }\n    }\n    #[cfg(target_os = \"ios\")]\n    {\n        // Unused:\n        _ = egui_ctx;\n        _ = pixels_per_point;\n        _ = position;\n        _ = inner_size;\n        _ = min_inner_size;\n        _ = max_inner_size;\n    }\n\n    if let Some(icon) = icon {\n        let winit_icon = to_winit_icon(&icon);\n        window_attributes = window_attributes.with_window_icon(winit_icon);\n    }\n\n    #[cfg(all(feature = \"wayland\", target_os = \"linux\"))]\n    if let Some(app_id) = _app_id {\n        use winit::platform::wayland::WindowAttributesExtWayland as _;\n        window_attributes = window_attributes.with_name(app_id, \"\");\n    }\n\n    #[cfg(all(feature = \"x11\", target_os = \"linux\"))]\n    {\n        use winit::platform::x11::WindowAttributesExtX11 as _;\n        if let Some(window_type) = _window_type {\n            use winit::platform::x11::WindowType;\n            window_attributes = window_attributes.with_x11_window_type(vec![match window_type {\n                egui::X11WindowType::Normal => WindowType::Normal,\n                egui::X11WindowType::Utility => WindowType::Utility,\n                egui::X11WindowType::Dock => WindowType::Dock,\n                egui::X11WindowType::Desktop => WindowType::Desktop,\n                egui::X11WindowType::Toolbar => WindowType::Toolbar,\n                egui::X11WindowType::Menu => WindowType::Menu,\n                egui::X11WindowType::Splash => WindowType::Splash,\n                egui::X11WindowType::Dialog => WindowType::Dialog,\n                egui::X11WindowType::DropdownMenu => WindowType::DropdownMenu,\n                egui::X11WindowType::PopupMenu => WindowType::PopupMenu,\n                egui::X11WindowType::Tooltip => WindowType::Tooltip,\n                egui::X11WindowType::Notification => WindowType::Notification,\n                egui::X11WindowType::Combo => WindowType::Combo,\n                egui::X11WindowType::Dnd => WindowType::Dnd,\n            }]);\n        }\n        if let Some(override_redirect) = _override_redirect {\n            window_attributes = window_attributes.with_override_redirect(override_redirect);\n        }\n    }\n\n    #[cfg(target_os = \"windows\")]\n    {\n        use winit::platform::windows::WindowAttributesExtWindows as _;\n        if let Some(enable) = _drag_and_drop {\n            window_attributes = window_attributes.with_drag_and_drop(enable);\n        }\n        if let Some(show) = _taskbar {\n            window_attributes = window_attributes.with_skip_taskbar(!show);\n        }\n    }\n\n    #[cfg(target_os = \"macos\")]\n    {\n        use winit::platform::macos::WindowAttributesExtMacOS as _;\n        window_attributes = window_attributes\n            .with_title_hidden(!_title_shown.unwrap_or(true))\n            .with_titlebar_buttons_hidden(!_titlebar_buttons_shown.unwrap_or(true))\n            .with_titlebar_transparent(!_titlebar_shown.unwrap_or(true))\n            .with_fullsize_content_view(_fullsize_content_view.unwrap_or(false))\n            .with_movable_by_window_background(_movable_by_window_background.unwrap_or(false))\n            .with_has_shadow(_has_shadow.unwrap_or(true));\n    }\n\n    window_attributes\n}\n\nfn to_winit_icon(icon: &egui::IconData) -> Option<winit::window::Icon> {\n    if icon.is_empty() {\n        None\n    } else {\n        profiling::function_scope!();\n        match winit::window::Icon::from_rgba(icon.rgba.clone(), icon.width, icon.height) {\n            Ok(winit_icon) => Some(winit_icon),\n            Err(err) => {\n                log::warn!(\"Invalid IconData: {err}\");\n                None\n            }\n        }\n    }\n}\n\n/// Applies what `create_winit_window_builder` couldn't\npub fn apply_viewport_builder_to_window(\n    egui_ctx: &egui::Context,\n    window: &Window,\n    builder: &ViewportBuilder,\n) {\n    if let Some(mouse_passthrough) = builder.mouse_passthrough\n        && let Err(err) = window.set_cursor_hittest(!mouse_passthrough)\n    {\n        log::warn!(\"set_cursor_hittest failed: {err}\");\n    }\n\n    {\n        // In `create_winit_window_builder` we didn't know\n        // on what monitor the window would appear, so we didn't know\n        // how to translate egui ui point to native physical pixels.\n        // Now we do know:\n\n        let pixels_per_point = pixels_per_point(egui_ctx, window);\n\n        if let Some(size) = builder.inner_size\n            && window\n                .request_inner_size(PhysicalSize::new(\n                    pixels_per_point * size.x,\n                    pixels_per_point * size.y,\n                ))\n                .is_some()\n        {\n            log::debug!(\"Failed to set window size\");\n        }\n        if let Some(size) = builder.min_inner_size {\n            window.set_min_inner_size(Some(PhysicalSize::new(\n                pixels_per_point * size.x,\n                pixels_per_point * size.y,\n            )));\n        }\n        if let Some(size) = builder.max_inner_size {\n            window.set_max_inner_size(Some(PhysicalSize::new(\n                pixels_per_point * size.x,\n                pixels_per_point * size.y,\n            )));\n        }\n        if let Some(pos) = builder.position {\n            let pos = PhysicalPosition::new(pixels_per_point * pos.x, pixels_per_point * pos.y);\n            window.set_outer_position(pos);\n        }\n        if let Some(maximized) = builder.maximized {\n            window.set_maximized(maximized);\n        }\n    }\n}\n\n// ---------------------------------------------------------------------------\n\n/// Short and fast description of a device event.\n/// Useful for logging and profiling.\npub fn short_device_event_description(event: &winit::event::DeviceEvent) -> &'static str {\n    use winit::event::DeviceEvent;\n\n    match event {\n        DeviceEvent::Added => \"DeviceEvent::Added\",\n        DeviceEvent::Removed => \"DeviceEvent::Removed\",\n        DeviceEvent::MouseMotion { .. } => \"DeviceEvent::MouseMotion\",\n        DeviceEvent::MouseWheel { .. } => \"DeviceEvent::MouseWheel\",\n        DeviceEvent::Motion { .. } => \"DeviceEvent::Motion\",\n        DeviceEvent::Button { .. } => \"DeviceEvent::Button\",\n        DeviceEvent::Key { .. } => \"DeviceEvent::Key\",\n    }\n}\n\n/// Short and fast description of a window event.\n/// Useful for logging and profiling.\npub fn short_window_event_description(event: &winit::event::WindowEvent) -> &'static str {\n    use winit::event::WindowEvent;\n\n    match event {\n        WindowEvent::ActivationTokenDone { .. } => \"WindowEvent::ActivationTokenDone\",\n        WindowEvent::Resized { .. } => \"WindowEvent::Resized\",\n        WindowEvent::Moved { .. } => \"WindowEvent::Moved\",\n        WindowEvent::CloseRequested => \"WindowEvent::CloseRequested\",\n        WindowEvent::Destroyed => \"WindowEvent::Destroyed\",\n        WindowEvent::DroppedFile { .. } => \"WindowEvent::DroppedFile\",\n        WindowEvent::HoveredFile { .. } => \"WindowEvent::HoveredFile\",\n        WindowEvent::HoveredFileCancelled => \"WindowEvent::HoveredFileCancelled\",\n        WindowEvent::Focused { .. } => \"WindowEvent::Focused\",\n        WindowEvent::KeyboardInput { .. } => \"WindowEvent::KeyboardInput\",\n        WindowEvent::ModifiersChanged { .. } => \"WindowEvent::ModifiersChanged\",\n        WindowEvent::Ime { .. } => \"WindowEvent::Ime\",\n        WindowEvent::CursorMoved { .. } => \"WindowEvent::CursorMoved\",\n        WindowEvent::CursorEntered { .. } => \"WindowEvent::CursorEntered\",\n        WindowEvent::CursorLeft { .. } => \"WindowEvent::CursorLeft\",\n        WindowEvent::MouseWheel { .. } => \"WindowEvent::MouseWheel\",\n        WindowEvent::MouseInput { .. } => \"WindowEvent::MouseInput\",\n        WindowEvent::PinchGesture { .. } => \"WindowEvent::PinchGesture\",\n        WindowEvent::RedrawRequested => \"WindowEvent::RedrawRequested\",\n        WindowEvent::DoubleTapGesture { .. } => \"WindowEvent::DoubleTapGesture\",\n        WindowEvent::RotationGesture { .. } => \"WindowEvent::RotationGesture\",\n        WindowEvent::TouchpadPressure { .. } => \"WindowEvent::TouchpadPressure\",\n        WindowEvent::AxisMotion { .. } => \"WindowEvent::AxisMotion\",\n        WindowEvent::Touch { .. } => \"WindowEvent::Touch\",\n        WindowEvent::ScaleFactorChanged { .. } => \"WindowEvent::ScaleFactorChanged\",\n        WindowEvent::ThemeChanged { .. } => \"WindowEvent::ThemeChanged\",\n        WindowEvent::Occluded { .. } => \"WindowEvent::Occluded\",\n        WindowEvent::PanGesture { .. } => \"WindowEvent::PanGesture\",\n    }\n}\n"
  },
  {
    "path": "crates/egui-winit/src/safe_area.rs",
    "content": "#[cfg(target_os = \"ios\")]\npub use ios::get_safe_area_insets;\n\n#[cfg(target_os = \"ios\")]\nmod ios {\n    use egui::{SafeAreaInsets, epaint::MarginF32};\n    use objc2::{ClassType, rc::Retained};\n    use objc2_foundation::{MainThreadMarker, NSObjectProtocol};\n    use objc2_ui_kit::{UIApplication, UISceneActivationState, UIWindowScene};\n\n    /// Gets the ios safe area insets.\n    ///\n    /// A safe area defines the area within a view that isn’t covered by a navigation bar, tab bar,\n    /// toolbar, or other views a window might provide. Safe areas are essential for avoiding a\n    /// device’s interactive and display features, like Dynamic Island on iPhone or the camera\n    /// housing on some Mac models.\n    ///\n    /// Once winit v0.31 has been released this can be removed in favor of\n    /// `winit::Window::safe_area`.\n    pub fn get_safe_area_insets() -> SafeAreaInsets {\n        let Some(main_thread_marker) = MainThreadMarker::new() else {\n            log::error!(\"Getting safe area insets needs to be performed on the main thread\");\n            return SafeAreaInsets::default();\n        };\n\n        let app = UIApplication::sharedApplication(main_thread_marker);\n\n        #[expect(unsafe_code)]\n        unsafe {\n            // Look for the first window scene that's in the foreground\n            for scene in app.connectedScenes() {\n                if scene.isKindOfClass(UIWindowScene::class())\n                    && matches!(\n                        scene.activationState(),\n                        UISceneActivationState::ForegroundActive\n                            | UISceneActivationState::ForegroundInactive\n                    )\n                {\n                    // Safe to cast, the class kind was checked above\n                    let window_scene = Retained::cast::<UIWindowScene>(scene.clone());\n                    if let Some(window) = window_scene.keyWindow() {\n                        let insets = window.safeAreaInsets();\n                        return SafeAreaInsets(MarginF32 {\n                            top: insets.top as f32,\n                            left: insets.left as f32,\n                            right: insets.right as f32,\n                            bottom: insets.bottom as f32,\n                        });\n                    }\n                }\n            }\n        }\n\n        SafeAreaInsets::default()\n    }\n}\n"
  },
  {
    "path": "crates/egui-winit/src/window_settings.rs",
    "content": "use egui::ViewportBuilder;\n\n/// Can be used to store native window settings (position and size).\n#[derive(Clone, Copy, Debug, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct WindowSettings {\n    /// Position of window content in physical pixels.\n    inner_position_pixels: Option<egui::Pos2>,\n\n    /// Position of window frame/titlebar in physical pixels.\n    outer_position_pixels: Option<egui::Pos2>,\n\n    fullscreen: bool,\n\n    maximized: bool,\n\n    /// Inner size of window in logical pixels\n    inner_size_points: Option<egui::Vec2>,\n}\n\nimpl WindowSettings {\n    pub fn from_window(egui_zoom_factor: f32, window: &winit::window::Window) -> Self {\n        let inner_size_points = window\n            .inner_size()\n            .to_logical::<f32>(egui_zoom_factor as f64 * window.scale_factor());\n\n        let inner_position_pixels = window\n            .inner_position()\n            .ok()\n            .map(|p| egui::pos2(p.x as f32, p.y as f32));\n\n        let outer_position_pixels = window\n            .outer_position()\n            .ok()\n            .map(|p| egui::pos2(p.x as f32, p.y as f32));\n\n        Self {\n            inner_position_pixels,\n            outer_position_pixels,\n\n            fullscreen: window.fullscreen().is_some(),\n            maximized: window.is_maximized(),\n\n            inner_size_points: Some(egui::vec2(\n                inner_size_points.width,\n                inner_size_points.height,\n            )),\n        }\n    }\n\n    pub fn inner_size_points(&self) -> Option<egui::Vec2> {\n        self.inner_size_points\n    }\n\n    pub fn initialize_viewport_builder(\n        &self,\n        egui_zoom_factor: f32,\n        event_loop: &winit::event_loop::ActiveEventLoop,\n        mut viewport_builder: ViewportBuilder,\n    ) -> ViewportBuilder {\n        profiling::function_scope!();\n\n        // `WindowBuilder::with_position` expects inner position in Macos, and outer position elsewhere\n        // See [`winit::window::WindowBuilder::with_position`] for details.\n        let pos_px = if cfg!(target_os = \"macos\") {\n            self.inner_position_pixels\n        } else {\n            self.outer_position_pixels\n        };\n        if let Some(pos) = pos_px {\n            let monitor_scale_factor = if let Some(inner_size_points) = self.inner_size_points {\n                find_active_monitor(egui_zoom_factor, event_loop, inner_size_points, &pos)\n                    .map_or(1.0, |monitor| monitor.scale_factor() as f32)\n            } else {\n                1.0\n            };\n\n            let scaled_pos = pos / (egui_zoom_factor * monitor_scale_factor);\n            viewport_builder = viewport_builder.with_position(scaled_pos);\n        }\n\n        if let Some(inner_size_points) = self.inner_size_points {\n            viewport_builder = viewport_builder\n                .with_inner_size(inner_size_points)\n                .with_fullscreen(self.fullscreen)\n                .with_maximized(self.maximized);\n        }\n\n        viewport_builder\n    }\n\n    pub fn initialize_window(&self, window: &winit::window::Window) {\n        if cfg!(target_os = \"macos\") {\n            // Mac sometimes has problems restoring the window to secondary monitors\n            // using only `WindowBuilder::with_position`, so we need this extra step:\n            if let Some(pos) = self.outer_position_pixels {\n                window.set_outer_position(winit::dpi::PhysicalPosition { x: pos.x, y: pos.y });\n            }\n        }\n    }\n\n    pub fn clamp_size_to_sane_values(&mut self, largest_monitor_size_points: egui::Vec2) {\n        use egui::NumExt as _;\n\n        if let Some(size) = &mut self.inner_size_points {\n            // Prevent ridiculously small windows:\n            let min_size = egui::Vec2::splat(64.0);\n            *size = size.at_least(min_size);\n\n            // Make sure we don't try to create a window larger than the largest monitor\n            // because on Linux that can lead to a crash.\n            *size = size.at_most(largest_monitor_size_points);\n        }\n    }\n\n    pub fn clamp_position_to_monitors(\n        &mut self,\n        egui_zoom_factor: f32,\n        event_loop: &winit::event_loop::ActiveEventLoop,\n    ) {\n        // If the app last ran on two monitors and only one is now connected, then\n        // the given position is invalid.\n        // If this happens on Mac, the window is clamped into valid area.\n        // If this happens on Windows, the window becomes invisible to the user 🤦‍♂️\n        // So on Windows we clamp the position to the monitor it is on.\n        if !cfg!(target_os = \"windows\") {\n            return;\n        }\n\n        let Some(inner_size_points) = self.inner_size_points else {\n            return;\n        };\n\n        if let Some(pos_px) = &mut self.inner_position_pixels {\n            clamp_pos_to_monitors(egui_zoom_factor, event_loop, inner_size_points, pos_px);\n        }\n        if let Some(pos_px) = &mut self.outer_position_pixels {\n            clamp_pos_to_monitors(egui_zoom_factor, event_loop, inner_size_points, pos_px);\n        }\n    }\n}\n\nfn find_active_monitor(\n    egui_zoom_factor: f32,\n    event_loop: &winit::event_loop::ActiveEventLoop,\n    window_size_pts: egui::Vec2,\n    position_px: &egui::Pos2,\n) -> Option<winit::monitor::MonitorHandle> {\n    profiling::function_scope!();\n    let monitors = event_loop.available_monitors();\n\n    // default to primary monitor, in case the correct monitor was disconnected.\n    let Some(mut active_monitor) = event_loop\n        .primary_monitor()\n        .or_else(|| event_loop.available_monitors().next())\n    else {\n        return None; // no monitors 🤷\n    };\n\n    for monitor in monitors {\n        let window_size_px = window_size_pts * (egui_zoom_factor * monitor.scale_factor() as f32);\n        let monitor_x_range = (monitor.position().x - window_size_px.x as i32)\n            ..(monitor.position().x + monitor.size().width as i32);\n        let monitor_y_range = (monitor.position().y - window_size_px.y as i32)\n            ..(monitor.position().y + monitor.size().height as i32);\n\n        if monitor_x_range.contains(&(position_px.x as i32))\n            && monitor_y_range.contains(&(position_px.y as i32))\n        {\n            active_monitor = monitor;\n        }\n    }\n\n    Some(active_monitor)\n}\n\nfn clamp_pos_to_monitors(\n    egui_zoom_factor: f32,\n    event_loop: &winit::event_loop::ActiveEventLoop,\n    window_size_pts: egui::Vec2,\n    position_px: &mut egui::Pos2,\n) {\n    profiling::function_scope!();\n\n    let Some(active_monitor) =\n        find_active_monitor(egui_zoom_factor, event_loop, window_size_pts, position_px)\n    else {\n        return; // no monitors 🤷\n    };\n\n    let mut window_size_px =\n        window_size_pts * (egui_zoom_factor * active_monitor.scale_factor() as f32);\n    // Add size of title bar. This is 32 px by default in Win 10/11.\n    if cfg!(target_os = \"windows\") {\n        window_size_px += egui::Vec2::new(\n            0.0,\n            32.0 * egui_zoom_factor * active_monitor.scale_factor() as f32,\n        );\n    }\n    let monitor_position = egui::Pos2::new(\n        active_monitor.position().x as f32,\n        active_monitor.position().y as f32,\n    );\n    let monitor_size_px = egui::Vec2::new(\n        active_monitor.size().width as f32,\n        active_monitor.size().height as f32,\n    );\n\n    // Window size cannot be negative or the subsequent `clamp` will panic.\n    let window_size = (monitor_size_px - window_size_px).max(egui::Vec2::ZERO);\n    // To get the maximum position, we get the rightmost corner of the display, then\n    // subtract the size of the window to get the bottom right most value window.position\n    // can have.\n    *position_px = position_px.clamp(monitor_position, monitor_position + window_size);\n}\n"
  },
  {
    "path": "crates/egui_demo_app/Cargo.toml",
    "content": "[package]\nname = \"egui_demo_app\"\nversion.workspace = true\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense.workspace = true\nedition.workspace = true\nrust-version.workspace = true\npublish = false\ndefault-run = \"egui_demo_app\"\n\n[package.metadata.cargo-machete]\nignored = [\"profiling\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n\n[features]\ndefault = [\"wgpu\", \"persistence\"]\n\n# image_viewer adds about 0.9 MB of WASM\nweb_app = [\"http\", \"persistence\"]\n\naccessibility_inspector = [\"dep:accesskit\", \"dep:accesskit_consumer\", \"eframe/accesskit\"]\nhttp = [\"ehttp\", \"image/jpeg\", \"poll-promise\", \"egui_extras/image\"]\nimage_viewer = [\"image/jpeg\", \"egui_extras/all_loaders\", \"rfd\"]\npersistence = [\"eframe/persistence\", \"egui_extras/serde\", \"egui/persistence\", \"serde\"]\npuffin = [\"dep:puffin\", \"dep:puffin_http\", \"profiling/profile-with-puffin\"]\nserde = [\"dep:serde\", \"egui_demo_lib/serde\", \"egui/serde\"]\nsyntect = [\"egui_demo_lib/syntect\"]\n\nglow = [\"eframe/glow\"]\nwgpu = [\"eframe/wgpu\", \"bytemuck\", \"dep:wgpu\"]\nwayland = [\"eframe/wayland\"]\nx11 = [\"eframe/x11\"]\n\n[dependencies]\nchrono = { workspace = true, features = [\"js-sys\", \"wasmbind\"] }\neframe = { workspace = true, default-features = false, features = [\"web_screen_reader\"] }\negui = { workspace = true, features = [\"callstack\", \"default\"] }\negui_demo_lib = { workspace = true, features = [\"default\", \"chrono\"] }\negui_extras = { workspace = true, features = [\"default\", \"image\"] }\nimage = { workspace = true, default-features = false, features = [\n  # Ensure we can display the test images\n  \"png\",\n] }\nlog.workspace = true\nprofiling.workspace = true\n\n# Optional dependencies:\naccesskit = { workspace = true, optional = true }\naccesskit_consumer = { workspace = true, optional = true }\nbytemuck = { workspace = true, optional = true }\npuffin = { workspace = true, optional = true }\npuffin_http = { workspace = true, optional = true }\n# Enable both WebGL & WebGPU when targeting the web (these features have no effect when not targeting wasm32)\n# Also enable the default features so we have a supported backend for every platform.\nwgpu = { workspace = true, features = [\"default\", \"webgpu\", \"webgl\"], optional = true }\n\n\n# feature \"http\":\nehttp = { workspace = true, optional = true }\npoll-promise = { workspace = true, optional = true }\n\n# feature \"persistence\":\nserde = { workspace = true, optional = true }\n\n\n# native:\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\nmimalloc.workspace = true\nrfd = { workspace = true, optional = true }\n\n# web:\n[target.'cfg(target_arch = \"wasm32\")'.dependencies]\nwasm-bindgen.workspace = true\nwasm-bindgen-futures.workspace = true\nweb-sys.workspace = true\n\n[dev-dependencies]\negui_kittest = { workspace = true, features = [\"eframe\", \"snapshot\", \"wgpu\"] }\n"
  },
  {
    "path": "crates/egui_demo_app/README.md",
    "content": "# egui demo app\nThis app demonstrates [`egui`](https://github.com/emilk/egui/) and [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe).\n\nView the demo app online at <https://egui.rs>.\n\nRun it locally with `cargo run --release -p egui_demo_app`.\n\n`egui_demo_app` can be compiled to WASM and viewed in a browser locally with:\n\n```sh\n./scripts/start_server.sh &\n./scripts/build_demo_web.sh --open\n```\n\n`egui_demo_app` uses [`egui_demo_lib`](https://github.com/emilk/egui/tree/main/crates/egui_demo_lib).\n\n\n## Running with `wgpu` backend\n`(cd egui_demo_app && cargo r --features wgpu)`\n"
  },
  {
    "path": "crates/egui_demo_app/src/accessibility_inspector.rs",
    "content": "use std::mem;\n\nuse accesskit::{Action, ActionRequest};\nuse accesskit_consumer::{FilterResult, Node, NodeId, Tree, TreeChangeHandler};\n\nuse eframe::epaint::text::TextWrapMode;\nuse egui::{\n    Button, Color32, Event, Frame, FullOutput, Id, Key, KeyboardShortcut, Label, Modifiers, Panel,\n    RawInput, RichText, ScrollArea, Ui, collapsing_header::CollapsingState,\n};\n\n/// This [`egui::Plugin`] adds an inspector panel.\n///\n/// It can be opened with the `(Cmd/Ctrl)+Alt+I`. It shows the current AccessKit tree and details\n/// for the selected node.\n/// Useful when debugging accessibility issues or trying to understand the structure of the Ui.\n///\n/// Add via\n/// ```\n/// # use egui_demo_app::accessibility_inspector::AccessibilityInspectorPlugin;\n/// # let ctx = egui::Context::default();\n/// ctx.add_plugin(AccessibilityInspectorPlugin::default());\n/// ```\n#[derive(Default, Debug)]\npub struct AccessibilityInspectorPlugin {\n    pub open: bool,\n    tree: Option<accesskit_consumer::Tree>,\n    selected_node: Option<NodeId>,\n    queued_action: Option<ActionRequest>,\n}\n\nstruct ChangeHandler;\n\nimpl TreeChangeHandler for ChangeHandler {\n    fn node_added(&mut self, _node: &Node<'_>) {}\n\n    fn node_updated(&mut self, _old_node: &Node<'_>, _new_node: &Node<'_>) {}\n\n    fn focus_moved(&mut self, _old_node: Option<&Node<'_>>, _new_node: Option<&Node<'_>>) {}\n\n    fn node_removed(&mut self, _node: &Node<'_>) {}\n}\n\nimpl egui::Plugin for AccessibilityInspectorPlugin {\n    fn debug_name(&self) -> &'static str {\n        \"Accessibility Inspector\"\n    }\n\n    fn input_hook(&mut self, input: &mut RawInput) {\n        if let Some(queued_action) = self.queued_action.take() {\n            input\n                .events\n                .push(Event::AccessKitActionRequest(queued_action));\n        }\n    }\n\n    fn output_hook(&mut self, output: &mut FullOutput) {\n        if let Some(update) = output.platform_output.accesskit_update.clone() {\n            self.tree = match mem::take(&mut self.tree) {\n                None => {\n                    // Create a new tree if it doesn't exist\n                    Some(Tree::new(update, true))\n                }\n                Some(mut tree) => {\n                    // Update the tree with the latest accesskit data\n                    tree.update_and_process_changes(update, &mut ChangeHandler);\n\n                    Some(tree)\n                }\n            }\n        }\n    }\n\n    fn on_begin_pass(&mut self, ui: &mut Ui) {\n        if ui.input_mut(|i| {\n            i.consume_shortcut(&KeyboardShortcut::new(\n                Modifiers::COMMAND | Modifiers::ALT,\n                Key::I,\n            ))\n        }) {\n            self.open = !self.open;\n        }\n\n        if !self.open {\n            return;\n        }\n\n        ui.enable_accesskit();\n\n        Panel::right(Self::id()).show_inside(ui, |ui| {\n            ui.heading(\"🔎 AccessKit Inspector\");\n            if let Some(selected_node) = self.selected_node {\n                Panel::bottom(Self::id().with(\"details_panel\"))\n                    .frame(Frame::new())\n                    .show_separator_line(false)\n                    .show_inside(ui, |ui| {\n                        self.selection_ui(ui, selected_node);\n                    });\n            }\n\n            ui.style_mut().wrap_mode = Some(TextWrapMode::Truncate);\n            ScrollArea::vertical().show(ui, |ui| {\n                if let Some(tree) = &self.tree {\n                    Self::node_ui(ui, &tree.state().root(), &mut self.selected_node);\n                }\n            });\n        });\n    }\n}\n\nimpl AccessibilityInspectorPlugin {\n    fn id() -> Id {\n        Id::new(\"Accessibility Inspector\")\n    }\n\n    fn selection_ui(&mut self, ui: &mut Ui, selected_node: NodeId) {\n        ui.separator();\n\n        if let Some(tree) = &self.tree\n            && let Some(node) = tree.state().node_by_id(selected_node)\n        {\n            // Safety: This is safe since the `accesskit::NodeId` was created from an `egui::Id`.\n            #[expect(unsafe_code)]\n            let egui_node_id = unsafe { Id::from_high_entropy_bits(node.locate().0.0) };\n\n            let node_response = ui.ctx().read_response(egui_node_id);\n\n            if let Some(widget_response) = node_response {\n                ui.debug_painter().debug_rect(\n                    widget_response.rect,\n                    ui.style_mut().visuals.selection.bg_fill,\n                    \"\",\n                );\n            }\n\n            egui::Grid::new(\"node_details_grid\")\n                .num_columns(2)\n                .show(ui, |ui| {\n                    ui.label(\"Node ID\");\n                    ui.strong(format!(\"{selected_node:?}\"));\n                    ui.end_row();\n\n                    ui.label(\"Role\");\n                    ui.strong(format!(\"{:?}\", node.role()));\n                    ui.end_row();\n\n                    ui.label(\"Label\");\n                    ui.add(\n                        Label::new(RichText::new(node.label().unwrap_or_default()).strong())\n                            .truncate(),\n                    );\n                    ui.end_row();\n\n                    ui.label(\"Value\");\n                    ui.add(\n                        Label::new(RichText::new(node.value().unwrap_or_default()).strong())\n                            .truncate(),\n                    );\n                    ui.end_row();\n\n                    ui.label(\"Children\");\n                    ui.label(RichText::new(node.children().len().to_string()).strong());\n\n                    ui.end_row();\n                });\n\n            ui.label(\"Actions\");\n            ui.horizontal_wrapped(|ui| {\n                // Iterate through all possible actions via the `Action::n` helper.\n                let mut current_action = 0;\n                let all_actions = std::iter::from_fn(|| {\n                    let action = Action::n(current_action);\n                    current_action += 1;\n                    action\n                });\n\n                for action in all_actions {\n                    if node.supports_action(action, &|_node| FilterResult::Include)\n                        && ui.button(format!(\"{action:?}\")).clicked()\n                    {\n                        let (target_node, target_tree) = node.locate();\n                        let action_request = ActionRequest {\n                            target_node,\n                            target_tree,\n                            action,\n                            data: None,\n                        };\n                        self.queued_action = Some(action_request);\n                    }\n                }\n            });\n        } else {\n            ui.label(\"Node not found\");\n        }\n    }\n\n    fn node_ui(ui: &mut Ui, node: &Node<'_>, selected_node: &mut Option<NodeId>) {\n        if node.locate() == (Self::id().value().into(), accesskit::TreeId::ROOT)\n            || node\n                .value()\n                .as_deref()\n                .is_some_and(|l| l.contains(\"AccessKit Inspector\"))\n        {\n            return;\n        }\n        let label = node\n            .label()\n            .or_else(|| node.value())\n            .unwrap_or_else(|| node.locate().0.0.to_string());\n        let label = format!(\"({:?}) {}\", node.role(), label);\n\n        // Safety: This is safe since the `accesskit::NodeId` was created from an `egui::Id`.\n        #[expect(unsafe_code)]\n        let egui_node_id = unsafe { Id::from_high_entropy_bits(node.locate().0.0) };\n\n        ui.push_id(node.id(), |ui| {\n            let child_count = node.children().len();\n            let has_children = child_count > 0;\n            let default_open = child_count == 1 && node.role() != accesskit::Role::Label;\n\n            let mut collapsing = CollapsingState::load_with_default_open(\n                ui.ctx(),\n                egui_node_id.with(\"ak_collapse\"),\n                default_open,\n            );\n\n            let header_response = ui.horizontal(|ui| {\n                let text = if collapsing.is_open() { \"⏷\" } else { \"⏵\" };\n\n                if ui\n                    .add_visible(has_children, Button::new(text).frame_when_inactive(false))\n                    .clicked()\n                {\n                    collapsing.set_open(!collapsing.is_open());\n                }\n                let label_response =\n                    ui.selectable_value(selected_node, Some(node.id()), label.clone());\n                if label_response.hovered() {\n                    let widget_response = ui.ctx().read_response(egui_node_id);\n\n                    if let Some(widget_response) = widget_response {\n                        ui.debug_painter()\n                            .debug_rect(widget_response.rect, Color32::RED, \"\");\n                    }\n                }\n            });\n\n            if has_children {\n                collapsing.show_body_indented(&header_response.response, ui, |ui| {\n                    node.children().for_each(|c| {\n                        Self::node_ui(ui, &c, selected_node);\n                    });\n                });\n            }\n\n            collapsing.store(ui.ctx());\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/apps/custom3d_glow.rs",
    "content": "#![expect(clippy::undocumented_unsafe_blocks)]\n\nuse std::sync::Arc;\n\nuse eframe::egui_glow;\nuse egui::mutex::Mutex;\nuse egui_glow::glow;\n\npub struct Custom3d {\n    /// Behind an `Arc<Mutex<…>>` so we can pass it to [`egui::PaintCallback`] and paint later.\n    rotating_triangle: Arc<Mutex<RotatingTriangle>>,\n    angle: f32,\n}\n\nimpl Custom3d {\n    pub fn new<'a>(cc: &'a eframe::CreationContext<'a>) -> Option<Self> {\n        let gl = cc.gl.as_ref()?;\n        Some(Self {\n            rotating_triangle: Arc::new(Mutex::new(RotatingTriangle::new(gl)?)),\n            angle: 0.0,\n        })\n    }\n}\n\nimpl crate::DemoApp for Custom3d {\n    fn demo_ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        // TODO(emilk): Use `ScrollArea::inner_margin`\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            egui::ScrollArea::both().auto_shrink(false).show(ui, |ui| {\n                ui.horizontal(|ui| {\n                    ui.spacing_mut().item_spacing.x = 0.0;\n                    ui.label(\"The triangle is being painted using \");\n                    ui.hyperlink_to(\"glow\", \"https://github.com/grovesNL/glow\");\n                    ui.label(\" (OpenGL).\");\n                });\n                ui.label(\n                    \"It's not a very impressive demo, but it shows you can embed 3D inside of egui.\",\n                );\n\n                egui::Frame::canvas(ui.style()).show(ui, |ui| {\n                    self.custom_painting(ui);\n                });\n                ui.label(\"Drag to rotate!\");\n                ui.add(egui_demo_lib::egui_github_link_file!());\n            });\n        });\n    }\n\n    fn on_exit(&mut self, gl: Option<&glow::Context>) {\n        if let Some(gl) = gl {\n            self.rotating_triangle.lock().destroy(gl);\n        }\n    }\n}\n\nimpl Custom3d {\n    fn custom_painting(&mut self, ui: &mut egui::Ui) {\n        let (rect, response) =\n            ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag());\n\n        self.angle += response.drag_motion().x * 0.01;\n\n        // Clone locals so we can move them into the paint callback:\n        let angle = self.angle;\n        let rotating_triangle = self.rotating_triangle.clone();\n\n        let cb = egui_glow::CallbackFn::new(move |_info, painter| {\n            rotating_triangle.lock().paint(painter.gl(), angle);\n        });\n\n        let callback = egui::PaintCallback {\n            rect,\n            callback: Arc::new(cb),\n        };\n        ui.painter().add(callback);\n    }\n}\n\nstruct RotatingTriangle {\n    program: glow::Program,\n    vertex_array: glow::VertexArray,\n}\n\n#[expect(unsafe_code)] // we need unsafe code to use glow\nimpl RotatingTriangle {\n    fn new(gl: &glow::Context) -> Option<Self> {\n        use glow::HasContext as _;\n\n        let shader_version = egui_glow::ShaderVersion::get(gl);\n\n        unsafe {\n            let program = gl.create_program().expect(\"Cannot create program\");\n\n            if !shader_version.is_new_shader_interface() {\n                log::warn!(\"Custom 3D painting hasn't been ported to {shader_version:?}\");\n                return None;\n            }\n\n            let (vertex_shader_source, fragment_shader_source) = (\n                r#\"\n                    const vec2 verts[3] = vec2[3](\n                        vec2(0.0, 1.0),\n                        vec2(-1.0, -1.0),\n                        vec2(1.0, -1.0)\n                    );\n                    const vec4 colors[3] = vec4[3](\n                        vec4(1.0, 0.0, 0.0, 1.0),\n                        vec4(0.0, 1.0, 0.0, 1.0),\n                        vec4(0.0, 0.0, 1.0, 1.0)\n                    );\n                    out vec4 v_color;\n                    uniform float u_angle;\n                    void main() {\n                        v_color = colors[gl_VertexID];\n                        gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0);\n                        gl_Position.x *= cos(u_angle);\n                    }\n                \"#,\n                r#\"\n                    precision mediump float;\n                    in vec4 v_color;\n                    out vec4 out_color;\n                    void main() {\n                        out_color = v_color;\n                    }\n                \"#,\n            );\n\n            let shader_sources = [\n                (glow::VERTEX_SHADER, vertex_shader_source),\n                (glow::FRAGMENT_SHADER, fragment_shader_source),\n            ];\n\n            let shaders: Vec<_> = shader_sources\n                .iter()\n                .map(|(shader_type, shader_source)| {\n                    let shader = gl\n                        .create_shader(*shader_type)\n                        .expect(\"Cannot create shader\");\n                    gl.shader_source(\n                        shader,\n                        &format!(\n                            \"{}\\n{}\",\n                            shader_version.version_declaration(),\n                            shader_source\n                        ),\n                    );\n                    gl.compile_shader(shader);\n                    assert!(\n                        gl.get_shader_compile_status(shader),\n                        \"Failed to compile custom_3d_glow {shader_type}: {}\",\n                        gl.get_shader_info_log(shader)\n                    );\n\n                    gl.attach_shader(program, shader);\n                    shader\n                })\n                .collect();\n\n            gl.link_program(program);\n            assert!(\n                gl.get_program_link_status(program),\n                \"{}\",\n                gl.get_program_info_log(program)\n            );\n\n            for shader in shaders {\n                gl.detach_shader(program, shader);\n                gl.delete_shader(shader);\n            }\n\n            let vertex_array = gl\n                .create_vertex_array()\n                .expect(\"Cannot create vertex array\");\n\n            Some(Self {\n                program,\n                vertex_array,\n            })\n        }\n    }\n\n    fn destroy(&self, gl: &glow::Context) {\n        use glow::HasContext as _;\n        unsafe {\n            gl.delete_program(self.program);\n            gl.delete_vertex_array(self.vertex_array);\n        }\n    }\n\n    fn paint(&self, gl: &glow::Context, angle: f32) {\n        use glow::HasContext as _;\n        unsafe {\n            gl.use_program(Some(self.program));\n            gl.uniform_1_f32(\n                gl.get_uniform_location(self.program, \"u_angle\").as_ref(),\n                angle,\n            );\n            gl.bind_vertex_array(Some(self.vertex_array));\n            gl.draw_arrays(glow::TRIANGLES, 0, 3);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/apps/custom3d_wgpu.rs",
    "content": "#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps\n\nuse std::num::NonZeroU64;\n\nuse eframe::{\n    egui_wgpu::wgpu::util::DeviceExt as _,\n    egui_wgpu::{self, wgpu},\n};\n\npub struct Custom3d {\n    angle: f32,\n}\n\nimpl Custom3d {\n    pub fn new<'a>(cc: &'a eframe::CreationContext<'a>) -> Option<Self> {\n        // Get the WGPU render state from the eframe creation context. This can also be retrieved\n        // from `eframe::Frame` when you don't have a `CreationContext` available.\n        let wgpu_render_state = cc.wgpu_render_state.as_ref()?;\n\n        let device = &wgpu_render_state.device;\n\n        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {\n            label: Some(\"custom3d\"),\n            source: wgpu::ShaderSource::Wgsl(include_str!(\"./custom3d_wgpu_shader.wgsl\").into()),\n        });\n\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            label: Some(\"custom3d\"),\n            entries: &[wgpu::BindGroupLayoutEntry {\n                binding: 0,\n                visibility: wgpu::ShaderStages::VERTEX,\n                ty: wgpu::BindingType::Buffer {\n                    ty: wgpu::BufferBindingType::Uniform,\n                    has_dynamic_offset: false,\n                    min_binding_size: NonZeroU64::new(16),\n                },\n                count: None,\n            }],\n        });\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            label: Some(\"custom3d\"),\n            bind_group_layouts: &[&bind_group_layout],\n            immediate_size: 0,\n        });\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            label: Some(\"custom3d\"),\n            layout: Some(&pipeline_layout),\n            vertex: wgpu::VertexState {\n                module: &shader,\n                entry_point: None,\n                buffers: &[],\n                compilation_options: wgpu::PipelineCompilationOptions::default(),\n            },\n            fragment: Some(wgpu::FragmentState {\n                module: &shader,\n                entry_point: Some(\"fs_main\"),\n                targets: &[Some(wgpu_render_state.target_format.into())],\n                compilation_options: wgpu::PipelineCompilationOptions::default(),\n            }),\n            primitive: wgpu::PrimitiveState::default(),\n            depth_stencil: None,\n            multisample: wgpu::MultisampleState::default(),\n            multiview_mask: None,\n            cache: None,\n        });\n\n        let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {\n            label: Some(\"custom3d\"),\n            contents: bytemuck::cast_slice(&[0.0_f32; 4]), // 16 bytes aligned!\n            // Mapping at creation (as done by the create_buffer_init utility) doesn't require us to to add the MAP_WRITE usage\n            // (this *happens* to workaround this bug )\n            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,\n        });\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            label: Some(\"custom3d\"),\n            layout: &bind_group_layout,\n            entries: &[wgpu::BindGroupEntry {\n                binding: 0,\n                resource: uniform_buffer.as_entire_binding(),\n            }],\n        });\n\n        // Because the graphics pipeline must have the same lifetime as the egui render pass,\n        // instead of storing the pipeline in our `Custom3D` struct, we insert it into the\n        // `paint_callback_resources` type map, which is stored alongside the render pass.\n        wgpu_render_state\n            .renderer\n            .write()\n            .callback_resources\n            .insert(TriangleRenderResources {\n                pipeline,\n                bind_group,\n                uniform_buffer,\n            });\n\n        Some(Self { angle: 0.0 })\n    }\n}\n\nimpl crate::DemoApp for Custom3d {\n    fn demo_ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        // TODO(emilk): Use `ScrollArea::inner_margin`\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            egui::ScrollArea::both().auto_shrink(false).show(ui, |ui| {\n                ui.horizontal(|ui| {\n                    ui.spacing_mut().item_spacing.x = 0.0;\n                    ui.label(\"The triangle is being painted using \");\n                    ui.hyperlink_to(\"WGPU\", \"https://wgpu.rs\");\n                    ui.label(\" (Portable Rust graphics API awesomeness)\");\n                });\n                ui.label(\n                    \"It's not a very impressive demo, but it shows you can embed 3D inside of egui.\",\n                );\n\n                egui::Frame::canvas(ui.style()).show(ui, |ui| {\n                    self.custom_painting(ui);\n                });\n                ui.label(\"Drag to rotate!\");\n                ui.add(egui_demo_lib::egui_github_link_file!());\n            });\n        });\n    }\n}\n\n// Callbacks in egui_wgpu have 3 stages:\n// * prepare (per callback impl)\n// * finish_prepare (once)\n// * paint (per callback impl)\n//\n// The prepare callback is called every frame before paint and is given access to the wgpu\n// Device and Queue, which can be used, for instance, to update buffers and uniforms before\n// rendering.\n// If [`egui_wgpu::Renderer`] has [`egui_wgpu::FinishPrepareCallback`] registered,\n// it will be called after all `prepare` callbacks have been called.\n// You can use this to update any shared resources that need to be updated once per frame\n// after all callbacks have been processed.\n//\n// On both prepare methods you can use the main `CommandEncoder` that is passed-in,\n// return an arbitrary number of user-defined `CommandBuffer`s, or both.\n// The main command buffer, as well as all user-defined ones, will be submitted together\n// to the GPU in a single call.\n//\n// The paint callback is called after finish prepare and is given access to egui's main render pass,\n// which can be used to issue draw commands.\nstruct CustomTriangleCallback {\n    angle: f32,\n}\n\nimpl egui_wgpu::CallbackTrait for CustomTriangleCallback {\n    fn prepare(\n        &self,\n        device: &wgpu::Device,\n        queue: &wgpu::Queue,\n        _screen_descriptor: &egui_wgpu::ScreenDescriptor,\n        _egui_encoder: &mut wgpu::CommandEncoder,\n        resources: &mut egui_wgpu::CallbackResources,\n    ) -> Vec<wgpu::CommandBuffer> {\n        let resources: &TriangleRenderResources = resources.get().unwrap();\n        resources.prepare(device, queue, self.angle);\n        Vec::new()\n    }\n\n    fn paint(\n        &self,\n        _info: egui::PaintCallbackInfo,\n        render_pass: &mut wgpu::RenderPass<'static>,\n        resources: &egui_wgpu::CallbackResources,\n    ) {\n        let resources: &TriangleRenderResources = resources.get().unwrap();\n        resources.paint(render_pass);\n    }\n}\n\nimpl Custom3d {\n    fn custom_painting(&mut self, ui: &mut egui::Ui) {\n        let (rect, response) =\n            ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag());\n\n        self.angle += response.drag_motion().x * 0.01;\n        ui.painter().add(egui_wgpu::Callback::new_paint_callback(\n            rect,\n            CustomTriangleCallback { angle: self.angle },\n        ));\n    }\n}\n\nstruct TriangleRenderResources {\n    pipeline: wgpu::RenderPipeline,\n    bind_group: wgpu::BindGroup,\n    uniform_buffer: wgpu::Buffer,\n}\n\nimpl TriangleRenderResources {\n    fn prepare(&self, _device: &wgpu::Device, queue: &wgpu::Queue, angle: f32) {\n        // Update our uniform buffer with the angle from the UI\n        queue.write_buffer(\n            &self.uniform_buffer,\n            0,\n            bytemuck::cast_slice(&[angle, 0.0, 0.0, 0.0]),\n        );\n    }\n\n    fn paint(&self, render_pass: &mut wgpu::RenderPass<'_>) {\n        // Draw our triangle!\n        render_pass.set_pipeline(&self.pipeline);\n        render_pass.set_bind_group(0, &self.bind_group, &[]);\n        render_pass.draw(0..3, 0..1);\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/apps/custom3d_wgpu_shader.wgsl",
    "content": "struct VertexOut {\n    @location(0) color: vec4<f32>,\n    @builtin(position) position: vec4<f32>,\n};\n\nstruct Uniforms {\n    @size(16) angle: f32, // pad to 16 bytes\n};\n\n@group(0) @binding(0)\nvar<uniform> uniforms: Uniforms;\n\nvar<private> v_positions: array<vec2<f32>, 3> = array<vec2<f32>, 3>(\n    vec2<f32>(0.0, 1.0),\n    vec2<f32>(1.0, -1.0),\n    vec2<f32>(-1.0, -1.0),\n);\n\nvar<private> v_colors: array<vec4<f32>, 3> = array<vec4<f32>, 3>(\n    vec4<f32>(1.0, 0.0, 0.0, 1.0),\n    vec4<f32>(0.0, 1.0, 0.0, 1.0),\n    vec4<f32>(0.0, 0.0, 1.0, 1.0),\n);\n\n@vertex\nfn vs_main(@builtin(vertex_index) v_idx: u32) -> VertexOut {\n    var out: VertexOut;\n\n    out.position = vec4<f32>(v_positions[v_idx], 0.0, 1.0);\n    out.position.x = out.position.x * cos(uniforms.angle);\n    out.color = v_colors[v_idx];\n\n    return out;\n}\n\n@fragment\nfn fs_main(in: VertexOut) -> @location(0) vec4<f32> {\n    return in.color;\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/apps/fractal_clock.rs",
    "content": "use egui::{\n    Color32, Painter, Pos2, Rect, Shape, Stroke, Ui, Vec2,\n    containers::{CollapsingHeader, Frame},\n    emath, pos2,\n    widgets::Slider,\n};\nuse std::f32::consts::TAU;\n\n#[derive(PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct FractalClock {\n    paused: bool,\n    time: f64,\n    zoom: f32,\n    start_line_width: f32,\n    depth: usize,\n    length_factor: f32,\n    luminance_factor: f32,\n    width_factor: f32,\n    line_count: usize,\n}\n\nimpl Default for FractalClock {\n    fn default() -> Self {\n        Self {\n            paused: false,\n            time: 0.0,\n            zoom: 0.25,\n            start_line_width: 2.5,\n            depth: 9,\n            length_factor: 0.8,\n            luminance_factor: 0.8,\n            width_factor: 0.9,\n            line_count: 0,\n        }\n    }\n}\n\nimpl FractalClock {\n    pub fn ui(&mut self, ui: &mut Ui, seconds_since_midnight: Option<f64>) {\n        if !self.paused {\n            self.time = seconds_since_midnight.unwrap_or_else(|| ui.input(|i| i.time));\n            ui.request_repaint();\n        }\n\n        let painter = Painter::new(\n            ui.ctx().clone(),\n            ui.layer_id(),\n            ui.available_rect_before_wrap(),\n        );\n        self.paint(&painter);\n        // Make sure we allocate what we used (everything)\n        ui.expand_to_include_rect(painter.clip_rect());\n\n        Frame::popup(ui.style())\n            .stroke(Stroke::NONE)\n            .show(ui, |ui| {\n                ui.set_max_width(270.0);\n                CollapsingHeader::new(\"Settings\")\n                    .show(ui, |ui| self.options_ui(ui, seconds_since_midnight));\n            });\n    }\n\n    fn options_ui(&mut self, ui: &mut Ui, seconds_since_midnight: Option<f64>) {\n        if seconds_since_midnight.is_some() {\n            ui.label(format!(\n                \"Local time: {:02}:{:02}:{:02}.{:03}\",\n                (self.time % (24.0 * 60.0 * 60.0) / 3600.0).floor(),\n                (self.time % (60.0 * 60.0) / 60.0).floor(),\n                (self.time % 60.0).floor(),\n                (self.time % 1.0 * 100.0).floor()\n            ));\n        } else {\n            ui.label(\"The fractal_clock clock is not showing the correct time\");\n        }\n        ui.label(format!(\"Painted line count: {}\", self.line_count));\n\n        ui.checkbox(&mut self.paused, \"Paused\");\n        ui.add(Slider::new(&mut self.zoom, 0.0..=1.0).text(\"zoom\"));\n        ui.add(Slider::new(&mut self.start_line_width, 0.0..=5.0).text(\"Start line width\"));\n        ui.add(Slider::new(&mut self.depth, 0..=14).text(\"depth\"));\n        ui.add(Slider::new(&mut self.length_factor, 0.0..=1.0).text(\"length factor\"));\n        ui.add(Slider::new(&mut self.luminance_factor, 0.0..=1.0).text(\"luminance factor\"));\n        ui.add(Slider::new(&mut self.width_factor, 0.0..=1.0).text(\"width factor\"));\n\n        egui::reset_button(ui, self, \"Reset\");\n\n        ui.hyperlink_to(\n            \"Inspired by a screensaver by Rob Mayoff\",\n            \"http://www.dqd.com/~mayoff/programs/FractalClock/\",\n        );\n        ui.add(egui_demo_lib::egui_github_link_file!());\n    }\n\n    fn paint(&mut self, painter: &Painter) {\n        struct Hand {\n            length: f32,\n            angle: f32,\n            vec: Vec2,\n        }\n\n        impl Hand {\n            fn from_length_angle(length: f32, angle: f32) -> Self {\n                Self {\n                    length,\n                    angle,\n                    vec: length * Vec2::angled(angle),\n                }\n            }\n        }\n\n        let angle_from_period =\n            |period| TAU * (self.time.rem_euclid(period) / period) as f32 - TAU / 4.0;\n\n        let hands = [\n            // Second hand:\n            Hand::from_length_angle(self.length_factor, angle_from_period(60.0)),\n            // Minute hand:\n            Hand::from_length_angle(self.length_factor, angle_from_period(60.0 * 60.0)),\n            // Hour hand:\n            Hand::from_length_angle(0.5, angle_from_period(12.0 * 60.0 * 60.0)),\n        ];\n\n        let mut shapes: Vec<Shape> = Vec::new();\n\n        let rect = painter.clip_rect();\n        let to_screen = emath::RectTransform::from_to(\n            Rect::from_center_size(Pos2::ZERO, rect.square_proportions() / self.zoom),\n            rect,\n        );\n\n        let mut paint_line = |points: [Pos2; 2], color: Color32, width: f32| {\n            let line = [to_screen * points[0], to_screen * points[1]];\n\n            // culling\n            if rect.intersects(Rect::from_two_pos(line[0], line[1])) {\n                shapes.push(Shape::line_segment(line, (width, color)));\n            }\n        };\n\n        let hand_rotations = [\n            hands[0].angle - hands[2].angle + TAU / 2.0,\n            hands[1].angle - hands[2].angle + TAU / 2.0,\n        ];\n\n        let hand_rotors = [\n            hands[0].length * emath::Rot2::from_angle(hand_rotations[0]),\n            hands[1].length * emath::Rot2::from_angle(hand_rotations[1]),\n        ];\n\n        #[derive(Clone, Copy)]\n        struct Node {\n            pos: Pos2,\n            dir: Vec2,\n        }\n\n        let mut nodes = Vec::new();\n\n        let mut width = self.start_line_width;\n\n        for (i, hand) in hands.iter().enumerate() {\n            let center = pos2(0.0, 0.0);\n            let end = center + hand.vec;\n            paint_line([center, end], Color32::from_additive_luminance(255), width);\n            if i < 2 {\n                nodes.push(Node {\n                    pos: end,\n                    dir: hand.vec,\n                });\n            }\n        }\n\n        let mut luminance = 0.7; // Start dimmer than main hands\n\n        let mut new_nodes = Vec::new();\n        for _ in 0..self.depth {\n            new_nodes.clear();\n            new_nodes.reserve(nodes.len() * 2);\n\n            luminance *= self.luminance_factor;\n            width *= self.width_factor;\n\n            let luminance_u8 = (255.0 * luminance).round() as u8;\n            if luminance_u8 == 0 {\n                break;\n            }\n\n            for &rotor in &hand_rotors {\n                for a in &nodes {\n                    let new_dir = rotor * a.dir;\n                    let b = Node {\n                        pos: a.pos + new_dir,\n                        dir: new_dir,\n                    };\n                    paint_line(\n                        [a.pos, b.pos],\n                        Color32::from_additive_luminance(luminance_u8),\n                        width,\n                    );\n                    new_nodes.push(b);\n                }\n            }\n\n            std::mem::swap(&mut nodes, &mut new_nodes);\n        }\n        self.line_count = shapes.len();\n        painter.extend(shapes);\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/apps/http_app.rs",
    "content": "use egui::Image;\nuse poll_promise::Promise;\n\nstruct Resource {\n    /// HTTP response\n    response: ehttp::Response,\n\n    text: Option<String>,\n\n    /// If set, the response was an image.\n    image: Option<Image<'static>>,\n\n    /// If set, the response was text with some supported syntax highlighting (e.g. \".rs\" or \".md\").\n    colored_text: Option<ColoredText>,\n}\n\nimpl Resource {\n    fn from_response(ctx: &egui::Context, response: ehttp::Response) -> Self {\n        let content_type = response.content_type().unwrap_or_default();\n        if content_type.starts_with(\"image/\") {\n            ctx.include_bytes(response.url.clone(), response.bytes.clone());\n            let image = Image::from_uri(response.url.clone());\n\n            Self {\n                response,\n                text: None,\n                colored_text: None,\n                image: Some(image),\n            }\n        } else {\n            let text = response.text();\n            let colored_text = text.and_then(|text| syntax_highlighting(ctx, &response, text));\n            let text = text.map(|text| text.to_owned());\n\n            Self {\n                response,\n                text,\n                colored_text,\n                image: None,\n            }\n        }\n    }\n}\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct HttpApp {\n    url: String,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    promise: Option<Promise<ehttp::Result<Resource>>>,\n}\n\nimpl Default for HttpApp {\n    fn default() -> Self {\n        Self {\n            url: \"https://raw.githubusercontent.com/emilk/egui/main/README.md\".to_owned(),\n            promise: Default::default(),\n        }\n    }\n}\n\nimpl crate::DemoApp for HttpApp {\n    fn demo_ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {\n        egui::Panel::bottom(\"http_bottom\").show_inside(ui, |ui| {\n            let layout = egui::Layout::top_down(egui::Align::Center).with_main_justify(true);\n            ui.allocate_ui_with_layout(ui.available_size(), layout, |ui| {\n                ui.add(egui_demo_lib::egui_github_link_file!())\n            })\n        });\n\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            let prev_url = self.url.clone();\n            let trigger_fetch = ui_url(ui, frame, &mut self.url);\n\n            ui.horizontal_wrapped(|ui| {\n                ui.spacing_mut().item_spacing.x = 0.0;\n                ui.label(\"HTTP requests made using \");\n                ui.hyperlink_to(\"ehttp\", \"https://www.github.com/emilk/ehttp\");\n                ui.label(\".\");\n            });\n\n            if trigger_fetch {\n                let ctx = ui.ctx().clone();\n                let (sender, promise) = Promise::new();\n                let request = ehttp::Request::get(&self.url);\n                ehttp::fetch(request, move |response| {\n                    ctx.forget_image(&prev_url);\n                    ctx.request_repaint(); // wake up UI thread\n                    let resource = response.map(|response| Resource::from_response(&ctx, response));\n                    sender.send(resource);\n                });\n                self.promise = Some(promise);\n            }\n\n            ui.separator();\n\n            if let Some(promise) = &self.promise {\n                if let Some(result) = promise.ready() {\n                    match result {\n                        Ok(resource) => {\n                            ui_resource(ui, resource);\n                        }\n                        Err(error) => {\n                            // This should only happen if the fetch API isn't available or something similar.\n                            ui.colored_label(\n                                ui.visuals().error_fg_color,\n                                if error.is_empty() { \"Error\" } else { error },\n                            );\n                        }\n                    }\n                } else {\n                    ui.spinner();\n                }\n            }\n        });\n    }\n}\n\nfn ui_url(ui: &mut egui::Ui, frame: &eframe::Frame, url: &mut String) -> bool {\n    let mut trigger_fetch = false;\n\n    ui.horizontal(|ui| {\n        ui.label(\"URL:\");\n        trigger_fetch |= ui\n            .add(egui::TextEdit::singleline(url).desired_width(f32::INFINITY))\n            .lost_focus();\n    });\n\n    if frame.is_web() {\n        ui.label(\"HINT: paste the url of this page into the field above!\");\n    }\n\n    ui.horizontal(|ui| {\n        if ui.button(\"Source code for this example\").clicked() {\n            *url = format!(\n                \"https://raw.githubusercontent.com/emilk/egui/main/{}\",\n                file!()\n            );\n            trigger_fetch = true;\n        }\n        if ui.button(\"Random image\").clicked() {\n            let seed = ui.input(|i| i.time);\n            let side = 640;\n            *url = format!(\"https://picsum.photos/seed/{seed}/{side}\");\n            trigger_fetch = true;\n        }\n    });\n\n    trigger_fetch\n}\n\nfn ui_resource(ui: &mut egui::Ui, resource: &Resource) {\n    let Resource {\n        response,\n        text,\n        image,\n        colored_text,\n    } = resource;\n\n    ui.monospace(format!(\"url:          {}\", response.url));\n    ui.monospace(format!(\n        \"status:       {} ({})\",\n        response.status, response.status_text\n    ));\n    ui.monospace(format!(\n        \"content-type: {}\",\n        response.content_type().unwrap_or_default()\n    ));\n    ui.monospace(format!(\n        \"size:         {:.1} kB\",\n        response.bytes.len() as f32 / 1000.0\n    ));\n\n    ui.separator();\n\n    egui::ScrollArea::vertical()\n        .auto_shrink(false)\n        .show(ui, |ui| {\n            egui::CollapsingHeader::new(\"Response headers\")\n                .default_open(false)\n                .show(ui, |ui| {\n                    egui::Grid::new(\"response_headers\")\n                        .spacing(egui::vec2(ui.spacing().item_spacing.x * 2.0, 0.0))\n                        .show(ui, |ui| {\n                            for (k, v) in &response.headers {\n                                ui.label(k);\n                                ui.label(v);\n                                ui.end_row();\n                            }\n                        })\n                });\n\n            ui.separator();\n\n            if let Some(text) = &text {\n                let tooltip = \"Click to copy the response body\";\n                if ui.button(\"📋\").on_hover_text(tooltip).clicked() {\n                    ui.copy_text(text.clone());\n                }\n                ui.separator();\n            }\n\n            if let Some(image) = image {\n                ui.add(image.clone());\n            } else if let Some(colored_text) = colored_text {\n                colored_text.ui(ui);\n            } else if let Some(text) = &text {\n                ui.add(egui::Label::new(text).selectable(true));\n            } else {\n                ui.monospace(\"[binary]\");\n            }\n        });\n}\n\n// ----------------------------------------------------------------------------\n// Syntax highlighting:\n\nfn syntax_highlighting(\n    ctx: &egui::Context,\n    response: &ehttp::Response,\n    text: &str,\n) -> Option<ColoredText> {\n    let extension_and_rest: Vec<&str> = response.url.rsplitn(2, '.').collect();\n    let extension = extension_and_rest.first()?;\n    let theme = egui_extras::syntax_highlighting::CodeTheme::from_style(&ctx.global_style());\n    Some(ColoredText(egui_extras::syntax_highlighting::highlight(\n        ctx,\n        &ctx.global_style(),\n        &theme,\n        text,\n        extension,\n    )))\n}\n\nstruct ColoredText(egui::text::LayoutJob);\n\nimpl ColoredText {\n    pub fn ui(&self, ui: &mut egui::Ui) {\n        let mut job = self.0.clone();\n        job.wrap.max_width = ui.available_width();\n        let galley = ui.fonts_mut(|f| f.layout_job(job));\n        ui.add(egui::Label::new(galley).selectable(true));\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/apps/image_viewer.rs",
    "content": "use egui::ImageFit;\nuse egui::Slider;\nuse egui::Vec2;\nuse egui::emath::Rot2;\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct ImageViewer {\n    current_uri: String,\n    uri_edit_text: String,\n    image_options: egui::ImageOptions,\n    chosen_fit: ChosenFit,\n    fit: ImageFit,\n    maintain_aspect_ratio: bool,\n    max_size: Vec2,\n    alt_text: String,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nenum ChosenFit {\n    ExactSize,\n    Fraction,\n    OriginalSize,\n}\n\nimpl ChosenFit {\n    fn as_str(&self) -> &'static str {\n        match self {\n            Self::ExactSize => \"exact size\",\n            Self::Fraction => \"fraction\",\n            Self::OriginalSize => \"original size\",\n        }\n    }\n}\n\nimpl Default for ImageViewer {\n    fn default() -> Self {\n        Self {\n            current_uri: \"https://picsum.photos/seed/1.759706314/1024\".to_owned(),\n            uri_edit_text: \"https://picsum.photos/seed/1.759706314/1024\".to_owned(),\n            image_options: egui::ImageOptions::default(),\n            chosen_fit: ChosenFit::Fraction,\n            fit: ImageFit::Fraction(Vec2::splat(1.0)),\n            maintain_aspect_ratio: true,\n            max_size: Vec2::splat(2048.0),\n            alt_text: \"My Image\".to_owned(),\n        }\n    }\n}\n\nimpl crate::DemoApp for ImageViewer {\n    fn demo_ui(&mut self, ui: &mut egui::Ui, _: &mut eframe::Frame) {\n        egui::Panel::top(\"url bar\").show_inside(ui, |ui| {\n            ui.horizontal_centered(|ui| {\n                let label = ui.label(\"URI:\");\n                ui.text_edit_singleline(&mut self.uri_edit_text)\n                    .labelled_by(label.id);\n                if ui.small_button(\"✔\").clicked() {\n                    ui.ctx().forget_image(&self.current_uri);\n                    self.uri_edit_text = self.uri_edit_text.trim().to_owned();\n                    self.current_uri = self.uri_edit_text.clone();\n                }\n\n                #[cfg(not(target_arch = \"wasm32\"))]\n                if ui.button(\"file…\").clicked()\n                    && let Some(path) = rfd::FileDialog::new().pick_file()\n                {\n                    self.uri_edit_text = format!(\"file://{}\", path.display());\n                    self.current_uri = self.uri_edit_text.clone();\n                }\n            });\n        });\n\n        egui::Panel::left(\"controls\").show_inside(ui, |ui| {\n            // uv\n            ui.label(\"UV\");\n            ui.add(Slider::new(&mut self.image_options.uv.min.x, 0.0..=1.0).text(\"min x\"));\n            ui.add(Slider::new(&mut self.image_options.uv.min.y, 0.0..=1.0).text(\"min y\"));\n            ui.add(Slider::new(&mut self.image_options.uv.max.x, 0.0..=1.0).text(\"max x\"));\n            ui.add(Slider::new(&mut self.image_options.uv.max.y, 0.0..=1.0).text(\"max y\"));\n\n            // rotation\n            ui.add_space(2.0);\n            let had_rotation = self.image_options.rotation.is_some();\n            let mut has_rotation = had_rotation;\n            ui.checkbox(&mut has_rotation, \"Rotation\");\n            match (had_rotation, has_rotation) {\n                (true, false) => self.image_options.rotation = None,\n                (false, true) => {\n                    self.image_options.rotation =\n                        Some((Rot2::from_angle(0.0), Vec2::new(0.5, 0.5)));\n                }\n                (true, true) | (false, false) => {}\n            }\n\n            if let Some((rot, origin)) = self.image_options.rotation.as_mut() {\n                let mut angle = rot.angle();\n\n                ui.label(\"angle\");\n                ui.drag_angle(&mut angle);\n                *rot = Rot2::from_angle(angle);\n\n                ui.add(Slider::new(&mut origin.x, 0.0..=1.0).text(\"origin x\"));\n                ui.add(Slider::new(&mut origin.y, 0.0..=1.0).text(\"origin y\"));\n            }\n\n            // bg_fill\n            ui.add_space(2.0);\n            ui.horizontal(|ui| {\n                ui.color_edit_button_srgba(&mut self.image_options.bg_fill);\n                ui.label(\"Background color\");\n            });\n\n            // tint\n            ui.add_space(2.0);\n            ui.horizontal(|ui| {\n                ui.color_edit_button_srgba(&mut self.image_options.tint);\n                ui.label(\"Tint\");\n            });\n\n            // fit\n            ui.add_space(10.0);\n            ui.label(\n                \"The chosen fit will determine how the image tries to fill the available space\",\n            );\n            egui::ComboBox::from_label(\"Fit\")\n                .selected_text(self.chosen_fit.as_str())\n                .show_ui(ui, |ui| {\n                    ui.selectable_value(\n                        &mut self.chosen_fit,\n                        ChosenFit::ExactSize,\n                        ChosenFit::ExactSize.as_str(),\n                    );\n                    ui.selectable_value(\n                        &mut self.chosen_fit,\n                        ChosenFit::Fraction,\n                        ChosenFit::Fraction.as_str(),\n                    );\n                    ui.selectable_value(\n                        &mut self.chosen_fit,\n                        ChosenFit::OriginalSize,\n                        ChosenFit::OriginalSize.as_str(),\n                    );\n                });\n\n            match self.chosen_fit {\n                ChosenFit::ExactSize => {\n                    if !matches!(self.fit, ImageFit::Exact(_)) {\n                        self.fit = ImageFit::Exact(Vec2::splat(128.0));\n                    }\n                    let ImageFit::Exact(size) = &mut self.fit else {\n                        unreachable!()\n                    };\n                    ui.add(Slider::new(&mut size.x, 0.0..=2048.0).text(\"width\"));\n                    ui.add(Slider::new(&mut size.y, 0.0..=2048.0).text(\"height\"));\n                }\n                ChosenFit::Fraction => {\n                    if !matches!(self.fit, ImageFit::Fraction(_)) {\n                        self.fit = ImageFit::Fraction(Vec2::splat(1.0));\n                    }\n                    let ImageFit::Fraction(fract) = &mut self.fit else {\n                        unreachable!()\n                    };\n                    ui.add(Slider::new(&mut fract.x, 0.0..=1.0).text(\"width\"));\n                    ui.add(Slider::new(&mut fract.y, 0.0..=1.0).text(\"height\"));\n                }\n                ChosenFit::OriginalSize => {\n                    if !matches!(self.fit, ImageFit::Original { .. }) {\n                        self.fit = ImageFit::Original { scale: 1.0 };\n                    }\n                    let ImageFit::Original { scale } = &mut self.fit else {\n                        unreachable!()\n                    };\n                    ui.add(Slider::new(scale, 0.1..=4.0).text(\"scale\"));\n                }\n            }\n\n            // max size\n            ui.add_space(5.0);\n            ui.label(\"The calculated size will not exceed the maximum size\");\n            ui.add(Slider::new(&mut self.max_size.x, 0.0..=2048.0).text(\"width\"));\n            ui.add(Slider::new(&mut self.max_size.y, 0.0..=2048.0).text(\"height\"));\n\n            // aspect ratio\n            ui.add_space(5.0);\n            ui.label(\"Aspect ratio is maintained by scaling both sides as necessary\");\n            ui.checkbox(&mut self.maintain_aspect_ratio, \"Maintain aspect ratio\");\n\n            // alt text\n            ui.add_space(5.0);\n            ui.label(\"Alt text\");\n            ui.text_edit_singleline(&mut self.alt_text);\n\n            // forget all images\n            if ui.button(\"Forget all images\").clicked() {\n                ui.ctx().forget_all_images();\n            }\n        });\n\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            egui::ScrollArea::both().show(ui, |ui| {\n                let mut image = egui::Image::from_uri(&self.current_uri);\n                image = image.uv(self.image_options.uv);\n                image = image.bg_fill(self.image_options.bg_fill);\n                image = image.tint(self.image_options.tint);\n                let (angle, origin) = self\n                    .image_options\n                    .rotation\n                    .map_or((0.0, Vec2::splat(0.5)), |(rot, origin)| {\n                        (rot.angle(), origin)\n                    });\n                image = image.rotate(angle, origin);\n                match self.fit {\n                    ImageFit::Original { scale } => image = image.fit_to_original_size(scale),\n                    ImageFit::Fraction(fract) => image = image.fit_to_fraction(fract),\n                    ImageFit::Exact(size) => image = image.fit_to_exact_size(size),\n                }\n                image = image.maintain_aspect_ratio(self.maintain_aspect_ratio);\n                image = image.max_size(self.max_size);\n                if !self.alt_text.is_empty() {\n                    image = image.alt_text(&self.alt_text);\n                }\n\n                ui.add_sized(ui.available_size(), image);\n            });\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/apps/mod.rs",
    "content": "#[cfg(all(feature = \"glow\", not(feature = \"wgpu\")))]\nmod custom3d_glow;\n\n#[cfg(feature = \"wgpu\")]\nmod custom3d_wgpu;\n\nmod fractal_clock;\n\n#[cfg(feature = \"http\")]\nmod http_app;\n\n#[cfg(feature = \"image_viewer\")]\nmod image_viewer;\n\n#[cfg(feature = \"image_viewer\")]\npub use image_viewer::ImageViewer;\n\n#[cfg(all(feature = \"glow\", not(feature = \"wgpu\")))]\npub use custom3d_glow::Custom3d;\n\n#[cfg(feature = \"wgpu\")]\npub use custom3d_wgpu::Custom3d;\n\npub use fractal_clock::FractalClock;\n\n#[cfg(feature = \"http\")]\npub use http_app::HttpApp;\n"
  },
  {
    "path": "crates/egui_demo_app/src/backend_panel.rs",
    "content": "/// How often we repaint the demo app by default\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\nenum RunMode {\n    /// This is the default for the demo.\n    ///\n    /// If this is selected, egui is only updated if are input events\n    /// (like mouse movements) or there are some animations in the GUI.\n    ///\n    /// Reactive mode saves CPU.\n    ///\n    /// The downside is that the UI can become out-of-date if something it is supposed to monitor changes.\n    /// For instance, a GUI for a thermostat need to repaint each time the temperature changes.\n    /// To ensure the UI is up to date you need to call `egui::Context::request_repaint()` each\n    /// time such an event happens. You can also chose to call `request_repaint()` once every second\n    /// or after every single frame - this is called [`Continuous`](RunMode::Continuous) mode,\n    /// and for games and interactive tools that need repainting every frame anyway, this should be the default.\n    Reactive,\n\n    /// This will call `egui::Context::request_repaint()` at the end of each frame\n    /// to request the backend to repaint as soon as possible.\n    ///\n    /// On most platforms this will mean that egui will run at the display refresh rate of e.g. 60 Hz.\n    ///\n    /// For this demo it is not any reason to do so except to\n    /// demonstrate how quickly egui runs.\n    ///\n    /// For games or other interactive apps, this is probably what you want to do.\n    /// It will guarantee that egui is always up-to-date.\n    Continuous,\n}\n\n/// Default for demo is Reactive since\n/// 1) We want to use minimal CPU\n/// 2) There are no external events that could invalidate the UI\n///    so there are no events to miss.\nimpl Default for RunMode {\n    fn default() -> Self {\n        Self::Reactive\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct BackendPanel {\n    pub open: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    // go back to [`RunMode::Reactive`] mode each time we start\n    run_mode: RunMode,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    frame_history: crate::frame_history::FrameHistory,\n\n    egui_windows: EguiWindows,\n}\n\nimpl BackendPanel {\n    pub fn update(&mut self, ctx: &egui::Context, frame: &eframe::Frame) {\n        self.frame_history\n            .on_new_frame(ctx.input(|i| i.time), frame.info().cpu_usage);\n\n        match self.run_mode {\n            RunMode::Continuous => {\n                // Tell the backend to repaint as soon as possible\n                ctx.request_repaint();\n            }\n            RunMode::Reactive => {\n                // let the computer rest for a bit\n            }\n        }\n    }\n\n    pub fn end_of_frame(&mut self, ctx: &egui::Context) {\n        self.egui_windows.windows(ctx);\n    }\n\n    pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {\n        integration_ui(ui, frame);\n\n        ui.separator();\n\n        self.run_mode_ui(ui);\n\n        ui.separator();\n\n        self.frame_history.ui(ui);\n\n        ui.separator();\n\n        ui.label(\"egui windows:\");\n        self.egui_windows.checkboxes(ui);\n\n        #[cfg(debug_assertions)]\n        if ui.global_style().debug.debug_on_hover_with_all_modifiers {\n            ui.separator();\n            ui.label(\"Press down all modifiers and hover a widget to see a callstack for it\");\n        }\n\n        #[cfg(target_arch = \"wasm32\")]\n        {\n            ui.separator();\n            let mut screen_reader = ui.options(|o| o.screen_reader);\n            ui.checkbox(&mut screen_reader, \"🔈 Screen reader\").on_hover_text(\"Experimental feature: checking this will turn on the screen reader on supported platforms\");\n            ui.options_mut(|o| o.screen_reader = screen_reader);\n        }\n\n        if cfg!(debug_assertions) && cfg!(target_arch = \"wasm32\") {\n            ui.separator();\n            // For testing panic handling on web:\n            #[expect(clippy::manual_assert)]\n            if ui.button(\"panic!()\").clicked() {\n                panic!(\"intentional panic!\");\n            }\n        }\n\n        if !cfg!(target_arch = \"wasm32\") {\n            ui.separator();\n            if ui.button(\"Quit\").clicked() {\n                ui.send_viewport_cmd(egui::ViewportCommand::Close);\n            }\n        }\n    }\n\n    fn run_mode_ui(&mut self, ui: &mut egui::Ui) {\n        ui.horizontal(|ui| {\n            let run_mode = &mut self.run_mode;\n            ui.label(\"Mode:\");\n            ui.radio_value(run_mode, RunMode::Reactive, \"Reactive\")\n                .on_hover_text(\"Repaint when there are animations or input (e.g. mouse movement)\");\n            ui.radio_value(run_mode, RunMode::Continuous, \"Continuous\")\n                .on_hover_text(\"Repaint everything each frame\");\n        });\n\n        if self.run_mode == RunMode::Continuous {\n            ui.label(format!(\n                \"Repainting the UI each frame. FPS: {:.1}\",\n                self.frame_history.fps()\n            ));\n        } else {\n            ui.label(\"Only running UI code when there are animations or input.\");\n\n            // Add a test for `request_repaint_after`, but only in debug\n            // builds to keep the noise down in the official demo.\n            if cfg!(debug_assertions) {\n                ui.collapsing(\"More…\", |ui| {\n                    ui.horizontal(|ui| {\n                        ui.label(\"Total ui frames:\");\n                        ui.monospace(ui.ctx().cumulative_frame_nr().to_string());\n                    });\n                    ui.horizontal(|ui| {\n                        ui.label(\"Total ui passes:\");\n                        ui.monospace(ui.ctx().cumulative_pass_nr().to_string());\n                    });\n                    if ui\n                        .button(\"Wait 2s, then request repaint after another 3s\")\n                        .clicked()\n                    {\n                        log::info!(\"Waiting 2s before requesting repaint…\");\n                        let ctx = ui.ctx().clone();\n                        call_after_delay(std::time::Duration::from_secs(2), move || {\n                            log::info!(\"Request a repaint in 3s…\");\n                            ctx.request_repaint_after(std::time::Duration::from_secs(3));\n                        });\n                    }\n\n                    ui.horizontal(|ui| {\n                        if ui.button(\"Request discard\").clicked() {\n                            ui.request_discard(\"Manual button click\");\n\n                            if !ui.ctx().will_discard() {\n                                ui.label(\"Discard denied!\");\n                            }\n                        }\n                    });\n                });\n            }\n        }\n    }\n}\n\nfn integration_ui(ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n    ui.horizontal(|ui| {\n        ui.spacing_mut().item_spacing.x = 0.0;\n        ui.label(\"egui running inside \");\n        ui.hyperlink_to(\n            \"eframe\",\n            \"https://github.com/emilk/egui/tree/main/crates/eframe\",\n        );\n        ui.label(\".\");\n    });\n\n    #[cfg(target_arch = \"wasm32\")]\n    ui.collapsing(\"Web info (location)\", |ui| {\n        ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);\n        ui.monospace(format!(\"{:#?}\", _frame.info().web_info.location));\n    });\n\n    #[cfg(feature = \"glow\")]\n    if _frame.gl().is_some() {\n        ui.horizontal(|ui| {\n            ui.label(\"Renderer:\");\n            ui.hyperlink_to(\"glow\", \"https://github.com/grovesNL/glow\");\n        });\n    }\n\n    #[cfg(feature = \"wgpu\")]\n    if let Some(render_state) = _frame.wgpu_render_state() {\n        let wgpu_adapter_details_ui = |ui: &mut egui::Ui, adapter: &eframe::wgpu::Adapter| {\n            let info = &adapter.get_info();\n\n            let wgpu::AdapterInfo {\n                name,\n                vendor,\n                device,\n                device_type,\n                driver,\n                driver_info,\n                backend,\n                device_pci_bus_id,\n                subgroup_min_size,\n                subgroup_max_size,\n                transient_saves_memory,\n            } = &info;\n\n            // Example values:\n            // > name: \"llvmpipe (LLVM 16.0.6, 256 bits)\", device_type: Cpu, backend: Vulkan, driver: \"llvmpipe\", driver_info: \"Mesa 23.1.6-arch1.4 (LLVM 16.0.6)\"\n            // > name: \"Apple M1 Pro\", device_type: IntegratedGpu, backend: Metal, driver: \"\", driver_info: \"\"\n            // > name: \"ANGLE (Apple, Apple M1 Pro, OpenGL 4.1)\", device_type: IntegratedGpu, backend: Gl, driver: \"\", driver_info: \"\"\n\n            egui::Grid::new(\"adapter_info\").show(ui, |ui| {\n                ui.label(\"Backend:\");\n                ui.label(format!(\"{backend:?}\"));\n                ui.end_row();\n\n                ui.label(\"Device Type:\");\n                ui.label(format!(\"{device_type:?}\"));\n                ui.end_row();\n\n                if !name.is_empty() {\n                    ui.label(\"Name:\");\n                    ui.label(format!(\"{name:?}\"));\n                    ui.end_row();\n                }\n                if !driver.is_empty() {\n                    ui.label(\"Driver:\");\n                    ui.label(format!(\"{driver:?}\"));\n                    ui.end_row();\n                }\n                if !driver_info.is_empty() {\n                    ui.label(\"Driver info:\");\n                    ui.label(format!(\"{driver_info:?}\"));\n                    ui.end_row();\n                }\n                if *vendor != 0 {\n                    // TODO(emilk): decode using https://github.com/gfx-rs/wgpu/blob/767ac03245ee937d3dc552edc13fe7ab0a860eec/wgpu-hal/src/auxil/mod.rs#L7\n                    ui.label(\"Vendor:\");\n                    ui.label(format!(\"0x{vendor:04X}\"));\n                    ui.end_row();\n                }\n                if *device != 0 {\n                    ui.label(\"Device:\");\n                    ui.label(format!(\"0x{device:02X}\"));\n                    ui.end_row();\n                }\n                if !device_pci_bus_id.is_empty() {\n                    ui.label(\"PCI Bus ID:\");\n                    ui.label(device_pci_bus_id.as_str());\n                    ui.end_row();\n                }\n                if *subgroup_min_size != 0 || *subgroup_max_size != 0 {\n                    ui.label(\"Subgroup size:\");\n                    ui.label(format!(\"{subgroup_min_size}..={subgroup_max_size}\"));\n                    ui.end_row();\n                }\n                ui.label(\"Transient saves memory:\");\n                ui.label(format!(\"{transient_saves_memory}\"));\n                ui.end_row();\n            });\n        };\n\n        let wgpu_adapter_ui = |ui: &mut egui::Ui, adapter: &eframe::wgpu::Adapter| {\n            let info = &adapter.get_info();\n            ui.label(format!(\"{:?}\", info.backend)).on_hover_ui(|ui| {\n                wgpu_adapter_details_ui(ui, adapter);\n            });\n        };\n\n        egui::Grid::new(\"wgpu_info\").num_columns(2).show(ui, |ui| {\n            ui.label(\"Renderer:\");\n            ui.hyperlink_to(\"wgpu\", \"https://wgpu.rs/\");\n            ui.end_row();\n\n            ui.label(\"Backend:\");\n            wgpu_adapter_ui(ui, &render_state.adapter);\n            ui.end_row();\n\n            #[cfg(not(target_arch = \"wasm32\"))]\n            if 1 < render_state.available_adapters.len() {\n                ui.label(\"Others:\");\n                ui.vertical(|ui| {\n                    for adapter in &*render_state.available_adapters {\n                        if adapter.get_info() != render_state.adapter.get_info() {\n                            wgpu_adapter_ui(ui, adapter);\n                        }\n                    }\n                });\n                ui.end_row();\n            }\n        });\n    }\n\n    #[cfg(not(target_arch = \"wasm32\"))]\n    {\n        ui.horizontal(|ui| {\n            {\n                let mut fullscreen = ui.input(|i| i.viewport().fullscreen.unwrap_or(false));\n                if ui\n                    .checkbox(&mut fullscreen, \"🗖 Fullscreen (F11)\")\n                    .on_hover_text(\"Fullscreen the window\")\n                    .changed()\n                {\n                    ui.send_viewport_cmd(egui::ViewportCommand::Fullscreen(fullscreen));\n                }\n            }\n\n            let mut size = None;\n            egui::ComboBox::from_id_salt(\"viewport-size-combo\")\n                .selected_text(\"Resize to…\")\n                .show_ui(ui, |ui| {\n                    ui.selectable_value(\n                        &mut size,\n                        Some(egui::vec2(375.0, 667.0)),\n                        \"📱 iPhone SE 2nd Gen\",\n                    );\n                    ui.selectable_value(&mut size, Some(egui::vec2(393.0, 852.0)), \"📱 iPhone 15\");\n                    ui.selectable_value(\n                        &mut size,\n                        Some(egui::vec2(1280.0, 720.0)),\n                        \"🖥 Desktop 720p\",\n                    );\n                    ui.selectable_value(\n                        &mut size,\n                        Some(egui::vec2(1920.0, 1080.0)),\n                        \"🖥 Desktop 1080p\",\n                    );\n                });\n\n            if let Some(size) = size {\n                ui.send_viewport_cmd(egui::ViewportCommand::InnerSize(size));\n                ui.send_viewport_cmd(egui::ViewportCommand::Fullscreen(false));\n                ui.close();\n            }\n        });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nstruct EguiWindows {\n    // egui stuff:\n    settings: bool,\n    inspection: bool,\n    memory: bool,\n    output_events: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    output_event_history: std::collections::VecDeque<egui::output::OutputEvent>,\n}\n\nimpl Default for EguiWindows {\n    fn default() -> Self {\n        Self::none()\n    }\n}\n\nimpl EguiWindows {\n    fn none() -> Self {\n        Self {\n            settings: false,\n            inspection: false,\n            memory: false,\n            output_events: false,\n            output_event_history: Default::default(),\n        }\n    }\n\n    fn checkboxes(&mut self, ui: &mut egui::Ui) {\n        let Self {\n            settings,\n            inspection,\n            memory,\n            output_events,\n            output_event_history: _,\n        } = self;\n\n        ui.checkbox(settings, \"🔧 Settings\");\n        ui.checkbox(inspection, \"🔍 Inspection\");\n        ui.checkbox(memory, \"📝 Memory\");\n        ui.checkbox(output_events, \"📤 Output Events\");\n    }\n\n    fn windows(&mut self, ctx: &egui::Context) {\n        let Self {\n            settings,\n            inspection,\n            memory,\n            output_events,\n            output_event_history,\n        } = self;\n\n        ctx.output(|o| {\n            for event in &o.events {\n                output_event_history.push_back(event.clone());\n            }\n        });\n        while output_event_history.len() > 1000 {\n            output_event_history.pop_front();\n        }\n\n        egui::Window::new(\"🔧 Settings\")\n            .open(settings)\n            .vscroll(true)\n            .show(ctx, |ui| {\n                ctx.settings_ui(ui);\n            });\n\n        egui::Window::new(\"🔍 Inspection\")\n            .open(inspection)\n            .vscroll(true)\n            .show(ctx, |ui| {\n                ctx.inspection_ui(ui);\n            });\n\n        egui::Window::new(\"📝 Memory\")\n            .open(memory)\n            .resizable(false)\n            .show(ctx, |ui| {\n                ctx.memory_ui(ui);\n            });\n\n        egui::Window::new(\"📤 Output Events\")\n            .open(output_events)\n            .resizable(true)\n            .default_width(520.0)\n            .show(ctx, |ui| {\n                ui.label(\n                    \"Recent output events from egui. \\\n            These are emitted when you interact with widgets, or move focus between them with TAB. \\\n            They can be hooked up to a screen reader on supported platforms.\",\n                );\n\n                ui.separator();\n\n                egui::ScrollArea::vertical()\n                    .stick_to_bottom(true)\n                    .show(ui, |ui| {\n                        for event in output_event_history {\n                            ui.label(format!(\"{event:?}\"));\n                        }\n                    });\n            });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg(not(target_arch = \"wasm32\"))]\nfn call_after_delay(delay: std::time::Duration, f: impl FnOnce() + Send + 'static) {\n    std::thread::Builder::new()\n        .name(\"call_after_delay\".to_owned())\n        .spawn(move || {\n            std::thread::sleep(delay);\n            f();\n        })\n        .expect(\"Failed to spawn a thread\");\n}\n\n#[cfg(target_arch = \"wasm32\")]\nfn call_after_delay(delay: std::time::Duration, f: impl FnOnce() + Send + 'static) {\n    #![expect(clippy::unwrap_used)]\n\n    use wasm_bindgen::prelude::*;\n    let window = web_sys::window().unwrap();\n    let closure = Closure::once(f);\n    let delay_ms = delay.as_millis() as _;\n    window\n        .set_timeout_with_callback_and_timeout_and_arguments_0(\n            closure.as_ref().unchecked_ref(),\n            delay_ms,\n        )\n        .unwrap();\n    closure.forget(); // We must forget it, or else the callback is canceled on drop\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/frame_history.rs",
    "content": "use egui::util::History;\n\npub struct FrameHistory {\n    frame_times: History<f32>,\n}\n\nimpl Default for FrameHistory {\n    fn default() -> Self {\n        let max_age: f32 = 1.0;\n        let max_len = (max_age * 300.0).round() as usize;\n        Self {\n            frame_times: History::new(0..max_len, max_age),\n        }\n    }\n}\n\nimpl FrameHistory {\n    // Called first\n    pub fn on_new_frame(&mut self, now: f64, previous_frame_time: Option<f32>) {\n        let previous_frame_time = previous_frame_time.unwrap_or_default();\n        if let Some(latest) = self.frame_times.latest_mut() {\n            *latest = previous_frame_time; // rewrite history now that we know\n        }\n        self.frame_times.add(now, previous_frame_time); // projected\n    }\n\n    pub fn mean_frame_time(&self) -> f32 {\n        self.frame_times.average().unwrap_or_default()\n    }\n\n    pub fn fps(&self) -> f32 {\n        1.0 / self.frame_times.mean_time_interval().unwrap_or_default()\n    }\n\n    pub fn ui(&self, ui: &mut egui::Ui) {\n        ui.label(format!(\n            \"Mean CPU usage: {:.2} ms / frame\",\n            1e3 * self.mean_frame_time()\n        ))\n        .on_hover_text(\n            \"Includes all app logic, egui layout, tessellation, and rendering.\\n\\\n            Does not include waiting for vsync.\",\n        );\n        egui::warn_if_debug_build(ui);\n\n        if !cfg!(target_arch = \"wasm32\") {\n            egui::CollapsingHeader::new(\"📊 CPU usage history\")\n                .default_open(false)\n                .show(ui, |ui| {\n                    self.graph(ui);\n                });\n        }\n    }\n\n    fn graph(&self, ui: &mut egui::Ui) -> egui::Response {\n        use egui::{Pos2, Rect, Sense, Shape, Stroke, TextStyle, emath, epaint, pos2, vec2};\n\n        ui.label(\"egui CPU usage history\");\n\n        let history = &self.frame_times;\n\n        // TODO(emilk): we should not use `slider_width` as default graph width.\n        let height = ui.spacing().slider_width;\n        let size = vec2(ui.available_size_before_wrap().x, height);\n        let (rect, response) = ui.allocate_at_least(size, Sense::hover());\n        let style = ui.style().noninteractive();\n\n        let graph_top_cpu_usage = 0.010;\n        let graph_rect = Rect::from_x_y_ranges(history.max_age()..=0.0, graph_top_cpu_usage..=0.0);\n        let to_screen = emath::RectTransform::from_to(graph_rect, rect);\n\n        let mut shapes = Vec::with_capacity(3 + 2 * history.len());\n        shapes.push(Shape::Rect(epaint::RectShape::new(\n            rect,\n            style.corner_radius,\n            ui.visuals().extreme_bg_color,\n            ui.style().noninteractive().bg_stroke,\n            egui::StrokeKind::Inside,\n        )));\n\n        let rect = rect.shrink(4.0);\n        let color = ui.visuals().text_color();\n        let line_stroke = Stroke::new(1.0, color);\n\n        if let Some(pointer_pos) = response.hover_pos() {\n            let y = pointer_pos.y;\n            shapes.push(Shape::line_segment(\n                [pos2(rect.left(), y), pos2(rect.right(), y)],\n                line_stroke,\n            ));\n            let cpu_usage = to_screen.inverse().transform_pos(pointer_pos).y;\n            let text = format!(\"{:.1} ms\", 1e3 * cpu_usage);\n            shapes.push(ui.fonts_mut(|f| {\n                Shape::text(\n                    f,\n                    pos2(rect.left(), y),\n                    egui::Align2::LEFT_BOTTOM,\n                    text,\n                    TextStyle::Monospace.resolve(ui.style()),\n                    color,\n                )\n            }));\n        }\n\n        let circle_color = color;\n        let radius = 2.0;\n        let right_side_time = ui.input(|i| i.time); // Time at right side of screen\n\n        for (time, cpu_usage) in history.iter() {\n            let age = (right_side_time - time) as f32;\n            let pos = to_screen.transform_pos_clamped(Pos2::new(age, cpu_usage));\n\n            shapes.push(Shape::line_segment(\n                [pos2(pos.x, rect.bottom()), pos],\n                line_stroke,\n            ));\n\n            if cpu_usage < graph_top_cpu_usage {\n                shapes.push(Shape::circle_filled(pos, radius, circle_color));\n            }\n        }\n\n        ui.painter().extend(shapes);\n\n        response\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/lib.rs",
    "content": "//! Demo app for egui\n\nmod apps;\nmod backend_panel;\nmod frame_history;\nmod wrap_app;\n\npub use wrap_app::{Anchor, WrapApp};\n\n/// Time of day as seconds since midnight. Used for clock in demo app.\npub(crate) fn seconds_since_midnight() -> f64 {\n    use chrono::Timelike as _;\n    let time = chrono::Local::now().time();\n    time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64)\n}\n\n/// Trait that wraps different parts of the demo app.\npub trait DemoApp {\n    fn demo_ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame);\n\n    #[cfg(feature = \"glow\")]\n    fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) {}\n}\n\n// ----------------------------------------------------------------------------\n#[cfg(feature = \"accessibility_inspector\")]\npub mod accessibility_inspector;\n#[cfg(target_arch = \"wasm32\")]\nmod web;\n\n#[cfg(target_arch = \"wasm32\")]\npub use web::*;\n"
  },
  {
    "path": "crates/egui_demo_app/src/main.rs",
    "content": "//! Demo app for egui\n\n#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n#![allow(clippy::allow_attributes, clippy::never_loop)]\n\n#[global_allocator]\nstatic GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; // Much faster allocator, can give 20% speedups: https://github.com/emilk/egui/pull/7029\n\n// When compiling natively:\nfn main() {\n    for arg in std::env::args().skip(1) {\n        match arg.as_str() {\n            \"--profile\" => {\n                #[cfg(feature = \"puffin\")]\n                start_puffin_server();\n\n                #[cfg(not(feature = \"puffin\"))]\n                panic!(\n                    \"Unknown argument: {arg} - you need to enable the 'puffin' feature to use this.\"\n                );\n            }\n\n            _ => {\n                panic!(\"Unknown argument: {arg}\");\n            }\n        }\n    }\n\n    {\n        // Silence wgpu log spam (https://github.com/gfx-rs/wgpu/issues/3206)\n        let mut rust_log = std::env::var(\"RUST_LOG\").unwrap_or_else(|_| {\n            if cfg!(debug_assertions) {\n                \"debug\".to_owned()\n            } else {\n                \"info\".to_owned()\n            }\n        });\n        for loud_crate in [\"naga\", \"wgpu_core\", \"wgpu_hal\"] {\n            if !rust_log.contains(&format!(\"{loud_crate}=\")) {\n                rust_log += &format!(\",{loud_crate}=warn\");\n            }\n        }\n\n        // SAFETY: we call this from the main thread without any other threads running.\n        #[expect(unsafe_code)]\n        unsafe {\n            std::env::set_var(\"RUST_LOG\", rust_log);\n        }\n    }\n\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default()\n            .with_inner_size([1280.0, 1024.0])\n            .with_drag_and_drop(true),\n\n        #[cfg(feature = \"wgpu\")]\n        renderer: eframe::Renderer::Wgpu,\n\n        ..Default::default()\n    };\n\n    let result = eframe::run_native(\n        \"egui demo app\",\n        options,\n        Box::new(|cc| Ok(Box::new(egui_demo_app::WrapApp::new(cc)))),\n    );\n\n    match result {\n        Ok(()) => {}\n        Err(err) => {\n            // This produces a nicer error message than returning the `Result`:\n            print_error_and_exit(&err);\n        }\n    }\n}\n\nfn print_error_and_exit(err: &eframe::Error) -> ! {\n    #![expect(clippy::print_stderr)]\n    #![expect(clippy::exit)]\n\n    eprintln!(\"Error: {err}\");\n    std::process::exit(1)\n}\n\n#[cfg(feature = \"puffin\")]\nfn start_puffin_server() {\n    puffin::set_scopes_on(true); // tell puffin to collect data\n\n    match puffin_http::Server::new(\"127.0.0.1:8585\") {\n        Ok(puffin_server) => {\n            log::info!(\"Run:  cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585\");\n\n            std::process::Command::new(\"puffin_viewer\")\n                .arg(\"--url\")\n                .arg(\"127.0.0.1:8585\")\n                .spawn()\n                .ok();\n\n            // We can store the server if we want, but in this case we just want\n            // it to keep running. Dropping it closes the server, so let's not drop it!\n            #[expect(clippy::mem_forget)]\n            std::mem::forget(puffin_server);\n        }\n        Err(err) => {\n            log::error!(\"Failed to start puffin server: {err}\");\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/web.rs",
    "content": "use eframe::wasm_bindgen::{self, prelude::*};\n\nuse crate::WrapApp;\n\n/// Our handle to the web app from JavaScript.\n#[derive(Clone)]\n#[wasm_bindgen]\npub struct WebHandle {\n    runner: eframe::WebRunner,\n}\n\n#[wasm_bindgen]\nimpl WebHandle {\n    /// Installs a panic hook, then returns.\n    #[allow(clippy::allow_attributes, clippy::new_without_default)]\n    #[wasm_bindgen(constructor)]\n    pub fn new() -> Self {\n        // Redirect [`log`] message to `console.log` and friends:\n        let log_level = if cfg!(debug_assertions) {\n            log::LevelFilter::Trace\n        } else {\n            log::LevelFilter::Debug\n        };\n        eframe::WebLogger::init(log_level).ok();\n\n        Self {\n            runner: eframe::WebRunner::new(),\n        }\n    }\n\n    /// Call this once from JavaScript to start your app.\n    ///\n    /// # Errors\n    /// Returns an error if the app could not start.\n    #[wasm_bindgen]\n    pub async fn start(\n        &self,\n        canvas: web_sys::HtmlCanvasElement,\n    ) -> Result<(), wasm_bindgen::JsValue> {\n        self.runner\n            .start(\n                canvas,\n                eframe::WebOptions::default(),\n                Box::new(|cc| Ok(Box::new(WrapApp::new(cc)))),\n            )\n            .await\n    }\n\n    #[wasm_bindgen]\n    pub fn destroy(&self) {\n        self.runner.destroy();\n    }\n\n    /// Example on how to call into your app from JavaScript.\n    #[wasm_bindgen]\n    pub fn example(&self) {\n        if let Some(_app) = self.runner.app_mut::<WrapApp>() {\n            // _app.example();\n        }\n    }\n\n    /// The JavaScript can check whether or not your app has crashed:\n    #[wasm_bindgen]\n    pub fn has_panicked(&self) -> bool {\n        self.runner.has_panicked()\n    }\n\n    #[wasm_bindgen]\n    pub fn panic_message(&self) -> Option<String> {\n        self.runner.panic_summary().map(|s| s.message())\n    }\n\n    #[wasm_bindgen]\n    pub fn panic_callstack(&self) -> Option<String> {\n        self.runner.panic_summary().map(|s| s.callstack())\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_app/src/wrap_app.rs",
    "content": "use egui_demo_lib::{DemoWindows, is_mobile};\n\n#[cfg(feature = \"glow\")]\nuse eframe::glow;\n\n#[cfg(target_arch = \"wasm32\")]\nuse core::any::Any;\n\nuse crate::DemoApp;\n\n#[derive(Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nstruct EasyMarkApp {\n    editor: egui_demo_lib::easy_mark::EasyMarkEditor,\n}\n\nimpl DemoApp for EasyMarkApp {\n    fn demo_ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        self.editor.panels(ui);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nimpl DemoApp for DemoWindows {\n    fn demo_ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        self.ui(ui);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct FractalClockApp {\n    fractal_clock: crate::apps::FractalClock,\n    pub mock_time: Option<f64>,\n}\n\nimpl DemoApp for FractalClockApp {\n    fn demo_ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::Frame::dark_canvas(ui.style())\n            .stroke(egui::Stroke::NONE)\n            .corner_radius(0)\n            .show(ui, |ui| {\n                self.fractal_clock.ui(\n                    ui,\n                    self.mock_time\n                        .or_else(|| Some(crate::seconds_since_midnight())),\n                );\n            });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct ColorTestApp {\n    color_test: egui_demo_lib::ColorTest,\n}\n\nimpl DemoApp for ColorTestApp {\n    fn demo_ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            if frame.is_web() {\n                ui.label(\n                        \"NOTE: Some old browsers stuck on WebGL1 without sRGB support will not pass the color test.\",\n                    );\n                ui.separator();\n            }\n            egui::ScrollArea::both().auto_shrink(false).show(ui, |ui| {\n                self.color_test.ui(ui);\n            });\n        });\n    }\n}\n\n#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum Anchor {\n    #[default]\n    Demo,\n\n    EasyMarkEditor,\n\n    #[cfg(feature = \"http\")]\n    Http,\n\n    #[cfg(feature = \"image_viewer\")]\n    ImageViewer,\n\n    Clock,\n\n    #[cfg(any(feature = \"glow\", feature = \"wgpu\"))]\n    Custom3d,\n\n    /// Rendering test\n    Rendering,\n}\n\nimpl Anchor {\n    #[cfg(target_arch = \"wasm32\")]\n    fn all() -> Vec<Self> {\n        vec![\n            Self::Demo,\n            Self::EasyMarkEditor,\n            #[cfg(feature = \"http\")]\n            Self::Http,\n            Self::Clock,\n            #[cfg(any(feature = \"glow\", feature = \"wgpu\"))]\n            Self::Custom3d,\n            Self::Rendering,\n        ]\n    }\n\n    #[cfg(target_arch = \"wasm32\")]\n    fn from_str_case_insensitive(anchor: &str) -> Option<Self> {\n        let anchor = anchor.to_lowercase();\n        Self::all().into_iter().find(|x| x.to_string() == anchor)\n    }\n}\n\nimpl std::fmt::Display for Anchor {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut name = format!(\"{self:?}\");\n        name.make_ascii_lowercase();\n        f.write_str(&name)\n    }\n}\n\nimpl From<Anchor> for egui::WidgetText {\n    fn from(value: Anchor) -> Self {\n        Self::from(value.to_string())\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Clone, Copy, Debug)]\n#[must_use]\nenum Command {\n    Nothing,\n    ResetEverything,\n}\n\n// ----------------------------------------------------------------------------\n\n/// The state that we persist (serialize).\n#[derive(Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct State {\n    demo: DemoWindows,\n    easy_mark_editor: EasyMarkApp,\n    #[cfg(feature = \"http\")]\n    http: crate::apps::HttpApp,\n    #[cfg(feature = \"image_viewer\")]\n    image_viewer: crate::apps::ImageViewer,\n    pub clock: FractalClockApp,\n    rendering_test: ColorTestApp,\n\n    selected_anchor: Anchor,\n    backend_panel: super::backend_panel::BackendPanel,\n}\n\n/// Wraps many demo/test apps into one.\npub struct WrapApp {\n    pub state: State,\n\n    #[cfg(any(feature = \"glow\", feature = \"wgpu\"))]\n    custom3d: Option<crate::apps::Custom3d>,\n\n    dropped_files: Vec<egui::DroppedFile>,\n}\n\nimpl WrapApp {\n    pub fn new(cc: &eframe::CreationContext<'_>) -> Self {\n        // This gives us image support:\n        egui_extras::install_image_loaders(&cc.egui_ctx);\n\n        #[cfg(feature = \"accessibility_inspector\")]\n        cc.egui_ctx\n            .add_plugin(crate::accessibility_inspector::AccessibilityInspectorPlugin::default());\n\n        #[allow(clippy::allow_attributes, unused_mut)]\n        let mut slf = Self {\n            state: State::default(),\n\n            #[cfg(any(feature = \"glow\", feature = \"wgpu\"))]\n            custom3d: crate::apps::Custom3d::new(cc),\n\n            dropped_files: Default::default(),\n        };\n\n        #[cfg(feature = \"persistence\")]\n        if let Some(storage) = cc.storage\n            && let Some(state) = eframe::get_value(storage, eframe::APP_KEY)\n        {\n            slf.state = state;\n        }\n\n        slf\n    }\n\n    pub fn apps_iter_mut(\n        &mut self,\n    ) -> impl Iterator<Item = (&'static str, Anchor, &mut dyn DemoApp)> {\n        let mut vec = vec![\n            (\n                \"✨ Demos\",\n                Anchor::Demo,\n                &mut self.state.demo as &mut dyn DemoApp,\n            ),\n            (\n                \"🖹 EasyMark editor\",\n                Anchor::EasyMarkEditor,\n                &mut self.state.easy_mark_editor as &mut dyn DemoApp,\n            ),\n            #[cfg(feature = \"http\")]\n            (\n                \"⬇ HTTP\",\n                Anchor::Http,\n                &mut self.state.http as &mut dyn DemoApp,\n            ),\n            (\n                \"🕑 Fractal Clock\",\n                Anchor::Clock,\n                &mut self.state.clock as &mut dyn DemoApp,\n            ),\n            #[cfg(feature = \"image_viewer\")]\n            (\n                \"🖼 Image Viewer\",\n                Anchor::ImageViewer,\n                &mut self.state.image_viewer as &mut dyn DemoApp,\n            ),\n        ];\n\n        #[cfg(any(feature = \"glow\", feature = \"wgpu\"))]\n        if let Some(custom3d) = &mut self.custom3d {\n            vec.push((\n                \"🔺 3D painting\",\n                Anchor::Custom3d,\n                custom3d as &mut dyn DemoApp,\n            ));\n        }\n\n        vec.push((\n            \"🎨 Rendering test\",\n            Anchor::Rendering,\n            &mut self.state.rendering_test as &mut dyn DemoApp,\n        ));\n\n        vec.into_iter()\n    }\n}\n\nimpl eframe::App for WrapApp {\n    #[cfg(feature = \"persistence\")]\n    fn save(&mut self, storage: &mut dyn eframe::Storage) {\n        eframe::set_value(storage, eframe::APP_KEY, &self.state);\n    }\n\n    fn clear_color(&self, visuals: &egui::Visuals) -> [f32; 4] {\n        // Give the area behind the floating windows a different color, because it looks better:\n        let color = egui::lerp(\n            egui::Rgba::from(visuals.panel_fill)..=egui::Rgba::from(visuals.extreme_bg_color),\n            0.5,\n        );\n        let color = egui::Color32::from(color);\n        color.to_normalized_gamma_f32()\n    }\n\n    fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {\n        #[cfg(target_arch = \"wasm32\")]\n        if let Some(anchor) = frame\n            .info()\n            .web_info\n            .location\n            .hash\n            .strip_prefix('#')\n            .and_then(Anchor::from_str_case_insensitive)\n        {\n            self.state.selected_anchor = anchor;\n        }\n\n        #[cfg(not(target_arch = \"wasm32\"))]\n        if ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::F11)) {\n            let fullscreen = ui.input(|i| i.viewport().fullscreen.unwrap_or(false));\n            ui.send_viewport_cmd(egui::ViewportCommand::Fullscreen(!fullscreen));\n        }\n\n        let mut cmd = Command::Nothing;\n        egui::Panel::top(\"wrap_app_top_bar\")\n            .frame(egui::Frame::new().inner_margin(4))\n            .show_inside(ui, |ui| {\n                ui.horizontal_wrapped(|ui| {\n                    ui.visuals_mut().button_frame = false;\n                    self.bar_contents(ui, frame, &mut cmd);\n                });\n            });\n\n        self.state.backend_panel.update(ui.ctx(), frame);\n\n        egui::CentralPanel::no_frame().show_inside(ui, |ui| {\n            if !is_mobile(ui.ctx()) {\n                cmd = self.backend_panel(ui, frame);\n            }\n\n            self.show_selected_app(ui, frame);\n        });\n\n        self.state.backend_panel.end_of_frame(ui.ctx());\n\n        self.ui_file_drag_and_drop(ui.ctx());\n\n        self.run_cmd(ui.ctx(), cmd);\n    }\n\n    #[cfg(feature = \"glow\")]\n    fn on_exit(&mut self, gl: Option<&glow::Context>) {\n        if let Some(custom3d) = &mut self.custom3d {\n            custom3d.on_exit(gl);\n        }\n    }\n\n    #[cfg(target_arch = \"wasm32\")]\n    fn as_any_mut(&mut self) -> Option<&mut dyn Any> {\n        Some(&mut *self)\n    }\n}\n\nimpl WrapApp {\n    fn backend_panel(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) -> Command {\n        // The backend-panel can be toggled on/off.\n        // We show a little animation when the user switches it.\n        let is_open = self.state.backend_panel.open || ui.memory(|mem| mem.everything_is_visible());\n\n        let mut cmd = Command::Nothing;\n\n        egui::Panel::left(\"backend_panel\")\n            .resizable(false)\n            .show_animated_inside(ui, is_open, |ui| {\n                ui.add_space(4.0);\n                ui.vertical_centered(|ui| {\n                    ui.heading(\"💻 Backend\");\n                });\n\n                ui.separator();\n                self.backend_panel_contents(ui, frame, &mut cmd);\n            });\n\n        cmd\n    }\n\n    fn run_cmd(&mut self, ctx: &egui::Context, cmd: Command) {\n        match cmd {\n            Command::Nothing => {}\n            Command::ResetEverything => {\n                self.state = Default::default();\n                ctx.memory_mut(|mem| *mem = Default::default());\n            }\n        }\n    }\n\n    fn backend_panel_contents(\n        &mut self,\n        ui: &mut egui::Ui,\n        frame: &mut eframe::Frame,\n        cmd: &mut Command,\n    ) {\n        self.state.backend_panel.ui(ui, frame);\n\n        ui.separator();\n\n        ui.horizontal(|ui| {\n            if ui\n                .button(\"Reset egui\")\n                .on_hover_text(\"Forget scroll, positions, sizes etc\")\n                .clicked()\n            {\n                ui.memory_mut(|mem| *mem = Default::default());\n                ui.close();\n            }\n\n            if ui.button(\"Reset everything\").clicked() {\n                *cmd = Command::ResetEverything;\n                ui.close();\n            }\n        });\n    }\n\n    fn show_selected_app(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {\n        let selected_anchor = self.state.selected_anchor;\n        for (_name, anchor, app) in self.apps_iter_mut() {\n            if anchor == selected_anchor || ui.memory(|mem| mem.everything_is_visible()) {\n                app.demo_ui(ui, frame);\n            }\n        }\n    }\n\n    fn bar_contents(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cmd: &mut Command) {\n        egui::widgets::global_theme_preference_switch(ui);\n\n        ui.separator();\n\n        if is_mobile(ui.ctx()) {\n            ui.menu_button(\"💻 Backend\", |ui| {\n                ui.set_style(ui.global_style()); // ignore the \"menu\" style set by `menu_button`.\n                self.backend_panel_contents(ui, frame, cmd);\n            });\n        } else {\n            ui.toggle_value(&mut self.state.backend_panel.open, \"💻 Backend\");\n        }\n\n        ui.separator();\n\n        let mut selected_anchor = self.state.selected_anchor;\n        for (name, anchor, _app) in self.apps_iter_mut() {\n            if ui\n                .selectable_label(selected_anchor == anchor, name)\n                .clicked()\n            {\n                selected_anchor = anchor;\n                if frame.is_web() {\n                    ui.open_url(egui::OpenUrl::same_tab(format!(\"#{anchor}\")));\n                }\n            }\n        }\n        self.state.selected_anchor = selected_anchor;\n\n        ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {\n            if false {\n                // TODO(emilk): fix the overlap on small screens\n                if clock_button(ui, crate::seconds_since_midnight()).clicked() {\n                    self.state.selected_anchor = Anchor::Clock;\n                    if frame.is_web() {\n                        ui.open_url(egui::OpenUrl::same_tab(\"#clock\"));\n                    }\n                }\n            }\n\n            egui::warn_if_debug_build(ui);\n        });\n    }\n\n    fn ui_file_drag_and_drop(&mut self, ctx: &egui::Context) {\n        use egui::{Align2, Color32, Id, LayerId, Order, TextStyle};\n        use std::fmt::Write as _;\n\n        // Preview hovering files:\n        if !ctx.input(|i| i.raw.hovered_files.is_empty()) {\n            let text = ctx.input(|i| {\n                let mut text = \"Dropping files:\\n\".to_owned();\n                for file in &i.raw.hovered_files {\n                    if let Some(path) = &file.path {\n                        write!(text, \"\\n{}\", path.display()).ok();\n                    } else if !file.mime.is_empty() {\n                        write!(text, \"\\n{}\", file.mime).ok();\n                    } else {\n                        text += \"\\n???\";\n                    }\n                }\n                text\n            });\n\n            let painter =\n                ctx.layer_painter(LayerId::new(Order::Foreground, Id::new(\"file_drop_target\")));\n\n            let content_rect = ctx.content_rect();\n            painter.rect_filled(content_rect, 0.0, Color32::from_black_alpha(192));\n            painter.text(\n                content_rect.center(),\n                Align2::CENTER_CENTER,\n                text,\n                TextStyle::Heading.resolve(&ctx.global_style()),\n                Color32::WHITE,\n            );\n        }\n\n        // Collect dropped files:\n        ctx.input(|i| {\n            if !i.raw.dropped_files.is_empty() {\n                self.dropped_files.clone_from(&i.raw.dropped_files);\n            }\n        });\n\n        // Show dropped files (if any):\n        if !self.dropped_files.is_empty() {\n            let mut open = true;\n            egui::Window::new(\"Dropped files\")\n                .open(&mut open)\n                .show(ctx, |ui| {\n                    for file in &self.dropped_files {\n                        let mut info = if let Some(path) = &file.path {\n                            path.display().to_string()\n                        } else if !file.name.is_empty() {\n                            file.name.clone()\n                        } else {\n                            \"???\".to_owned()\n                        };\n\n                        let mut additional_info = vec![];\n                        if !file.mime.is_empty() {\n                            additional_info.push(format!(\"type: {}\", file.mime));\n                        }\n                        if let Some(bytes) = &file.bytes {\n                            additional_info.push(format!(\"{} bytes\", bytes.len()));\n                        }\n                        if !additional_info.is_empty() {\n                            info += &format!(\" ({})\", additional_info.join(\", \"));\n                        }\n\n                        ui.label(info);\n                    }\n                });\n            if !open {\n                self.dropped_files.clear();\n            }\n        }\n    }\n}\n\nfn clock_button(ui: &mut egui::Ui, seconds_since_midnight: f64) -> egui::Response {\n    let time = seconds_since_midnight;\n    let time = format!(\n        \"{:02}:{:02}:{:02}.{:02}\",\n        (time % (24.0 * 60.0 * 60.0) / 3600.0).floor(),\n        (time % (60.0 * 60.0) / 60.0).floor(),\n        (time % 60.0).floor(),\n        (time % 1.0 * 100.0).floor()\n    );\n\n    ui.button(egui::RichText::new(time).monospace())\n}\n"
  },
  {
    "path": "crates/egui_demo_app/tests/test_demo_app.rs",
    "content": "use egui::Vec2;\nuse egui::accesskit::Role;\nuse egui_demo_app::{Anchor, WrapApp};\nuse egui_kittest::SnapshotResults;\nuse egui_kittest::kittest::Queryable as _;\n\n#[test]\nfn test_demo_app() {\n    let mut harness = egui_kittest::Harness::builder()\n        .with_size(Vec2::new(900.0, 600.0))\n        .wgpu()\n        .build_eframe(|cc| WrapApp::new(cc));\n\n    let app = harness.state_mut();\n\n    // Mock the fractal clock time so snapshots are consistent.\n    app.state.clock.mock_time = Some(36383.0);\n\n    let apps = app\n        .apps_iter_mut()\n        .map(|(name, anchor, _)| (name, anchor))\n        .collect::<Vec<_>>();\n\n    #[cfg(feature = \"wgpu\")]\n    assert!(\n        apps.iter()\n            .any(|(_, anchor)| matches!(anchor, Anchor::Custom3d)),\n        \"Expected to find the Custom3d app.\",\n    );\n\n    let mut results = SnapshotResults::new();\n\n    for (name, anchor) in apps {\n        harness.get_by_role_and_label(Role::Button, name).click();\n\n        match anchor {\n            // The widget gallery demo shows the current date, so we can't use it for snapshot testing\n            Anchor::Demo => {\n                continue;\n            }\n            // This is already tested extensively elsewhere\n            Anchor::Rendering => {\n                continue;\n            }\n            // We don't want to rely on a network connection for tests\n            #[cfg(feature = \"http\")]\n            Anchor::Http => {\n                continue;\n            }\n            // Load a local image where we know it exists and loads quickly\n            #[cfg(feature = \"image_viewer\")]\n            Anchor::ImageViewer => {\n                harness.step();\n\n                harness\n                    .get_by_role_and_label(Role::TextInput, \"URI:\")\n                    .focus();\n                harness.key_press_modifiers(egui::Modifiers::COMMAND, egui::Key::A);\n\n                harness\n                    .get_by_role_and_label(Role::TextInput, \"URI:\")\n                    .type_text(\"file://../eframe/data/icon.png\");\n\n                harness.get_by_role_and_label(Role::Button, \"✔\").click();\n\n                // Wait for the image to load\n                harness.try_run_realtime().ok();\n            }\n            _ => {}\n        }\n\n        // Can't use Harness::run because fractal clock keeps requesting repaints\n        harness.run_steps(4);\n\n        results.add(harness.try_snapshot(anchor.to_string()));\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/Cargo.toml",
    "content": "[package]\nname = \"egui_demo_lib\"\nversion.workspace = true\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\ndescription = \"Example library for egui\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui/tree/main/crates/egui_demo_lib\"\nlicense.workspace = true\nreadme = \"README.md\"\nrepository = \"https://github.com/emilk/egui/tree/main/crates/egui_demo_lib\"\ncategories = [\"gui\", \"graphics\"]\nkeywords = [\"glow\", \"egui\", \"gui\", \"gamedev\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"Cargo.toml\", \"data/*\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[lib]\n\n\n[features]\ndefault = []\n\nchrono = [\"egui_extras/datepicker\", \"dep:chrono\"]\n\n## Allow serialization using [`serde`](https://docs.rs/serde).\nserde = [\"egui/serde\", \"dep:serde\", \"egui_extras/serde\"]\n\n## Enable better syntax highlighting using [`syntect`](https://docs.rs/syntect).\nsyntect = [\"egui_extras/syntect\"]\n\n\n[dependencies]\negui = { workspace = true, default-features = false, features = [\"color-hex\"] }\negui_extras = { workspace = true, features = [\"image\", \"svg\"] }\n\nunicode_names2.workspace = true # this old version has fewer dependencies\n\n#! ### Optional dependencies\nchrono = { workspace = true, optional = true, features = [\"js-sys\", \"wasmbind\"] }\n## Enable this when generating docs.\ndocument-features = { workspace = true, optional = true }\nserde = { workspace = true, optional = true }\n\n\n[dev-dependencies]\ncriterion.workspace = true\negui = { workspace = true, features = [\"default_fonts\"] }\negui_extras = { workspace = true, features = [\"image\", \"svg\"] }\negui_kittest = { workspace = true, features = [\"wgpu\", \"snapshot\"] }\nimage = { workspace = true, features = [\"png\"] }\nmimalloc.workspace = true # for benchmarks\nrand.workspace = true\n\n[[bench]]\nname = \"benchmark\"\nharness = false\n"
  },
  {
    "path": "crates/egui_demo_lib/README.md",
    "content": "# [`egui`](https://github.com/emilk/egui) demo library\n\n[![Latest version](https://img.shields.io/crates/v/egui_demo_lib.svg)](https://crates.io/crates/egui_demo_lib)\n[![Documentation](https://docs.rs/egui_demo_lib/badge.svg)](https://docs.rs/egui_demo_lib)\n[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\nThis crate contains example code for [`egui`](https://github.com/emilk/egui).\n\nThe demo library is a separate crate for three reasons:\n\n* To ensure it only uses the public `egui` api.\n* To remove the amount of code in `egui` proper.\n* To make it easy for 3rd party egui integrations to use it for tests.\n  - See for instance https://github.com/not-fl3/egui-miniquad/blob/master/examples/demo.rs\n\nThis crate also contains benchmarks for egui. \nRun them with \n```bash\n# Run all benchmarks\ncargo bench -p egui_demo_lib \n\n# Run a single benchmark\ncargo bench -p egui_demo_lib \"benchmark name\"\n\n# Profile benchmarks with cargo-flamegraph (--root flag is necessary for MacOS)\nCARGO_PROFILE_BENCH_DEBUG=true cargo flamegraph --bench benchmark --root -p egui_demo_lib  -- --bench \"benchmark name\"\n\n# Profile with cargo-instruments\nCARGO_PROFILE_BENCH_DEBUG=true cargo instruments --profile bench --bench benchmark -p egui_demo_lib -t time -- --bench \"benchmark name\" \n```\n"
  },
  {
    "path": "crates/egui_demo_lib/benches/benchmark.rs",
    "content": "use std::fmt::Write as _;\n\nuse criterion::{BatchSize, Criterion, criterion_group, criterion_main};\n\nuse egui::epaint::TextShape;\nuse egui::load::SizedTexture;\nuse egui::{Button, Id, RichText, TextureId, Ui, UiBuilder, Vec2};\nuse egui_demo_lib::LOREM_IPSUM_LONG;\nuse rand::Rng as _;\n\n#[global_allocator]\nstatic GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; // Much faster allocator\n\n/// Each iteration should be called in their own `Ui` with an intentional id clash,\n/// to prevent the Context from building a massive map of `WidgetRects` (which would slow the test,\n/// causing unreliable results).\nfn create_benchmark_ui(ctx: &egui::Context) -> Ui {\n    Ui::new(ctx.clone(), Id::new(\"clashing_id\"), UiBuilder::new())\n}\n\npub fn criterion_benchmark(c: &mut Criterion) {\n    use egui::RawInput;\n\n    {\n        let ctx = egui::Context::default();\n        let mut demo_windows = egui_demo_lib::DemoWindows::default();\n\n        // The most end-to-end benchmark.\n        c.bench_function(\"demo_with_tessellate__realistic\", |b| {\n            b.iter(|| {\n                let full_output = ctx.run_ui(RawInput::default(), |ui| {\n                    demo_windows.ui(ui);\n                });\n                ctx.tessellate(full_output.shapes, full_output.pixels_per_point)\n            });\n        });\n\n        c.bench_function(\"demo_no_tessellate\", |b| {\n            b.iter(|| {\n                ctx.run_ui(RawInput::default(), |ui| {\n                    demo_windows.ui(ui);\n                })\n            });\n        });\n\n        let full_output = ctx.run_ui(RawInput::default(), |ui| {\n            demo_windows.ui(ui);\n        });\n        c.bench_function(\"demo_only_tessellate\", |b| {\n            b.iter(|| ctx.tessellate(full_output.shapes.clone(), full_output.pixels_per_point));\n        });\n    }\n\n    if false {\n        let ctx = egui::Context::default();\n        ctx.memory_mut(|m| m.set_everything_is_visible(true)); // give us everything\n        let mut demo_windows = egui_demo_lib::DemoWindows::default();\n        c.bench_function(\"demo_full_no_tessellate\", |b| {\n            b.iter(|| {\n                ctx.run_ui(RawInput::default(), |ui| {\n                    demo_windows.ui(ui);\n                })\n            });\n        });\n    }\n\n    {\n        let ctx = egui::Context::default();\n        let _ = ctx.run_ui(RawInput::default(), |ui| {\n            c.bench_function(\"label &str\", |b| {\n                b.iter_batched_ref(\n                    || create_benchmark_ui(ui),\n                    |ui| {\n                        ui.label(\"the quick brown fox jumps over the lazy dog\");\n                    },\n                    BatchSize::LargeInput,\n                );\n            });\n            c.bench_function(\"label format!\", |b| {\n                b.iter_batched_ref(\n                    || create_benchmark_ui(ui),\n                    |ui| {\n                        ui.label(\"the quick brown fox jumps over the lazy dog\".to_owned());\n                    },\n                    BatchSize::LargeInput,\n                );\n            });\n        });\n    }\n\n    {\n        let ctx = egui::Context::default();\n        let _ = ctx.run_ui(RawInput::default(), |ui| {\n            let mut group = c.benchmark_group(\"button\");\n\n            // To ensure we have a valid image, let's use the font texture. The size\n            // shouldn't be important for this benchmark.\n            let image = SizedTexture::new(TextureId::default(), Vec2::splat(16.0));\n\n            group.bench_function(\"1_button_text\", |b| {\n                b.iter_batched_ref(\n                    || create_benchmark_ui(ui),\n                    |ui| {\n                        ui.add(Button::new(\"Hello World\"));\n                    },\n                    BatchSize::LargeInput,\n                );\n            });\n            group.bench_function(\"2_button_text_image\", |b| {\n                b.iter_batched_ref(\n                    || create_benchmark_ui(ui),\n                    |ui| {\n                        ui.add(Button::image_and_text(image, \"Hello World\"));\n                    },\n                    BatchSize::LargeInput,\n                );\n            });\n            group.bench_function(\"3_button_text_image_right_text\", |b| {\n                b.iter_batched_ref(\n                    || create_benchmark_ui(ui),\n                    |ui| {\n                        ui.add(Button::image_and_text(image, \"Hello World\").right_text(\"⏵\"));\n                    },\n                    BatchSize::LargeInput,\n                );\n            });\n            group.bench_function(\"4_button_italic\", |b| {\n                b.iter_batched_ref(\n                    || create_benchmark_ui(ui),\n                    |ui| {\n                        ui.add(Button::new(RichText::new(\"Hello World\").italics()));\n                    },\n                    BatchSize::LargeInput,\n                );\n            });\n        });\n    }\n\n    {\n        let ctx = egui::Context::default();\n        ctx.begin_pass(RawInput::default());\n\n        let painter =\n            egui::Painter::new(ctx.clone(), egui::LayerId::background(), ctx.content_rect());\n\n        c.bench_function(\"Painter::rect\", |b| {\n            let rect = painter.clip_rect();\n            b.iter(|| {\n                painter.rect(\n                    rect,\n                    2.0,\n                    egui::Color32::RED,\n                    (1.0, egui::Color32::WHITE),\n                    egui::StrokeKind::Inside,\n                );\n            });\n        });\n\n        // Don't call `end_pass` to not have to drain the huge paint list\n    }\n\n    {\n        let pixels_per_point = 1.0;\n        let wrap_width = 512.0;\n        let font_id = egui::FontId::default();\n        let text_color = egui::Color32::WHITE;\n        let mut fonts =\n            egui::epaint::text::Fonts::new(Default::default(), egui::FontDefinitions::default());\n        {\n            c.bench_function(\"text_layout_uncached\", |b| {\n                b.iter(|| {\n                    use egui::epaint::text::{LayoutJob, layout};\n\n                    let job = LayoutJob::simple(\n                        LOREM_IPSUM_LONG.to_owned(),\n                        font_id.clone(),\n                        text_color,\n                        wrap_width,\n                    );\n                    layout(&mut fonts.fonts, pixels_per_point, job.into())\n                });\n            });\n        }\n        c.bench_function(\"text_layout_cached\", |b| {\n            b.iter(|| {\n                fonts.with_pixels_per_point(pixels_per_point).layout(\n                    LOREM_IPSUM_LONG.to_owned(),\n                    font_id.clone(),\n                    text_color,\n                    wrap_width,\n                )\n            });\n        });\n\n        c.bench_function(\"text_layout_cached_many_lines_modified\", |b| {\n            const NUM_LINES: usize = 2_000;\n\n            let mut string = String::new();\n            for _ in 0..NUM_LINES {\n                for i in 0..30_u8 {\n                    #[expect(clippy::unwrap_used)]\n                    write!(string, \"{i:02X} \").unwrap();\n                }\n                string.push('\\n');\n            }\n\n            let mut rng = rand::rng();\n            b.iter(|| {\n                fonts.begin_pass(egui::epaint::TextOptions::default());\n\n                // Delete a random character, simulating a user making an edit in a long file:\n                let mut new_string = string.clone();\n                let idx = rng.random_range(0..string.len());\n                new_string.remove(idx);\n\n                fonts.with_pixels_per_point(pixels_per_point).layout(\n                    new_string,\n                    font_id.clone(),\n                    text_color,\n                    wrap_width,\n                );\n            });\n        });\n\n        let galley = fonts.with_pixels_per_point(pixels_per_point).layout(\n            LOREM_IPSUM_LONG.to_owned(),\n            font_id,\n            text_color,\n            wrap_width,\n        );\n        let font_image_size = fonts.font_image_size();\n        let prepared_discs = fonts.texture_atlas().prepared_discs();\n        let mut tessellator = egui::epaint::Tessellator::new(\n            1.0,\n            Default::default(),\n            font_image_size,\n            prepared_discs,\n        );\n        let mut mesh = egui::epaint::Mesh::default();\n        let text_shape = TextShape::new(egui::Pos2::ZERO, galley, text_color);\n        c.bench_function(\"tessellate_text\", |b| {\n            b.iter(|| {\n                tessellator.tessellate_text(&text_shape, &mut mesh);\n                mesh.clear();\n            });\n        });\n    }\n}\n\ncriterion_group!(benches, criterion_benchmark);\ncriterion_main!(benches);\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/about.rs",
    "content": "#[derive(Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct About {}\n\nimpl crate::Demo for About {\n    fn name(&self) -> &'static str {\n        \"About egui\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .default_width(320.0)\n            .default_height(480.0)\n            .open(open)\n            .resizable([true, false])\n            .scroll(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for About {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        use egui::special_emojis::{OS_APPLE, OS_LINUX, OS_WINDOWS};\n\n        ui.heading(\"egui\");\n        ui.label(format!(\n            \"egui is an immediate mode GUI library written in Rust. egui runs both on the web and natively on {}{}{}. \\\n            On the web it is compiled to WebAssembly and rendered with WebGL.{}\",\n            OS_APPLE, OS_LINUX, OS_WINDOWS,\n            if cfg!(target_arch = \"wasm32\") {\n                \" Everything you see is rendered as textured triangles. There is no DOM, HTML, JS or CSS. Just Rust.\"\n            } else {\"\"}\n        ));\n        ui.label(\"egui is designed to be easy to use, portable, and fast.\");\n\n        ui.add_space(12.0);\n\n        ui.heading(\"Immediate mode\");\n        about_immediate_mode(ui);\n\n        ui.add_space(12.0);\n\n        ui.heading(\"Links\");\n        links(ui);\n\n        ui.add_space(12.0);\n\n        ui.horizontal_wrapped(|ui| {\n            ui.spacing_mut().item_spacing.x = 0.0;\n            ui.label(\"egui development is sponsored by \");\n            ui.hyperlink_to(\"Rerun.io\", \"https://www.rerun.io/\");\n            ui.label(\", a startup building an SDK for visualizing streams of multimodal data. \");\n            ui.label(\"For an example of a real-world egui app, see \");\n            ui.hyperlink_to(\"rerun.io/viewer\", \"https://www.rerun.io/viewer\");\n            ui.label(\" (runs in your browser).\");\n        });\n\n        ui.add_space(12.0);\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n\nfn about_immediate_mode(ui: &mut egui::Ui) {\n    ui.style_mut().spacing.interact_size.y = 0.0; // hack to make `horizontal_wrapped` work better with text.\n\n    ui.horizontal_wrapped(|ui| {\n            ui.spacing_mut().item_spacing.x = 0.0;\n            ui.label(\"Immediate mode is a GUI paradigm that lets you create a GUI with less code and simpler control flow. For example, this is how you create a \");\n            let _ = ui.small_button(\"button\");\n            ui.label(\" in egui:\");\n        });\n\n    ui.add_space(8.0);\n    crate::rust_view_ui(\n        ui,\n        r#\"\n  if ui.button(\"Save\").clicked() {\n      my_state.save();\n  }\"#\n        .trim_start_matches('\\n'),\n    );\n    ui.add_space(8.0);\n\n    ui.horizontal_wrapped(|ui| {\n        ui.spacing_mut().item_spacing.x = 0.0;\n        ui.label(\"There are no callbacks or messages, and no button state to store. \");\n        ui.label(\"Read more about immediate mode \");\n        ui.hyperlink_to(\"here\", \"https://github.com/emilk/egui#why-immediate-mode\");\n        ui.label(\".\");\n    });\n}\n\nfn links(ui: &mut egui::Ui) {\n    use egui::special_emojis::GITHUB;\n    ui.hyperlink_to(\n        format!(\"{GITHUB} github.com/emilk/egui\"),\n        \"https://github.com/emilk/egui\",\n    );\n    ui.hyperlink_to(\n        \"@ernerfeldt.bsky.social\",\n        \"https://bsky.app/profile/ernerfeldt.bsky.social\",\n    );\n    ui.hyperlink_to(\"📓 egui documentation\", \"https://docs.rs/egui/\");\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/code_editor.rs",
    "content": "// ----------------------------------------------------------------------------\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct CodeEditor {\n    language: String,\n    code: String,\n}\n\nimpl Default for CodeEditor {\n    fn default() -> Self {\n        Self {\n            language: \"rs\".into(),\n            code: \"// A very simple example\\n\\\nfn main() {\\n\\\n\\tprintln!(\\\"Hello world!\\\");\\n\\\n}\\n\\\n\"\n            .into(),\n        }\n    }\n}\n\nimpl crate::Demo for CodeEditor {\n    fn name(&self) -> &'static str {\n        \"🖮 Code Editor\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        use crate::View as _;\n        egui::Window::new(self.name())\n            .open(open)\n            .default_height(500.0)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl crate::View for CodeEditor {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        let Self { language, code } = self;\n\n        ui.horizontal(|ui| {\n            ui.set_height(0.0);\n            ui.label(\"An example of syntax highlighting in a TextEdit.\");\n            ui.add(crate::egui_github_link_file!());\n        });\n\n        if cfg!(feature = \"syntect\") {\n            ui.horizontal(|ui| {\n                ui.label(\"Language:\");\n                ui.text_edit_singleline(language);\n            });\n            ui.horizontal_wrapped(|ui| {\n                ui.spacing_mut().item_spacing.x = 0.0;\n                ui.label(\"Syntax highlighting powered by \");\n                ui.hyperlink_to(\"syntect\", \"https://github.com/trishume/syntect\");\n                ui.label(\".\");\n            });\n        } else {\n            ui.horizontal_wrapped(|ui| {\n                ui.spacing_mut().item_spacing.x = 0.0;\n                ui.label(\"Compile the demo with the \");\n                ui.code(\"syntax_highlighting\");\n                ui.label(\" feature to enable more accurate syntax highlighting using \");\n                ui.hyperlink_to(\"syntect\", \"https://github.com/trishume/syntect\");\n                ui.label(\".\");\n            });\n        }\n\n        let mut theme =\n            egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style());\n        ui.collapsing(\"Theme\", |ui| {\n            ui.group(|ui| {\n                theme.ui(ui);\n                theme.clone().store_in_memory(ui.ctx());\n            });\n        });\n\n        let mut layouter = |ui: &egui::Ui, buf: &dyn egui::TextBuffer, wrap_width: f32| {\n            let mut layout_job = egui_extras::syntax_highlighting::highlight(\n                ui.ctx(),\n                ui.style(),\n                &theme,\n                buf.as_str(),\n                language,\n            );\n            layout_job.wrap.max_width = wrap_width;\n            ui.fonts_mut(|f| f.layout_job(layout_job))\n        };\n\n        egui::ScrollArea::vertical().show(ui, |ui| {\n            let editor = egui::TextEdit::multiline(code)\n                .font(egui::TextStyle::Monospace) // for cursor height\n                .code_editor()\n                .desired_rows(10)\n                .lock_focus(true)\n                .desired_width(f32::INFINITY)\n                .layouter(&mut layouter);\n            let editor = if cfg!(feature = \"syntect\") {\n                editor\n            } else {\n                use egui::Color32;\n                let background_color = if theme.is_dark() {\n                    Color32::BLACK\n                } else {\n                    Color32::WHITE\n                };\n                editor.background_color(background_color)\n            };\n            ui.add(editor);\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/code_example.rs",
    "content": "#[derive(Debug)]\npub struct CodeExample {\n    name: String,\n    age: u32,\n}\n\nimpl Default for CodeExample {\n    fn default() -> Self {\n        Self {\n            name: \"Arthur\".to_owned(),\n            age: 42,\n        }\n    }\n}\n\nimpl CodeExample {\n    fn samples_in_grid(&mut self, ui: &mut egui::Ui) {\n        // Note: we keep the code narrow so that the example fits on a mobile screen.\n\n        let Self { name, age } = self; // for brevity later on\n\n        show_code(ui, r#\"ui.heading(\"Example\");\"#);\n        ui.heading(\"Example\");\n        ui.end_row();\n\n        show_code(\n            ui,\n            r#\"\n            ui.horizontal(|ui| {\n                ui.label(\"Name\");\n                ui.text_edit_singleline(name);\n            });\"#,\n        );\n        // Putting things on the same line using ui.horizontal:\n        ui.horizontal(|ui| {\n            ui.label(\"Name\");\n            ui.text_edit_singleline(name);\n        });\n        ui.end_row();\n\n        show_code(\n            ui,\n            r#\"\n            ui.add(\n                egui::DragValue::new(age)\n                    .range(0..=120)\n                    .suffix(\" years\"),\n            );\"#,\n        );\n        ui.add(egui::DragValue::new(age).range(0..=120).suffix(\" years\"));\n        ui.end_row();\n\n        show_code(\n            ui,\n            r#\"\n            if ui.button(\"Increment\").clicked() {\n                *age += 1;\n            }\"#,\n        );\n        if ui.button(\"Increment\").clicked() {\n            *age += 1;\n        }\n        ui.end_row();\n\n        #[expect(clippy::literal_string_with_formatting_args)]\n        show_code(ui, r#\"ui.label(format!(\"{name} is {age}\"));\"#);\n        ui.label(format!(\"{name} is {age}\"));\n        ui.end_row();\n    }\n\n    fn code(&mut self, ui: &mut egui::Ui) {\n        show_code(\n            ui,\n            r\"\npub struct CodeExample {\n    name: String,\n    age: u32,\n}\n\nimpl CodeExample {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        // Saves us from writing `&mut self.name` etc\n        let Self { name, age } = self;\",\n        );\n\n        ui.horizontal(|ui| {\n            let font_id = egui::TextStyle::Monospace.resolve(ui.style());\n            let indentation = 2.0 * 4.0 * ui.fonts_mut(|f| f.glyph_width(&font_id, ' '));\n            ui.add_space(indentation);\n\n            egui::Grid::new(\"code_samples\")\n                .striped(true)\n                .num_columns(2)\n                .show(ui, |ui| {\n                    self.samples_in_grid(ui);\n                });\n        });\n\n        crate::rust_view_ui(ui, \"    }\\n}\");\n    }\n}\n\nimpl crate::Demo for CodeExample {\n    fn name(&self) -> &'static str {\n        \"🖮 Code Example\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        use crate::View as _;\n        egui::Window::new(self.name())\n            .open(open)\n            .min_width(375.0)\n            .default_size([390.0, 500.0])\n            .scroll(false)\n            .resizable([true, false]) // resizable so we can shrink if the text edit grows\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl crate::View for CodeExample {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.scope(|ui| {\n            ui.spacing_mut().item_spacing = egui::vec2(8.0, 6.0);\n            self.code(ui);\n        });\n\n        ui.separator();\n\n        crate::rust_view_ui(ui, &format!(\"{self:#?}\"));\n\n        ui.separator();\n\n        let mut theme =\n            egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style());\n        ui.collapsing(\"Theme\", |ui| {\n            theme.ui(ui);\n            theme.store_in_memory(ui.ctx());\n        });\n\n        ui.separator();\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n\nfn show_code(ui: &mut egui::Ui, code: &str) {\n    let code = remove_leading_indentation(code.trim_start_matches('\\n'));\n    crate::rust_view_ui(ui, &code);\n}\n\nfn remove_leading_indentation(code: &str) -> String {\n    fn is_indent(c: &u8) -> bool {\n        matches!(*c, b' ' | b'\\t')\n    }\n\n    let first_line_indent = code.bytes().take_while(is_indent).count();\n\n    let mut out = String::new();\n\n    let mut code = code;\n    while !code.is_empty() {\n        let indent = code.bytes().take_while(is_indent).count();\n        let start = first_line_indent.min(indent);\n        let end = code\n            .find('\\n')\n            .map_or_else(|| code.len(), |endline| endline + 1);\n        out += &code[start..end];\n        code = &code[end..];\n    }\n    out\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/dancing_strings.rs",
    "content": "use egui::{\n    Color32, Pos2, Rect, Ui,\n    containers::{Frame, Window},\n    emath, epaint,\n    epaint::PathStroke,\n    hex_color, lerp, pos2, remap, vec2,\n};\n\n#[derive(Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct DancingStrings {\n    colors: bool,\n}\n\nimpl crate::Demo for DancingStrings {\n    fn name(&self) -> &'static str {\n        \"♫ Dancing Strings\"\n    }\n\n    fn show(&mut self, ui: &mut Ui, open: &mut bool) {\n        use crate::View as _;\n        Window::new(self.name())\n            .open(open)\n            .default_size(vec2(512.0, 256.0))\n            .vscroll(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl crate::View for DancingStrings {\n    fn ui(&mut self, ui: &mut Ui) {\n        let color = if ui.visuals().dark_mode {\n            Color32::from_additive_luminance(196)\n        } else {\n            Color32::from_black_alpha(240)\n        };\n\n        ui.checkbox(&mut self.colors, \"Colored\")\n            .on_hover_text(\"Demonstrates how a path can have varying color across its length.\");\n\n        Frame::canvas(ui.style()).show(ui, |ui| {\n            ui.request_repaint();\n            let time = ui.input(|i| i.time);\n\n            let desired_size = ui.available_width() * vec2(1.0, 0.35);\n            let (_id, rect) = ui.allocate_space(desired_size);\n\n            let to_screen =\n                emath::RectTransform::from_to(Rect::from_x_y_ranges(0.0..=1.0, -1.0..=1.0), rect);\n\n            let mut shapes = vec![];\n\n            for &mode in &[2, 3, 5] {\n                let mode = mode as f64;\n                let n = 120;\n                let speed = 1.5;\n\n                let points: Vec<Pos2> = (0..=n)\n                    .map(|i| {\n                        let t = i as f64 / (n as f64);\n                        let amp = (time * speed * mode).sin() / mode;\n                        let y = amp * (t * std::f64::consts::TAU / 2.0 * mode).sin();\n                        to_screen * pos2(t as f32, y as f32)\n                    })\n                    .collect();\n\n                let thickness = 10.0 / mode as f32;\n                shapes.push(epaint::Shape::line(\n                    points,\n                    if self.colors {\n                        PathStroke::new_uv(thickness, move |rect, p| {\n                            let t = remap(p.x, rect.x_range(), -1.0..=1.0).abs();\n                            let center_color = hex_color!(\"#5BCEFA\");\n                            let outer_color = hex_color!(\"#F5A9B8\");\n\n                            Color32::from_rgb(\n                                lerp(center_color.r() as f32..=outer_color.r() as f32, t) as u8,\n                                lerp(center_color.g() as f32..=outer_color.g() as f32, t) as u8,\n                                lerp(center_color.b() as f32..=outer_color.b() as f32, t) as u8,\n                            )\n                        })\n                    } else {\n                        PathStroke::new(thickness, color)\n                    },\n                ));\n            }\n\n            ui.painter().extend(shapes);\n        });\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/demo_app_windows.rs",
    "content": "use std::collections::BTreeSet;\n\nuse super::About;\nuse crate::Demo;\nuse crate::View as _;\nuse crate::is_mobile;\nuse egui::containers::menu;\nuse egui::style::StyleModifier;\nuse egui::{Modifiers, ScrollArea, Ui};\n// ----------------------------------------------------------------------------\n\nstruct DemoGroup {\n    demos: Vec<Box<dyn Demo>>,\n}\n\nimpl std::ops::Add for DemoGroup {\n    type Output = Self;\n\n    fn add(self, other: Self) -> Self {\n        let mut demos = self.demos;\n        demos.extend(other.demos);\n        Self { demos }\n    }\n}\n\nimpl DemoGroup {\n    pub fn new(demos: Vec<Box<dyn Demo>>) -> Self {\n        Self { demos }\n    }\n\n    pub fn checkboxes(&mut self, ui: &mut Ui, open: &mut BTreeSet<String>) {\n        let Self { demos } = self;\n        for demo in demos {\n            if demo.is_enabled(ui.ctx()) {\n                let mut is_open = open.contains(demo.name());\n                ui.toggle_value(&mut is_open, demo.name());\n                set_open(open, demo.name(), is_open);\n            }\n        }\n    }\n\n    pub fn windows(&mut self, ui: &mut Ui, open: &mut BTreeSet<String>) {\n        let Self { demos } = self;\n        for demo in demos {\n            let mut is_open = open.contains(demo.name());\n            demo.show(ui, &mut is_open);\n            set_open(open, demo.name(), is_open);\n        }\n    }\n}\n\nfn set_open(open: &mut BTreeSet<String>, key: &'static str, is_open: bool) {\n    if is_open {\n        if !open.contains(key) {\n            open.insert(key.to_owned());\n        }\n    } else {\n        open.remove(key);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\npub struct DemoGroups {\n    about: About,\n    demos: DemoGroup,\n    tests: DemoGroup,\n}\n\nimpl Default for DemoGroups {\n    fn default() -> Self {\n        Self {\n            about: About::default(),\n            demos: DemoGroup::new(vec![\n                Box::<super::paint_bezier::PaintBezier>::default(),\n                Box::<super::code_editor::CodeEditor>::default(),\n                Box::<super::code_example::CodeExample>::default(),\n                Box::<super::dancing_strings::DancingStrings>::default(),\n                Box::<super::drag_and_drop::DragAndDropDemo>::default(),\n                Box::<super::extra_viewport::ExtraViewport>::default(),\n                Box::<super::font_book::FontBook>::default(),\n                Box::<super::frame_demo::FrameDemo>::default(),\n                Box::<super::highlighting::Highlighting>::default(),\n                Box::<super::interactive_container::InteractiveContainerDemo>::default(),\n                Box::<super::MiscDemoWindow>::default(),\n                Box::<super::modals::Modals>::default(),\n                Box::<super::multi_touch::MultiTouch>::default(),\n                Box::<super::painting::Painting>::default(),\n                Box::<super::panels::Panels>::default(),\n                Box::<super::popups::PopupsDemo>::default(),\n                Box::<super::scene::SceneDemo>::default(),\n                Box::<super::screenshot::Screenshot>::default(),\n                Box::<super::scrolling::Scrolling>::default(),\n                Box::<super::sliders::Sliders>::default(),\n                Box::<super::strip_demo::StripDemo>::default(),\n                Box::<super::table_demo::TableDemo>::default(),\n                Box::<super::text_edit::TextEditDemo>::default(),\n                Box::<super::text_layout::TextLayoutDemo>::default(),\n                Box::<super::tooltips::Tooltips>::default(),\n                Box::<super::undo_redo::UndoRedoDemo>::default(),\n                Box::<super::widget_gallery::WidgetGallery>::default(),\n                Box::<super::window_options::WindowOptions>::default(),\n            ]),\n            tests: DemoGroup::new(vec![\n                Box::<super::tests::ClipboardTest>::default(),\n                Box::<super::tests::CursorTest>::default(),\n                Box::<super::tests::GridTest>::default(),\n                Box::<super::tests::IdTest>::default(),\n                Box::<super::tests::InputEventHistory>::default(),\n                Box::<super::tests::InputTest>::default(),\n                Box::<super::tests::LayoutTest>::default(),\n                Box::<super::tests::ManualLayoutTest>::default(),\n                Box::<super::tests::SvgTest>::default(),\n                Box::<super::tests::TessellationTest>::default(),\n                Box::<super::tests::WindowResizeTest>::default(),\n            ]),\n        }\n    }\n}\n\nimpl DemoGroups {\n    pub fn checkboxes(&mut self, ui: &mut Ui, open: &mut BTreeSet<String>) {\n        let Self {\n            about,\n            demos,\n            tests,\n        } = self;\n\n        {\n            let mut is_open = open.contains(about.name());\n            ui.toggle_value(&mut is_open, about.name());\n            set_open(open, about.name(), is_open);\n        }\n        ui.separator();\n        demos.checkboxes(ui, open);\n        ui.separator();\n        tests.checkboxes(ui, open);\n    }\n\n    pub fn windows(&mut self, ui: &mut Ui, open: &mut BTreeSet<String>) {\n        let Self {\n            about,\n            demos,\n            tests,\n        } = self;\n        {\n            let mut is_open = open.contains(about.name());\n            about.show(ui, &mut is_open);\n            set_open(open, about.name(), is_open);\n        }\n        demos.windows(ui, open);\n        tests.windows(ui, open);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A menu bar in which you can select different demo windows to show.\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct DemoWindows {\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    groups: DemoGroups,\n\n    open: BTreeSet<String>,\n}\n\nimpl Default for DemoWindows {\n    fn default() -> Self {\n        let mut open = BTreeSet::new();\n\n        // Explains egui very well\n        set_open(&mut open, About::default().name(), true);\n\n        // Explains egui very well\n        set_open(\n            &mut open,\n            super::code_example::CodeExample::default().name(),\n            true,\n        );\n\n        // Shows off the features\n        set_open(\n            &mut open,\n            super::widget_gallery::WidgetGallery::default().name(),\n            true,\n        );\n\n        Self {\n            groups: Default::default(),\n            open,\n        }\n    }\n}\n\nimpl DemoWindows {\n    /// Show the app ui (menu bar and windows).\n    pub fn ui(&mut self, ui: &mut egui::Ui) {\n        if is_mobile(ui.ctx()) {\n            self.mobile_ui(ui);\n        } else {\n            self.desktop_ui(ui);\n        }\n    }\n\n    fn about_is_open(&self) -> bool {\n        self.open.contains(About::default().name())\n    }\n\n    fn mobile_ui(&mut self, ui: &mut egui::Ui) {\n        if self.about_is_open() {\n            let mut close = false;\n\n            egui::ScrollArea::vertical()\n                .auto_shrink(false)\n                .show(ui, |ui| {\n                    self.groups.about.ui(ui);\n                    ui.add_space(12.0);\n                    ui.vertical_centered_justified(|ui| {\n                        if ui\n                            .button(egui::RichText::new(\"Continue to the demo!\").size(20.0))\n                            .clicked()\n                        {\n                            close = true;\n                        }\n                    });\n                });\n\n            if close {\n                set_open(&mut self.open, About::default().name(), false);\n            }\n        } else {\n            self.mobile_top_bar(ui);\n            self.groups.windows(ui, &mut self.open);\n        }\n    }\n\n    fn mobile_top_bar(&mut self, ui: &mut egui::Ui) {\n        egui::Panel::top(\"menu_bar\").show_inside(ui, |ui| {\n            menu::MenuBar::new()\n                .config(menu::MenuConfig::new().style(StyleModifier::default()))\n                .ui(ui, |ui| {\n                    let font_size = 16.5;\n\n                    ui.menu_button(egui::RichText::new(\"⏷ demos\").size(font_size), |ui| {\n                        self.demo_list_ui(ui);\n                    });\n\n                    ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {\n                        use egui::special_emojis::GITHUB;\n                        ui.hyperlink_to(\n                            egui::RichText::new(\"🦋\").size(font_size),\n                            \"https://bsky.app/profile/ernerfeldt.bsky.social\",\n                        );\n                        ui.hyperlink_to(\n                            egui::RichText::new(GITHUB).size(font_size),\n                            \"https://github.com/emilk/egui\",\n                        );\n                    });\n                });\n        });\n    }\n\n    fn desktop_ui(&mut self, ui: &mut egui::Ui) {\n        egui::Panel::right(\"egui_demo_panel\")\n            .resizable(false)\n            .default_size(160.0)\n            .min_size(160.0)\n            .show_inside(ui, |ui| {\n                ui.add_space(4.0);\n                ui.vertical_centered(|ui| {\n                    ui.heading(\"✒ egui demos\");\n                });\n\n                ui.separator();\n\n                use egui::special_emojis::GITHUB;\n                ui.hyperlink_to(\n                    format!(\"{GITHUB} egui on GitHub\"),\n                    \"https://github.com/emilk/egui\",\n                );\n                ui.hyperlink_to(\n                    \"@ernerfeldt.bsky.social\",\n                    \"https://bsky.app/profile/ernerfeldt.bsky.social\",\n                );\n\n                ui.separator();\n\n                self.demo_list_ui(ui);\n            });\n\n        egui::Panel::top(\"menu_bar\").show_inside(ui, |ui| {\n            menu::MenuBar::new().ui(ui, |ui| {\n                file_menu_button(ui);\n            });\n        });\n\n        self.groups.windows(ui, &mut self.open);\n    }\n\n    fn demo_list_ui(&mut self, ui: &mut egui::Ui) {\n        ScrollArea::vertical().show(ui, |ui| {\n            ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| {\n                self.groups.checkboxes(ui, &mut self.open);\n                ui.separator();\n                if ui.button(\"Organize windows\").clicked() {\n                    ui.memory_mut(|mem| mem.reset_areas());\n                }\n            });\n        });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nfn file_menu_button(ui: &mut Ui) {\n    let organize_shortcut =\n        egui::KeyboardShortcut::new(Modifiers::CTRL | Modifiers::SHIFT, egui::Key::O);\n    let reset_shortcut =\n        egui::KeyboardShortcut::new(Modifiers::CTRL | Modifiers::SHIFT, egui::Key::R);\n\n    // NOTE: we must check the shortcuts OUTSIDE of the actual \"File\" menu,\n    // or else they would only be checked if the \"File\" menu was actually open!\n\n    if ui.input_mut(|i| i.consume_shortcut(&organize_shortcut)) {\n        ui.memory_mut(|mem| mem.reset_areas());\n    }\n\n    if ui.input_mut(|i| i.consume_shortcut(&reset_shortcut)) {\n        ui.memory_mut(|mem| *mem = Default::default());\n    }\n\n    ui.menu_button(\"File\", |ui| {\n        ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);\n\n        // On the web the browser controls the zoom\n        #[cfg(not(target_arch = \"wasm32\"))]\n        {\n            egui::gui_zoom::zoom_menu_buttons(ui);\n            ui.weak(format!(\n                \"Current zoom: {:.0}%\",\n                100.0 * ui.ctx().zoom_factor()\n            ))\n            .on_hover_text(\"The UI zoom level, on top of the operating system's default value\");\n            ui.separator();\n        }\n\n        if ui\n            .add(\n                egui::Button::new(\"Organize Windows\")\n                    .shortcut_text(ui.ctx().format_shortcut(&organize_shortcut)),\n            )\n            .clicked()\n        {\n            ui.memory_mut(|mem| mem.reset_areas());\n        }\n\n        if ui\n            .add(\n                egui::Button::new(\"Reset egui memory\")\n                    .shortcut_text(ui.ctx().format_shortcut(&reset_shortcut)),\n            )\n            .on_hover_text(\"Forget scroll, positions, sizes etc\")\n            .clicked()\n        {\n            ui.memory_mut(|mem| *mem = Default::default());\n        }\n    });\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{Demo as _, demo::demo_app_windows::DemoGroups};\n\n    use egui::Vec2;\n    use egui_kittest::{HarnessBuilder, OsThreshold, SnapshotOptions, SnapshotResults};\n\n    #[test]\n    fn demos_should_match_snapshot() {\n        let DemoGroups {\n            demos,\n            tests,\n            about: _,\n        } = DemoGroups::default();\n        let demos = demos + tests;\n\n        let mut results = SnapshotResults::new();\n\n        for mut demo in demos.demos {\n            // Widget Gallery needs to be customized (to set a specific date) and has its own test\n            if demo.name() == crate::WidgetGallery::default().name() {\n                continue;\n            }\n\n            let name = remove_leading_emoji(demo.name());\n\n            let mut harness = HarnessBuilder::default()\n                .with_size(Vec2::splat(2048.0))\n                .build_ui(|ui| {\n                    egui_extras::install_image_loaders(ui);\n                    demo.show(ui, &mut true);\n                });\n\n            // Resize to fit every window, plus some margin:\n            harness.set_size(harness.ctx.globally_used_rect().max.to_vec2() + Vec2::splat(16.0));\n\n            // Run the app for some more frames...\n            harness.run_ok();\n\n            let mut options = SnapshotOptions::default();\n\n            if name == \"Bézier Curve\" {\n                // The Bézier Curve demo needs a threshold of 2.1 to pass on linux:\n                options = options.threshold(OsThreshold::new(0.0).linux(2.1));\n            }\n\n            results.add(harness.try_snapshot_options(format!(\"demos/{name}\"), &options));\n        }\n    }\n\n    fn remove_leading_emoji(full_name: &str) -> &str {\n        if let Some((start, name)) = full_name.split_once(' ')\n            && start.len() <= 4\n            && start.bytes().next().is_some_and(|byte| byte >= 128)\n        {\n            return name;\n        }\n        full_name\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/drag_and_drop.rs",
    "content": "use egui::{Color32, Frame, Id, Ui, Window, vec2};\n\n#[derive(Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct DragAndDropDemo {\n    /// columns with items\n    columns: Vec<Vec<String>>,\n}\n\nimpl Default for DragAndDropDemo {\n    fn default() -> Self {\n        Self {\n            columns: vec![\n                vec![\"Item A\", \"Item B\", \"Item C\", \"Item D\"],\n                vec![\"Item E\", \"Item F\", \"Item G\"],\n                vec![\"Item H\", \"Item I\", \"Item J\", \"Item K\"],\n            ]\n            .into_iter()\n            .map(|v| v.into_iter().map(ToString::to_string).collect())\n            .collect(),\n        }\n    }\n}\n\nimpl crate::Demo for DragAndDropDemo {\n    fn name(&self) -> &'static str {\n        \"✋ Drag and Drop\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        use crate::View as _;\n        Window::new(self.name())\n            .open(open)\n            .default_size(vec2(256.0, 256.0))\n            .vscroll(false)\n            .resizable(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\n/// What is being dragged.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\nstruct Location {\n    col: usize,\n    row: usize,\n}\n\nimpl crate::View for DragAndDropDemo {\n    fn ui(&mut self, ui: &mut Ui) {\n        ui.label(\"This is a simple example of drag-and-drop in egui.\");\n        ui.label(\"Drag items between columns.\");\n\n        // If there is a drop, store the location of the item being dragged, and the destination for the drop.\n        let mut from = None;\n        let mut to = None;\n\n        ui.columns(self.columns.len(), |uis| {\n            for (col_idx, column) in self.columns.clone().into_iter().enumerate() {\n                let ui = &mut uis[col_idx];\n\n                let frame = Frame::default().inner_margin(4.0);\n\n                let (_, dropped_payload) = ui.dnd_drop_zone::<Location, ()>(frame, |ui| {\n                    ui.set_min_size(vec2(64.0, 100.0));\n                    for (row_idx, item) in column.iter().enumerate() {\n                        let item_id = Id::new((\"my_drag_and_drop_demo\", col_idx, row_idx));\n                        let item_location = Location {\n                            col: col_idx,\n                            row: row_idx,\n                        };\n                        let response = ui\n                            .dnd_drag_source(item_id, item_location, |ui| {\n                                ui.label(item);\n                            })\n                            .response;\n\n                        // Detect drops onto this item:\n                        if let (Some(pointer), Some(hovered_payload)) = (\n                            ui.input(|i| i.pointer.interact_pos()),\n                            response.dnd_hover_payload::<Location>(),\n                        ) {\n                            let rect = response.rect;\n\n                            // Preview insertion:\n                            let stroke = egui::Stroke::new(1.0, Color32::WHITE);\n                            let insert_row_idx = if *hovered_payload == item_location {\n                                // We are dragged onto ourselves\n                                ui.painter().hline(rect.x_range(), rect.center().y, stroke);\n                                row_idx\n                            } else if pointer.y < rect.center().y {\n                                // Above us\n                                ui.painter().hline(rect.x_range(), rect.top(), stroke);\n                                row_idx\n                            } else {\n                                // Below us\n                                ui.painter().hline(rect.x_range(), rect.bottom(), stroke);\n                                row_idx + 1\n                            };\n\n                            if let Some(dragged_payload) = response.dnd_release_payload() {\n                                // The user dropped onto this item.\n                                from = Some(dragged_payload);\n                                to = Some(Location {\n                                    col: col_idx,\n                                    row: insert_row_idx,\n                                });\n                            }\n                        }\n                    }\n                });\n\n                if let Some(dragged_payload) = dropped_payload {\n                    // The user dropped onto the column, but not on any one item.\n                    from = Some(dragged_payload);\n                    to = Some(Location {\n                        col: col_idx,\n                        row: usize::MAX, // Inset last\n                    });\n                }\n            }\n        });\n\n        if let (Some(from), Some(mut to)) = (from, to) {\n            if from.col == to.col {\n                // Dragging within the same column.\n                // Adjust row index if we are re-ordering:\n                to.row -= (from.row < to.row) as usize;\n            }\n\n            let item = self.columns[from.col].remove(from.row);\n\n            let column = &mut self.columns[to.col];\n            to.row = to.row.min(column.len());\n            column.insert(to.row, item);\n        }\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/extra_viewport.rs",
    "content": "#[derive(Default)]\npub struct ExtraViewport {}\n\nimpl crate::Demo for ExtraViewport {\n    fn is_enabled(&self, ctx: &egui::Context) -> bool {\n        !ctx.embed_viewports()\n    }\n\n    fn name(&self) -> &'static str {\n        \"🗖 Extra Viewport\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        if !*open {\n            return;\n        }\n\n        let id = egui::Id::new(self.name());\n\n        ui.show_viewport_immediate(\n            egui::ViewportId(id),\n            egui::ViewportBuilder::default()\n                .with_title(self.name())\n                .with_inner_size([400.0, 512.0]),\n            |ui, class| {\n                if class == egui::ViewportClass::EmbeddedWindow {\n                    // Not a real viewport\n                    ui.label(\"This egui integration does not support multiple viewports\");\n                } else {\n                    egui::CentralPanel::default().show_inside(ui, |ui| {\n                        viewport_content(ui, open);\n                    });\n                }\n            },\n        );\n    }\n}\n\nfn viewport_content(ui: &mut egui::Ui, open: &mut bool) {\n    ui.label(\"egui and eframe supports having multiple native windows like this, which egui calls 'viewports'.\");\n\n    ui.label(format!(\n        \"This viewport has id: {:?}, child of viewport {:?}\",\n        ui.viewport_id(),\n        ui.parent_viewport_id()\n    ));\n\n    ui.label(\"Here you can see all the open viewports:\");\n\n    egui::ScrollArea::vertical().show(ui, |ui| {\n        let viewports = ui.input(|i| i.raw.viewports.clone());\n        let ordered_viewports = viewports\n            .iter()\n            .map(|(id, viewport)| (*id, viewport.clone()))\n            .collect::<egui::OrderedViewportIdMap<_>>();\n        for (id, viewport) in ordered_viewports {\n            ui.group(|ui| {\n                ui.label(format!(\"viewport {id:?}\"));\n                ui.push_id(id, |ui| {\n                    viewport.ui(ui);\n                });\n            });\n        }\n    });\n\n    if ui.input(|i| i.viewport().close_requested()) {\n        *open = false;\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/font_book.rs",
    "content": "use std::collections::BTreeMap;\n\nstruct GlyphInfo {\n    name: String,\n\n    // What fonts it is available in\n    fonts: Vec<String>,\n}\n\npub struct FontBook {\n    filter: String,\n    font_id: egui::FontId,\n    available_glyphs: BTreeMap<egui::FontFamily, BTreeMap<char, GlyphInfo>>,\n}\n\nimpl Default for FontBook {\n    fn default() -> Self {\n        Self {\n            filter: Default::default(),\n            font_id: egui::FontId::proportional(18.0),\n            available_glyphs: Default::default(),\n        }\n    }\n}\n\nimpl crate::Demo for FontBook {\n    fn name(&self) -> &'static str {\n        \"🔤 Font Book\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for FontBook {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n\n        ui.label(format!(\n            \"The selected font supports {} characters.\",\n            self.available_glyphs\n                .get(&self.font_id.family)\n                .map(|map| map.len())\n                .unwrap_or_default()\n        ));\n\n        ui.horizontal_wrapped(|ui| {\n            ui.spacing_mut().item_spacing.x = 0.0;\n            ui.label(\"You can add more characters by installing additional fonts with \");\n            ui.add(egui::Hyperlink::from_label_and_url(\n                egui::RichText::new(\"Context::set_fonts\").text_style(egui::TextStyle::Monospace),\n                \"https://docs.rs/egui/latest/egui/struct.Context.html#method.set_fonts\",\n            ));\n            ui.label(\".\");\n        });\n\n        ui.separator();\n\n        egui::introspection::font_id_ui(ui, &mut self.font_id);\n\n        ui.horizontal(|ui| {\n            ui.label(\"Filter:\");\n            ui.add(egui::TextEdit::singleline(&mut self.filter).desired_width(120.0));\n            self.filter = self.filter.to_lowercase();\n            if ui.button(\"ｘ\").clicked() {\n                self.filter.clear();\n            }\n        });\n\n        let filter = &self.filter;\n        let available_glyphs = self\n            .available_glyphs\n            .entry(self.font_id.family.clone())\n            .or_insert_with(|| available_characters(ui, &self.font_id.family));\n\n        ui.separator();\n\n        egui::ScrollArea::vertical().show(ui, |ui| {\n            ui.horizontal_wrapped(|ui| {\n                ui.spacing_mut().item_spacing = egui::Vec2::splat(2.0);\n\n                for (&chr, glyph_info) in available_glyphs.iter() {\n                    if filter.is_empty()\n                        || glyph_info.name.contains(filter)\n                        || *filter == chr.to_string()\n                    {\n                        let button = egui::Button::new(\n                            egui::RichText::new(chr.to_string()).font(self.font_id.clone()),\n                        )\n                        .frame(false);\n\n                        let tooltip_ui = |ui: &mut egui::Ui| {\n                            let font_id = self.font_id.clone();\n\n                            char_info_ui(ui, chr, glyph_info, font_id);\n                        };\n\n                        if ui.add(button).on_hover_ui(tooltip_ui).clicked() {\n                            ui.copy_text(chr.to_string());\n                        }\n                    }\n                }\n            });\n        });\n    }\n}\n\nfn char_info_ui(ui: &mut egui::Ui, chr: char, glyph_info: &GlyphInfo, font_id: egui::FontId) {\n    let resp = ui.label(egui::RichText::new(chr.to_string()).font(font_id));\n\n    egui::Grid::new(\"char_info\")\n        .num_columns(2)\n        .striped(true)\n        .show(ui, |ui| {\n            ui.label(\"Name\");\n            ui.label(glyph_info.name.clone());\n            ui.end_row();\n\n            ui.label(\"Hex\");\n            ui.label(format!(\"{:X}\", chr as u32));\n            ui.end_row();\n\n            ui.label(\"Width\");\n            ui.label(format!(\"{:.1} pts\", resp.rect.width()));\n            ui.end_row();\n\n            ui.label(\"Fonts\");\n            ui.label(\n                format!(\"{:?}\", glyph_info.fonts)\n                    .trim_start_matches('[')\n                    .trim_end_matches(']'),\n            );\n            ui.end_row();\n        });\n}\n\nfn available_characters(ui: &egui::Ui, family: &egui::FontFamily) -> BTreeMap<char, GlyphInfo> {\n    ui.fonts_mut(|f| {\n        f.fonts\n            .font(family)\n            .characters()\n            .iter()\n            .filter(|(chr, _fonts)| !chr.is_whitespace() && !chr.is_ascii_control())\n            .map(|(chr, fonts)| {\n                (\n                    *chr,\n                    GlyphInfo {\n                        name: char_name(*chr),\n                        fonts: fonts.clone(),\n                    },\n                )\n            })\n            .collect()\n    })\n}\n\nfn char_name(chr: char) -> String {\n    special_char_name(chr)\n        .map(|s| s.to_owned())\n        .or_else(|| unicode_names2::name(chr).map(|name| name.to_string().to_lowercase()))\n        .unwrap_or_else(|| \"unknown\".to_owned())\n}\n\nfn special_char_name(chr: char) -> Option<&'static str> {\n    #[expect(clippy::match_same_arms)] // many \"flag\"\n    match chr {\n        // Special private-use-area extensions found in `emoji-icon-font.ttf`:\n        // Private use area extensions:\n        '\\u{FE4E5}' => Some(\"flag japan\"),\n        '\\u{FE4E6}' => Some(\"flag usa\"),\n        '\\u{FE4E7}' => Some(\"flag\"),\n        '\\u{FE4E8}' => Some(\"flag\"),\n        '\\u{FE4E9}' => Some(\"flag\"),\n        '\\u{FE4EA}' => Some(\"flag great britain\"),\n        '\\u{FE4EB}' => Some(\"flag\"),\n        '\\u{FE4EC}' => Some(\"flag\"),\n        '\\u{FE4ED}' => Some(\"flag\"),\n        '\\u{FE4EE}' => Some(\"flag south korea\"),\n        '\\u{FE82C}' => Some(\"number sign in square\"),\n        '\\u{FE82E}' => Some(\"digit one in square\"),\n        '\\u{FE82F}' => Some(\"digit two in square\"),\n        '\\u{FE830}' => Some(\"digit three in square\"),\n        '\\u{FE831}' => Some(\"digit four in square\"),\n        '\\u{FE832}' => Some(\"digit five in square\"),\n        '\\u{FE833}' => Some(\"digit six in square\"),\n        '\\u{FE834}' => Some(\"digit seven in square\"),\n        '\\u{FE835}' => Some(\"digit eight in square\"),\n        '\\u{FE836}' => Some(\"digit nine in square\"),\n        '\\u{FE837}' => Some(\"digit zero in square\"),\n\n        // Special private-use-area extensions found in `emoji-icon-font.ttf`:\n        // Web services / operating systems / browsers\n        '\\u{E600}' => Some(\"web-dribbble\"),\n        '\\u{E601}' => Some(\"web-stackoverflow\"),\n        '\\u{E602}' => Some(\"web-vimeo\"),\n        '\\u{E604}' => Some(\"web-facebook\"),\n        '\\u{E605}' => Some(\"web-googleplus\"),\n        '\\u{E606}' => Some(\"web-pinterest\"),\n        '\\u{E607}' => Some(\"web-tumblr\"),\n        '\\u{E608}' => Some(\"web-linkedin\"),\n        '\\u{E60A}' => Some(\"web-stumbleupon\"),\n        '\\u{E60B}' => Some(\"web-lastfm\"),\n        '\\u{E60C}' => Some(\"web-rdio\"),\n        '\\u{E60D}' => Some(\"web-spotify\"),\n        '\\u{E60E}' => Some(\"web-qq\"),\n        '\\u{E60F}' => Some(\"web-instagram\"),\n        '\\u{E610}' => Some(\"web-dropbox\"),\n        '\\u{E611}' => Some(\"web-evernote\"),\n        '\\u{E612}' => Some(\"web-flattr\"),\n        '\\u{E613}' => Some(\"web-skype\"),\n        '\\u{E614}' => Some(\"web-renren\"),\n        '\\u{E615}' => Some(\"web-sina-weibo\"),\n        '\\u{E616}' => Some(\"web-paypal\"),\n        '\\u{E617}' => Some(\"web-picasa\"),\n        '\\u{E618}' => Some(\"os-android\"),\n        '\\u{E619}' => Some(\"web-mixi\"),\n        '\\u{E61A}' => Some(\"web-behance\"),\n        '\\u{E61B}' => Some(\"web-circles\"),\n        '\\u{E61C}' => Some(\"web-vk\"),\n        '\\u{E61D}' => Some(\"web-smashing\"),\n        '\\u{E61E}' => Some(\"web-forrst\"),\n        '\\u{E61F}' => Some(\"os-windows\"),\n        '\\u{E620}' => Some(\"web-flickr\"),\n        '\\u{E621}' => Some(\"web-picassa\"),\n        '\\u{E622}' => Some(\"web-deviantart\"),\n        '\\u{E623}' => Some(\"web-steam\"),\n        '\\u{E624}' => Some(\"web-github\"),\n        '\\u{E625}' => Some(\"web-git\"),\n        '\\u{E626}' => Some(\"web-blogger\"),\n        '\\u{E627}' => Some(\"web-soundcloud\"),\n        '\\u{E628}' => Some(\"web-reddit\"),\n        '\\u{E629}' => Some(\"web-delicious\"),\n        '\\u{E62A}' => Some(\"browser-chrome\"),\n        '\\u{E62B}' => Some(\"browser-firefox\"),\n        '\\u{E62C}' => Some(\"browser-ie\"),\n        '\\u{E62D}' => Some(\"browser-opera\"),\n        '\\u{E62E}' => Some(\"browser-safari\"),\n        '\\u{E62F}' => Some(\"web-google-drive\"),\n        '\\u{E630}' => Some(\"web-wordpress\"),\n        '\\u{E631}' => Some(\"web-joomla\"),\n        '\\u{E632}' => Some(\"lastfm\"),\n        '\\u{E633}' => Some(\"web-foursquare\"),\n        '\\u{E634}' => Some(\"web-yelp\"),\n        '\\u{E635}' => Some(\"web-drupal\"),\n        '\\u{E636}' => Some(\"youtube\"),\n        '\\u{F189}' => Some(\"vk\"),\n        '\\u{F1A6}' => Some(\"digg\"),\n        '\\u{F1CA}' => Some(\"web-vine\"),\n        '\\u{F8FF}' => Some(\"os-apple\"),\n\n        // Special private-use-area extensions found in `Ubuntu-Light.ttf`\n        '\\u{F000}' => Some(\"uniF000\"),\n        '\\u{F001}' => Some(\"fi\"),\n        '\\u{F002}' => Some(\"fl\"),\n        '\\u{F506}' => Some(\"one seventh\"),\n        '\\u{F507}' => Some(\"two sevenths\"),\n        '\\u{F508}' => Some(\"three sevenths\"),\n        '\\u{F509}' => Some(\"four sevenths\"),\n        '\\u{F50A}' => Some(\"five sevenths\"),\n        '\\u{F50B}' => Some(\"six sevenths\"),\n        '\\u{F50C}' => Some(\"one ninth\"),\n        '\\u{F50D}' => Some(\"two ninths\"),\n        '\\u{F50E}' => Some(\"four ninths\"),\n        '\\u{F50F}' => Some(\"five ninths\"),\n        '\\u{F510}' => Some(\"seven ninths\"),\n        '\\u{F511}' => Some(\"eight ninths\"),\n        '\\u{F800}' => Some(\"zero.alt\"),\n        '\\u{F801}' => Some(\"one.alt\"),\n        '\\u{F802}' => Some(\"two.alt\"),\n        '\\u{F803}' => Some(\"three.alt\"),\n        '\\u{F804}' => Some(\"four.alt\"),\n        '\\u{F805}' => Some(\"five.alt\"),\n        '\\u{F806}' => Some(\"six.alt\"),\n        '\\u{F807}' => Some(\"seven.alt\"),\n        '\\u{F808}' => Some(\"eight.alt\"),\n        '\\u{F809}' => Some(\"nine.alt\"),\n        '\\u{F80A}' => Some(\"zero.sups\"),\n        '\\u{F80B}' => Some(\"one.sups\"),\n        '\\u{F80C}' => Some(\"two.sups\"),\n        '\\u{F80D}' => Some(\"three.sups\"),\n        '\\u{F80E}' => Some(\"four.sups\"),\n        '\\u{F80F}' => Some(\"five.sups\"),\n        '\\u{F810}' => Some(\"six.sups\"),\n        '\\u{F811}' => Some(\"seven.sups\"),\n        '\\u{F812}' => Some(\"eight.sups\"),\n        '\\u{F813}' => Some(\"nine.sups\"),\n        '\\u{F814}' => Some(\"zero.sinf\"),\n        '\\u{F815}' => Some(\"one.sinf\"),\n        '\\u{F816}' => Some(\"two.sinf\"),\n        '\\u{F817}' => Some(\"three.sinf\"),\n        '\\u{F818}' => Some(\"four.sinf\"),\n        '\\u{F819}' => Some(\"five.sinf\"),\n        '\\u{F81A}' => Some(\"six.sinf\"),\n        '\\u{F81B}' => Some(\"seven.sinf\"),\n        '\\u{F81C}' => Some(\"eight.sinf\"),\n        '\\u{F81D}' => Some(\"nine.sinf\"),\n\n        _ => None,\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/frame_demo.rs",
    "content": "/// Shows off a table with dynamic layout\n#[derive(PartialEq)]\npub struct FrameDemo {\n    frame: egui::Frame,\n}\n\nimpl Default for FrameDemo {\n    fn default() -> Self {\n        Self {\n            frame: egui::Frame::new()\n                .inner_margin(12)\n                .outer_margin(24)\n                .corner_radius(14)\n                .shadow(egui::Shadow {\n                    offset: [8, 12],\n                    blur: 16,\n                    spread: 0,\n                    color: egui::Color32::from_black_alpha(180),\n                })\n                .fill(egui::Color32::from_rgba_unmultiplied(97, 0, 255, 128))\n                .stroke(egui::Stroke::new(1.0, egui::Color32::GRAY)),\n        }\n    }\n}\n\nimpl crate::Demo for FrameDemo {\n    fn name(&self) -> &'static str {\n        \"▣ Frame\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for FrameDemo {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.horizontal(|ui| {\n            ui.vertical(|ui| {\n                ui.add(&mut self.frame);\n\n                ui.add_space(8.0);\n                ui.set_max_width(ui.min_size().x);\n                ui.vertical_centered(|ui| egui::reset_button(ui, self, \"Reset\"));\n            });\n\n            ui.separator();\n\n            ui.vertical(|ui| {\n                // We want to paint a background around the outer margin of the demonstration frame, so we use another frame around it:\n                egui::Frame::default()\n                    .stroke(ui.visuals().widgets.noninteractive.bg_stroke)\n                    .corner_radius(ui.visuals().widgets.noninteractive.corner_radius)\n                    .show(ui, |ui| {\n                        self.frame.show(ui, |ui| {\n                            ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);\n                            ui.label(egui::RichText::new(\"Content\").color(egui::Color32::WHITE));\n                        });\n                    });\n            });\n        });\n\n        ui.set_max_width(ui.min_size().x);\n        ui.separator();\n        ui.vertical_centered(|ui| ui.add(crate::egui_github_link_file!()));\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/highlighting.rs",
    "content": "#[derive(Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Highlighting {}\n\nimpl crate::Demo for Highlighting {\n    fn name(&self) -> &'static str {\n        \"✨ Highlighting\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .default_width(320.0)\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for Highlighting {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n\n        ui.label(\"This demo demonstrates highlighting a widget.\");\n        ui.add_space(4.0);\n        let label_response = ui.label(\"Hover me to highlight the button!\");\n        ui.add_space(4.0);\n        let mut button_response = ui.button(\"Hover the button to highlight the label!\");\n\n        if label_response.hovered() {\n            button_response = button_response.highlight();\n        }\n        if button_response.hovered() {\n            label_response.highlight();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/interactive_container.rs",
    "content": "use egui::{Frame, Label, RichText, Sense, UiBuilder, Widget as _};\n\n/// Showcase [`egui::Ui::response`].\n#[derive(PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct InteractiveContainerDemo {\n    count: usize,\n}\n\nimpl crate::Demo for InteractiveContainerDemo {\n    fn name(&self) -> &'static str {\n        \"\\u{20E3} Interactive Container\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(false)\n            .default_width(250.0)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for InteractiveContainerDemo {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n\n        ui.horizontal_wrapped(|ui| {\n            ui.spacing_mut().item_spacing.x = 0.0;\n            ui.label(\"This demo showcases how to use \");\n            ui.code(\"Ui::response\");\n            ui.label(\" to create interactive container widgets that may contain other widgets.\");\n        });\n\n        let response = ui\n            .scope_builder(\n                UiBuilder::new()\n                    .id_salt(\"interactive_container\")\n                    .sense(Sense::click()),\n                |ui| {\n                    let response = ui.response();\n                    let visuals = ui.style().interact(&response);\n                    let text_color = visuals.text_color();\n\n                    Frame::canvas(ui.style())\n                        .fill(visuals.bg_fill.gamma_multiply(0.3))\n                        .stroke(visuals.bg_stroke)\n                        .inner_margin(ui.spacing().menu_margin)\n                        .show(ui, |ui| {\n                            ui.set_width(ui.available_width());\n\n                            ui.add_space(32.0);\n                            ui.vertical_centered(|ui| {\n                                Label::new(\n                                    RichText::new(format!(\"{}\", self.count))\n                                        .color(text_color)\n                                        .size(32.0),\n                                )\n                                .selectable(false)\n                                .ui(ui);\n                            });\n                            ui.add_space(32.0);\n\n                            ui.horizontal(|ui| {\n                                if ui.button(\"Reset\").clicked() {\n                                    self.count = 0;\n                                }\n                                if ui.button(\"+ 100\").clicked() {\n                                    self.count += 100;\n                                }\n                            });\n                        });\n                },\n            )\n            .response;\n\n        if response.clicked() {\n            self.count += 1;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/misc_demo_window.rs",
    "content": "use std::sync::Arc;\n\nuse super::{Demo, View};\n\nuse egui::{\n    Align, Align2, Checkbox, CollapsingHeader, Color32, ComboBox, FontId, Resize, RichText, Sense,\n    Slider, Stroke, TextFormat, TextStyle, Ui, Vec2, Window, vec2,\n};\n\n/// Showcase some ui code\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct MiscDemoWindow {\n    num_columns: usize,\n\n    widgets: Widgets,\n    colors: ColorWidgets,\n    custom_collapsing_header: CustomCollapsingHeader,\n    tree: Tree,\n    box_painting: BoxPainting,\n    text_rotation: TextRotation,\n\n    dummy_bool: bool,\n    dummy_usize: usize,\n    checklist: [bool; 3],\n}\n\nimpl Default for MiscDemoWindow {\n    fn default() -> Self {\n        Self {\n            num_columns: 2,\n\n            widgets: Default::default(),\n            colors: Default::default(),\n            custom_collapsing_header: Default::default(),\n            tree: Tree::demo(),\n            box_painting: Default::default(),\n            text_rotation: Default::default(),\n\n            dummy_bool: false,\n            dummy_usize: 0,\n            checklist: std::array::from_fn(|i| i == 0),\n        }\n    }\n}\n\nimpl Demo for MiscDemoWindow {\n    fn name(&self) -> &'static str {\n        \"✨ Misc Demos\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        Window::new(self.name())\n            .open(open)\n            .vscroll(true)\n            .hscroll(true)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl View for MiscDemoWindow {\n    fn ui(&mut self, ui: &mut Ui) {\n        ui.set_min_width(250.0);\n\n        CollapsingHeader::new(\"Label\")\n            .default_open(true)\n            .show(ui, |ui| {\n                label_ui(ui);\n            });\n\n        CollapsingHeader::new(\"Misc widgets\")\n            .default_open(false)\n            .show(ui, |ui| {\n                self.widgets.ui(ui);\n            });\n\n        CollapsingHeader::new(\"Text layout\")\n            .default_open(false)\n            .show(ui, |ui| {\n                text_layout_demo(ui);\n                ui.vertical_centered(|ui| {\n                    ui.add(crate::egui_github_link_file_line!());\n                });\n            });\n\n        CollapsingHeader::new(\"Text rotation\")\n            .default_open(false)\n            .show(ui, |ui| self.text_rotation.ui(ui));\n\n        CollapsingHeader::new(\"Colors\")\n            .default_open(false)\n            .show(ui, |ui| {\n                self.colors.ui(ui);\n            });\n\n        CollapsingHeader::new(\"Custom Collapsing Header\")\n            .default_open(false)\n            .show(ui, |ui| self.custom_collapsing_header.ui(ui));\n\n        CollapsingHeader::new(\"Tree\")\n            .default_open(false)\n            .show(ui, |ui| self.tree.ui(ui));\n\n        CollapsingHeader::new(\"Checkboxes\")\n            .default_open(false)\n            .show(ui, |ui| {\n                ui.label(\"Checkboxes with empty labels take up very little space:\");\n                ui.spacing_mut().item_spacing = Vec2::ZERO;\n                ui.horizontal_wrapped(|ui| {\n                    for _ in 0..64 {\n                        ui.checkbox(&mut self.dummy_bool, \"\");\n                    }\n                });\n                ui.checkbox(&mut self.dummy_bool, \"checkbox\");\n\n                ui.label(\"Radiobuttons are similar:\");\n                ui.spacing_mut().item_spacing = Vec2::ZERO;\n                ui.horizontal_wrapped(|ui| {\n                    for i in 0..64 {\n                        ui.radio_value(&mut self.dummy_usize, i, \"\");\n                    }\n                });\n                ui.radio_value(&mut self.dummy_usize, 64, \"radio_value\");\n                ui.label(\"Checkboxes can be in an indeterminate state:\");\n                let mut all_checked = self.checklist.iter().all(|item| *item);\n                let any_checked = self.checklist.iter().any(|item| *item);\n                let indeterminate = any_checked && !all_checked;\n                if ui\n                    .add(\n                        Checkbox::new(&mut all_checked, \"Check/uncheck all\")\n                            .indeterminate(indeterminate),\n                    )\n                    .changed()\n                {\n                    for check in &mut self.checklist {\n                        *check = all_checked;\n                    }\n                }\n                for (i, checked) in self.checklist.iter_mut().enumerate() {\n                    ui.checkbox(checked, format!(\"Item {}\", i + 1));\n                }\n            });\n\n        ui.collapsing(\"Columns\", |ui| {\n            ui.add(Slider::new(&mut self.num_columns, 1..=10).text(\"Columns\"));\n            ui.columns(self.num_columns, |cols| {\n                for (i, col) in cols.iter_mut().enumerate() {\n                    col.label(format!(\"Column {} out of {}\", i + 1, self.num_columns));\n                    if i + 1 == self.num_columns && col.button(\"Delete this\").clicked() {\n                        self.num_columns -= 1;\n                    }\n                }\n            });\n        });\n\n        CollapsingHeader::new(\"Test box rendering\")\n            .default_open(false)\n            .show(ui, |ui| self.box_painting.ui(ui));\n\n        CollapsingHeader::new(\"Resize\")\n            .default_open(false)\n            .show(ui, |ui| {\n                Resize::default().default_height(100.0).show(ui, |ui| {\n                    ui.label(\"This ui can be resized!\");\n                    ui.label(\"Just pull the handle on the bottom right\");\n                });\n            });\n\n        CollapsingHeader::new(\"Ui Stack\")\n            .default_open(false)\n            .show(ui, ui_stack_demo);\n\n        CollapsingHeader::new(\"Misc\")\n            .default_open(false)\n            .show(ui, |ui| {\n                ui.horizontal(|ui| {\n                    ui.label(\"You can pretty easily paint your own small icons:\");\n                    use std::f32::consts::TAU;\n                    let size = Vec2::splat(16.0);\n                    let (response, painter) = ui.allocate_painter(size, Sense::hover());\n                    let rect = response.rect;\n                    let c = rect.center();\n                    let r = rect.width() / 2.0 - 1.0;\n                    let color = Color32::from_gray(128);\n                    let stroke = Stroke::new(1.0, color);\n                    painter.circle_stroke(c, r, stroke);\n                    painter.line_segment([c - vec2(0.0, r), c + vec2(0.0, r)], stroke);\n                    painter.line_segment([c, c + r * Vec2::angled(TAU * 1.0 / 8.0)], stroke);\n                    painter.line_segment([c, c + r * Vec2::angled(TAU * 3.0 / 8.0)], stroke);\n                });\n            });\n\n        CollapsingHeader::new(\"Many circles of different sizes\")\n            .default_open(false)\n            .show(ui, |ui| {\n                ui.horizontal_wrapped(|ui| {\n                    for i in 0..100 {\n                        let r = i as f32 * 0.5;\n                        let size = Vec2::splat(2.0 * r + 5.0);\n                        let (rect, _response) = ui.allocate_at_least(size, Sense::hover());\n                        ui.painter()\n                            .circle_filled(rect.center(), r, ui.visuals().text_color());\n                    }\n                });\n            });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nfn label_ui(ui: &mut egui::Ui) {\n    ui.vertical_centered(|ui| {\n        ui.add(crate::egui_github_link_file_line!());\n    });\n\n    ui.horizontal_wrapped(|ui| {\n            // Trick so we don't have to add spaces in the text below:\n            let width = ui.fonts_mut(|f|f.glyph_width(&TextStyle::Body.resolve(ui.style()), ' '));\n            ui.spacing_mut().item_spacing.x = width;\n\n            ui.label(RichText::new(\"Text can have\").color(Color32::from_rgb(110, 255, 110)));\n            ui.colored_label(Color32::from_rgb(128, 140, 255), \"color\"); // Shortcut version\n            ui.label(\"and tooltips.\").on_hover_text(\n                \"This is a multiline tooltip that demonstrates that you can easily add tooltips to any element.\\nThis is the second line.\\nThis is the third.\",\n            );\n\n            ui.label(\"You can mix in other widgets into text, like\");\n            let _ = ui.small_button(\"this button\");\n            ui.label(\".\");\n\n            ui.label(\"The default font supports all latin and cyrillic characters (ИÅđ…), common math symbols (∫√∞²⅓…), and many emojis (💓🌟🖩…).\")\n                .on_hover_text(\"There is currently no support for right-to-left languages.\");\n            ui.label(\"See the 🔤 Font Book for more!\");\n\n            ui.monospace(\"There is also a monospace font.\");\n        });\n\n    ui.add(\n        egui::Label::new(\n            \"Labels containing long text can be set to elide the text that doesn't fit on a single line using `Label::truncate`. When hovered, the label will show the full text.\",\n        )\n        .truncate(),\n    );\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Widgets {\n    angle: f32,\n    password: String,\n}\n\nimpl Default for Widgets {\n    fn default() -> Self {\n        Self {\n            angle: std::f32::consts::TAU / 3.0,\n            password: \"hunter2\".to_owned(),\n        }\n    }\n}\n\nimpl Widgets {\n    pub fn ui(&mut self, ui: &mut Ui) {\n        let Self { angle, password } = self;\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file_line!());\n        });\n\n        ui.separator();\n\n        ui.horizontal(|ui| {\n            ui.label(\"An angle:\");\n            ui.drag_angle(angle);\n            ui.label(format!(\"≈ {:.3}τ\", *angle / std::f32::consts::TAU))\n                .on_hover_text(\"Each τ represents one turn (τ = 2π)\");\n        })\n        .response\n        .on_hover_text(\"The angle is stored in radians, but presented in degrees\");\n\n        ui.separator();\n\n        ui.horizontal(|ui| {\n            ui.hyperlink_to(\"Password:\", super::password::url_to_file_source_code())\n                .on_hover_text(\"See the example code for how to use egui to store UI state\");\n            ui.add(super::password::password(password));\n        });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\nstruct ColorWidgets {\n    srgba_unmul: [u8; 4],\n    srgba_premul: [u8; 4],\n    rgba_unmul: [f32; 4],\n    rgba_premul: [f32; 4],\n}\n\nimpl Default for ColorWidgets {\n    fn default() -> Self {\n        // Approximately the same color.\n        Self {\n            srgba_unmul: [0, 255, 183, 127],\n            srgba_premul: [0, 187, 140, 127],\n            rgba_unmul: [0.0, 1.0, 0.5, 0.5],\n            rgba_premul: [0.0, 0.5, 0.25, 0.5],\n        }\n    }\n}\n\nimpl ColorWidgets {\n    fn ui(&mut self, ui: &mut Ui) {\n        egui::reset_button(ui, self, \"Reset\");\n\n        ui.label(\"egui lets you edit colors stored as either sRGBA or linear RGBA and with or without premultiplied alpha\");\n\n        let Self {\n            srgba_unmul,\n            srgba_premul,\n            rgba_unmul,\n            rgba_premul,\n        } = self;\n\n        ui.horizontal(|ui| {\n            ui.color_edit_button_srgba_unmultiplied(srgba_unmul);\n            ui.label(format!(\n                \"sRGBA: {} {} {} {}\",\n                srgba_unmul[0], srgba_unmul[1], srgba_unmul[2], srgba_unmul[3],\n            ));\n        });\n\n        ui.horizontal(|ui| {\n            ui.color_edit_button_srgba_premultiplied(srgba_premul);\n            ui.label(format!(\n                \"sRGBA with premultiplied alpha: {} {} {} {}\",\n                srgba_premul[0], srgba_premul[1], srgba_premul[2], srgba_premul[3],\n            ));\n        });\n\n        ui.horizontal(|ui| {\n            ui.color_edit_button_rgba_unmultiplied(rgba_unmul);\n            ui.label(format!(\n                \"Linear RGBA: {:.02} {:.02} {:.02} {:.02}\",\n                rgba_unmul[0], rgba_unmul[1], rgba_unmul[2], rgba_unmul[3],\n            ));\n        });\n\n        ui.horizontal(|ui| {\n            ui.color_edit_button_rgba_premultiplied(rgba_premul);\n            ui.label(format!(\n                \"Linear RGBA with premultiplied alpha: {:.02} {:.02} {:.02} {:.02}\",\n                rgba_premul[0], rgba_premul[1], rgba_premul[2], rgba_premul[3],\n            ));\n        });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\nstruct BoxPainting {\n    size: Vec2,\n    corner_radius: f32,\n    stroke_width: f32,\n    num_boxes: usize,\n}\n\nimpl Default for BoxPainting {\n    fn default() -> Self {\n        Self {\n            size: vec2(64.0, 32.0),\n            corner_radius: 5.0,\n            stroke_width: 2.0,\n            num_boxes: 1,\n        }\n    }\n}\n\nimpl BoxPainting {\n    pub fn ui(&mut self, ui: &mut Ui) {\n        ui.add(Slider::new(&mut self.size.x, 0.0..=500.0).text(\"width\"));\n        ui.add(Slider::new(&mut self.size.y, 0.0..=500.0).text(\"height\"));\n        ui.add(Slider::new(&mut self.corner_radius, 0.0..=50.0).text(\"corner_radius\"));\n        ui.add(Slider::new(&mut self.stroke_width, 0.0..=10.0).text(\"stroke_width\"));\n        ui.add(Slider::new(&mut self.num_boxes, 0..=8).text(\"num_boxes\"));\n\n        ui.horizontal_wrapped(|ui| {\n            for _ in 0..self.num_boxes {\n                let (rect, _response) = ui.allocate_at_least(self.size, Sense::hover());\n                ui.painter().rect(\n                    rect,\n                    self.corner_radius,\n                    ui.visuals().text_color().gamma_multiply(0.5),\n                    Stroke::new(self.stroke_width, Color32::WHITE),\n                    egui::StrokeKind::Inside,\n                );\n            }\n        });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nstruct CustomCollapsingHeader {\n    selected: bool,\n    radio_value: bool,\n}\n\nimpl Default for CustomCollapsingHeader {\n    fn default() -> Self {\n        Self {\n            selected: true,\n            radio_value: false,\n        }\n    }\n}\n\nimpl CustomCollapsingHeader {\n    pub fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.label(\"Example of a collapsing header with custom header:\");\n\n        let id = ui.make_persistent_id(\"my_collapsing_header\");\n        egui::collapsing_header::CollapsingState::load_with_default_open(ui.ctx(), id, true)\n            .show_header(ui, |ui| {\n                ui.toggle_value(&mut self.selected, \"Click to select/unselect\");\n                ui.radio_value(&mut self.radio_value, false, \"\");\n                ui.radio_value(&mut self.radio_value, true, \"\");\n            })\n            .body(|ui| {\n                ui.label(\"The body is always custom\");\n            });\n\n        CollapsingHeader::new(\"Normal collapsing header for comparison\").show(ui, |ui| {\n            ui.label(\"Nothing exciting here\");\n        });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Clone, Copy, PartialEq)]\nenum Action {\n    Keep,\n    Delete,\n}\n\n#[derive(Clone, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nstruct Tree(Vec<Self>);\n\nimpl Tree {\n    pub fn demo() -> Self {\n        Self(vec![\n            Self(vec![Self::default(); 4]),\n            Self(vec![Self(vec![Self::default(); 2]); 3]),\n        ])\n    }\n\n    pub fn ui(&mut self, ui: &mut Ui) -> Action {\n        self.ui_impl(ui, 0, \"root\")\n    }\n}\n\nimpl Tree {\n    fn ui_impl(&mut self, ui: &mut Ui, depth: usize, name: &str) -> Action {\n        CollapsingHeader::new(name)\n            .default_open(depth < 1)\n            .show(ui, |ui| self.children_ui(ui, depth))\n            .body_returned\n            .unwrap_or(Action::Keep)\n    }\n\n    fn children_ui(&mut self, ui: &mut Ui, depth: usize) -> Action {\n        if depth > 0\n            && ui\n                .button(RichText::new(\"delete\").color(ui.visuals().warn_fg_color))\n                .clicked()\n        {\n            return Action::Delete;\n        }\n\n        self.0 = std::mem::take(self)\n            .0\n            .into_iter()\n            .enumerate()\n            .filter_map(|(i, mut tree)| {\n                if tree.ui_impl(ui, depth + 1, &format!(\"child #{i}\")) == Action::Keep {\n                    Some(tree)\n                } else {\n                    None\n                }\n            })\n            .collect();\n\n        if ui.button(\"+\").clicked() {\n            self.0.push(Self::default());\n        }\n\n        Action::Keep\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nfn ui_stack_demo(ui: &mut Ui) {\n    ui.horizontal_wrapped(|ui| {\n        ui.label(\"The\");\n        ui.code(\"egui::Ui\");\n        ui.label(\"core type is typically deeply nested in\");\n        ui.code(\"egui\");\n        ui.label(\n            \"applications. To provide context to nested code, it maintains a stack \\\n                        with various information.\\n\\nThis is how the stack looks like here:\",\n        );\n    });\n    let stack = Arc::clone(ui.stack());\n    egui::Frame::new()\n        .inner_margin(ui.spacing().menu_margin)\n        .stroke(ui.visuals().widgets.noninteractive.bg_stroke)\n        .show(ui, |ui| {\n            egui_extras::TableBuilder::new(ui)\n                .column(egui_extras::Column::auto())\n                .column(egui_extras::Column::auto())\n                .header(18.0, |mut header| {\n                    header.col(|ui| {\n                        ui.strong(\"id\");\n                    });\n                    header.col(|ui| {\n                        ui.strong(\"kind\");\n                    });\n                })\n                .body(|mut body| {\n                    for node in stack.iter() {\n                        body.row(18.0, |mut row| {\n                            row.col(|ui| {\n                                let response = ui.label(format!(\"{:?}\", node.id));\n\n                                if response.hovered() {\n                                    ui.debug_painter().debug_rect(\n                                        node.max_rect,\n                                        Color32::GREEN,\n                                        \"max_rect\",\n                                    );\n                                    ui.debug_painter().circle_filled(\n                                        node.min_rect.min,\n                                        2.0,\n                                        Color32::RED,\n                                    );\n                                }\n                            });\n\n                            row.col(|ui| {\n                                ui.label(if let Some(kind) = node.kind() {\n                                    format!(\"{kind:?}\")\n                                } else {\n                                    \"-\".to_owned()\n                                });\n                            });\n                        });\n                    }\n                });\n        });\n\n    ui.small(\"Hover on UI's ids to display their origin and max rect.\");\n}\n\n// ----------------------------------------------------------------------------\n\nfn text_layout_demo(ui: &mut Ui) {\n    use egui::text::LayoutJob;\n\n    let mut job = LayoutJob::default();\n\n    let first_row_indentation = 10.0;\n\n    let (default_color, strong_color) = if ui.visuals().dark_mode {\n        (Color32::LIGHT_GRAY, Color32::WHITE)\n    } else {\n        (Color32::DARK_GRAY, Color32::BLACK)\n    };\n\n    job.append(\n        \"This\",\n        first_row_indentation,\n        TextFormat {\n            color: default_color,\n            font_id: FontId::proportional(20.0),\n            ..Default::default()\n        },\n    );\n    job.append(\n        \" is a demonstration of \",\n        0.0,\n        TextFormat {\n            color: default_color,\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"the egui text layout engine. \",\n        0.0,\n        TextFormat {\n            color: strong_color,\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"It supports \",\n        0.0,\n        TextFormat {\n            color: default_color,\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"different \",\n        0.0,\n        TextFormat {\n            color: Color32::from_rgb(110, 255, 110),\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"colors, \",\n        0.0,\n        TextFormat {\n            color: Color32::from_rgb(128, 140, 255),\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"backgrounds, \",\n        0.0,\n        TextFormat {\n            color: default_color,\n            background: Color32::from_rgb(128, 32, 32),\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"mixing \",\n        0.0,\n        TextFormat {\n            font_id: FontId::proportional(20.0),\n            color: default_color,\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"fonts, \",\n        0.0,\n        TextFormat {\n            font_id: FontId::monospace(12.0),\n            color: default_color,\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"raised text, \",\n        0.0,\n        TextFormat {\n            font_id: FontId::proportional(7.0),\n            color: default_color,\n            valign: Align::TOP,\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"with \",\n        0.0,\n        TextFormat {\n            color: default_color,\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"underlining\",\n        0.0,\n        TextFormat {\n            color: default_color,\n            underline: Stroke::new(1.0, Color32::LIGHT_BLUE),\n            ..Default::default()\n        },\n    );\n    job.append(\n        \" and \",\n        0.0,\n        TextFormat {\n            color: default_color,\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"strikethrough\",\n        0.0,\n        TextFormat {\n            color: default_color,\n            strikethrough: Stroke::new(2.0, Color32::RED.linear_multiply(0.5)),\n            ..Default::default()\n        },\n    );\n    job.append(\n        \". Of course, \",\n        0.0,\n        TextFormat {\n            color: default_color,\n            ..Default::default()\n        },\n    );\n    job.append(\n        \"you can\",\n        0.0,\n        TextFormat {\n            color: default_color,\n            strikethrough: Stroke::new(1.0, strong_color),\n            ..Default::default()\n        },\n    );\n    job.append(\n        \" mix these!\",\n        0.0,\n        TextFormat {\n            font_id: FontId::proportional(7.0),\n            color: Color32::LIGHT_BLUE,\n            background: Color32::from_rgb(128, 0, 0),\n            underline: Stroke::new(1.0, strong_color),\n            ..Default::default()\n        },\n    );\n\n    ui.label(job);\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\nstruct TextRotation {\n    size: Vec2,\n    angle: f32,\n    align: egui::Align2,\n}\n\nimpl Default for TextRotation {\n    fn default() -> Self {\n        Self {\n            size: vec2(200.0, 200.0),\n            angle: 0.0,\n            align: egui::Align2::LEFT_TOP,\n        }\n    }\n}\n\nimpl TextRotation {\n    pub fn ui(&mut self, ui: &mut Ui) {\n        ui.add(Slider::new(&mut self.angle, 0.0..=2.0 * std::f32::consts::PI).text(\"angle\"));\n\n        let default_color = if ui.visuals().dark_mode {\n            Color32::LIGHT_GRAY\n        } else {\n            Color32::DARK_GRAY\n        };\n\n        let aligns = [\n            (Align2::LEFT_TOP, \"LEFT_TOP\"),\n            (Align2::LEFT_CENTER, \"LEFT_CENTER\"),\n            (Align2::LEFT_BOTTOM, \"LEFT_BOTTOM\"),\n            (Align2::CENTER_TOP, \"CENTER_TOP\"),\n            (Align2::CENTER_CENTER, \"CENTER_CENTER\"),\n            (Align2::CENTER_BOTTOM, \"CENTER_BOTTOM\"),\n            (Align2::RIGHT_TOP, \"RIGHT_TOP\"),\n            (Align2::RIGHT_CENTER, \"RIGHT_CENTER\"),\n            (Align2::RIGHT_BOTTOM, \"RIGHT_BOTTOM\"),\n        ];\n\n        ComboBox::new(\"anchor\", \"Anchor\")\n            .selected_text(aligns.iter().find(|(a, _)| *a == self.align).unwrap().1)\n            .show_ui(ui, |ui| {\n                for (align2, name) in &aligns {\n                    ui.selectable_value(&mut self.align, *align2, *name);\n                }\n            });\n\n        ui.horizontal_wrapped(|ui| {\n            let (response, painter) = ui.allocate_painter(self.size, Sense::empty());\n            let rect = response.rect;\n\n            let start_pos = self.size / 2.0;\n\n            let s = ui.ctx().fonts_mut(|f| {\n                let mut t = egui::Shape::text(\n                    f,\n                    rect.min + start_pos,\n                    egui::Align2::LEFT_TOP,\n                    \"sample_text\",\n                    egui::FontId::new(12.0, egui::FontFamily::Proportional),\n                    default_color,\n                );\n\n                if let egui::epaint::Shape::Text(ts) = &mut t {\n                    let new = ts.clone().with_angle_and_anchor(self.angle, self.align);\n                    *ts = new;\n                }\n\n                t\n            });\n\n            if let egui::epaint::Shape::Text(ts) = &s {\n                let align_pt =\n                    rect.min + start_pos + self.align.pos_in_rect(&ts.galley.rect).to_vec2();\n                painter.circle(align_pt, 2.0, Color32::RED, (0.0, Color32::RED));\n            }\n\n            painter.rect(\n                rect,\n                0.0,\n                default_color.gamma_multiply(0.3),\n                (0.0, Color32::BLACK),\n                egui::StrokeKind::Middle,\n            );\n            painter.add(s);\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/mod.rs",
    "content": "//! Demo-code for showing how egui is used.\n//!\n//! The demo-code is also used in benchmarks and tests.\n\n// ----------------------------------------------------------------------------\n\npub mod about;\npub mod code_editor;\npub mod code_example;\npub mod dancing_strings;\npub mod demo_app_windows;\npub mod drag_and_drop;\npub mod extra_viewport;\npub mod font_book;\npub mod frame_demo;\npub mod highlighting;\npub mod interactive_container;\npub mod misc_demo_window;\npub mod modals;\npub mod multi_touch;\npub mod paint_bezier;\npub mod painting;\npub mod panels;\npub mod password;\nmod popups;\npub mod scene;\npub mod screenshot;\npub mod scrolling;\npub mod sliders;\npub mod strip_demo;\npub mod table_demo;\npub mod tests;\npub mod text_edit;\npub mod text_layout;\npub mod toggle_switch;\npub mod tooltips;\npub mod undo_redo;\npub mod widget_gallery;\npub mod window_options;\n\npub use {\n    about::About, demo_app_windows::DemoWindows, misc_demo_window::MiscDemoWindow,\n    widget_gallery::WidgetGallery,\n};\n\n// ----------------------------------------------------------------------------\n\n/// Something to view in the demo windows\npub trait View {\n    fn ui(&mut self, ui: &mut egui::Ui);\n}\n\n/// Something to view\npub trait Demo {\n    /// Is the demo enabled for this integration?\n    fn is_enabled(&self, _ctx: &egui::Context) -> bool {\n        true\n    }\n\n    /// `&'static` so we can also use it as a key to store open/close state.\n    fn name(&self) -> &'static str;\n\n    /// Show windows, etc\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool);\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/modals.rs",
    "content": "use egui::{ComboBox, Id, Modal, ProgressBar, Ui, Widget as _, Window};\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Modals {\n    user_modal_open: bool,\n    save_modal_open: bool,\n    save_progress: Option<f32>,\n\n    role: &'static str,\n    name: String,\n}\n\nimpl Default for Modals {\n    fn default() -> Self {\n        Self {\n            user_modal_open: false,\n            save_modal_open: false,\n            save_progress: None,\n            role: Self::ROLES[0],\n            name: \"John Doe\".to_owned(),\n        }\n    }\n}\n\nimpl Modals {\n    const ROLES: [&'static str; 2] = [\"user\", \"admin\"];\n}\n\nimpl crate::Demo for Modals {\n    fn name(&self) -> &'static str {\n        \"🗖 Modals\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        use crate::View as _;\n        Window::new(self.name())\n            .open(open)\n            .vscroll(false)\n            .resizable(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl crate::View for Modals {\n    fn ui(&mut self, ui: &mut Ui) {\n        let Self {\n            user_modal_open,\n            save_modal_open,\n            save_progress,\n            role,\n            name,\n        } = self;\n\n        ui.horizontal(|ui| {\n            if ui.button(\"Open User Modal\").clicked() {\n                *user_modal_open = true;\n            }\n\n            if ui.button(\"Open Save Modal\").clicked() {\n                *save_modal_open = true;\n            }\n        });\n\n        ui.label(\"Click one of the buttons to open a modal.\");\n        ui.label(\"Modals have a backdrop and prevent interaction with the rest of the UI.\");\n        ui.label(\n            \"You can show modals on top of each other and close the topmost modal with \\\n            escape or by clicking outside the modal.\",\n        );\n\n        if *user_modal_open {\n            let modal = Modal::new(Id::new(\"Modal A\")).show(ui.ctx(), |ui| {\n                ui.set_width(250.0);\n\n                ui.heading(\"Edit User\");\n\n                ui.label(\"Name:\");\n                ui.text_edit_singleline(name);\n\n                ComboBox::new(\"role\", \"Role\")\n                    .selected_text(*role)\n                    .show_ui(ui, |ui| {\n                        for r in Self::ROLES {\n                            ui.selectable_value(role, r, r);\n                        }\n                    });\n\n                ui.separator();\n\n                egui::Sides::new().show(\n                    ui,\n                    |_ui| {},\n                    |ui| {\n                        if ui.button(\"Save\").clicked() {\n                            *save_modal_open = true;\n                        }\n                        if ui.button(\"Cancel\").clicked() {\n                            // You can call `ui.close()` to close the modal.\n                            // (This causes the current modals `should_close` to return true)\n                            ui.close();\n                        }\n                    },\n                );\n            });\n\n            if modal.should_close() {\n                *user_modal_open = false;\n            }\n        }\n\n        if *save_modal_open {\n            let modal = Modal::new(Id::new(\"Modal B\")).show(ui.ctx(), |ui| {\n                ui.set_width(200.0);\n                ui.heading(\"Save? Are you sure?\");\n\n                ui.add_space(32.0);\n\n                egui::Sides::new().show(\n                    ui,\n                    |_ui| {},\n                    |ui| {\n                        if ui.button(\"Yes Please\").clicked() {\n                            *save_progress = Some(0.0);\n                        }\n\n                        if ui.button(\"No Thanks\").clicked() {\n                            ui.close();\n                        }\n                    },\n                );\n            });\n\n            if modal.should_close() {\n                *save_modal_open = false;\n            }\n        }\n\n        if let Some(progress) = *save_progress {\n            Modal::new(Id::new(\"Modal C\")).show(ui.ctx(), |ui| {\n                ui.set_width(70.0);\n                ui.heading(\"Saving…\");\n\n                ProgressBar::new(progress).ui(ui);\n\n                if progress >= 1.0 {\n                    *save_progress = None;\n                    *save_modal_open = false;\n                    *user_modal_open = false;\n                } else {\n                    *save_progress = Some(progress + 0.003);\n                    ui.request_repaint();\n                }\n            });\n        }\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::Demo as _;\n    use crate::demo::modals::Modals;\n    use egui::accesskit::Role;\n    use egui::{Key, Popup};\n    use egui_kittest::kittest::Queryable as _;\n    use egui_kittest::{Harness, SnapshotResults};\n\n    #[test]\n    fn clicking_escape_when_popup_open_should_not_close_modal() {\n        let initial_state = Modals {\n            user_modal_open: true,\n            ..Modals::default()\n        };\n\n        let mut harness = Harness::new_ui_state(\n            |ui, modals| {\n                modals.show(ui, &mut true);\n            },\n            initial_state,\n        );\n\n        harness.get_by_role(Role::ComboBox).click();\n\n        // Harness::run would fail because we keep requesting repaints to simulate progress.\n        harness.run_ok();\n        assert!(Popup::is_any_open(&harness.ctx));\n        assert!(harness.state().user_modal_open);\n\n        harness.key_press(Key::Escape);\n        harness.run_ok();\n        assert!(!Popup::is_any_open(&harness.ctx));\n        assert!(harness.state().user_modal_open);\n    }\n\n    #[test]\n    fn escape_should_close_top_modal() {\n        let initial_state = Modals {\n            user_modal_open: true,\n            save_modal_open: true,\n            ..Modals::default()\n        };\n\n        let mut harness = Harness::new_ui_state(\n            |ui, modals| {\n                modals.show(ui, &mut true);\n            },\n            initial_state,\n        );\n\n        assert!(harness.state().user_modal_open);\n        assert!(harness.state().save_modal_open);\n\n        harness.key_press(Key::Escape);\n        harness.run();\n\n        assert!(harness.state().user_modal_open);\n        assert!(!harness.state().save_modal_open);\n    }\n\n    #[test]\n    fn should_match_snapshot() {\n        let initial_state = Modals {\n            user_modal_open: true,\n            ..Modals::default()\n        };\n\n        let mut harness = Harness::new_ui_state(\n            |ui, modals| {\n                modals.show(ui, &mut true);\n            },\n            initial_state,\n        );\n\n        let mut results = SnapshotResults::new();\n\n        harness.run();\n        results.add(harness.try_snapshot(\"modals_1\"));\n\n        harness.get_by_label(\"Save\").click();\n        harness.run_ok();\n        results.add(harness.try_snapshot(\"modals_2\"));\n\n        harness.get_by_label(\"Yes Please\").click();\n        harness.run_ok();\n        results.add(harness.try_snapshot(\"modals_3\"));\n    }\n\n    // This tests whether the backdrop actually prevents interaction with lower layers.\n    #[test]\n    fn backdrop_should_prevent_focusing_lower_area() {\n        let initial_state = Modals {\n            save_modal_open: true,\n            save_progress: Some(0.0),\n            ..Modals::default()\n        };\n\n        let mut harness = Harness::new_ui_state(\n            |ui, modals| {\n                modals.show(ui, &mut true);\n            },\n            initial_state,\n        );\n\n        harness.run_ok();\n\n        harness.get_by_label(\"Yes Please\").click();\n\n        harness.run_ok();\n\n        // This snapshots should show the progress bar modal on top of the save modal.\n        harness.snapshot(\"modals_backdrop_should_prevent_focusing_lower_area\");\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/multi_touch.rs",
    "content": "use egui::{\n    Color32, Event, Frame, Pos2, Rect, Sense, Stroke, Vec2,\n    emath::{RectTransform, Rot2},\n    vec2,\n};\n\npub struct MultiTouch {\n    rotation: f32,\n    translation: Vec2,\n    zoom: f32,\n    last_touch_time: f64,\n}\n\nimpl Default for MultiTouch {\n    fn default() -> Self {\n        Self {\n            rotation: 0.,\n            translation: Vec2::ZERO,\n            zoom: 1.,\n            last_touch_time: 0.0,\n        }\n    }\n}\n\nimpl crate::Demo for MultiTouch {\n    fn name(&self) -> &'static str {\n        \"👌 Multi Touch\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .default_size(vec2(544.0, 512.0))\n            .resizable(true)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for MultiTouch {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n        ui.strong(\n            \"This demo only works on devices with multitouch support (e.g. mobiles, tablets, and trackpads).\",\n        );\n        ui.separator();\n        ui.label(\"Try touch gestures Pinch/Stretch, Rotation, and Pressure with 2+ fingers.\");\n\n        let relative_pointer_gesture = ui.input(|i| {\n            i.events.iter().any(|event| {\n                matches!(\n                    event,\n                    Event::MouseWheel { .. } | Event::Zoom { .. } | Event::Rotate { .. }\n                )\n            })\n        });\n        let num_touches = ui.input(|i| i.multi_touch().map_or(0, |mt| mt.num_touches));\n        let num_touches_str = format!(\"{num_touches}-finger touch\");\n        ui.label(format!(\n            \"Input source: {}\",\n            if ui.input(|i| i.multi_touch().is_some()) {\n                num_touches_str.as_str()\n            } else if relative_pointer_gesture {\n                \"cursor\"\n            } else {\n                \"none\"\n            }\n        ));\n\n        let color = if ui.visuals().dark_mode {\n            Color32::WHITE\n        } else {\n            Color32::BLACK\n        };\n\n        Frame::canvas(ui.style()).show(ui, |ui| {\n            // Note that we use `Sense::drag()` although we do not use any pointer events. With\n            // the current implementation, the fact that a touch event of two or more fingers is\n            // recognized, does not mean that the pointer events are suppressed, which are always\n            // generated for the first finger. Therefore, if we do not explicitly consume pointer\n            // events, the window will move around, not only when dragged with a single finger, but\n            // also when a two-finger touch is active. I guess this problem can only be cleanly\n            // solved when the synthetic pointer events are created by egui, and not by the\n            // backend.\n\n            // set up the drawing canvas with normalized coordinates:\n            let (response, painter) =\n                ui.allocate_painter(ui.available_size_before_wrap(), Sense::drag());\n\n            // normalize painter coordinates to ±1 units in each direction with [0,0] in the center:\n            let painter_proportions = response.rect.square_proportions();\n            let to_screen = RectTransform::from_to(\n                Rect::from_min_size(Pos2::ZERO - painter_proportions, 2. * painter_proportions),\n                response.rect,\n            );\n\n            // check for touch input (or the lack thereof) and update zoom and scale factors, plus\n            // color and width:\n            let mut stroke_width = 1.;\n            if ui.input(|i| i.multi_touch().is_some()) || relative_pointer_gesture {\n                ui.input(|input| {\n                    // This adjusts the current zoom factor, rotation angle, and translation according\n                    // to the dynamic change (for the current frame) of the touch gesture:\n                    self.zoom *= input.zoom_delta();\n                    self.rotation += input.rotation_delta();\n                    self.translation += to_screen.inverse().scale() * input.translation_delta();\n                    // touch pressure will make the arrow thicker (not all touch devices support this):\n                    stroke_width += 10. * input.multi_touch().map_or(0.0, |touch| touch.force);\n\n                    self.last_touch_time = input.time;\n                });\n            } else {\n                self.slowly_reset(ui);\n            }\n            let zoom_and_rotate = self.zoom * Rot2::from_angle(self.rotation);\n            let arrow_start_offset = self.translation + zoom_and_rotate * vec2(-0.5, 0.5);\n\n            // Paints an arrow pointing from bottom-left (-0.5, 0.5) to top-right (0.5, -0.5), but\n            // scaled, rotated, and translated according to the current touch gesture:\n            let arrow_start = Pos2::ZERO + arrow_start_offset;\n            let arrow_direction = zoom_and_rotate * vec2(1., -1.);\n            painter.arrow(\n                to_screen * arrow_start,\n                to_screen.scale() * arrow_direction,\n                Stroke::new(stroke_width, color),\n            );\n        });\n    }\n}\n\nimpl MultiTouch {\n    fn slowly_reset(&mut self, ui: &egui::Ui) {\n        // This has nothing to do with the touch gesture. It just smoothly brings the\n        // painted arrow back into its original position, for a nice visual effect:\n\n        let time_since_last_touch = (ui.input(|i| i.time) - self.last_touch_time) as f32;\n\n        let delay = 0.5;\n        if time_since_last_touch < delay {\n            ui.request_repaint();\n        } else {\n            // seconds after which half the amount of zoom/rotation will be reverted:\n            let half_life =\n                egui::remap_clamp(time_since_last_touch, delay..=1.0, 1.0..=0.0).powf(4.0);\n\n            if half_life <= 1e-3 {\n                self.zoom = 1.0;\n                self.rotation = 0.0;\n                self.translation = Vec2::ZERO;\n            } else {\n                let dt = ui.input(|i| i.unstable_dt);\n                let half_life_factor = (-(2_f32.ln()) / half_life * dt).exp();\n                self.zoom = 1. + ((self.zoom - 1.) * half_life_factor);\n                self.rotation *= half_life_factor;\n                self.translation *= half_life_factor;\n                ui.request_repaint();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/paint_bezier.rs",
    "content": "use egui::{\n    Color32, Frame, Grid, Pos2, Rect, Sense, Shape, Stroke, StrokeKind, Ui, Vec2, Widget as _,\n    Window, emath,\n    epaint::{self, CubicBezierShape, PathShape, QuadraticBezierShape},\n    pos2,\n};\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct PaintBezier {\n    /// Bézier curve degree, it can be 3, 4.\n    degree: usize,\n\n    /// The control points. The [`Self::degree`] first of them are used.\n    control_points: [Pos2; 4],\n\n    /// Stroke for Bézier curve.\n    stroke: Stroke,\n\n    /// Fill for Bézier curve.\n    fill: Color32,\n\n    /// Stroke for auxiliary lines.\n    aux_stroke: Stroke,\n\n    bounding_box_stroke: Stroke,\n}\n\nimpl Default for PaintBezier {\n    fn default() -> Self {\n        Self {\n            degree: 4,\n            control_points: [\n                pos2(50.0, 50.0),\n                pos2(60.0, 250.0),\n                pos2(200.0, 200.0),\n                pos2(250.0, 50.0),\n            ],\n            stroke: Stroke::new(1.0, Color32::from_rgb(25, 200, 100)),\n            fill: Color32::from_rgb(50, 100, 150).linear_multiply(0.25),\n            aux_stroke: Stroke::new(1.0, Color32::RED.linear_multiply(0.25)),\n            bounding_box_stroke: Stroke::new(0.0, Color32::LIGHT_GREEN.linear_multiply(0.25)),\n        }\n    }\n}\n\nimpl PaintBezier {\n    pub fn ui_control(&mut self, ui: &mut egui::Ui) {\n        ui.collapsing(\"Colors\", |ui| {\n            Grid::new(\"colors\")\n                .num_columns(2)\n                .spacing([12.0, 8.0])\n                .striped(true)\n                .show(ui, |ui| {\n                    ui.label(\"Fill color\");\n                    ui.color_edit_button_srgba(&mut self.fill);\n                    ui.end_row();\n\n                    ui.label(\"Curve Stroke\");\n                    ui.add(&mut self.stroke);\n                    ui.end_row();\n\n                    ui.label(\"Auxiliary Stroke\");\n                    ui.add(&mut self.aux_stroke);\n                    ui.end_row();\n\n                    ui.label(\"Bounding Box Stroke\");\n                    ui.add(&mut self.bounding_box_stroke);\n                    ui.end_row();\n                });\n        });\n\n        ui.collapsing(\"Global tessellation options\", |ui| {\n            let mut tessellation_options = ui.ctx().tessellation_options(|to| *to);\n            tessellation_options.ui(ui);\n            ui.tessellation_options_mut(|to| *to = tessellation_options);\n        });\n\n        ui.radio_value(&mut self.degree, 3, \"Quadratic Bézier\");\n        ui.radio_value(&mut self.degree, 4, \"Cubic Bézier\");\n        ui.label(\"Move the points by dragging them.\");\n        ui.small(\"Only convex curves can be accurately filled.\");\n    }\n\n    pub fn ui_content(&mut self, ui: &mut Ui) -> egui::Response {\n        let (response, painter) =\n            ui.allocate_painter(Vec2::new(ui.available_width(), 300.0), Sense::hover());\n\n        let to_screen = emath::RectTransform::from_to(\n            Rect::from_min_size(Pos2::ZERO, response.rect.size()),\n            response.rect,\n        );\n\n        let control_point_radius = 8.0;\n\n        let control_point_shapes: Vec<Shape> = self\n            .control_points\n            .iter_mut()\n            .enumerate()\n            .take(self.degree)\n            .map(|(i, point)| {\n                let size = Vec2::splat(2.0 * control_point_radius);\n\n                let point_in_screen = to_screen.transform_pos(*point);\n                let point_rect = Rect::from_center_size(point_in_screen, size);\n                let point_id = response.id.with(i);\n                let point_response = ui.interact(point_rect, point_id, Sense::drag());\n\n                *point += point_response.drag_delta();\n                *point = to_screen.from().clamp(*point);\n\n                let point_in_screen = to_screen.transform_pos(*point);\n                let stroke = ui.style().interact(&point_response).fg_stroke;\n\n                Shape::circle_stroke(point_in_screen, control_point_radius, stroke)\n            })\n            .collect();\n\n        let points_in_screen: Vec<Pos2> = self\n            .control_points\n            .iter()\n            .take(self.degree)\n            .map(|p| to_screen * *p)\n            .collect();\n\n        match self.degree {\n            3 => {\n                let points = points_in_screen.clone().try_into().unwrap();\n                let shape =\n                    QuadraticBezierShape::from_points_stroke(points, true, self.fill, self.stroke);\n                painter.add(epaint::RectShape::stroke(\n                    shape.visual_bounding_rect(),\n                    0.0,\n                    self.bounding_box_stroke,\n                    StrokeKind::Outside,\n                ));\n                painter.add(shape);\n            }\n            4 => {\n                let points = points_in_screen.clone().try_into().unwrap();\n                let shape =\n                    CubicBezierShape::from_points_stroke(points, true, self.fill, self.stroke);\n                painter.add(epaint::RectShape::stroke(\n                    shape.visual_bounding_rect(),\n                    0.0,\n                    self.bounding_box_stroke,\n                    StrokeKind::Outside,\n                ));\n                painter.add(shape);\n            }\n            _ => {\n                unreachable!();\n            }\n        }\n\n        painter.add(PathShape::line(points_in_screen, self.aux_stroke));\n        painter.extend(control_point_shapes);\n\n        response\n    }\n}\n\nimpl crate::Demo for PaintBezier {\n    fn name(&self) -> &'static str {\n        \"） Bézier Curve\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        use crate::View as _;\n        Window::new(self.name())\n            .open(open)\n            .vscroll(false)\n            .resizable(false)\n            .default_size([300.0, 350.0])\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl crate::View for PaintBezier {\n    fn ui(&mut self, ui: &mut Ui) {\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n        self.ui_control(ui);\n\n        Frame::canvas(ui.style()).show(ui, |ui| {\n            self.ui_content(ui);\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/painting.rs",
    "content": "use egui::{Color32, Frame, Pos2, Rect, Sense, Stroke, Ui, Window, emath, vec2};\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Painting {\n    /// in 0-1 normalized coordinates\n    lines: Vec<Vec<Pos2>>,\n    stroke: Stroke,\n}\n\nimpl Default for Painting {\n    fn default() -> Self {\n        Self {\n            lines: Default::default(),\n            stroke: Stroke::new(1.0, Color32::from_rgb(25, 200, 100)),\n        }\n    }\n}\n\nimpl Painting {\n    pub fn ui_control(&mut self, ui: &mut egui::Ui) -> egui::Response {\n        ui.horizontal(|ui| {\n            ui.label(\"Stroke:\");\n            ui.add(&mut self.stroke);\n            ui.separator();\n            if ui.button(\"Clear Painting\").clicked() {\n                self.lines.clear();\n            }\n        })\n        .response\n    }\n\n    pub fn ui_content(&mut self, ui: &mut Ui) -> egui::Response {\n        let (mut response, painter) =\n            ui.allocate_painter(ui.available_size_before_wrap(), Sense::drag());\n\n        let to_screen = emath::RectTransform::from_to(\n            Rect::from_min_size(Pos2::ZERO, response.rect.square_proportions()),\n            response.rect,\n        );\n        let from_screen = to_screen.inverse();\n\n        if self.lines.is_empty() {\n            self.lines.push(vec![]);\n        }\n\n        let current_line = self.lines.last_mut().unwrap();\n\n        if let Some(pointer_pos) = response.interact_pointer_pos() {\n            let canvas_pos = from_screen * pointer_pos;\n            if current_line.last() != Some(&canvas_pos) {\n                current_line.push(canvas_pos);\n                response.mark_changed();\n            }\n        } else if !current_line.is_empty() {\n            self.lines.push(vec![]);\n            response.mark_changed();\n        }\n\n        let shapes = self\n            .lines\n            .iter()\n            .filter(|line| line.len() >= 2)\n            .map(|line| {\n                let points: Vec<Pos2> = line.iter().map(|p| to_screen * *p).collect();\n                egui::Shape::line(points, self.stroke)\n            });\n\n        painter.extend(shapes);\n\n        response\n    }\n}\n\nimpl crate::Demo for Painting {\n    fn name(&self) -> &'static str {\n        \"🖊 Painting\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        use crate::View as _;\n        Window::new(self.name())\n            .open(open)\n            .default_size(vec2(512.0, 512.0))\n            .vscroll(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl crate::View for Painting {\n    fn ui(&mut self, ui: &mut Ui) {\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n        self.ui_control(ui);\n        ui.label(\"Paint with your mouse/touch!\");\n        Frame::canvas(ui.style()).show(ui, |ui| {\n            self.ui_content(ui);\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/panels.rs",
    "content": "#[derive(Clone, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Panels {}\n\nimpl crate::Demo for Panels {\n    fn name(&self) -> &'static str {\n        \"🗖 Panels\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        use crate::View as _;\n        egui::Window::new(\"Panels\")\n            .default_width(600.0)\n            .default_height(400.0)\n            .vscroll(false)\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl crate::View for Panels {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        // Note that the order we add the panels is very important!\n\n        egui::Panel::top(\"top_panel\")\n            .resizable(true)\n            .min_size(32.0)\n            .show_inside(ui, |ui| {\n                egui::ScrollArea::vertical().show(ui, |ui| {\n                    ui.vertical_centered(|ui| {\n                        ui.heading(\"Expandable Upper Panel\");\n                    });\n                    lorem_ipsum(ui);\n                });\n            });\n\n        egui::Panel::left(\"left_panel\")\n            .resizable(true)\n            .default_size(150.0)\n            .size_range(80.0..=200.0)\n            .show_inside(ui, |ui| {\n                ui.vertical_centered(|ui| {\n                    ui.heading(\"Left Panel\");\n                });\n                egui::ScrollArea::vertical().show(ui, |ui| {\n                    lorem_ipsum(ui);\n                });\n            });\n\n        egui::Panel::right(\"right_panel\")\n            .resizable(true)\n            .default_size(150.0)\n            .size_range(80.0..=200.0)\n            .show_inside(ui, |ui| {\n                ui.vertical_centered(|ui| {\n                    ui.heading(\"Right Panel\");\n                });\n                egui::ScrollArea::vertical().show(ui, |ui| {\n                    lorem_ipsum(ui);\n                });\n            });\n\n        egui::Panel::bottom(\"bottom_panel\")\n            .resizable(false)\n            .min_size(0.0)\n            .show_inside(ui, |ui| {\n                ui.vertical_centered(|ui| {\n                    ui.heading(\"Bottom Panel\");\n                });\n                ui.vertical_centered(|ui| {\n                    ui.add(crate::egui_github_link_file!());\n                });\n            });\n\n        // TODO(emilk): This extra panel is superfluous - just use what's left of `ui` instead\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.vertical_centered(|ui| {\n                ui.heading(\"Central Panel\");\n            });\n            egui::ScrollArea::vertical().show(ui, |ui| {\n                lorem_ipsum(ui);\n            });\n        });\n    }\n}\n\nfn lorem_ipsum(ui: &mut egui::Ui) {\n    ui.with_layout(\n        egui::Layout::top_down(egui::Align::LEFT).with_cross_justify(true),\n        |ui| {\n            ui.label(egui::RichText::new(crate::LOREM_IPSUM_LONG).small().weak());\n            ui.add(egui::Separator::default().grow(8.0));\n            ui.label(egui::RichText::new(crate::LOREM_IPSUM_LONG).small().weak());\n        },\n    );\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/password.rs",
    "content": "//! Source code example about creating a widget which uses `egui::Memory` to store UI state.\n//!\n//! This is meant to be read as a tutorial, hence the plethora of comments.\n\n/// Password entry field with ability to toggle character hiding.\n///\n/// ## Example:\n/// ``` ignore\n/// password_ui(ui, &mut my_password);\n/// ```\npub fn password_ui(ui: &mut egui::Ui, password: &mut String) -> egui::Response {\n    // This widget has its own state — show or hide password characters (`show_plaintext`).\n    // In this case we use a simple `bool`, but you can also declare your own type.\n    // It must implement at least `Clone` and be `'static`.\n    // If you use the `persistence` feature, it also must implement `serde::{Deserialize, Serialize}`.\n\n    // Generate an id for the state\n    let state_id = ui.id().with(\"show_plaintext\");\n\n    // Get state for this widget.\n    // You should get state by value, not by reference to avoid borrowing of [`Memory`].\n    let mut show_plaintext = ui.data_mut(|d| d.get_temp::<bool>(state_id).unwrap_or(false));\n\n    // Process ui, change a local copy of the state\n    // We want TextEdit to fill entire space, and have button after that, so in that case we can\n    // change direction to right_to_left.\n    let result = ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {\n        // Toggle the `show_plaintext` bool with a button:\n        let response = ui\n            .selectable_label(show_plaintext, \"👁\")\n            .on_hover_text(\"Show/hide password\");\n\n        if response.clicked() {\n            show_plaintext = !show_plaintext;\n        }\n\n        // Show the password field:\n        ui.add_sized(\n            ui.available_size(),\n            egui::TextEdit::singleline(password).password(!show_plaintext),\n        );\n    });\n\n    // Store the (possibly changed) state:\n    ui.data_mut(|d| d.insert_temp(state_id, show_plaintext));\n\n    // All done! Return the interaction response so the user can check what happened\n    // (hovered, clicked, …) and maybe show a tooltip:\n    result.response\n}\n\n// A wrapper that allows the more idiomatic usage pattern: `ui.add(…)`\n/// Password entry field with ability to toggle character hiding.\n///\n/// ## Example:\n/// ``` ignore\n/// ui.add(password(&mut my_password));\n/// ```\npub fn password(password: &mut String) -> impl egui::Widget + '_ {\n    move |ui: &mut egui::Ui| password_ui(ui, password)\n}\n\npub fn url_to_file_source_code() -> String {\n    format!(\"https://github.com/emilk/egui/blob/main/{}\", file!())\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/popups.rs",
    "content": "use crate::rust_view_ui;\nuse egui::color_picker::{Alpha, color_picker_color32};\nuse egui::containers::menu::{MenuConfig, SubMenuButton};\nuse egui::{\n    Align, Align2, Atom, Button, ComboBox, Frame, Id, Layout, Popup, PopupCloseBehavior, RectAlign,\n    RichText, Tooltip, Ui, UiBuilder, include_image,\n};\n\n/// Showcase [`Popup`].\n#[derive(Clone, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct PopupsDemo {\n    align4: RectAlign,\n    gap: f32,\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    close_behavior: PopupCloseBehavior,\n    popup_open: bool,\n    checked: bool,\n    color: egui::Color32,\n}\n\nimpl Default for PopupsDemo {\n    fn default() -> Self {\n        Self {\n            align4: RectAlign::default(),\n            gap: 4.0,\n            close_behavior: PopupCloseBehavior::CloseOnClick,\n            popup_open: false,\n            checked: true,\n            color: egui::Color32::RED,\n        }\n    }\n}\n\nimpl PopupsDemo {\n    fn apply_options<'a>(&self, popup: Popup<'a>) -> Popup<'a> {\n        popup\n            .align(self.align4)\n            .gap(self.gap)\n            .close_behavior(self.close_behavior)\n    }\n\n    fn nested_menus(&mut self, ui: &mut Ui) {\n        ui.set_max_width(200.0); // To make sure we wrap long text\n\n        if ui.button(\"Open…\").clicked() {\n            ui.close();\n        }\n        ui.menu_button(\"Popups can have submenus\", |ui| {\n            ui.menu_button(\"SubMenu\", |ui| {\n                if ui.button(\"Open…\").clicked() {\n                    ui.close();\n                }\n                let _ = ui.button(\"Item\");\n                ui.menu_button(\"Recursive\", |ui| self.nested_menus(ui));\n            });\n            ui.menu_button(\"SubMenu\", |ui| {\n                if ui.button(\"Open…\").clicked() {\n                    ui.close();\n                }\n                let _ = ui.button(\"Item\");\n            });\n            let _ = ui.button(\"Item\");\n            if ui.button(\"Open…\").clicked() {\n                ui.close();\n            }\n        });\n        ui.add_enabled_ui(false, |ui| {\n            ui.menu_button(\"SubMenus can be disabled\", |_| {});\n        });\n        ui.menu_image_text_button(\n            include_image!(\"../../data/icon.png\"),\n            \"I have an icon!\",\n            |ui| {\n                let _ = ui.button(\"Item1\");\n                let _ = ui.button(\"Item2\");\n                let _ = ui.button(\"Item3\");\n                let _ = ui.button(\"Item4\");\n                if ui.button(\"Open…\").clicked() {\n                    ui.close();\n                }\n            },\n        );\n        let _ = ui.button(\"Very long text for this item that should be wrapped\");\n        SubMenuButton::new(\"Always CloseOnClickOutside\")\n            .config(MenuConfig::new().close_behavior(PopupCloseBehavior::CloseOnClickOutside))\n            .ui(ui, |ui| {\n                ui.checkbox(&mut self.checked, \"Checkbox\");\n\n                // Customized color SubMenuButton\n                let is_bright = self.color.intensity() > 0.5;\n                let text_color = if is_bright {\n                    egui::Color32::BLACK\n                } else {\n                    egui::Color32::WHITE\n                };\n\n                let button = Button::new((\n                    RichText::new(\"Background\").color(text_color),\n                    Atom::grow(),\n                    RichText::new(SubMenuButton::RIGHT_ARROW).color(text_color),\n                ))\n                .fill(self.color);\n\n                SubMenuButton::from_button(button).ui(ui, |ui| {\n                    ui.spacing_mut().slider_width = 200.0;\n                    color_picker_color32(ui, &mut self.color, Alpha::Opaque);\n                });\n\n                if self.checked {\n                    ui.menu_button(\"Only visible when checked\", |ui| {\n                        if ui.button(\"Remove myself\").clicked() {\n                            self.checked = false;\n                        }\n                    });\n                }\n\n                if ui.button(\"Open…\").clicked() {\n                    ui.close();\n                }\n            });\n    }\n}\n\nimpl crate::Demo for PopupsDemo {\n    fn name(&self) -> &'static str {\n        \"\\u{2755} Popups\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(false)\n            .default_width(250.0)\n            .constrain(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for PopupsDemo {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        let response = Frame::group(ui.style())\n            .show(ui, |ui| {\n                ui.set_width(ui.available_width());\n                ui.vertical_centered(|ui| ui.button(\"Click, right-click and hover me!\"))\n                    .inner\n            })\n            .inner;\n\n        self.apply_options(Popup::menu(&response).id(Id::new(\"menu\")))\n            .show(|ui| self.nested_menus(ui));\n\n        self.apply_options(Popup::context_menu(&response).id(Id::new(\"context_menu\")))\n            .show(|ui| self.nested_menus(ui));\n\n        if self.popup_open {\n            self.apply_options(Popup::from_response(&response).id(Id::new(\"popup\")))\n                .show(|ui| {\n                    ui.label(\"Popup contents\");\n                });\n        }\n\n        let mut tooltip = Tooltip::for_enabled(&response);\n        tooltip.popup = self.apply_options(tooltip.popup);\n        tooltip.show(|ui| {\n            ui.label(\"Tooltips are popups, too!\");\n        });\n\n        Frame::canvas(ui.style()).show(ui, |ui| {\n            let mut reset_btn_ui = ui.new_child(\n                UiBuilder::new()\n                    .max_rect(ui.max_rect())\n                    .layout(Layout::right_to_left(Align::Min)),\n            );\n            if reset_btn_ui\n                .button(\"⟲\")\n                .on_hover_text(\"Reset to defaults\")\n                .clicked()\n            {\n                *self = Self::default();\n            }\n\n            ui.set_width(ui.available_width());\n            ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);\n            ui.spacing_mut().item_spacing.x = 0.0;\n            let align_combobox = |ui: &mut Ui, label: &str, align: &mut Align2| {\n                let aligns = [\n                    (Align2::LEFT_TOP, \"LEFT_TOP\"),\n                    (Align2::LEFT_CENTER, \"LEFT_CENTER\"),\n                    (Align2::LEFT_BOTTOM, \"LEFT_BOTTOM\"),\n                    (Align2::CENTER_TOP, \"CENTER_TOP\"),\n                    (Align2::CENTER_CENTER, \"CENTER_CENTER\"),\n                    (Align2::CENTER_BOTTOM, \"CENTER_BOTTOM\"),\n                    (Align2::RIGHT_TOP, \"RIGHT_TOP\"),\n                    (Align2::RIGHT_CENTER, \"RIGHT_CENTER\"),\n                    (Align2::RIGHT_BOTTOM, \"RIGHT_BOTTOM\"),\n                ];\n\n                ComboBox::new(label, \"\")\n                    .selected_text(aligns.iter().find(|(a, _)| a == align).unwrap().1)\n                    .show_ui(ui, |ui| {\n                        for (align2, name) in &aligns {\n                            ui.selectable_value(align, *align2, *name);\n                        }\n                    });\n            };\n\n            rust_view_ui(ui, \"let align = RectAlign {\");\n            ui.horizontal(|ui| {\n                rust_view_ui(ui, \"    parent: Align2::\");\n                align_combobox(ui, \"parent\", &mut self.align4.parent);\n                rust_view_ui(ui, \",\");\n            });\n            ui.horizontal(|ui| {\n                rust_view_ui(ui, \"    child: Align2::\");\n                align_combobox(ui, \"child\", &mut self.align4.child);\n                rust_view_ui(ui, \",\");\n            });\n            rust_view_ui(ui, \"};\");\n\n            ui.horizontal(|ui| {\n                rust_view_ui(ui, \"let align = RectAlign::\");\n\n                let presets = [\n                    (RectAlign::TOP_START, \"TOP_START\"),\n                    (RectAlign::TOP, \"TOP\"),\n                    (RectAlign::TOP_END, \"TOP_END\"),\n                    (RectAlign::RIGHT_START, \"RIGHT_START\"),\n                    (RectAlign::RIGHT, \"RIGHT\"),\n                    (RectAlign::RIGHT_END, \"RIGHT_END\"),\n                    (RectAlign::BOTTOM_START, \"BOTTOM_START\"),\n                    (RectAlign::BOTTOM, \"BOTTOM\"),\n                    (RectAlign::BOTTOM_END, \"BOTTOM_END\"),\n                    (RectAlign::LEFT_START, \"LEFT_START\"),\n                    (RectAlign::LEFT, \"LEFT\"),\n                    (RectAlign::LEFT_END, \"LEFT_END\"),\n                ];\n\n                ComboBox::new(\"Preset\", \"\")\n                    .selected_text(\n                        presets\n                            .iter()\n                            .find(|(a, _)| a == &self.align4)\n                            .map_or(\"<Select Preset>\", |(_, name)| *name),\n                    )\n                    .show_ui(ui, |ui| {\n                        for (align4, name) in &presets {\n                            ui.selectable_value(&mut self.align4, *align4, *name);\n                        }\n                    });\n                rust_view_ui(ui, \";\");\n            });\n\n            ui.horizontal(|ui| {\n                rust_view_ui(ui, \"let gap = \");\n                ui.add(egui::DragValue::new(&mut self.gap));\n                rust_view_ui(ui, \";\");\n            });\n\n            rust_view_ui(ui, \"let close_behavior\");\n            ui.horizontal(|ui| {\n                rust_view_ui(ui, \"    = PopupCloseBehavior::\");\n                let close_behaviors = [\n                    (\n                        PopupCloseBehavior::CloseOnClick,\n                        \"CloseOnClick\",\n                        \"Closes when the user clicks anywhere (inside or outside)\",\n                    ),\n                    (\n                        PopupCloseBehavior::CloseOnClickOutside,\n                        \"CloseOnClickOutside\",\n                        \"Closes when the user clicks outside the popup\",\n                    ),\n                    (\n                        PopupCloseBehavior::IgnoreClicks,\n                        \"IgnoreClicks\",\n                        \"Close only when the button is clicked again\",\n                    ),\n                ];\n                ComboBox::new(\"Close behavior\", \"\")\n                    .selected_text(\n                        close_behaviors\n                            .iter()\n                            .find_map(|(behavior, text, _)| {\n                                (behavior == &self.close_behavior).then_some(*text)\n                            })\n                            .unwrap(),\n                    )\n                    .show_ui(ui, |ui| {\n                        for (close_behavior, name, tooltip) in &close_behaviors {\n                            ui.selectable_value(&mut self.close_behavior, *close_behavior, *name)\n                                .on_hover_text(*tooltip);\n                        }\n                    });\n                rust_view_ui(ui, \";\");\n            });\n\n            ui.horizontal(|ui| {\n                rust_view_ui(ui, \"let popup_open = \");\n                ui.checkbox(&mut self.popup_open, \"\");\n                rust_view_ui(ui, \";\");\n            });\n            ui.monospace(\"\");\n            rust_view_ui(ui, \"let response = ui.button(\\\"Click me!\\\");\");\n            rust_view_ui(ui, \"Popup::menu(&response)\");\n            rust_view_ui(ui, \"    .gap(gap).align(align)\");\n            rust_view_ui(ui, \"    .close_behavior(close_behavior)\");\n            rust_view_ui(ui, \"    .show(|ui| { /* menu contents */ });\");\n        });\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/scene.rs",
    "content": "use egui::{Pos2, Rect, Scene, Vec2};\n\nuse super::widget_gallery;\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct SceneDemo {\n    widget_gallery: widget_gallery::WidgetGallery,\n    scene_rect: Rect,\n}\n\nimpl Default for SceneDemo {\n    fn default() -> Self {\n        Self {\n            widget_gallery: widget_gallery::WidgetGallery::default().with_date_button(false), // disable date button so that we don't fail the snapshot test\n            scene_rect: Rect::ZERO, // `egui::Scene` will initialize this to something valid\n        }\n    }\n}\n\nimpl crate::Demo for SceneDemo {\n    fn name(&self) -> &'static str {\n        \"🔍 Scene\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        use crate::View as _;\n        egui::Window::new(\"Scene\")\n            .default_width(300.0)\n            .default_height(300.0)\n            .scroll(false)\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl crate::View for SceneDemo {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.label(\n            \"You can pan by scrolling, and zoom using cmd-scroll. \\\n            Double click on the background to reset view.\",\n        );\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n        ui.separator();\n\n        ui.label(format!(\"Scene rect: {:#?}\", &mut self.scene_rect));\n\n        ui.separator();\n\n        egui::Frame::group(ui.style())\n            .inner_margin(0.0)\n            .show(ui, |ui| {\n                let scene = Scene::new()\n                    .max_inner_size([350.0, 1000.0])\n                    .zoom_range(0.1..=2.0);\n\n                let mut reset_view = false;\n                let mut inner_rect = Rect::NAN;\n                let response = scene\n                    .show(ui, &mut self.scene_rect, |ui| {\n                        reset_view = ui.button(\"Reset view\").clicked();\n\n                        ui.add_space(16.0);\n\n                        self.widget_gallery.ui(ui);\n\n                        ui.put(\n                            Rect::from_min_size(Pos2::new(0.0, -64.0), Vec2::new(200.0, 16.0)),\n                            egui::Label::new(\"You can put a widget anywhere\").selectable(false),\n                        );\n\n                        inner_rect = ui.min_rect();\n                    })\n                    .response;\n\n                if reset_view || response.double_clicked() {\n                    self.scene_rect = inner_rect;\n                }\n            });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/screenshot.rs",
    "content": "use egui::{Image, UserData, ViewportCommand, Widget as _};\nuse std::sync::Arc;\n\n/// Showcase [`ViewportCommand::Screenshot`].\n#[derive(PartialEq, Eq, Default)]\npub struct Screenshot {\n    image: Option<(Arc<egui::ColorImage>, egui::TextureHandle)>,\n    continuous: bool,\n}\n\nimpl crate::Demo for Screenshot {\n    fn name(&self) -> &'static str {\n        \"📷 Screenshot\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(false)\n            .default_width(250.0)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for Screenshot {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.set_width(300.0);\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n\n        ui.horizontal_wrapped(|ui| {\n            ui.spacing_mut().item_spacing.x = 0.0;\n            ui.label(\"This demo showcases how to take screenshots via \");\n            ui.code(\"ViewportCommand::Screenshot\");\n            ui.label(\".\");\n        });\n\n        ui.horizontal_top(|ui| {\n            let capture = ui.button(\"📷 Take Screenshot\").clicked();\n            ui.checkbox(&mut self.continuous, \"Capture continuously\");\n            if capture || self.continuous {\n                ui.send_viewport_cmd(ViewportCommand::Screenshot(UserData::default()));\n            }\n        });\n\n        let image = ui.input(|i| {\n            i.events\n                .iter()\n                .filter_map(|e| {\n                    if let egui::Event::Screenshot { image, .. } = e {\n                        Some(Arc::clone(image))\n                    } else {\n                        None\n                    }\n                })\n                .next_back()\n        });\n\n        if let Some(image) = image {\n            self.image = Some((\n                Arc::clone(&image),\n                ui.ctx()\n                    .load_texture(\"screenshot_demo\", image, Default::default()),\n            ));\n        }\n\n        if let Some((_, texture)) = &self.image {\n            Image::new(texture).shrink_to_fit().ui(ui);\n        } else {\n            ui.group(|ui| {\n                ui.set_width(ui.available_width());\n                ui.set_height(100.0);\n                ui.centered_and_justified(|ui| {\n                    ui.label(\"No screenshot taken yet.\");\n                });\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/scrolling.rs",
    "content": "use egui::{\n    Align, Align2, Color32, DragValue, NumExt as _, Rect, ScrollArea, Sense, Slider, TextStyle,\n    TextWrapMode, Ui, Vec2, Widget as _, pos2, scroll_area::ScrollBarVisibility,\n};\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\nenum ScrollDemo {\n    #[default]\n    ScrollAppearance,\n    ScrollTo,\n    ManyLines,\n    LargeCanvas,\n    StickToEnd,\n    Bidirectional,\n}\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\n#[derive(Default, PartialEq)]\npub struct Scrolling {\n    appearance: ScrollAppearance,\n    demo: ScrollDemo,\n    scroll_to: ScrollTo,\n    scroll_stick_to: ScrollStickTo,\n}\n\nimpl crate::Demo for Scrolling {\n    fn name(&self) -> &'static str {\n        \"↕ Scrolling\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(true)\n            .hscroll(false)\n            .vscroll(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for Scrolling {\n    fn ui(&mut self, ui: &mut Ui) {\n        ui.horizontal(|ui| {\n            ui.selectable_value(&mut self.demo, ScrollDemo::ScrollAppearance, \"Appearance\");\n            ui.selectable_value(&mut self.demo, ScrollDemo::ScrollTo, \"Scroll to\");\n            ui.selectable_value(\n                &mut self.demo,\n                ScrollDemo::ManyLines,\n                \"Scroll a lot of lines\",\n            );\n            ui.selectable_value(\n                &mut self.demo,\n                ScrollDemo::LargeCanvas,\n                \"Scroll a large canvas\",\n            );\n            ui.selectable_value(&mut self.demo, ScrollDemo::StickToEnd, \"Stick to end\");\n            ui.selectable_value(&mut self.demo, ScrollDemo::Bidirectional, \"Bidirectional\");\n        });\n        ui.separator();\n        match self.demo {\n            ScrollDemo::ScrollAppearance => {\n                self.appearance.ui(ui);\n            }\n            ScrollDemo::ScrollTo => {\n                self.scroll_to.ui(ui);\n            }\n            ScrollDemo::ManyLines => {\n                huge_content_lines(ui);\n            }\n            ScrollDemo::LargeCanvas => {\n                huge_content_painter(ui);\n            }\n            ScrollDemo::StickToEnd => {\n                self.scroll_stick_to.ui(ui);\n            }\n            ScrollDemo::Bidirectional => {\n                egui::ScrollArea::both().show(ui, |ui| {\n                    ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);\n                    for _ in 0..100 {\n                        ui.label(crate::LOREM_IPSUM);\n                    }\n                });\n            }\n        }\n    }\n}\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\n#[derive(PartialEq)]\nstruct ScrollAppearance {\n    num_lorem_ipsums: usize,\n    visibility: ScrollBarVisibility,\n}\n\nimpl Default for ScrollAppearance {\n    fn default() -> Self {\n        Self {\n            num_lorem_ipsums: 2,\n            visibility: ScrollBarVisibility::default(),\n        }\n    }\n}\n\nimpl ScrollAppearance {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        let Self {\n            num_lorem_ipsums,\n            visibility,\n        } = self;\n\n        let mut scroll = ui.global_style().spacing.scroll;\n\n        scroll.ui(ui);\n\n        ui.add_space(8.0);\n\n        ui.horizontal(|ui| {\n            ui.label(\"ScrollBarVisibility:\");\n            for option in ScrollBarVisibility::ALL {\n                ui.selectable_value(visibility, option, format!(\"{option:?}\"));\n            }\n        });\n        ui.weak(\"When to show scroll bars; resize the window to see the effect.\");\n\n        ui.add_space(8.0);\n\n        ui.ctx().all_styles_mut(|s| s.spacing.scroll = scroll);\n\n        ui.separator();\n\n        ui.add(\n            egui::Slider::new(num_lorem_ipsums, 1..=100)\n                .text(\"Content length\")\n                .logarithmic(true),\n        );\n\n        ui.separator();\n\n        ScrollArea::vertical()\n            .auto_shrink(false)\n            .scroll_bar_visibility(*visibility)\n            .show(ui, |ui| {\n                ui.with_layout(\n                    egui::Layout::top_down(egui::Align::LEFT).with_cross_justify(true),\n                    |ui| {\n                        for _ in 0..*num_lorem_ipsums {\n                            ui.label(crate::LOREM_IPSUM_LONG);\n                        }\n                    },\n                );\n            });\n    }\n}\n\nfn huge_content_lines(ui: &mut egui::Ui) {\n    ui.label(\n        \"A lot of rows, but only the visible ones are laid out, so performance is still good:\",\n    );\n    ui.add_space(4.0);\n\n    let text_style = TextStyle::Body;\n    let row_height = ui.text_style_height(&text_style);\n    let num_rows = 10_000;\n    ScrollArea::vertical().auto_shrink(false).show_rows(\n        ui,\n        row_height,\n        num_rows,\n        |ui, row_range| {\n            for row in row_range {\n                let text = format!(\"This is row {}/{}\", row + 1, num_rows);\n                ui.label(text);\n            }\n        },\n    );\n}\n\nfn huge_content_painter(ui: &mut egui::Ui) {\n    // This is similar to the other demo, but is fully manual, for when you want to do custom painting.\n    ui.label(\"A lot of rows, but only the visible ones are painted, so performance is still good:\");\n    ui.add_space(4.0);\n\n    let font_id = TextStyle::Body.resolve(ui.style());\n    let row_height = ui.fonts_mut(|f| f.row_height(&font_id)) + ui.spacing().item_spacing.y;\n    let num_rows = 10_000;\n\n    ScrollArea::vertical()\n        .auto_shrink(false)\n        .show_viewport(ui, |ui, viewport| {\n            ui.set_height(row_height * num_rows as f32);\n\n            let first_item = (viewport.min.y / row_height).floor().at_least(0.0) as usize;\n            let last_item = (viewport.max.y / row_height).ceil() as usize + 1;\n            let last_item = last_item.at_most(num_rows);\n\n            let mut used_rect = Rect::NOTHING;\n\n            for i in first_item..last_item {\n                let indentation = (i % 100) as f32;\n                let x = ui.min_rect().left() + indentation;\n                let y = ui.min_rect().top() + i as f32 * row_height;\n                let text = format!(\n                    \"This is row {}/{}, indented by {} pixels\",\n                    i + 1,\n                    num_rows,\n                    indentation\n                );\n                let text_rect = ui.painter().text(\n                    pos2(x, y),\n                    Align2::LEFT_TOP,\n                    text,\n                    font_id.clone(),\n                    ui.visuals().text_color(),\n                );\n                used_rect |= text_rect;\n            }\n\n            ui.allocate_rect(used_rect, Sense::hover()); // make sure it is visible!\n        });\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\n#[derive(PartialEq)]\nstruct ScrollTo {\n    track_item: usize,\n    tack_item_align: Option<Align>,\n    offset: f32,\n    delta: f32,\n}\n\nimpl Default for ScrollTo {\n    fn default() -> Self {\n        Self {\n            track_item: 25,\n            tack_item_align: Some(Align::Center),\n            offset: 0.0,\n            delta: 64.0,\n        }\n    }\n}\n\nimpl crate::View for ScrollTo {\n    fn ui(&mut self, ui: &mut Ui) {\n        ui.label(\"This shows how you can scroll to a specific item or pixel offset\");\n\n        let num_items = 500;\n\n        let mut track_item = false;\n        let mut go_to_scroll_offset = false;\n        let mut scroll_top = false;\n        let mut scroll_bottom = false;\n        let mut scroll_delta = None;\n\n        ui.horizontal(|ui| {\n            ui.label(\"Scroll to a specific item index:\");\n            track_item |= ui\n                .add(Slider::new(&mut self.track_item, 1..=num_items).text(\"Track Item\"))\n                .dragged();\n        });\n\n        ui.horizontal(|ui| {\n            ui.label(\"Item align:\");\n            track_item |= ui\n                .radio_value(&mut self.tack_item_align, Some(Align::Min), \"Top\")\n                .clicked();\n            track_item |= ui\n                .radio_value(&mut self.tack_item_align, Some(Align::Center), \"Center\")\n                .clicked();\n            track_item |= ui\n                .radio_value(&mut self.tack_item_align, Some(Align::Max), \"Bottom\")\n                .clicked();\n            track_item |= ui\n                .radio_value(&mut self.tack_item_align, None, \"None (Bring into view)\")\n                .clicked();\n        });\n\n        ui.horizontal(|ui| {\n            ui.label(\"Scroll to a specific offset:\");\n            go_to_scroll_offset |= ui\n                .add(DragValue::new(&mut self.offset).speed(1.0).suffix(\"px\"))\n                .dragged();\n        });\n\n        ui.horizontal(|ui| {\n            scroll_top |= ui.button(\"Scroll to top\").clicked();\n            scroll_bottom |= ui.button(\"Scroll to bottom\").clicked();\n        });\n\n        ui.horizontal(|ui| {\n            ui.label(\"Scroll by\");\n            DragValue::new(&mut self.delta)\n                .speed(1.0)\n                .suffix(\"px\")\n                .ui(ui);\n            if ui.button(\"⬇\").clicked() {\n                scroll_delta = Some(self.delta * Vec2::UP); // scroll down (move contents up)\n            }\n            if ui.button(\"⬆\").clicked() {\n                scroll_delta = Some(self.delta * Vec2::DOWN); // scroll up (move contents down)\n            }\n        });\n\n        let mut scroll_area = ScrollArea::vertical().max_height(200.0).auto_shrink(false);\n        if go_to_scroll_offset {\n            scroll_area = scroll_area.vertical_scroll_offset(self.offset);\n        }\n\n        ui.separator();\n        let (current_scroll, max_scroll) = scroll_area\n            .show(ui, |ui| {\n                if scroll_top {\n                    ui.scroll_to_cursor(Some(Align::TOP));\n                }\n                if let Some(scroll_delta) = scroll_delta {\n                    ui.scroll_with_delta(scroll_delta);\n                }\n\n                ui.vertical(|ui| {\n                    for item in 1..=num_items {\n                        if track_item && item == self.track_item {\n                            let response =\n                                ui.colored_label(Color32::YELLOW, format!(\"This is item {item}\"));\n                            response.scroll_to_me(self.tack_item_align);\n                        } else {\n                            ui.label(format!(\"This is item {item}\"));\n                        }\n                    }\n                });\n\n                if scroll_bottom {\n                    ui.scroll_to_cursor(Some(Align::BOTTOM));\n                }\n\n                let margin = ui.visuals().clip_rect_margin;\n\n                let current_scroll = ui.clip_rect().top() - ui.min_rect().top() + margin;\n                let max_scroll = ui.min_rect().height() - ui.clip_rect().height() + 2.0 * margin;\n                (current_scroll, max_scroll)\n            })\n            .inner;\n        ui.separator();\n\n        ui.label(format!(\n            \"Scroll offset: {current_scroll:.0}/{max_scroll:.0} px\"\n        ));\n\n        ui.separator();\n        ui.vertical_centered(|ui| {\n            egui::reset_button(ui, self, \"Reset\");\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\n#[derive(Default, PartialEq)]\nstruct ScrollStickTo {\n    n_items: usize,\n}\n\nimpl crate::View for ScrollStickTo {\n    fn ui(&mut self, ui: &mut Ui) {\n        ui.label(\"Rows enter from the bottom, we want the scroll handle to start and stay at bottom unless moved\");\n\n        ui.add_space(4.0);\n\n        let text_style = TextStyle::Body;\n        let row_height = ui.text_style_height(&text_style);\n        ScrollArea::vertical().stick_to_bottom(true).show_rows(\n            ui,\n            row_height,\n            self.n_items,\n            |ui, row_range| {\n                for row in row_range {\n                    let text = format!(\"This is row {}\", row + 1);\n                    ui.label(text);\n                }\n            },\n        );\n\n        self.n_items += 1;\n        ui.request_repaint();\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/sliders.rs",
    "content": "use egui::{Slider, SliderClamping, SliderOrientation, Ui, style::HandleShape};\n\n/// Showcase sliders\n#[derive(PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct Sliders {\n    pub min: f64,\n    pub max: f64,\n    pub logarithmic: bool,\n    pub clamping: SliderClamping,\n    pub smart_aim: bool,\n    pub step: f64,\n    pub use_steps: bool,\n    pub integer: bool,\n    pub vertical: bool,\n    pub value: f64,\n    pub trailing_fill: bool,\n    pub handle_shape: HandleShape,\n}\n\nimpl Default for Sliders {\n    fn default() -> Self {\n        Self {\n            min: 0.0,\n            max: 10000.0,\n            logarithmic: true,\n            clamping: SliderClamping::Always,\n            smart_aim: true,\n            step: 10.0,\n            use_steps: false,\n            integer: false,\n            vertical: false,\n            value: 10.0,\n            trailing_fill: false,\n            handle_shape: HandleShape::Circle,\n        }\n    }\n}\n\nimpl crate::Demo for Sliders {\n    fn name(&self) -> &'static str {\n        \"⬌ Sliders\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for Sliders {\n    fn ui(&mut self, ui: &mut Ui) {\n        let Self {\n            min,\n            max,\n            logarithmic,\n            clamping,\n            smart_aim,\n            step,\n            use_steps,\n            integer,\n            vertical,\n            value,\n            trailing_fill,\n            handle_shape,\n        } = self;\n\n        ui.label(\"You can click a slider value to edit it with the keyboard.\");\n\n        let (type_min, type_max) = if *integer {\n            ((i32::MIN as f64), (i32::MAX as f64))\n        } else if *logarithmic {\n            (-f64::INFINITY, f64::INFINITY)\n        } else {\n            (-1e5, 1e5) // linear sliders make little sense with huge numbers\n        };\n\n        *min = min.clamp(type_min, type_max);\n        *max = max.clamp(type_min, type_max);\n\n        let orientation = if *vertical {\n            SliderOrientation::Vertical\n        } else {\n            SliderOrientation::Horizontal\n        };\n\n        let istep = if *use_steps { *step } else { 0.0 };\n        if *integer {\n            let mut value_i32 = *value as i32;\n            ui.add(\n                Slider::new(&mut value_i32, (*min as i32)..=(*max as i32))\n                    .logarithmic(*logarithmic)\n                    .clamping(*clamping)\n                    .smart_aim(*smart_aim)\n                    .orientation(orientation)\n                    .text(\"i32 demo slider\")\n                    .step_by(istep)\n                    .trailing_fill(*trailing_fill)\n                    .handle_shape(*handle_shape),\n            );\n            *value = value_i32 as f64;\n        } else {\n            ui.add(\n                Slider::new(value, (*min)..=(*max))\n                    .logarithmic(*logarithmic)\n                    .clamping(*clamping)\n                    .smart_aim(*smart_aim)\n                    .orientation(orientation)\n                    .text(\"f64 demo slider\")\n                    .step_by(istep)\n                    .trailing_fill(*trailing_fill)\n                    .handle_shape(*handle_shape),\n            );\n\n            ui.label(\n                \"Sliders will intelligently pick how many decimals to show. \\\n                You can always see the full precision value by hovering the value.\",\n            );\n\n            if ui.button(\"Assign PI\").clicked() {\n                self.value = std::f64::consts::PI;\n            }\n        }\n\n        ui.separator();\n\n        ui.label(\"Slider range:\");\n        ui.add(\n            Slider::new(min, type_min..=type_max)\n                .logarithmic(true)\n                .smart_aim(*smart_aim)\n                .text(\"left\")\n                .trailing_fill(*trailing_fill)\n                .handle_shape(*handle_shape),\n        );\n        ui.add(\n            Slider::new(max, type_min..=type_max)\n                .logarithmic(true)\n                .smart_aim(*smart_aim)\n                .text(\"right\")\n                .trailing_fill(*trailing_fill)\n                .handle_shape(*handle_shape),\n        );\n\n        ui.separator();\n\n        ui.checkbox(trailing_fill, \"Toggle trailing color\");\n        ui.label(\"When enabled, trailing color will be painted up until the handle.\");\n\n        ui.separator();\n\n        handle_shape.ui(ui);\n\n        ui.separator();\n\n        ui.checkbox(use_steps, \"Use steps\");\n        ui.label(\"When enabled, the minimal value change would be restricted to a given step.\");\n        if *use_steps {\n            ui.add(egui::DragValue::new(step).speed(1.0));\n        }\n\n        ui.separator();\n\n        ui.horizontal(|ui| {\n            ui.label(\"Slider type:\");\n            ui.radio_value(integer, true, \"i32\");\n            ui.radio_value(integer, false, \"f64\");\n        })\n        .response\n        .on_hover_text(\"All numeric types (f32, usize, …) are supported.\");\n\n        ui.horizontal(|ui| {\n            ui.label(\"Slider orientation:\");\n            ui.radio_value(vertical, false, \"Horizontal\");\n            ui.radio_value(vertical, true, \"Vertical\");\n        });\n        ui.add_space(8.0);\n\n        ui.checkbox(logarithmic, \"Logarithmic\");\n        ui.label(\"Logarithmic sliders are great for when you want to span a huge range, i.e. from zero to a million.\");\n        ui.label(\"Logarithmic sliders can include infinity and zero.\");\n        ui.add_space(8.0);\n\n        ui.horizontal(|ui| {\n            ui.label(\"Clamping:\");\n            ui.selectable_value(clamping, SliderClamping::Never, \"Never\");\n            ui.selectable_value(clamping, SliderClamping::Edits, \"Edits\");\n            ui.selectable_value(clamping, SliderClamping::Always, \"Always\");\n        });\n        ui.label(\"If true, the slider will clamp incoming and outgoing values to the given range.\");\n        ui.label(\"If false, the slider can show values outside its range, and you cannot enter new values outside the range.\");\n        ui.add_space(8.0);\n\n        ui.checkbox(smart_aim, \"Smart Aim\");\n        ui.label(\"Smart Aim will guide you towards round values when you drag the slider so you you are more likely to hit 250 than 247.23\");\n        ui.add_space(8.0);\n\n        ui.vertical_centered(|ui| {\n            egui::reset_button(ui, self, \"Reset\");\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/strip_demo.rs",
    "content": "use egui::{Color32, TextStyle};\nuse egui_extras::{Size, StripBuilder};\n\n/// Shows off a table with dynamic layout\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[derive(Default)]\npub struct StripDemo {}\n\nimpl crate::Demo for StripDemo {\n    fn name(&self) -> &'static str {\n        \"▣ Strip\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(true)\n            .default_width(400.0)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for StripDemo {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        let dark_mode = ui.visuals().dark_mode;\n        let faded_color = ui.visuals().window_fill();\n        let faded_color = |color: Color32| -> Color32 {\n            use egui::Rgba;\n            let t = if dark_mode { 0.95 } else { 0.8 };\n            egui::lerp(Rgba::from(color)..=Rgba::from(faded_color), t).into()\n        };\n\n        let body_text_size = TextStyle::Body.resolve(ui.style()).size;\n        StripBuilder::new(ui)\n            .size(Size::exact(50.0))\n            .size(Size::remainder())\n            .size(Size::relative(0.5).at_least(60.0))\n            .size(Size::exact(body_text_size))\n            .vertical(|mut strip| {\n                strip.cell(|ui| {\n                    ui.painter().rect_filled(\n                        ui.available_rect_before_wrap(),\n                        0.0,\n                        faded_color(Color32::BLUE),\n                    );\n                    ui.label(\"width: 100%\\nheight: 50px\");\n                });\n                strip.strip(|builder| {\n                    builder.sizes(Size::remainder(), 2).horizontal(|mut strip| {\n                        strip.cell(|ui| {\n                            ui.painter().rect_filled(\n                                ui.available_rect_before_wrap(),\n                                0.0,\n                                faded_color(Color32::RED),\n                            );\n                            ui.label(\"width: 50%\\nheight: remaining\");\n                        });\n                        strip.strip(|builder| {\n                            builder.sizes(Size::remainder(), 3).vertical(|mut strip| {\n                                strip.empty();\n                                strip.cell(|ui| {\n                                    ui.painter().rect_filled(\n                                        ui.available_rect_before_wrap(),\n                                        0.0,\n                                        faded_color(Color32::YELLOW),\n                                    );\n                                    ui.label(\"width: 50%\\nheight: 1/3 of the red region\");\n                                });\n                                strip.empty();\n                            });\n                        });\n                    });\n                });\n                strip.strip(|builder| {\n                    builder\n                        .size(Size::remainder())\n                        .size(Size::exact(120.0))\n                        .size(Size::remainder())\n                        .size(Size::exact(70.0))\n                        .horizontal(|mut strip| {\n                            strip.empty();\n                            strip.strip(|builder| {\n                                builder\n                                    .size(Size::remainder())\n                                    .size(Size::exact(60.0))\n                                    .size(Size::remainder())\n                                    .vertical(|mut strip| {\n                                        strip.empty();\n                                        strip.cell(|ui| {\n                                            ui.painter().rect_filled(\n                                                ui.available_rect_before_wrap(),\n                                                0.0,\n                                                faded_color(Color32::GOLD),\n                                            );\n                                            ui.label(\"width: 120px\\nheight: 60px\");\n                                        });\n                                    });\n                            });\n                            strip.empty();\n                            strip.cell(|ui| {\n                                ui.painter().rect_filled(\n                                    ui.available_rect_before_wrap(),\n                                    0.0,\n                                    faded_color(Color32::GREEN),\n                                );\n                                ui.label(\"width: 70px\\n\\nheight: 50%, but at least 60px.\");\n                            });\n                        });\n                });\n                strip.cell(|ui| {\n                    ui.vertical_centered(|ui| {\n                        ui.add(crate::egui_github_link_file!());\n                    });\n                });\n            });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/table_demo.rs",
    "content": "use egui::{TextStyle, TextWrapMode};\n\n#[derive(PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nenum DemoType {\n    Manual,\n    ManyHomogeneous,\n    ManyHeterogenous,\n}\n\n/// Shows off a table with dynamic layout\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct TableDemo {\n    demo: DemoType,\n    striped: bool,\n    overline: bool,\n    resizable: bool,\n    clickable: bool,\n    num_rows: usize,\n    scroll_to_row_slider: usize,\n    scroll_to_row: Option<usize>,\n    selection: std::collections::HashSet<usize>,\n    checked: bool,\n    reversed: bool,\n}\n\nimpl Default for TableDemo {\n    fn default() -> Self {\n        Self {\n            demo: DemoType::Manual,\n            striped: true,\n            overline: true,\n            resizable: true,\n            clickable: true,\n            num_rows: 10_000,\n            scroll_to_row_slider: 0,\n            scroll_to_row: None,\n            selection: Default::default(),\n            checked: false,\n            reversed: false,\n        }\n    }\n}\n\nimpl crate::Demo for TableDemo {\n    fn name(&self) -> &'static str {\n        \"☰ Table\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .default_width(400.0)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nconst NUM_MANUAL_ROWS: usize = 20;\n\nimpl crate::View for TableDemo {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        let mut reset = false;\n\n        ui.vertical(|ui| {\n            ui.horizontal(|ui| {\n                ui.checkbox(&mut self.striped, \"Striped\");\n                ui.checkbox(&mut self.overline, \"Overline some rows\");\n                ui.checkbox(&mut self.resizable, \"Resizable columns\");\n                ui.checkbox(&mut self.clickable, \"Clickable rows\");\n            });\n\n            ui.label(\"Table type:\");\n            ui.radio_value(&mut self.demo, DemoType::Manual, \"Few, manual rows\");\n            ui.radio_value(\n                &mut self.demo,\n                DemoType::ManyHomogeneous,\n                \"Thousands of rows of same height\",\n            );\n            ui.radio_value(\n                &mut self.demo,\n                DemoType::ManyHeterogenous,\n                \"Thousands of rows of differing heights\",\n            );\n\n            if self.demo != DemoType::Manual {\n                ui.add(\n                    egui::Slider::new(&mut self.num_rows, 0..=100_000)\n                        .logarithmic(true)\n                        .text(\"Num rows\"),\n                );\n            }\n\n            {\n                let max_rows = if self.demo == DemoType::Manual {\n                    NUM_MANUAL_ROWS\n                } else {\n                    self.num_rows\n                };\n\n                let slider_response = ui.add(\n                    egui::Slider::new(&mut self.scroll_to_row_slider, 0..=max_rows)\n                        .logarithmic(true)\n                        .text(\"Row to scroll to\"),\n                );\n                if slider_response.changed() {\n                    self.scroll_to_row = Some(self.scroll_to_row_slider);\n                }\n            }\n\n            reset = ui.button(\"Reset\").clicked();\n        });\n\n        ui.separator();\n\n        // Leave room for the source code link after the table demo:\n        let body_text_size = TextStyle::Body.resolve(ui.style()).size;\n        use egui_extras::{Size, StripBuilder};\n        StripBuilder::new(ui)\n            .size(Size::remainder().at_least(100.0)) // for the table\n            .size(Size::exact(body_text_size)) // for the source code link\n            .vertical(|mut strip| {\n                strip.cell(|ui| {\n                    egui::ScrollArea::horizontal().show(ui, |ui| {\n                        self.table_ui(ui, reset);\n                    });\n                });\n                strip.cell(|ui| {\n                    ui.vertical_centered(|ui| {\n                        ui.add(crate::egui_github_link_file!());\n                    });\n                });\n            });\n    }\n}\n\nimpl TableDemo {\n    fn table_ui(&mut self, ui: &mut egui::Ui, reset: bool) {\n        use egui_extras::{Column, TableBuilder};\n\n        let text_height = egui::TextStyle::Body\n            .resolve(ui.style())\n            .size\n            .max(ui.spacing().interact_size.y);\n\n        let available_height = ui.available_height();\n        let mut table = TableBuilder::new(ui)\n            .striped(self.striped)\n            .resizable(self.resizable)\n            .cell_layout(egui::Layout::left_to_right(egui::Align::Center))\n            .column(Column::auto())\n            .column(\n                Column::remainder()\n                    .at_least(40.0)\n                    .clip(true)\n                    .resizable(true),\n            )\n            .column(Column::auto())\n            .column(Column::remainder())\n            .column(Column::remainder())\n            .min_scrolled_height(0.0)\n            .max_scroll_height(available_height);\n\n        if self.clickable {\n            table = table.sense(egui::Sense::click());\n        }\n\n        if let Some(row_index) = self.scroll_to_row.take() {\n            table = table.scroll_to_row(row_index, None);\n        }\n\n        if reset {\n            table.reset();\n        }\n\n        table\n            .header(20.0, |mut header| {\n                header.col(|ui| {\n                    egui::Sides::new().show(\n                        ui,\n                        |ui| {\n                            ui.strong(\"Row\");\n                        },\n                        |ui| {\n                            self.reversed ^=\n                                ui.button(if self.reversed { \"⬆\" } else { \"⬇\" }).clicked();\n                        },\n                    );\n                });\n                header.col(|ui| {\n                    ui.strong(\"Clipped text\");\n                });\n                header.col(|ui| {\n                    ui.strong(\"Expanding content\");\n                });\n                header.col(|ui| {\n                    ui.strong(\"Interaction\");\n                });\n                header.col(|ui| {\n                    ui.strong(\"Content\");\n                });\n            })\n            .body(|mut body| match self.demo {\n                DemoType::Manual => {\n                    for row_index in 0..NUM_MANUAL_ROWS {\n                        let row_index = if self.reversed {\n                            NUM_MANUAL_ROWS - 1 - row_index\n                        } else {\n                            row_index\n                        };\n\n                        let is_thick = thick_row(row_index);\n                        let row_height = if is_thick { 30.0 } else { 18.0 };\n                        body.row(row_height, |mut row| {\n                            row.set_selected(self.selection.contains(&row_index));\n                            row.set_overline(self.overline && row_index % 7 == 3);\n\n                            row.col(|ui| {\n                                ui.label(row_index.to_string());\n                            });\n                            row.col(|ui| {\n                                ui.label(long_text(row_index));\n                            });\n                            row.col(|ui| {\n                                expanding_content(ui);\n                            });\n                            row.col(|ui| {\n                                ui.checkbox(&mut self.checked, \"Click me\");\n                            });\n                            row.col(|ui| {\n                                ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);\n                                if is_thick {\n                                    ui.heading(\"Extra thick row\");\n                                } else {\n                                    ui.label(\"Normal row\");\n                                }\n                            });\n\n                            self.toggle_row_selection(row_index, &row.response());\n                        });\n                    }\n                }\n                DemoType::ManyHomogeneous => {\n                    body.rows(text_height, self.num_rows, |mut row| {\n                        let row_index = if self.reversed {\n                            self.num_rows - 1 - row.index()\n                        } else {\n                            row.index()\n                        };\n\n                        row.set_selected(self.selection.contains(&row_index));\n                        row.set_overline(self.overline && row_index % 7 == 3);\n\n                        row.col(|ui| {\n                            ui.label(row_index.to_string());\n                        });\n                        row.col(|ui| {\n                            ui.label(long_text(row_index));\n                        });\n                        row.col(|ui| {\n                            expanding_content(ui);\n                        });\n                        row.col(|ui| {\n                            ui.checkbox(&mut self.checked, \"Click me\");\n                        });\n                        row.col(|ui| {\n                            ui.add(\n                                egui::Label::new(\"Thousands of rows of even height\")\n                                    .wrap_mode(TextWrapMode::Extend),\n                            );\n                        });\n\n                        self.toggle_row_selection(row_index, &row.response());\n                    });\n                }\n                DemoType::ManyHeterogenous => {\n                    let row_height = |i: usize| if thick_row(i) { 30.0 } else { 18.0 };\n                    body.heterogeneous_rows((0..self.num_rows).map(row_height), |mut row| {\n                        let row_index = if self.reversed {\n                            self.num_rows - 1 - row.index()\n                        } else {\n                            row.index()\n                        };\n\n                        row.set_selected(self.selection.contains(&row_index));\n                        row.set_overline(self.overline && row_index % 7 == 3);\n\n                        row.col(|ui| {\n                            ui.label(row_index.to_string());\n                        });\n                        row.col(|ui| {\n                            ui.label(long_text(row_index));\n                        });\n                        row.col(|ui| {\n                            expanding_content(ui);\n                        });\n                        row.col(|ui| {\n                            ui.checkbox(&mut self.checked, \"Click me\");\n                        });\n                        row.col(|ui| {\n                            ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);\n                            if thick_row(row_index) {\n                                ui.heading(\"Extra thick row\");\n                            } else {\n                                ui.label(\"Normal row\");\n                            }\n                        });\n\n                        self.toggle_row_selection(row_index, &row.response());\n                    });\n                }\n            });\n    }\n\n    fn toggle_row_selection(&mut self, row_index: usize, row_response: &egui::Response) {\n        if row_response.clicked() {\n            if self.selection.contains(&row_index) {\n                self.selection.remove(&row_index);\n            } else {\n                self.selection.insert(row_index);\n            }\n        }\n    }\n}\n\nfn expanding_content(ui: &mut egui::Ui) {\n    ui.add(egui::Separator::default().horizontal());\n}\n\nfn long_text(row_index: usize) -> String {\n    format!(\n        \"Row {row_index} has some long text that you may want to clip, or it will take up too much horizontal space!\"\n    )\n}\n\nfn thick_row(row_index: usize) -> bool {\n    row_index.is_multiple_of(6)\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/clipboard_test.rs",
    "content": "pub struct ClipboardTest {\n    text: String,\n}\n\nimpl Default for ClipboardTest {\n    fn default() -> Self {\n        Self {\n            text: \"Example text you can copy-and-paste\".to_owned(),\n        }\n    }\n}\n\nimpl crate::Demo for ClipboardTest {\n    fn name(&self) -> &'static str {\n        \"Clipboard Test\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for ClipboardTest {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.label(\"egui integrates with the system clipboard.\");\n        ui.label(\"Try copy-cut-pasting text in the text edit below.\");\n\n        let text_edit_response = ui\n            .horizontal(|ui| {\n                let text_edit_response = ui.text_edit_singleline(&mut self.text);\n                if ui.button(\"📋\").clicked() {\n                    ui.copy_text(self.text.clone());\n                }\n                text_edit_response\n            })\n            .inner;\n\n        if !cfg!(target_arch = \"wasm32\") {\n            // These commands are not yet implemented on web\n            ui.horizontal(|ui| {\n                for (name, cmd) in [\n                    (\"Copy\", egui::ViewportCommand::RequestCopy),\n                    (\"Cut\", egui::ViewportCommand::RequestCut),\n                    (\"Paste\", egui::ViewportCommand::RequestPaste),\n                ] {\n                    if ui.button(name).clicked() {\n                        // Next frame we should get a copy/cut/paste-event…\n                        ui.send_viewport_cmd(cmd);\n\n                        // …that should en up here:\n                        text_edit_response.request_focus();\n                    }\n                }\n            });\n        }\n\n        ui.separator();\n\n        ui.label(\"You can also copy images:\");\n        ui.horizontal(|ui| {\n            let image_source = egui::include_image!(\"../../../data/icon.png\");\n            let uri = image_source.uri().unwrap().to_owned();\n            ui.image(image_source);\n\n            if let Ok(egui::load::ImagePoll::Ready { image }) =\n                ui.ctx().try_load_image(&uri, Default::default())\n                && ui.button(\"📋\").clicked()\n            {\n                ui.copy_image((*image).clone());\n            }\n        });\n\n        ui.vertical_centered_justified(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/cursor_test.rs",
    "content": "#[derive(Default)]\npub struct CursorTest {}\n\nimpl crate::Demo for CursorTest {\n    fn name(&self) -> &'static str {\n        \"Cursor Test\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for CursorTest {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.vertical_centered_justified(|ui| {\n            ui.heading(\"Hover to switch cursor icon:\");\n            for &cursor_icon in &egui::CursorIcon::ALL {\n                let _ = ui\n                    .button(format!(\"{cursor_icon:?}\"))\n                    .on_hover_cursor(cursor_icon);\n            }\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/grid_test.rs",
    "content": "#[derive(PartialEq)]\npub struct GridTest {\n    num_cols: usize,\n    num_rows: usize,\n    min_col_width: f32,\n    max_col_width: f32,\n    text_length: usize,\n}\n\nimpl Default for GridTest {\n    fn default() -> Self {\n        Self {\n            num_cols: 4,\n            num_rows: 4,\n            min_col_width: 10.0,\n            max_col_width: 200.0,\n            text_length: 10,\n        }\n    }\n}\n\nimpl crate::Demo for GridTest {\n    fn name(&self) -> &'static str {\n        \"Grid Test\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for GridTest {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.add(\n            egui::Slider::new(&mut self.min_col_width, 0.0..=400.0).text(\"Minimum column width\"),\n        );\n        ui.add(\n            egui::Slider::new(&mut self.max_col_width, 0.0..=400.0).text(\"Maximum column width\"),\n        );\n        ui.add(egui::Slider::new(&mut self.num_cols, 0..=5).text(\"Columns\"));\n        ui.add(egui::Slider::new(&mut self.num_rows, 0..=20).text(\"Rows\"));\n\n        ui.separator();\n\n        let words = [\n            \"random\", \"words\", \"in\", \"a\", \"random\", \"order\", \"that\", \"just\", \"keeps\", \"going\",\n            \"with\", \"some\", \"more\",\n        ];\n\n        egui::Grid::new(\"my_grid\")\n            .striped(true)\n            .min_col_width(self.min_col_width)\n            .max_col_width(self.max_col_width)\n            .show(ui, |ui| {\n                for row in 0..self.num_rows {\n                    for col in 0..self.num_cols {\n                        if col == 0 {\n                            ui.label(format!(\"row {row}\"));\n                        } else {\n                            let word_idx = row * 3 + col * 5;\n                            let word_count = (row * 5 + col * 75) % 13;\n                            let mut string = String::new();\n                            for word in words.iter().cycle().skip(word_idx).take(word_count) {\n                                string += word;\n                                string += \" \";\n                            }\n                            ui.label(string);\n                        }\n                    }\n                    ui.end_row();\n                }\n            });\n\n        ui.separator();\n        ui.add(egui::Slider::new(&mut self.text_length, 1..=40).text(\"Text length\"));\n        egui::Grid::new(\"parent grid\").striped(true).show(ui, |ui| {\n            ui.vertical(|ui| {\n                ui.label(\"Vertical nest1\");\n                ui.label(\"Vertical nest2\");\n            });\n            ui.label(\"First row, second column\");\n            ui.end_row();\n\n            ui.horizontal(|ui| {\n                ui.label(\"Horizontal nest1\");\n                ui.label(\"Horizontal nest2\");\n            });\n            ui.label(\"Second row, second column\");\n            ui.end_row();\n\n            ui.scope(|ui| {\n                ui.label(\"Scope nest 1\");\n                ui.label(\"Scope nest 2\");\n            });\n            ui.label(\"Third row, second column\");\n            ui.end_row();\n\n            egui::Grid::new(\"nested grid\").show(ui, |ui| {\n                ui.label(\"Grid nest11\");\n                ui.label(\"Grid nest12\");\n                ui.end_row();\n                ui.label(\"Grid nest21\");\n                ui.label(\"Grid nest22\");\n                ui.end_row();\n            });\n            ui.label(\"Fourth row, second column\");\n            ui.end_row();\n\n            let mut dyn_text = String::from(\"O\");\n            dyn_text.extend(std::iter::repeat_n('h', self.text_length));\n            ui.label(dyn_text);\n            ui.label(\"Fifth row, second column\");\n            ui.end_row();\n        });\n\n        ui.vertical_centered(|ui| {\n            egui::reset_button(ui, self, \"Reset\");\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/id_test.rs",
    "content": "#[derive(Default)]\npub struct IdTest {}\n\nimpl crate::Demo for IdTest {\n    fn name(&self) -> &'static str {\n        \"ID Test\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for IdTest {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        // Make sure the warnings are on (by default they are only on in debug builds).\n        ui.options_mut(|opt| opt.warn_on_id_clash = true);\n\n        ui.heading(\"Name collision example\");\n\n        ui.label(\"\\\n            Widgets that store state require unique and persisting identifiers so we can track their state between frames.\\n\\\n            For instance, collapsible headers needs to store whether or not they are open. \\\n            Their Id:s are derived from their names. \\\n            If you fail to give them unique names then clicking one will open both. \\\n            To help you debug this, an error message is printed on screen:\");\n\n        ui.collapsing(\"Collapsing header\", |ui| {\n            ui.label(\"Contents of first foldable ui\");\n        });\n        ui.collapsing(\"Collapsing header\", |ui| {\n            ui.label(\"Contents of second foldable ui\");\n        });\n\n        ui.label(\"\\\n            Any widget that can be interacted with also need a unique Id. \\\n            For most widgets the Id is generated by a running counter. \\\n            As long as elements are not added or removed, the Id stays the same. \\\n            This is fine, because during interaction (i.e. while dragging a slider), \\\n            the number of widgets previously in the same window is most likely not changing \\\n            (and if it is, the window will have a new layout, and the slider will end up somewhere else, and so aborting the interaction probably makes sense).\");\n\n        ui.label(\"So these buttons have automatic Id:s, and therefore there is no name clash:\");\n        let _ = ui.button(\"Button\");\n        let _ = ui.button(\"Button\");\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/input_event_history.rs",
    "content": "//! Show the history of all the input events to\n\nstruct HistoryEntry {\n    summary: String,\n    entries: Vec<String>,\n}\n\n#[derive(Default)]\nstruct DeduplicatedHistory {\n    history: std::collections::VecDeque<HistoryEntry>,\n}\n\nimpl DeduplicatedHistory {\n    fn add(&mut self, summary: String, full: String) {\n        if let Some(entry) = self.history.back_mut()\n            && entry.summary == summary\n        {\n            entry.entries.push(full);\n            return;\n        }\n        self.history.push_back(HistoryEntry {\n            summary,\n            entries: vec![full],\n        });\n        if self.history.len() > 100 {\n            self.history.pop_front();\n        }\n    }\n\n    fn ui(&self, ui: &mut egui::Ui) {\n        egui::ScrollArea::vertical()\n            .auto_shrink(false)\n            .show(ui, |ui| {\n                ui.spacing_mut().item_spacing.y = 4.0;\n                ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);\n\n                for HistoryEntry { summary, entries } in self.history.iter().rev() {\n                    ui.horizontal(|ui| {\n                        let response = ui.code(summary);\n                        if entries.len() < 2 {\n                            response\n                        } else {\n                            response | ui.weak(format!(\" x{}\", entries.len()))\n                        }\n                    })\n                    .inner\n                    .on_hover_ui(|ui| {\n                        ui.spacing_mut().item_spacing.y = 4.0;\n                        ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);\n                        for entry in entries.iter().rev() {\n                            ui.code(entry);\n                        }\n                    });\n                }\n            });\n    }\n}\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[derive(Default)]\npub struct InputEventHistory {\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    history: DeduplicatedHistory,\n\n    include_pointer_movements: bool,\n}\n\nimpl crate::Demo for InputEventHistory {\n    fn name(&self) -> &'static str {\n        \"Input Event History\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .default_width(800.0)\n            .open(open)\n            .resizable(true)\n            .scroll(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for InputEventHistory {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.input(|i| {\n            for event in &i.raw.events {\n                if !self.include_pointer_movements\n                    && matches!(\n                        event,\n                        egui::Event::PointerMoved { .. }\n                            | egui::Event::MouseMoved { .. }\n                            | egui::Event::Touch { .. }\n                    )\n                {\n                    continue;\n                }\n\n                let summary = event_summary(event);\n                let full = format!(\"{event:#?}\");\n                self.history.add(summary, full);\n            }\n        });\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n\n        ui.label(\"Recent history of raw input events to egui.\");\n        ui.label(\"Hover any entry for details.\");\n        ui.checkbox(\n            &mut self.include_pointer_movements,\n            \"Include pointer/mouse movements\",\n        );\n\n        ui.add_space(8.0);\n\n        self.history.ui(ui);\n    }\n}\n\nfn event_summary(event: &egui::Event) -> String {\n    match event {\n        egui::Event::PointerMoved { .. } => \"PointerMoved { .. }\".to_owned(),\n        egui::Event::MouseMoved { .. } => \"MouseMoved { .. }\".to_owned(),\n        egui::Event::Zoom { .. } => \"Zoom { .. }\".to_owned(),\n        egui::Event::Touch { phase, .. } => format!(\"Touch {{ phase: {phase:?}, .. }}\"),\n        egui::Event::MouseWheel { unit, .. } => format!(\"MouseWheel {{ unit: {unit:?}, .. }}\"),\n\n        _ => format!(\"{event:?}\"),\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/input_test.rs",
    "content": "struct HistoryEntry {\n    text: String,\n    repeated: usize,\n}\n\n#[derive(Default)]\nstruct DeduplicatedHistory {\n    history: std::collections::VecDeque<HistoryEntry>,\n}\n\nimpl DeduplicatedHistory {\n    fn add(&mut self, text: String) {\n        if let Some(entry) = self.history.back_mut()\n            && entry.text == text\n        {\n            entry.repeated += 1;\n            return;\n        }\n        self.history.push_back(HistoryEntry { text, repeated: 1 });\n        if self.history.len() > 100 {\n            self.history.pop_front();\n        }\n    }\n\n    fn ui(&self, ui: &mut egui::Ui) {\n        egui::ScrollArea::vertical()\n            .auto_shrink(false)\n            .show(ui, |ui| {\n                ui.spacing_mut().item_spacing.y = 4.0;\n                for HistoryEntry { text, repeated } in self.history.iter().rev() {\n                    ui.horizontal(|ui| {\n                        if text.is_empty() {\n                            ui.weak(\"(empty)\");\n                        } else {\n                            ui.label(text);\n                        }\n                        if 1 < *repeated {\n                            ui.weak(format!(\" x{repeated}\"));\n                        }\n                    });\n                }\n            });\n    }\n}\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[derive(Default)]\npub struct InputTest {\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    history: [DeduplicatedHistory; 4],\n\n    late_interaction: bool,\n\n    show_hovers: bool,\n}\n\nimpl crate::Demo for InputTest {\n    fn name(&self) -> &'static str {\n        \"Input Test\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .default_width(800.0)\n            .open(open)\n            .resizable(true)\n            .scroll(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for InputTest {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.spacing_mut().item_spacing.y = 8.0;\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n\n        ui.horizontal(|ui| {\n            if ui.button(\"Clear\").clicked() {\n                *self = Default::default();\n            }\n\n            ui.checkbox(&mut self.show_hovers, \"Show hover state\");\n        });\n\n        ui.checkbox(&mut self.late_interaction, \"Use Response::interact\");\n\n        ui.label(\"This tests how egui::Response reports events.\\n\\\n            The different buttons are sensitive to different things.\\n\\\n            Try interacting with them with any mouse button by clicking, double-clicking, triple-clicking, or dragging them.\");\n\n        ui.columns(4, |columns| {\n            for (i, (sense_name, sense)) in [\n                (\"Sense::hover\", egui::Sense::hover()),\n                (\"Sense::click\", egui::Sense::click()),\n                (\"Sense::drag\", egui::Sense::drag()),\n                (\"Sense::click_and_drag\", egui::Sense::click_and_drag()),\n            ]\n            .into_iter()\n            .enumerate()\n            {\n                columns[i].push_id(i, |ui| {\n                    let response = if self.late_interaction {\n                        let first_response =\n                            ui.add(egui::Button::new(sense_name).sense(egui::Sense::hover()));\n                        first_response.interact(sense)\n                    } else {\n                        ui.add(egui::Button::new(sense_name).sense(sense))\n                    };\n                    let info = response_summary(&response, self.show_hovers);\n                    self.history[i].add(info.trim().to_owned());\n                    self.history[i].ui(ui);\n                });\n            }\n        });\n    }\n}\n\nfn response_summary(response: &egui::Response, show_hovers: bool) -> String {\n    use std::fmt::Write as _;\n\n    let mut new_info = String::new();\n\n    if show_hovers {\n        if response.hovered() {\n            writeln!(new_info, \"hovered\").ok();\n        }\n        if response.contains_pointer() {\n            writeln!(new_info, \"contains_pointer\").ok();\n        }\n        if response.is_pointer_button_down_on() {\n            writeln!(new_info, \"pointer_down_on\").ok();\n        }\n        if let Some(pos) = response.interact_pointer_pos() {\n            writeln!(new_info, \"response.interact_pointer_pos: {pos:?}\").ok();\n        }\n    }\n\n    for &button in &[\n        egui::PointerButton::Primary,\n        egui::PointerButton::Secondary,\n        egui::PointerButton::Middle,\n        egui::PointerButton::Extra1,\n        egui::PointerButton::Extra2,\n    ] {\n        let button_suffix = if button == egui::PointerButton::Primary {\n            // Reduce visual clutter in common case:\n            String::default()\n        } else {\n            format!(\" by {button:?} button\")\n        };\n\n        // These are in inverse logical/chonological order, because we show them in the ui that way:\n\n        if response.triple_clicked_by(button) {\n            writeln!(new_info, \"Triple-clicked{button_suffix}\").ok();\n        }\n        if response.double_clicked_by(button) {\n            writeln!(new_info, \"Double-clicked{button_suffix}\").ok();\n        }\n        if response.clicked_by(button) {\n            writeln!(new_info, \"Clicked{button_suffix}\").ok();\n        }\n\n        if response.drag_stopped_by(button) {\n            writeln!(new_info, \"Drag stopped{button_suffix}\").ok();\n        }\n        if response.dragged_by(button) {\n            writeln!(new_info, \"Dragged{button_suffix}\").ok();\n        }\n        if response.drag_started_by(button) {\n            writeln!(new_info, \"Drag started{button_suffix}\").ok();\n        }\n    }\n\n    if response.long_touched() {\n        writeln!(new_info, \"Clicked with long-press\").ok();\n    }\n\n    new_info\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/layout_test.rs",
    "content": "use egui::{Align, Direction, Layout, Resize, Slider, Ui, vec2};\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct LayoutTest {\n    // Identical to contents of `egui::Layout`\n    layout: LayoutSettings,\n\n    // Extra for testing wrapping:\n    wrap_column_width: f32,\n    wrap_row_height: f32,\n}\n\nimpl Default for LayoutTest {\n    fn default() -> Self {\n        Self {\n            layout: LayoutSettings::top_down(),\n            wrap_column_width: 150.0,\n            wrap_row_height: 20.0,\n        }\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct LayoutSettings {\n    // Similar to the contents of `egui::Layout`\n    main_dir: Direction,\n    main_wrap: bool,\n    cross_align: Align,\n    cross_justify: bool,\n}\n\nimpl Default for LayoutSettings {\n    fn default() -> Self {\n        Self::top_down()\n    }\n}\n\nimpl LayoutSettings {\n    fn top_down() -> Self {\n        Self {\n            main_dir: Direction::TopDown,\n            main_wrap: false,\n            cross_align: Align::Min,\n            cross_justify: false,\n        }\n    }\n\n    fn top_down_justified_centered() -> Self {\n        Self {\n            main_dir: Direction::TopDown,\n            main_wrap: false,\n            cross_align: Align::Center,\n            cross_justify: true,\n        }\n    }\n\n    fn horizontal_wrapped() -> Self {\n        Self {\n            main_dir: Direction::LeftToRight,\n            main_wrap: true,\n            cross_align: Align::Center,\n            cross_justify: false,\n        }\n    }\n\n    fn layout(&self) -> Layout {\n        Layout::from_main_dir_and_cross_align(self.main_dir, self.cross_align)\n            .with_main_wrap(self.main_wrap)\n            .with_cross_justify(self.cross_justify)\n    }\n}\n\nimpl crate::Demo for LayoutTest {\n    fn name(&self) -> &'static str {\n        \"Layout Test\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for LayoutTest {\n    fn ui(&mut self, ui: &mut Ui) {\n        ui.label(\"Tests and demonstrates the limits of the egui layouts\");\n        self.content_ui(ui);\n        Resize::default()\n            .default_size([150.0, 200.0])\n            .show(ui, |ui| {\n                if self.layout.main_wrap {\n                    if self.layout.main_dir.is_horizontal() {\n                        ui.allocate_ui(\n                            vec2(ui.available_size_before_wrap().x, self.wrap_row_height),\n                            |ui| ui.with_layout(self.layout.layout(), demo_ui),\n                        );\n                    } else {\n                        ui.allocate_ui(\n                            vec2(self.wrap_column_width, ui.available_size_before_wrap().y),\n                            |ui| ui.with_layout(self.layout.layout(), demo_ui),\n                        );\n                    }\n                } else {\n                    ui.with_layout(self.layout.layout(), demo_ui);\n                }\n            });\n        ui.label(\"Resize to see effect\");\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n\nimpl LayoutTest {\n    pub fn content_ui(&mut self, ui: &mut Ui) {\n        ui.horizontal(|ui| {\n            ui.selectable_value(&mut self.layout, LayoutSettings::top_down(), \"Top-down\");\n            ui.selectable_value(\n                &mut self.layout,\n                LayoutSettings::top_down_justified_centered(),\n                \"Top-down, centered and justified\",\n            );\n            ui.selectable_value(\n                &mut self.layout,\n                LayoutSettings::horizontal_wrapped(),\n                \"Horizontal wrapped\",\n            );\n        });\n\n        ui.horizontal(|ui| {\n            ui.label(\"Main Direction:\");\n            for &dir in &[\n                Direction::LeftToRight,\n                Direction::RightToLeft,\n                Direction::TopDown,\n                Direction::BottomUp,\n            ] {\n                ui.radio_value(&mut self.layout.main_dir, dir, format!(\"{dir:?}\"));\n            }\n        });\n\n        ui.horizontal(|ui| {\n            ui.checkbox(&mut self.layout.main_wrap, \"Main wrap\")\n                .on_hover_text(\"Wrap when next widget doesn't fit the current row/column\");\n\n            if self.layout.main_wrap {\n                if self.layout.main_dir.is_horizontal() {\n                    ui.add(Slider::new(&mut self.wrap_row_height, 0.0..=200.0).text(\"Row height\"));\n                } else {\n                    ui.add(\n                        Slider::new(&mut self.wrap_column_width, 0.0..=200.0).text(\"Column width\"),\n                    );\n                }\n            }\n        });\n\n        ui.horizontal(|ui| {\n            ui.label(\"Cross Align:\");\n            for &align in &[Align::Min, Align::Center, Align::Max] {\n                ui.radio_value(&mut self.layout.cross_align, align, format!(\"{align:?}\"));\n            }\n        });\n\n        ui.checkbox(&mut self.layout.cross_justify, \"Cross Justified\")\n            .on_hover_text(\"Try to fill full width/height (e.g. buttons)\");\n    }\n}\n\nfn demo_ui(ui: &mut Ui) {\n    ui.add(egui::Label::new(\"Wrapping text followed by example widgets:\").wrap());\n    let mut dummy = false;\n    ui.checkbox(&mut dummy, \"checkbox\");\n    ui.radio_value(&mut dummy, false, \"radio\");\n    let _ = ui.button(\"button\");\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/manual_layout_test.rs",
    "content": "#[derive(Clone, Copy, Debug, PartialEq)]\nenum WidgetType {\n    Label,\n    Button,\n    TextEdit,\n}\n\n#[derive(Clone, Debug, PartialEq)]\npub struct ManualLayoutTest {\n    widget_offset: egui::Vec2,\n    widget_size: egui::Vec2,\n    widget_type: WidgetType,\n    text_edit_contents: String,\n}\n\nimpl Default for ManualLayoutTest {\n    fn default() -> Self {\n        Self {\n            widget_offset: egui::Vec2::splat(150.0),\n            widget_size: egui::vec2(200.0, 100.0),\n            widget_type: WidgetType::Button,\n            text_edit_contents: crate::LOREM_IPSUM.to_owned(),\n        }\n    }\n}\n\nimpl crate::Demo for ManualLayoutTest {\n    fn name(&self) -> &'static str {\n        \"Manual Layout Test\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .resizable(false)\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for ManualLayoutTest {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        egui::reset_button(ui, self, \"Reset\");\n\n        let Self {\n            widget_offset,\n            widget_size,\n            widget_type,\n            text_edit_contents,\n        } = self;\n        ui.horizontal(|ui| {\n            ui.label(\"Test widget:\");\n            ui.radio_value(widget_type, WidgetType::Button, \"Button\");\n            ui.radio_value(widget_type, WidgetType::Label, \"Label\");\n            ui.radio_value(widget_type, WidgetType::TextEdit, \"TextEdit\");\n        });\n        egui::Grid::new(\"pos_size\").show(ui, |ui| {\n            ui.label(\"Widget position:\");\n            ui.add(egui::Slider::new(&mut widget_offset.x, 0.0..=400.0));\n            ui.add(egui::Slider::new(&mut widget_offset.y, 0.0..=400.0));\n            ui.end_row();\n\n            ui.label(\"Widget size:\");\n            ui.add(egui::Slider::new(&mut widget_size.x, 0.0..=400.0));\n            ui.add(egui::Slider::new(&mut widget_size.y, 0.0..=400.0));\n            ui.end_row();\n        });\n\n        let widget_rect =\n            egui::Rect::from_min_size(ui.min_rect().min + *widget_offset, *widget_size);\n\n        ui.add(crate::egui_github_link_file!());\n\n        // Showing how to place a widget anywhere in the [`Ui`]:\n        match *widget_type {\n            WidgetType::Button => {\n                ui.put(widget_rect, egui::Button::new(\"Example button\"));\n            }\n            WidgetType::Label => {\n                ui.put(widget_rect, egui::Label::new(\"Example label\"));\n            }\n            WidgetType::TextEdit => {\n                ui.put(widget_rect, egui::TextEdit::multiline(text_edit_contents));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/mod.rs",
    "content": "mod clipboard_test;\nmod cursor_test;\nmod grid_test;\nmod id_test;\nmod input_event_history;\nmod input_test;\nmod layout_test;\nmod manual_layout_test;\nmod svg_test;\nmod tessellation_test;\nmod window_resize_test;\n\npub use clipboard_test::ClipboardTest;\npub use cursor_test::CursorTest;\npub use grid_test::GridTest;\npub use id_test::IdTest;\npub use input_event_history::InputEventHistory;\npub use input_test::InputTest;\npub use layout_test::LayoutTest;\npub use manual_layout_test::ManualLayoutTest;\npub use svg_test::SvgTest;\npub use tessellation_test::TessellationTest;\npub use window_resize_test::WindowResizeTest;\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/svg_test.rs",
    "content": "pub struct SvgTest {\n    color: egui::Color32,\n}\n\nimpl Default for SvgTest {\n    fn default() -> Self {\n        Self {\n            color: egui::Color32::LIGHT_RED,\n        }\n    }\n}\n\nimpl crate::Demo for SvgTest {\n    fn name(&self) -> &'static str {\n        \"SVG Test\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for SvgTest {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        let Self { color } = self;\n        ui.color_edit_button_srgba(color);\n        let img_src = egui::include_image!(\"../../../data/peace.svg\");\n\n        // First paint a small version, sized the same as the source…\n        ui.add(\n            egui::Image::new(img_src.clone())\n                .fit_to_original_size(1.0)\n                .tint(*color),\n        );\n\n        // …then a big one, to make sure they are both crisp\n        ui.add(egui::Image::new(img_src).tint(*color));\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/tessellation_test.rs",
    "content": "use std::sync::Arc;\n\nuse egui::{\n    Color32, Pos2, Rect, Sense, StrokeKind, Vec2,\n    emath::{GuiRounding as _, TSTransform},\n    epaint::{self, RectShape},\n    vec2,\n};\n\n#[derive(Clone, Debug, PartialEq)]\npub struct TessellationTest {\n    shape: RectShape,\n\n    magnification_pixel_size: f32,\n    tessellation_options: epaint::TessellationOptions,\n    paint_edges: bool,\n}\n\nimpl Default for TessellationTest {\n    fn default() -> Self {\n        let shape = Self::interesting_shapes()[0].1.clone();\n        Self {\n            shape,\n            magnification_pixel_size: 12.0,\n            tessellation_options: Default::default(),\n            paint_edges: false,\n        }\n    }\n}\n\nimpl TessellationTest {\n    fn interesting_shapes() -> Vec<(&'static str, RectShape)> {\n        fn sized(size: impl Into<Vec2>) -> Rect {\n            Rect::from_center_size(Pos2::ZERO, size.into())\n        }\n\n        let baby_blue = Color32::from_rgb(0, 181, 255);\n\n        let mut shapes = vec![\n            (\n                \"Normal\",\n                RectShape::new(\n                    sized([20.0, 16.0]),\n                    2.0,\n                    baby_blue,\n                    (1.0, Color32::WHITE),\n                    StrokeKind::Inside,\n                ),\n            ),\n            (\n                \"Minimal rounding\",\n                RectShape::new(\n                    sized([20.0, 16.0]),\n                    1.0,\n                    baby_blue,\n                    (1.0, Color32::WHITE),\n                    StrokeKind::Inside,\n                ),\n            ),\n            (\n                \"Thin filled\",\n                RectShape::filled(sized([20.0, 0.5]), 2.0, baby_blue),\n            ),\n            (\n                \"Thin stroked\",\n                RectShape::new(\n                    sized([20.0, 0.5]),\n                    2.0,\n                    baby_blue,\n                    (0.5, Color32::WHITE),\n                    StrokeKind::Inside,\n                ),\n            ),\n            (\n                \"Blurred\",\n                RectShape::filled(sized([20.0, 16.0]), 2.0, baby_blue).with_blur_width(50.0),\n            ),\n            (\n                \"Thick stroke, minimal rounding\",\n                RectShape::new(\n                    sized([20.0, 16.0]),\n                    1.0,\n                    baby_blue,\n                    (3.0, Color32::WHITE),\n                    StrokeKind::Inside,\n                ),\n            ),\n            (\n                \"Blurred stroke\",\n                RectShape::new(\n                    sized([20.0, 16.0]),\n                    0.0,\n                    baby_blue,\n                    (5.0, Color32::WHITE),\n                    StrokeKind::Inside,\n                )\n                .with_blur_width(5.0),\n            ),\n            (\n                \"Additive rectangle\",\n                RectShape::new(\n                    sized([24.0, 12.0]),\n                    0.0,\n                    egui::Color32::LIGHT_RED.additive().linear_multiply(0.025),\n                    (\n                        1.0,\n                        egui::Color32::LIGHT_BLUE.additive().linear_multiply(0.1),\n                    ),\n                    StrokeKind::Outside,\n                ),\n            ),\n        ];\n\n        for (_name, shape) in &mut shapes {\n            shape.round_to_pixels = Some(true);\n        }\n\n        shapes\n    }\n}\n\nimpl crate::Demo for TessellationTest {\n    fn name(&self) -> &'static str {\n        \"Tessellation Test\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .resizable(false)\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for TessellationTest {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.add(crate::egui_github_link_file!());\n        egui::reset_button(ui, self, \"Reset\");\n\n        ui.horizontal(|ui| {\n            ui.group(|ui| {\n                ui.vertical(|ui| {\n                    rect_shape_ui(ui, &mut self.shape);\n                });\n            });\n\n            ui.group(|ui| {\n                ui.vertical(|ui| {\n                    ui.heading(\"Real size\");\n                    egui::Frame::dark_canvas(ui.style()).show(ui, |ui| {\n                        let (resp, painter) =\n                            ui.allocate_painter(Vec2::splat(128.0), Sense::hover());\n                        let canvas = resp.rect;\n\n                        let pixels_per_point = ui.pixels_per_point();\n                        let pixel_size = 1.0 / pixels_per_point;\n                        let mut shape = self.shape.clone();\n                        shape.rect = Rect::from_center_size(canvas.center(), shape.rect.size())\n                            .round_to_pixel_center(pixels_per_point)\n                            .translate(Vec2::new(pixel_size / 3.0, pixel_size / 5.0)); // Intentionally offset to test the effect of rounding\n                        painter.add(shape);\n                    });\n                });\n            });\n        });\n\n        ui.group(|ui| {\n            ui.heading(\"Zoomed in\");\n            let magnification_pixel_size = &mut self.magnification_pixel_size;\n            let tessellation_options = &mut self.tessellation_options;\n\n            egui::Grid::new(\"TessellationOptions\")\n                .num_columns(2)\n                .spacing([12.0, 8.0])\n                .striped(true)\n                .show(ui, |ui| {\n                    ui.label(\"Magnification\");\n                    ui.add(\n                        egui::DragValue::new(magnification_pixel_size)\n                            .speed(0.5)\n                            .range(1.0..=32.0),\n                    );\n                    ui.end_row();\n\n                    ui.label(\"Feathering width\");\n                    ui.horizontal(|ui| {\n                        ui.checkbox(&mut tessellation_options.feathering, \"\");\n                        ui.add_enabled(\n                            tessellation_options.feathering,\n                            egui::DragValue::new(\n                                &mut tessellation_options.feathering_size_in_pixels,\n                            )\n                            .speed(0.1)\n                            .range(0.0..=4.0)\n                            .suffix(\" px\"),\n                        );\n                    });\n                    ui.end_row();\n\n                    ui.label(\"Paint edges\");\n                    ui.checkbox(&mut self.paint_edges, \"\");\n                    ui.end_row();\n                });\n\n            let magnification_pixel_size = *magnification_pixel_size;\n\n            egui::Frame::dark_canvas(ui.style()).show(ui, |ui| {\n                let (resp, painter) = ui.allocate_painter(\n                    magnification_pixel_size * (self.shape.rect.size() + Vec2::splat(8.0)),\n                    Sense::hover(),\n                );\n                let canvas = resp.rect;\n\n                let mut shape = self.shape.clone();\n                shape.rect = shape.rect.translate(Vec2::new(1.0 / 3.0, 1.0 / 5.0)); // Intentionally offset to test the effect of rounding\n\n                let mut mesh = epaint::Mesh::default();\n                let mut tessellator = epaint::Tessellator::new(\n                    1.0,\n                    *tessellation_options,\n                    ui.fonts(|f| f.font_image_size()),\n                    vec![],\n                );\n                tessellator.tessellate_rect(&shape, &mut mesh);\n\n                // Scale and position the mesh:\n                mesh.transform(\n                    TSTransform::from_translation(canvas.center().to_vec2())\n                        * TSTransform::from_scaling(magnification_pixel_size),\n                );\n                let mesh = Arc::new(mesh);\n                painter.add(epaint::Shape::mesh(Arc::clone(&mesh)));\n\n                if self.paint_edges {\n                    let stroke = epaint::Stroke::new(0.5, Color32::MAGENTA);\n                    for triangle in mesh.triangles() {\n                        let a = mesh.vertices[triangle[0] as usize];\n                        let b = mesh.vertices[triangle[1] as usize];\n                        let c = mesh.vertices[triangle[2] as usize];\n\n                        painter.line_segment([a.pos, b.pos], stroke);\n                        painter.line_segment([b.pos, c.pos], stroke);\n                        painter.line_segment([c.pos, a.pos], stroke);\n                    }\n                }\n\n                if 3.0 < magnification_pixel_size {\n                    // Draw pixel centers:\n                    let pixel_radius = 0.75;\n                    let pixel_color = Color32::GRAY;\n                    for yi in 0.. {\n                        let y = (yi as f32 + 0.5) * magnification_pixel_size;\n                        if y > canvas.height() / 2.0 {\n                            break;\n                        }\n                        for xi in 0.. {\n                            let x = (xi as f32 + 0.5) * magnification_pixel_size;\n                            if x > canvas.width() / 2.0 {\n                                break;\n                            }\n                            for offset in [vec2(x, y), vec2(x, -y), vec2(-x, y), vec2(-x, -y)] {\n                                painter.circle_filled(\n                                    canvas.center() + offset,\n                                    pixel_radius,\n                                    pixel_color,\n                                );\n                            }\n                        }\n                    }\n                }\n            });\n        });\n    }\n}\n\nfn rect_shape_ui(ui: &mut egui::Ui, shape: &mut RectShape) {\n    egui::ComboBox::from_id_salt(\"prefabs\")\n        .selected_text(\"Prefabs\")\n        .show_ui(ui, |ui| {\n            for (name, prefab) in TessellationTest::interesting_shapes() {\n                ui.selectable_value(shape, prefab, name);\n            }\n        });\n\n    ui.add_space(4.0);\n\n    let RectShape {\n        rect,\n        corner_radius,\n        fill,\n        stroke,\n        stroke_kind,\n        blur_width,\n        round_to_pixels,\n        brush: _,\n    } = shape;\n\n    let round_to_pixels = round_to_pixels.get_or_insert(true);\n\n    egui::Grid::new(\"RectShape\")\n        .num_columns(2)\n        .spacing([12.0, 8.0])\n        .striped(true)\n        .show(ui, |ui| {\n            ui.label(\"Size\");\n            ui.horizontal(|ui| {\n                let mut size = rect.size();\n                ui.add(\n                    egui::DragValue::new(&mut size.x)\n                        .speed(0.2)\n                        .range(0.0..=64.0),\n                );\n                ui.add(\n                    egui::DragValue::new(&mut size.y)\n                        .speed(0.2)\n                        .range(0.0..=64.0),\n                );\n                *rect = Rect::from_center_size(Pos2::ZERO, size);\n            });\n            ui.end_row();\n\n            ui.label(\"Corner radius\");\n            ui.add(corner_radius);\n            ui.end_row();\n\n            ui.label(\"Fill\");\n            ui.color_edit_button_srgba(fill);\n            ui.end_row();\n\n            ui.label(\"Stroke\");\n            ui.add(stroke);\n            ui.end_row();\n\n            ui.label(\"Stroke kind\");\n            ui.horizontal(|ui| {\n                ui.selectable_value(stroke_kind, StrokeKind::Inside, \"Inside\");\n                ui.selectable_value(stroke_kind, StrokeKind::Middle, \"Middle\");\n                ui.selectable_value(stroke_kind, StrokeKind::Outside, \"Outside\");\n            });\n            ui.end_row();\n\n            ui.label(\"Blur width\");\n            ui.add(\n                egui::DragValue::new(blur_width)\n                    .speed(0.5)\n                    .range(0.0..=20.0),\n            );\n            ui.end_row();\n\n            ui.label(\"Round to pixels\");\n            ui.checkbox(round_to_pixels, \"\");\n            ui.end_row();\n        });\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::View as _;\n    use egui_kittest::SnapshotResults;\n\n    use super::*;\n\n    #[test]\n    fn snapshot_tessellation_test() {\n        let mut results = SnapshotResults::new();\n        for (name, shape) in TessellationTest::interesting_shapes() {\n            let mut test = TessellationTest {\n                shape,\n                ..Default::default()\n            };\n            let mut harness = egui_kittest::Harness::new_ui(|ui| {\n                test.ui(ui);\n            });\n\n            harness.fit_contents();\n            harness.run();\n\n            harness.snapshot(format!(\"tessellation_test/{name}\"));\n            results.extend_harness(&mut harness);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tests/window_resize_test.rs",
    "content": "pub struct WindowResizeTest {\n    text: String,\n}\n\nimpl Default for WindowResizeTest {\n    fn default() -> Self {\n        Self {\n            text: crate::LOREM_IPSUM_LONG.to_owned(),\n        }\n    }\n}\n\nimpl crate::Demo for WindowResizeTest {\n    fn name(&self) -> &'static str {\n        \"Window Resize Test\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        use egui::{Resize, ScrollArea, TextEdit, Window};\n\n        Window::new(\"↔ auto-sized\")\n            .open(open)\n            .auto_sized()\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                ui.label(\"This window will auto-size based on its contents.\");\n                ui.heading(\"Resize this area:\");\n                Resize::default().show(ui, |ui| {\n                    lorem_ipsum(ui, crate::LOREM_IPSUM);\n                });\n                ui.heading(\"Resize the above area!\");\n            });\n\n        Window::new(\"↔ resizable + scroll\")\n            .open(open)\n            .vscroll(true)\n            .resizable(true)\n            .default_height(300.0)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                ui.label(\n                    \"This window is resizable and has a scroll area. You can shrink it to any size.\",\n                );\n                ui.separator();\n                lorem_ipsum(ui, crate::LOREM_IPSUM_LONG);\n            });\n\n        Window::new(\"↔ resizable + embedded scroll\")\n            .open(open)\n            .vscroll(false)\n            .resizable(true)\n            .default_height(300.0)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                ui.label(\"This window is resizable but has no built-in scroll area.\");\n                ui.label(\"However, we have a sub-region with a scroll bar:\");\n                ui.separator();\n                ScrollArea::vertical().show(ui, |ui| {\n                    let lorem_ipsum_extra_long =\n                        format!(\"{}\\n\\n{}\", crate::LOREM_IPSUM_LONG, crate::LOREM_IPSUM_LONG);\n                    lorem_ipsum(ui, &lorem_ipsum_extra_long);\n                });\n                // ui.heading(\"Some additional text here, that should also be visible\"); // this works, but messes with the resizing a bit\n            });\n\n        Window::new(\"↔ resizable without scroll\")\n            .open(open)\n            .vscroll(false)\n            .resizable(true)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                ui.label(\"This window is resizable but has no scroll area. This means it can only be resized to a size where all the contents is visible.\");\n                ui.label(\"egui will not clip the contents of a window, nor add whitespace to it.\");\n                ui.separator();\n                lorem_ipsum(ui, crate::LOREM_IPSUM);\n            });\n\n        Window::new(\"↔ resizable with TextEdit\")\n            .open(open)\n            .vscroll(false)\n            .resizable(true)\n            .default_height(300.0)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                ui.label(\"Shows how you can fill an area with a widget.\");\n                ui.add_sized(ui.available_size(), TextEdit::multiline(&mut self.text));\n            });\n\n        Window::new(\"↔ freely resized\")\n            .open(open)\n            .vscroll(false)\n            .resizable(true)\n            .default_size([250.0, 150.0])\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                ui.label(\"This window has empty space that fills up the available space, preventing auto-shrink.\");\n                ui.vertical_centered(|ui| {\n                    ui.add(crate::egui_github_link_file!());\n                });\n                ui.allocate_space(ui.available_size());\n            });\n    }\n}\n\nfn lorem_ipsum(ui: &mut egui::Ui, text: &str) {\n    ui.with_layout(\n        egui::Layout::top_down(egui::Align::LEFT).with_cross_justify(true),\n        |ui| {\n            ui.label(egui::RichText::new(text).weak());\n        },\n    );\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/text_edit.rs",
    "content": "/// Showcase [`egui::TextEdit`].\n#[derive(PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct TextEditDemo {\n    pub text: String,\n}\n\nimpl Default for TextEditDemo {\n    fn default() -> Self {\n        Self {\n            text: \"Edit this text\".to_owned(),\n        }\n    }\n}\n\nimpl crate::Demo for TextEditDemo {\n    fn name(&self) -> &'static str {\n        \"🖹 TextEdit\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for TextEditDemo {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n\n        let Self { text } = self;\n\n        ui.horizontal(|ui| {\n            ui.spacing_mut().item_spacing.x = 0.0;\n            ui.label(\"Advanced usage of \");\n            ui.code(\"TextEdit\");\n            ui.label(\".\");\n        });\n\n        let output = egui::TextEdit::multiline(text)\n            .hint_text(\"Type something!\")\n            .show(ui);\n\n        ui.horizontal(|ui| {\n            ui.spacing_mut().item_spacing.x = 0.0;\n            ui.label(\"Selected text: \");\n            if let Some(text_cursor_range) = output.cursor_range {\n                let selected_text = text_cursor_range.slice_str(text);\n                ui.code(selected_text);\n            }\n        });\n\n        let anything_selected = output.cursor_range.is_some_and(|cursor| !cursor.is_empty());\n\n        ui.add_enabled(\n            anything_selected,\n            egui::Label::new(\"Press ctrl+Y to toggle the case of selected text (cmd+Y on Mac)\"),\n        );\n\n        if output.response.has_focus()\n            && ui.input_mut(|i| i.consume_key(egui::Modifiers::COMMAND, egui::Key::Y))\n            && let Some(text_cursor_range) = output.cursor_range\n        {\n            use egui::TextBuffer as _;\n            let selected_chars = text_cursor_range.as_sorted_char_range();\n            let selected_text = text.char_range(selected_chars.clone());\n            let upper_case = selected_text.to_uppercase();\n            let new_text = if selected_text == upper_case {\n                selected_text.to_lowercase()\n            } else {\n                upper_case\n            };\n            text.delete_char_range(selected_chars.clone());\n            text.insert_text(&new_text, selected_chars.start);\n        }\n\n        ui.horizontal(|ui| {\n            ui.label(\"Move cursor to the:\");\n\n            if ui.button(\"start\").clicked() {\n                let text_edit_id = output.response.id;\n                if let Some(mut state) = egui::TextEdit::load_state(ui.ctx(), text_edit_id) {\n                    let ccursor = egui::text::CCursor::new(0);\n                    state\n                        .cursor\n                        .set_char_range(Some(egui::text::CCursorRange::one(ccursor)));\n                    state.store(ui.ctx(), text_edit_id);\n                    ui.memory_mut(|mem| mem.request_focus(text_edit_id)); // give focus back to the [`TextEdit`].\n                }\n            }\n\n            if ui.button(\"end\").clicked() {\n                let text_edit_id = output.response.id;\n                if let Some(mut state) = egui::TextEdit::load_state(ui.ctx(), text_edit_id) {\n                    let ccursor = egui::text::CCursor::new(text.chars().count());\n                    state\n                        .cursor\n                        .set_char_range(Some(egui::text::CCursorRange::one(ccursor)));\n                    state.store(ui.ctx(), text_edit_id);\n                    ui.memory_mut(|mem| mem.request_focus(text_edit_id)); // give focus back to the [`TextEdit`].\n                }\n            }\n        });\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use egui::{CentralPanel, Key, Modifiers, accesskit};\n    use egui_kittest::Harness;\n    use egui_kittest::kittest::Queryable as _;\n\n    #[test]\n    pub fn should_type() {\n        let text = \"Hello, world!\".to_owned();\n        let mut harness = Harness::new_ui_state(\n            move |ui, text| {\n                CentralPanel::default().show_inside(ui, |ui| {\n                    ui.text_edit_singleline(text);\n                });\n            },\n            text,\n        );\n\n        harness.run();\n\n        let text_edit = harness.get_by_role(accesskit::Role::TextInput);\n        assert_eq!(text_edit.value().as_deref(), Some(\"Hello, world!\"));\n        text_edit.focus();\n\n        harness.key_press_modifiers(Modifiers::COMMAND, Key::A);\n        text_edit.type_text(\"Hi \");\n\n        harness.run();\n        harness\n            .get_by_role(accesskit::Role::TextInput)\n            .type_text(\"there!\");\n\n        harness.run();\n        let text_edit = harness.get_by_role(accesskit::Role::TextInput);\n        assert_eq!(text_edit.value().as_deref(), Some(\"Hi there!\"));\n        assert_eq!(harness.state(), \"Hi there!\");\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/text_layout.rs",
    "content": "/// Showcase text layout\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct TextLayoutDemo {\n    break_anywhere: bool,\n    max_rows: usize,\n    overflow_character: Option<char>,\n    extra_letter_spacing: f32,\n    line_height_pixels: u32,\n    lorem_ipsum: bool,\n}\n\nimpl Default for TextLayoutDemo {\n    fn default() -> Self {\n        Self {\n            max_rows: 6,\n            break_anywhere: true,\n            overflow_character: Some('…'),\n            extra_letter_spacing: 0.0,\n            line_height_pixels: 0,\n            lorem_ipsum: true,\n        }\n    }\n}\n\nimpl crate::Demo for TextLayoutDemo {\n    fn name(&self) -> &'static str {\n        \"🖹 Text Layout\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(true)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for TextLayoutDemo {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        let Self {\n            break_anywhere,\n            max_rows,\n            overflow_character,\n            extra_letter_spacing,\n            line_height_pixels,\n            lorem_ipsum,\n        } = self;\n\n        use egui::text::LayoutJob;\n\n        let pixels_per_point = ui.pixels_per_point();\n        let points_per_pixel = 1.0 / pixels_per_point;\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file_line!());\n        });\n\n        ui.add_space(12.0);\n\n        egui::Grid::new(\"TextLayoutDemo\")\n            .num_columns(2)\n            .show(ui, |ui| {\n                ui.label(\"Max rows:\");\n                ui.add(egui::DragValue::new(max_rows));\n                ui.end_row();\n\n                ui.label(\"Line-break:\");\n                ui.horizontal(|ui| {\n                    ui.radio_value(break_anywhere, false, \"word boundaries\");\n                    ui.radio_value(break_anywhere, true, \"anywhere\");\n                });\n                ui.end_row();\n\n                ui.label(\"Overflow character:\");\n                ui.horizontal(|ui| {\n                    ui.selectable_value(overflow_character, None, \"None\");\n                    ui.selectable_value(overflow_character, Some('…'), \"…\");\n                    ui.selectable_value(overflow_character, Some('—'), \"—\");\n                    ui.selectable_value(overflow_character, Some('-'), \"  -  \");\n                });\n                ui.end_row();\n\n                ui.label(\"Extra letter spacing:\");\n                ui.add(egui::DragValue::new(extra_letter_spacing).speed(0.1));\n                ui.end_row();\n\n                ui.label(\"Line height:\");\n                ui.horizontal(|ui| {\n                    if ui\n                        .selectable_label(*line_height_pixels == 0, \"Default\")\n                        .clicked()\n                    {\n                        *line_height_pixels = 0;\n                    }\n                    if ui\n                        .selectable_label(*line_height_pixels != 0, \"Custom\")\n                        .clicked()\n                    {\n                        *line_height_pixels = (pixels_per_point * 20.0).round() as _;\n                    }\n                    if *line_height_pixels != 0 {\n                        ui.add(egui::DragValue::new(line_height_pixels).suffix(\" pixels\"));\n                    }\n                });\n                ui.end_row();\n\n                ui.label(\"Text:\");\n                ui.horizontal(|ui| {\n                    ui.selectable_value(lorem_ipsum, true, \"Lorem Ipsum\");\n                    ui.selectable_value(lorem_ipsum, false, \"La Pasionaria\");\n                });\n            });\n\n        ui.add_space(12.0);\n\n        let text = if *lorem_ipsum {\n            crate::LOREM_IPSUM_LONG\n        } else {\n            TO_BE_OR_NOT_TO_BE\n        };\n\n        egui::ScrollArea::vertical()\n            .auto_shrink(false)\n            .show(ui, |ui| {\n                let line_height = (*line_height_pixels != 0)\n                    .then_some(points_per_pixel * *line_height_pixels as f32);\n\n                let mut job = LayoutJob::single_section(\n                    text.to_owned(),\n                    egui::TextFormat {\n                        extra_letter_spacing: *extra_letter_spacing,\n                        line_height,\n                        ..Default::default()\n                    },\n                );\n                job.wrap = egui::text::TextWrapping {\n                    max_rows: *max_rows,\n                    break_anywhere: *break_anywhere,\n                    overflow_character: *overflow_character,\n                    ..Default::default()\n                };\n\n                // NOTE: `Label` overrides some of the wrapping settings, e.g. wrap width\n                ui.label(job);\n            });\n    }\n}\n\n/// Excerpt from Dolores Ibárruri's farwel speech to the International Brigades:\nconst TO_BE_OR_NOT_TO_BE: &str = \"Mothers! Women!\\n\nWhen the years pass by and the wounds of war are stanched; when the memory of the sad and bloody days dissipates in a present of liberty, of peace and of wellbeing; when the rancor have died out and pride in a free country is felt equally by all Spaniards, speak to your children. Tell them of these men of the International Brigades.\\n\\\n\\n\\\nRecount for them how, coming over seas and mountains, crossing frontiers bristling with bayonets, sought by raving dogs thirsting to tear their flesh, these men reached our country as crusaders for freedom, to fight and die for Spain’s liberty and independence threatened by German and Italian fascism. \\\nThey gave up everything — their loves, their countries, home and fortune, fathers, mothers, wives, brothers, sisters and children — and they came and said to us: “We are here. Your cause, Spain’s cause, is ours. It is the cause of all advanced and progressive mankind.”\\n\\\n\\n\\\n- Dolores Ibárruri, 1938\";\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/toggle_switch.rs",
    "content": "//! Source code example of how to create your own widget.\n//! This is meant to be read as a tutorial, hence the plethora of comments.\n\n/// iOS-style toggle switch:\n///\n/// ``` text\n///      _____________\n///     /       /.....\\\n///    |       |.......|\n///     \\_______\\_____/\n/// ```\n///\n/// ## Example:\n/// ``` ignore\n/// toggle_ui(ui, &mut my_bool);\n/// ```\npub fn toggle_ui(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {\n    // Widget code can be broken up in four steps:\n    //  1. Decide a size for the widget\n    //  2. Allocate space for it\n    //  3. Handle interactions with the widget (if any)\n    //  4. Paint the widget\n\n    // 1. Deciding widget size:\n    // You can query the `ui` how much space is available,\n    // but in this example we have a fixed size widget based on the height of a standard button:\n    let desired_size = ui.spacing().interact_size.y * egui::vec2(2.0, 1.0);\n\n    // 2. Allocating space:\n    // This is where we get a region of the screen assigned.\n    // We also tell the Ui to sense clicks in the allocated region.\n    let (rect, mut response) = ui.allocate_exact_size(desired_size, egui::Sense::click());\n\n    // 3. Interact: Time to check for clicks!\n    if response.clicked() {\n        *on = !*on;\n        response.mark_changed(); // report back that the value changed\n    }\n\n    // Attach some meta-data to the response which can be used by screen readers:\n    response.widget_info(|| {\n        egui::WidgetInfo::selected(egui::WidgetType::Checkbox, ui.is_enabled(), *on, \"\")\n    });\n\n    // 4. Paint!\n    // Make sure we need to paint:\n    if ui.is_rect_visible(rect) {\n        // Let's ask for a simple animation from egui.\n        // egui keeps track of changes in the boolean associated with the id and\n        // returns an animated value in the 0-1 range for how much \"on\" we are.\n        let how_on = ui.ctx().animate_bool_responsive(response.id, *on);\n        // We will follow the current style by asking\n        // \"how should something that is being interacted with be painted?\".\n        // This will, for instance, give us different colors when the widget is hovered or clicked.\n        let visuals = ui.style().interact_selectable(&response, *on);\n        // All coordinates are in absolute screen coordinates so we use `rect` to place the elements.\n        let rect = rect.expand(visuals.expansion);\n        let radius = 0.5 * rect.height();\n        ui.painter().rect(\n            rect,\n            radius,\n            visuals.bg_fill,\n            visuals.bg_stroke,\n            egui::StrokeKind::Inside,\n        );\n        // Paint the circle, animating it from left to right with `how_on`:\n        let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on);\n        let center = egui::pos2(circle_x, rect.center().y);\n        ui.painter()\n            .circle(center, 0.75 * radius, visuals.bg_fill, visuals.fg_stroke);\n    }\n\n    // All done! Return the interaction response so the user can check what happened\n    // (hovered, clicked, ...) and maybe show a tooltip:\n    response\n}\n\n/// Here is the same code again, but a bit more compact:\n#[expect(dead_code)]\nfn toggle_ui_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {\n    let desired_size = ui.spacing().interact_size.y * egui::vec2(2.0, 1.0);\n    let (rect, mut response) = ui.allocate_exact_size(desired_size, egui::Sense::click());\n    if response.clicked() {\n        *on = !*on;\n        response.mark_changed();\n    }\n    response.widget_info(|| {\n        egui::WidgetInfo::selected(egui::WidgetType::Checkbox, ui.is_enabled(), *on, \"\")\n    });\n\n    if ui.is_rect_visible(rect) {\n        let how_on = ui.ctx().animate_bool_responsive(response.id, *on);\n        let visuals = ui.style().interact_selectable(&response, *on);\n        let rect = rect.expand(visuals.expansion);\n        let radius = 0.5 * rect.height();\n        ui.painter().rect(\n            rect,\n            radius,\n            visuals.bg_fill,\n            visuals.bg_stroke,\n            egui::StrokeKind::Inside,\n        );\n        let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on);\n        let center = egui::pos2(circle_x, rect.center().y);\n        ui.painter()\n            .circle(center, 0.75 * radius, visuals.bg_fill, visuals.fg_stroke);\n    }\n\n    response\n}\n\n// A wrapper that allows the more idiomatic usage pattern: `ui.add(toggle(&mut my_bool))`\n/// iOS-style toggle switch.\n///\n/// ## Example:\n/// ``` ignore\n/// ui.add(toggle(&mut my_bool));\n/// ```\npub fn toggle(on: &mut bool) -> impl egui::Widget + '_ {\n    move |ui: &mut egui::Ui| toggle_ui(ui, on)\n}\n\npub fn url_to_file_source_code() -> String {\n    format!(\"https://github.com/emilk/egui/blob/main/{}\", file!())\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/tooltips.rs",
    "content": "#[derive(Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Tooltips {\n    enabled: bool,\n}\n\nimpl Default for Tooltips {\n    fn default() -> Self {\n        Self { enabled: true }\n    }\n}\n\nimpl crate::Demo for Tooltips {\n    fn name(&self) -> &'static str {\n        \"🗖 Tooltips\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        use crate::View as _;\n        egui::Window::new(\"Tooltips\")\n            .constrain(false) // So we can test how tooltips behave close to the screen edge\n            .resizable(true)\n            .default_size([450.0, 300.0])\n            .scroll(false)\n            .open(open)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl crate::View for Tooltips {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.spacing_mut().item_spacing.y = 8.0;\n\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file_line!());\n        });\n\n        egui::Panel::right(\"scroll_test\").show_inside(ui, |ui| {\n            ui.label(\n                \"The scroll area below has many labels with interactive tooltips. \\\n                 The purpose is to test that the tooltips close when you scroll.\",\n            )\n            .on_hover_text(\"Try hovering a label below, then scroll!\");\n            egui::ScrollArea::vertical()\n                .auto_shrink(false)\n                .show(ui, |ui| {\n                    for i in 0..1000 {\n                        ui.label(format!(\"This is line {i}\")).on_hover_ui(|ui| {\n                            ui.style_mut().interaction.selectable_labels = true;\n                            ui.label(\n                            \"This tooltip is interactive, because the text in it is selectable.\",\n                        );\n                        });\n                    }\n                });\n        });\n\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            self.misc_tests(ui);\n        });\n    }\n}\n\nimpl Tooltips {\n    fn misc_tests(&mut self, ui: &mut egui::Ui) {\n        ui.label(\"All labels in this demo have tooltips.\")\n            .on_hover_text(\"Yes, even this one.\");\n\n        ui.label(\"Some widgets have multiple tooltips!\")\n            .on_hover_text(\"The first tooltip.\")\n            .on_hover_text(\"The second tooltip.\");\n\n        ui.label(\"Tooltips can contain interactive widgets.\")\n            .on_hover_ui(|ui| {\n                ui.label(\"This tooltip contains a link:\");\n                ui.hyperlink_to(\"www.egui.rs\", \"https://www.egui.rs/\")\n                    .on_hover_text(\"The tooltip has a tooltip in it!\");\n            });\n\n        ui.label(\"You can put selectable text in tooltips too.\")\n            .on_hover_ui(|ui| {\n                ui.style_mut().interaction.selectable_labels = true;\n                ui.label(\"You can select this text.\");\n            });\n\n        ui.label(\"This tooltip shows at the mouse cursor.\")\n            .on_hover_text_at_pointer(\"Move me around!!\");\n\n        ui.separator(); // ---------------------------------------------------------\n\n        let tooltip_ui = |ui: &mut egui::Ui| {\n            ui.horizontal(|ui| {\n                ui.label(\"This tooltip was created with\");\n                ui.code(\".on_hover_ui(…)\");\n            });\n        };\n        let disabled_tooltip_ui = |ui: &mut egui::Ui| {\n            ui.label(\"A different tooltip when widget is disabled.\");\n            ui.horizontal(|ui| {\n                ui.label(\"This tooltip was created with\");\n                ui.code(\".on_disabled_hover_ui(…)\");\n            });\n        };\n\n        ui.label(\"You can have different tooltips depending on whether or not a widget is enabled:\")\n            .on_hover_text(\"Check the tooltip of the button below, and see how it changes depending on whether or not it is enabled.\");\n\n        ui.horizontal(|ui| {\n            ui.checkbox(&mut self.enabled, \"Enabled\")\n                .on_hover_text(\"Controls whether or not the following button is enabled.\");\n\n            ui.add_enabled(self.enabled, egui::Button::new(\"Sometimes clickable\"))\n                .on_hover_ui(tooltip_ui)\n                .on_disabled_hover_ui(disabled_tooltip_ui);\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/undo_redo.rs",
    "content": "use egui::{Button, util::undoer::Undoer};\n\n#[derive(Debug, PartialEq, Eq, Clone)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct State {\n    pub toggle_value: bool,\n    pub text: String,\n}\n\nimpl Default for State {\n    fn default() -> Self {\n        Self {\n            toggle_value: Default::default(),\n            text: \"Text with undo/redo\".to_owned(),\n        }\n    }\n}\n\n/// Showcase [`egui::util::undoer::Undoer`]\n#[derive(Debug, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct UndoRedoDemo {\n    pub state: State,\n    pub undoer: Undoer<State>,\n}\n\nimpl crate::Demo for UndoRedoDemo {\n    fn name(&self) -> &'static str {\n        \"⟲ Undo Redo\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable(false)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for UndoRedoDemo {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n\n        ui.checkbox(&mut self.state.toggle_value, \"Checkbox with undo/redo\");\n        ui.text_edit_singleline(&mut self.state.text);\n\n        ui.separator();\n\n        let can_undo = self.undoer.has_undo(&self.state);\n        let can_redo = self.undoer.has_redo(&self.state);\n\n        ui.horizontal(|ui| {\n            let undo = ui.add_enabled(can_undo, Button::new(\"⟲ Undo\")).clicked();\n            let redo = ui.add_enabled(can_redo, Button::new(\"⟳ Redo\")).clicked();\n\n            if undo && let Some(undo_text) = self.undoer.undo(&self.state) {\n                self.state = undo_text.clone();\n            }\n            if redo && let Some(redo_text) = self.undoer.redo(&self.state) {\n                self.state = redo_text.clone();\n            }\n        });\n\n        self.undoer\n            .feed_state(ui.input(|input| input.time), &self.state);\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/widget_gallery.rs",
    "content": "#[derive(Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nenum Enum {\n    First,\n    Second,\n    Third,\n}\n\n/// Shows off one example of each major type of widget.\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct WidgetGallery {\n    enabled: bool,\n    visible: bool,\n    boolean: bool,\n    opacity: f32,\n    radio: Enum,\n    scalar: f32,\n    string: String,\n    color: egui::Color32,\n    animate_progress_bar: bool,\n\n    #[cfg(feature = \"chrono\")]\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    date: Option<chrono::NaiveDate>,\n\n    #[cfg(feature = \"chrono\")]\n    with_date_button: bool,\n}\n\nimpl Default for WidgetGallery {\n    fn default() -> Self {\n        Self {\n            enabled: true,\n            visible: true,\n            opacity: 1.0,\n            boolean: false,\n            radio: Enum::First,\n            scalar: 42.0,\n            string: Default::default(),\n            color: egui::Color32::LIGHT_BLUE.linear_multiply(0.5),\n            animate_progress_bar: false,\n            #[cfg(feature = \"chrono\")]\n            date: None,\n            #[cfg(feature = \"chrono\")]\n            with_date_button: true,\n        }\n    }\n}\n\nimpl WidgetGallery {\n    #[allow(clippy::allow_attributes, unused_mut)] // if not chrono\n    #[inline]\n    pub fn with_date_button(mut self, _with_date_button: bool) -> Self {\n        #[cfg(feature = \"chrono\")]\n        {\n            self.with_date_button = _with_date_button;\n        }\n        self\n    }\n}\n\nimpl crate::Demo for WidgetGallery {\n    fn name(&self) -> &'static str {\n        \"🗄 Widget Gallery\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        egui::Window::new(self.name())\n            .open(open)\n            .resizable([true, false]) // resizable so we can shrink if the text edit grows\n            .default_width(280.0)\n            .constrain_to(ui.available_rect_before_wrap())\n            .show(ui, |ui| {\n                use crate::View as _;\n                self.ui(ui);\n            });\n    }\n}\n\nimpl crate::View for WidgetGallery {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        let mut ui_builder = egui::UiBuilder::new();\n        if !self.enabled {\n            ui_builder = ui_builder.disabled();\n        }\n        if !self.visible {\n            ui_builder = ui_builder.invisible();\n        }\n\n        ui.scope_builder(ui_builder, |ui| {\n            ui.multiply_opacity(self.opacity);\n\n            egui::Grid::new(\"my_grid\")\n                .num_columns(2)\n                .spacing([40.0, 4.0])\n                .striped(true)\n                .show(ui, |ui| {\n                    self.gallery_grid_contents(ui);\n                });\n        });\n\n        ui.separator();\n\n        ui.horizontal(|ui| {\n            ui.checkbox(&mut self.visible, \"Visible\")\n                .on_hover_text(\"Uncheck to hide all the widgets.\");\n            if self.visible {\n                ui.checkbox(&mut self.enabled, \"Interactive\")\n                    .on_hover_text(\"Uncheck to inspect how the widgets look when disabled.\");\n                (ui.add(\n                    egui::DragValue::new(&mut self.opacity)\n                        .speed(0.01)\n                        .range(0.0..=1.0),\n                ) | ui.label(\"Opacity\"))\n                .on_hover_text(\"Reduce this value to make widgets semi-transparent\");\n            }\n        });\n\n        ui.separator();\n\n        ui.vertical_centered(|ui| {\n            let tooltip_text = \"The full egui documentation.\\nYou can also click the different widgets names in the left column.\";\n            ui.hyperlink(\"https://docs.rs/egui/\").on_hover_text(tooltip_text);\n            ui.add(crate::egui_github_link_file!(\n                \"Source code of the widget gallery\"\n            ));\n        });\n    }\n}\n\nimpl WidgetGallery {\n    fn gallery_grid_contents(&mut self, ui: &mut egui::Ui) {\n        let Self {\n            enabled: _,\n            visible: _,\n            opacity: _,\n            boolean,\n            radio,\n            scalar,\n            string,\n            color,\n            animate_progress_bar,\n            #[cfg(feature = \"chrono\")]\n            date,\n            #[cfg(feature = \"chrono\")]\n            with_date_button,\n        } = self;\n\n        ui.add(doc_link_label(\"Label\", \"label\"));\n        ui.label(\"Welcome to the widget gallery!\");\n        ui.end_row();\n\n        ui.add(doc_link_label(\"Hyperlink\", \"Hyperlink\"));\n        use egui::special_emojis::GITHUB;\n        ui.hyperlink_to(\n            format!(\"{GITHUB} egui on GitHub\"),\n            \"https://github.com/emilk/egui\",\n        );\n        ui.end_row();\n\n        ui.add(doc_link_label(\"TextEdit\", \"TextEdit\"));\n        ui.add(egui::TextEdit::singleline(string).hint_text(\"Write something here\"));\n        ui.end_row();\n\n        ui.add(doc_link_label(\"Button\", \"button\"));\n        if ui.button(\"Click me!\").clicked() {\n            *boolean = !*boolean;\n        }\n        ui.end_row();\n\n        ui.add(doc_link_label(\"Link\", \"link\"));\n        if ui.link(\"Click me!\").clicked() {\n            *boolean = !*boolean;\n        }\n        ui.end_row();\n\n        ui.add(doc_link_label(\"Checkbox\", \"checkbox\"));\n        ui.checkbox(boolean, \"Checkbox\");\n        ui.end_row();\n\n        ui.add(doc_link_label(\"RadioButton\", \"radio\"));\n        ui.horizontal(|ui| {\n            ui.radio_value(radio, Enum::First, \"First\");\n            ui.radio_value(radio, Enum::Second, \"Second\");\n            ui.radio_value(radio, Enum::Third, \"Third\");\n        });\n        ui.end_row();\n\n        ui.add(doc_link_label(\"SelectableLabel\", \"SelectableLabel\"));\n        ui.horizontal(|ui| {\n            ui.selectable_value(radio, Enum::First, \"First\");\n            ui.selectable_value(radio, Enum::Second, \"Second\");\n            ui.selectable_value(radio, Enum::Third, \"Third\");\n        });\n        ui.end_row();\n\n        ui.add(doc_link_label(\"ComboBox\", \"ComboBox\"));\n\n        egui::ComboBox::from_label(\"Take your pick\")\n            .selected_text(format!(\"{radio:?}\"))\n            .show_ui(ui, |ui| {\n                ui.selectable_value(radio, Enum::First, \"First\");\n                ui.selectable_value(radio, Enum::Second, \"Second\");\n                ui.selectable_value(radio, Enum::Third, \"Third\");\n            });\n        ui.end_row();\n\n        ui.add(doc_link_label(\"Slider\", \"Slider\"));\n        ui.add(egui::Slider::new(scalar, 0.0..=360.0).suffix(\"°\"));\n        ui.end_row();\n\n        ui.add(doc_link_label(\"DragValue\", \"DragValue\"));\n        ui.add(egui::DragValue::new(scalar).speed(1.0));\n        ui.end_row();\n\n        ui.add(doc_link_label(\"ProgressBar\", \"ProgressBar\"));\n        let progress = *scalar / 360.0;\n        let progress_bar = egui::ProgressBar::new(progress)\n            .show_percentage()\n            .animate(*animate_progress_bar);\n        *animate_progress_bar = ui\n            .add(progress_bar)\n            .on_hover_text(\"The progress bar can be animated!\")\n            .hovered();\n        ui.end_row();\n\n        ui.add(doc_link_label(\"Color picker\", \"color_edit\"));\n        ui.color_edit_button_srgba(color);\n        ui.end_row();\n\n        ui.add(doc_link_label(\"Image\", \"Image\"));\n        let egui_icon = egui::include_image!(\"../../data/icon.png\");\n        ui.add(egui::Image::new(egui_icon.clone()));\n        ui.end_row();\n\n        ui.add(doc_link_label(\n            \"Button with image\",\n            \"Button::image_and_text\",\n        ));\n        if ui\n            .add(egui::Button::image_and_text(egui_icon, \"Click me!\"))\n            .clicked()\n        {\n            *boolean = !*boolean;\n        }\n        ui.end_row();\n\n        #[cfg(feature = \"chrono\")]\n        if *with_date_button {\n            let date = date.get_or_insert_with(|| chrono::offset::Utc::now().date_naive());\n            ui.add(doc_link_label_with_crate(\n                \"egui_extras\",\n                \"DatePickerButton\",\n                \"DatePickerButton\",\n            ));\n            ui.add(egui_extras::DatePickerButton::new(date));\n            ui.end_row();\n        }\n\n        ui.add(doc_link_label(\"Separator\", \"separator\"));\n        ui.separator();\n        ui.end_row();\n\n        ui.add(doc_link_label(\"CollapsingHeader\", \"collapsing\"));\n        ui.collapsing(\"Click to see what is hidden!\", |ui| {\n            ui.horizontal_wrapped(|ui| {\n                ui.spacing_mut().item_spacing.x = 0.0;\n                ui.label(\"It's a \");\n                ui.add(doc_link_label(\"Spinner\", \"spinner\"));\n                ui.add_space(4.0);\n                ui.add(egui::Spinner::new());\n            });\n        });\n        ui.end_row();\n\n        ui.hyperlink_to(\n            \"Custom widget\",\n            super::toggle_switch::url_to_file_source_code(),\n        );\n        ui.add(super::toggle_switch::toggle(boolean)).on_hover_text(\n            \"It's easy to create your own widgets!\\n\\\n            This toggle switch is just 15 lines of code.\",\n        );\n        ui.end_row();\n    }\n}\n\nfn doc_link_label<'a>(title: &'a str, search_term: &'a str) -> impl egui::Widget + 'a {\n    doc_link_label_with_crate(\"egui\", title, search_term)\n}\n\nfn doc_link_label_with_crate<'a>(\n    crate_name: &'a str,\n    title: &'a str,\n    search_term: &'a str,\n) -> impl egui::Widget + 'a {\n    let url = format!(\"https://docs.rs/{crate_name}?search={search_term}\");\n    move |ui: &mut egui::Ui| {\n        ui.hyperlink_to(title, url).on_hover_ui(|ui| {\n            ui.horizontal_wrapped(|ui| {\n                ui.label(\"Search egui docs for\");\n                ui.code(search_term);\n            });\n        })\n    }\n}\n\n#[cfg(feature = \"chrono\")]\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::View as _;\n    use egui::Vec2;\n    use egui_kittest::{Harness, SnapshotResults};\n\n    #[test]\n    pub fn should_match_screenshot() {\n        let mut demo = WidgetGallery {\n            // If we don't set a fixed date, the snapshot test will fail.\n            date: Some(chrono::NaiveDate::from_ymd_opt(2024, 1, 1).unwrap()),\n            ..Default::default()\n        };\n\n        let mut results = SnapshotResults::new();\n\n        for pixels_per_point in [1, 2] {\n            for theme in [egui::Theme::Light, egui::Theme::Dark] {\n                let mut harness = Harness::builder()\n                    .with_pixels_per_point(pixels_per_point as f32)\n                    .with_theme(theme)\n                    .with_size(Vec2::new(380.0, 550.0))\n                    .build_ui(|ui| {\n                        egui_extras::install_image_loaders(ui.ctx());\n                        demo.ui(ui);\n                    });\n\n                harness.fit_contents();\n\n                let theme_name = match theme {\n                    egui::Theme::Light => \"light\",\n                    egui::Theme::Dark => \"dark\",\n                };\n                let image_name = format!(\"widget_gallery_{theme_name}_x{pixels_per_point}\");\n                harness.snapshot(&image_name);\n                results.extend_harness(&mut harness);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/demo/window_options.rs",
    "content": "use egui::{UiKind, Vec2b};\n\n#[derive(Clone, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct WindowOptions {\n    title: String,\n    title_bar: bool,\n    closable: bool,\n    collapsible: bool,\n    resizable: bool,\n    constrain: bool,\n    scroll2: Vec2b,\n    disabled_time: f64,\n\n    anchored: bool,\n    anchor: egui::Align2,\n    anchor_offset: egui::Vec2,\n}\n\nimpl Default for WindowOptions {\n    fn default() -> Self {\n        Self {\n            title: \"🗖 Window Options\".to_owned(),\n            title_bar: true,\n            closable: true,\n            collapsible: true,\n            resizable: true,\n            constrain: true,\n            scroll2: Vec2b::TRUE,\n            disabled_time: f64::NEG_INFINITY,\n            anchored: false,\n            anchor: egui::Align2::RIGHT_TOP,\n            anchor_offset: egui::Vec2::ZERO,\n        }\n    }\n}\n\nimpl crate::Demo for WindowOptions {\n    fn name(&self) -> &'static str {\n        \"🗖 Window Options\"\n    }\n\n    fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {\n        let Self {\n            title,\n            title_bar,\n            closable,\n            collapsible,\n            resizable,\n            constrain,\n            scroll2,\n            disabled_time,\n            anchored,\n            anchor,\n            anchor_offset,\n        } = self.clone();\n\n        let enabled = ui.input(|i| i.time) - disabled_time > 2.0;\n        if !enabled {\n            ui.request_repaint();\n        }\n\n        use crate::View as _;\n        let mut window = egui::Window::new(title)\n            .id(egui::Id::new(\"demo_window_options\")) // required since we change the title\n            .resizable(resizable)\n            .constrain(constrain)\n            .collapsible(collapsible)\n            .title_bar(title_bar)\n            .scroll(scroll2)\n            .constrain_to(ui.available_rect_before_wrap())\n            .enabled(enabled);\n        if closable {\n            window = window.open(open);\n        }\n        if anchored {\n            window = window.anchor(anchor, anchor_offset);\n        }\n        window.show(ui, |ui| self.ui(ui));\n    }\n}\n\nimpl crate::View for WindowOptions {\n    fn ui(&mut self, ui: &mut egui::Ui) {\n        let Self {\n            title,\n            title_bar,\n            closable,\n            collapsible,\n            resizable,\n            constrain,\n            scroll2,\n            disabled_time: _,\n            anchored,\n            anchor,\n            anchor_offset,\n        } = self;\n        ui.horizontal(|ui| {\n            ui.label(\"title:\");\n            ui.text_edit_singleline(title);\n        });\n\n        ui.horizontal(|ui| {\n            ui.group(|ui| {\n                ui.vertical(|ui| {\n                    ui.checkbox(title_bar, \"title_bar\");\n                    ui.checkbox(closable, \"closable\");\n                    ui.checkbox(collapsible, \"collapsible\");\n                    ui.checkbox(resizable, \"resizable\");\n                    ui.checkbox(constrain, \"constrain\")\n                        .on_hover_text(\"Constrain window to the screen\");\n                    ui.checkbox(&mut scroll2[0], \"hscroll\");\n                    ui.checkbox(&mut scroll2[1], \"vscroll\");\n                });\n            });\n            ui.group(|ui| {\n                ui.vertical(|ui| {\n                    ui.checkbox(anchored, \"anchored\");\n                    if !*anchored {\n                        ui.disable();\n                    }\n                    ui.horizontal(|ui| {\n                        ui.label(\"x:\");\n                        ui.selectable_value(&mut anchor[0], egui::Align::LEFT, \"Left\");\n                        ui.selectable_value(&mut anchor[0], egui::Align::Center, \"Center\");\n                        ui.selectable_value(&mut anchor[0], egui::Align::RIGHT, \"Right\");\n                    });\n                    ui.horizontal(|ui| {\n                        ui.label(\"y:\");\n                        ui.selectable_value(&mut anchor[1], egui::Align::TOP, \"Top\");\n                        ui.selectable_value(&mut anchor[1], egui::Align::Center, \"Center\");\n                        ui.selectable_value(&mut anchor[1], egui::Align::BOTTOM, \"Bottom\");\n                    });\n                    ui.horizontal(|ui| {\n                        ui.label(\"Offset:\");\n                        ui.add(egui::DragValue::new(&mut anchor_offset.x));\n                        ui.add(egui::DragValue::new(&mut anchor_offset.y));\n                    });\n                });\n            });\n        });\n\n        ui.separator();\n        let on_top = Some(ui.layer_id()) == ui.ctx().top_layer_id();\n        ui.label(format!(\"This window is on top: {on_top}.\"));\n\n        ui.separator();\n        ui.horizontal(|ui| {\n            if ui.button(\"Disable for 2 seconds\").clicked() {\n                self.disabled_time = ui.input(|i| i.time);\n            }\n            egui::reset_button(ui, self, \"Reset\");\n            if ui\n                .button(\"Close\")\n                .on_hover_text(\"You can collapse / close Windows via Ui::close\")\n                .clicked()\n            {\n                // Calling close would close the collapsible within the window\n                // ui.close();\n                // Instead, we close the window itself\n                ui.close_kind(UiKind::Window);\n            }\n            ui.add(crate::egui_github_link_file!());\n        });\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/easy_mark/easy_mark_editor.rs",
    "content": "use egui::{\n    Key, KeyboardShortcut, Modifiers, ScrollArea, TextBuffer, TextEdit, Ui, text::CCursorRange,\n};\n\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct EasyMarkEditor {\n    code: String,\n    highlight_editor: bool,\n    show_rendered: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    highlighter: crate::easy_mark::MemoizedEasymarkHighlighter,\n}\n\nimpl PartialEq for EasyMarkEditor {\n    fn eq(&self, other: &Self) -> bool {\n        (&self.code, self.highlight_editor, self.show_rendered)\n            == (&other.code, other.highlight_editor, other.show_rendered)\n    }\n}\n\nimpl Default for EasyMarkEditor {\n    fn default() -> Self {\n        Self {\n            code: DEFAULT_CODE.trim().to_owned(),\n            highlight_editor: true,\n            show_rendered: true,\n            highlighter: Default::default(),\n        }\n    }\n}\n\nimpl EasyMarkEditor {\n    pub fn panels(&mut self, ui: &mut egui::Ui) {\n        egui::Panel::bottom(\"easy_mark_bottom\").show_inside(ui, |ui| {\n            let layout = egui::Layout::top_down(egui::Align::Center).with_main_justify(true);\n            ui.allocate_ui_with_layout(ui.available_size(), layout, |ui| {\n                ui.add(crate::egui_github_link_file!())\n            })\n        });\n\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            self.ui(ui);\n        });\n    }\n\n    pub fn ui(&mut self, ui: &mut egui::Ui) {\n        egui::Grid::new(\"controls\").show(ui, |ui| {\n            let _response = ui.button(\"Hotkeys\").on_hover_ui(nested_hotkeys_ui);\n            ui.checkbox(&mut self.show_rendered, \"Show rendered\");\n            ui.checkbox(&mut self.highlight_editor, \"Highlight editor\");\n            egui::reset_button(ui, self, \"Reset\");\n            ui.end_row();\n        });\n        ui.separator();\n\n        if self.show_rendered {\n            ui.columns(2, |columns| {\n                ScrollArea::vertical()\n                    .id_salt(\"source\")\n                    .show(&mut columns[0], |ui| self.editor_ui(ui));\n                ScrollArea::vertical()\n                    .id_salt(\"rendered\")\n                    .show(&mut columns[1], |ui| {\n                        // TODO(emilk): we can save some more CPU by caching the rendered output.\n                        crate::easy_mark::easy_mark(ui, &self.code);\n                    });\n            });\n        } else {\n            ScrollArea::vertical()\n                .id_salt(\"source\")\n                .show(ui, |ui| self.editor_ui(ui));\n        }\n    }\n\n    fn editor_ui(&mut self, ui: &mut egui::Ui) {\n        let Self {\n            code, highlighter, ..\n        } = self;\n\n        let response = if self.highlight_editor {\n            let mut layouter = |ui: &egui::Ui, easymark: &dyn TextBuffer, wrap_width: f32| {\n                let mut layout_job = highlighter.highlight(ui.style(), easymark.as_str());\n                layout_job.wrap.max_width = wrap_width;\n                ui.fonts_mut(|f| f.layout_job(layout_job))\n            };\n\n            ui.add(\n                egui::TextEdit::multiline(code)\n                    .desired_width(f32::INFINITY)\n                    .font(egui::TextStyle::Monospace) // for cursor height\n                    .layouter(&mut layouter),\n            )\n        } else {\n            ui.add(egui::TextEdit::multiline(code).desired_width(f32::INFINITY))\n        };\n\n        if let Some(mut state) = TextEdit::load_state(ui.ctx(), response.id)\n            && let Some(mut ccursor_range) = state.cursor.char_range()\n        {\n            let any_change = shortcuts(ui, code, &mut ccursor_range);\n            if any_change {\n                state.cursor.set_char_range(Some(ccursor_range));\n                state.store(ui.ctx(), response.id);\n            }\n        }\n    }\n}\n\npub const SHORTCUT_BOLD: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::B);\npub const SHORTCUT_CODE: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::N);\npub const SHORTCUT_ITALICS: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::I);\npub const SHORTCUT_SUBSCRIPT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::L);\npub const SHORTCUT_SUPERSCRIPT: KeyboardShortcut =\n    KeyboardShortcut::new(Modifiers::COMMAND, Key::Y);\npub const SHORTCUT_STRIKETHROUGH: KeyboardShortcut =\n    KeyboardShortcut::new(Modifiers::CTRL.plus(Modifiers::SHIFT), Key::Q);\npub const SHORTCUT_UNDERLINE: KeyboardShortcut =\n    KeyboardShortcut::new(Modifiers::CTRL.plus(Modifiers::SHIFT), Key::W);\npub const SHORTCUT_INDENT: KeyboardShortcut =\n    KeyboardShortcut::new(Modifiers::CTRL.plus(Modifiers::SHIFT), Key::E);\n\nfn nested_hotkeys_ui(ui: &mut egui::Ui) {\n    egui::Grid::new(\"shortcuts\").striped(true).show(ui, |ui| {\n        let mut label = |shortcut, what| {\n            ui.label(what);\n            ui.weak(ui.ctx().format_shortcut(&shortcut));\n            ui.end_row();\n        };\n\n        label(SHORTCUT_BOLD, \"*bold*\");\n        label(SHORTCUT_CODE, \"`code`\");\n        label(SHORTCUT_ITALICS, \"/italics/\");\n        label(SHORTCUT_SUBSCRIPT, \"$subscript$\");\n        label(SHORTCUT_SUPERSCRIPT, \"^superscript^\");\n        label(SHORTCUT_STRIKETHROUGH, \"~strikethrough~\");\n        label(SHORTCUT_UNDERLINE, \"_underline_\");\n        label(SHORTCUT_INDENT, \"two spaces\"); // Placeholder for tab indent\n    });\n}\n\nfn shortcuts(ui: &Ui, code: &mut dyn TextBuffer, ccursor_range: &mut CCursorRange) -> bool {\n    let mut any_change = false;\n\n    if ui.input_mut(|i| i.consume_shortcut(&SHORTCUT_INDENT)) {\n        // This is a placeholder till we can indent the active line\n        any_change = true;\n        let [primary, _secondary] = ccursor_range.sorted_cursors();\n\n        let advance = code.insert_text(\"  \", primary.index);\n        ccursor_range.primary.index += advance;\n        ccursor_range.secondary.index += advance;\n    }\n\n    for (shortcut, surrounding) in [\n        (SHORTCUT_BOLD, \"*\"),\n        (SHORTCUT_CODE, \"`\"),\n        (SHORTCUT_ITALICS, \"/\"),\n        (SHORTCUT_SUBSCRIPT, \"$\"),\n        (SHORTCUT_SUPERSCRIPT, \"^\"),\n        (SHORTCUT_STRIKETHROUGH, \"~\"),\n        (SHORTCUT_UNDERLINE, \"_\"),\n    ] {\n        if ui.input_mut(|i| i.consume_shortcut(&shortcut)) {\n            any_change = true;\n            toggle_surrounding(code, ccursor_range, surrounding);\n        }\n    }\n\n    any_change\n}\n\n/// E.g. toggle *strong* with `toggle_surrounding(&mut text, &mut cursor, \"*\")`\nfn toggle_surrounding(\n    code: &mut dyn TextBuffer,\n    ccursor_range: &mut CCursorRange,\n    surrounding: &str,\n) {\n    let [primary, secondary] = ccursor_range.sorted_cursors();\n\n    let surrounding_ccount = surrounding.chars().count();\n\n    let prefix_crange = primary.index.saturating_sub(surrounding_ccount)..primary.index;\n    let suffix_crange = secondary.index..secondary.index.saturating_add(surrounding_ccount);\n    let already_surrounded = code.char_range(prefix_crange.clone()) == surrounding\n        && code.char_range(suffix_crange.clone()) == surrounding;\n\n    if already_surrounded {\n        code.delete_char_range(suffix_crange);\n        code.delete_char_range(prefix_crange);\n        ccursor_range.primary.index -= surrounding_ccount;\n        ccursor_range.secondary.index -= surrounding_ccount;\n    } else {\n        code.insert_text(surrounding, secondary.index);\n        let advance = code.insert_text(surrounding, primary.index);\n\n        ccursor_range.primary.index += advance;\n        ccursor_range.secondary.index += advance;\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nconst DEFAULT_CODE: &str = r#\"\n# EasyMark\nEasyMark is a markup language, designed for extreme simplicity.\n\n```\nWARNING: EasyMark is still an evolving specification,\nand is also missing some features.\n```\n\n----------------\n\n# At a glance\n- inline text:\n  - normal, `code`, *strong*, ~strikethrough~, _underline_, /italics/, ^raised^, $small$\n  - `\\` escapes the next character\n  - [hyperlink](https://github.com/emilk/egui)\n  - Embedded URL: <https://github.com/emilk/egui>\n- `# ` header\n- `---` separator (horizontal line)\n- `> ` quote\n- `- ` bullet list\n- `1. ` numbered list\n- \\`\\`\\` code fence\n- a^2^ + b^2^ = c^2^\n- $Remember to read the small print$\n\n# Design\n> /\"Why do what everyone else is doing, when everyone else is already doing it?\"\n>   \\- Emil\n\nGoals:\n1. easy to parse\n2. easy to learn\n3. similar to markdown\n\n[The reference parser](https://github.com/emilk/egui/blob/main/crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs) is \\~250 lines of code, using only the Rust standard library. The parser uses no look-ahead or recursion.\n\nThere is never more than one way to accomplish the same thing, and each special character is only used for one thing. For instance `*` is used for *strong* and `-` is used for bullet lists. There is no alternative way to specify the *strong* style or getting a bullet list.\n\nSimilarity to markdown is kept when possible, but with much less ambiguity and some improvements (like _underlining_).\n\n# Details\nAll style changes are single characters, so it is `*strong*`, NOT `**strong**`. Style is reset by a matching character, or at the end of the line.\n\nStyle change characters and escapes (`\\`) work everywhere except for in inline code, code blocks and in URLs.\n\nYou can mix styles. For instance: /italics _underline_/ and *strong `code`*.\n\nYou can use styles on URLs: ~my webpage is at <http://www.example.com>~.\n\nNewlines are preserved. If you want to continue text on the same line, just do so. Alternatively, escape the newline by ending the line with a backslash (`\\`). \\\nEscaping the newline effectively ignores it.\n\nThe style characters are chosen to be similar to what they are representing:\n  `_` = _underline_\n  `~` = ~strikethrough~ (`-` is used for bullet points)\n  `/` = /italics/\n  `*` = *strong*\n  `$` = $small$\n  `^` = ^raised^\n\n# To do\n- Sub-headers (`## h2`, `### h3` etc)\n- Hotkey Editor\n- International keyboard algorithm for non-letter keys\n- ALT+SHIFT+Num1 is not a functioning hotkey\n- Tab Indent Increment/Decrement CTRL+], CTRL+[\n\n- Images\n  - we want to be able to optionally specify size (width and\\/or height)\n  - centering of images is very desirable\n  - captioning (image with a text underneath it)\n  - `![caption=My image][width=200][center](url)` ?\n- Nicer URL:s\n  - `<url>` and `[url](url)` do the same thing yet look completely different.\n  - let's keep similarity with images\n- Tables\n- Inspiration: <https://mycorrhiza.wiki/help/en/mycomarkup>\n\"#;\n"
  },
  {
    "path": "crates/egui_demo_lib/src/easy_mark/easy_mark_highlighter.rs",
    "content": "use crate::easy_mark::easy_mark_parser;\n\n/// Highlight easymark, memoizing previous output to save CPU.\n///\n/// In practice, the highlighter is fast enough not to need any caching.\n#[derive(Default)]\npub struct MemoizedEasymarkHighlighter {\n    style: egui::Style,\n    code: String,\n    output: egui::text::LayoutJob,\n}\n\nimpl MemoizedEasymarkHighlighter {\n    pub fn highlight(&mut self, egui_style: &egui::Style, code: &str) -> egui::text::LayoutJob {\n        if (&self.style, self.code.as_str()) != (egui_style, code) {\n            self.style = egui_style.clone();\n            code.clone_into(&mut self.code);\n            self.output = highlight_easymark(egui_style, code);\n        }\n        self.output.clone()\n    }\n}\n\npub fn highlight_easymark(egui_style: &egui::Style, mut text: &str) -> egui::text::LayoutJob {\n    let mut job = egui::text::LayoutJob::default();\n    let mut style = easy_mark_parser::Style::default();\n    let mut start_of_line = true;\n\n    while !text.is_empty() {\n        if start_of_line && text.starts_with(\"```\") {\n            let end = text.find(\"\\n```\").map_or_else(|| text.len(), |i| i + 4);\n            job.append(\n                &text[..end],\n                0.0,\n                format_from_style(\n                    egui_style,\n                    &easy_mark_parser::Style {\n                        code: true,\n                        ..Default::default()\n                    },\n                ),\n            );\n            text = &text[end..];\n            style = Default::default();\n            continue;\n        }\n\n        if text.starts_with('`') {\n            style.code = true;\n            let end = text[1..]\n                .find(&['`', '\\n'][..])\n                .map_or_else(|| text.len(), |i| i + 2);\n            job.append(&text[..end], 0.0, format_from_style(egui_style, &style));\n            text = &text[end..];\n            style.code = false;\n            continue;\n        }\n\n        let mut skip;\n\n        if text.starts_with('\\\\') && text.len() >= 2 {\n            skip = 2;\n        } else if start_of_line && text.starts_with(' ') {\n            // we don't preview indentation, because it is confusing\n            skip = 1;\n        } else if start_of_line && text.starts_with(\"# \") {\n            style.heading = true;\n            skip = 2;\n        } else if start_of_line && text.starts_with(\"> \") {\n            style.quoted = true;\n            skip = 2;\n            // we don't preview indentation, because it is confusing\n        } else if start_of_line && text.starts_with(\"- \") {\n            skip = 2;\n            // we don't preview indentation, because it is confusing\n        } else if text.starts_with('*') {\n            skip = 1;\n            if style.strong {\n                // Include the character that is ending this style:\n                job.append(&text[..skip], 0.0, format_from_style(egui_style, &style));\n                text = &text[skip..];\n                skip = 0;\n            }\n            style.strong ^= true;\n        } else if text.starts_with('$') {\n            skip = 1;\n            if style.small {\n                // Include the character that is ending this style:\n                job.append(&text[..skip], 0.0, format_from_style(egui_style, &style));\n                text = &text[skip..];\n                skip = 0;\n            }\n            style.small ^= true;\n        } else if text.starts_with('^') {\n            skip = 1;\n            if style.raised {\n                // Include the character that is ending this style:\n                job.append(&text[..skip], 0.0, format_from_style(egui_style, &style));\n                text = &text[skip..];\n                skip = 0;\n            }\n            style.raised ^= true;\n        } else {\n            skip = 0;\n        }\n        // Note: we don't preview underline, strikethrough and italics because it confuses things.\n\n        // Swallow everything up to the next special character:\n        let line_end = text[skip..]\n            .find('\\n')\n            .map_or_else(|| text.len(), |i| skip + i + 1);\n        let end = text[skip..]\n            .find(&['*', '`', '~', '_', '/', '$', '^', '\\\\', '<', '['][..])\n            .map_or_else(|| text.len(), |i| (skip + i).max(1));\n\n        if line_end <= end {\n            job.append(\n                &text[..line_end],\n                0.0,\n                format_from_style(egui_style, &style),\n            );\n            text = &text[line_end..];\n            start_of_line = true;\n            style = Default::default();\n        } else {\n            job.append(&text[..end], 0.0, format_from_style(egui_style, &style));\n            text = &text[end..];\n            start_of_line = false;\n        }\n    }\n\n    job\n}\n\nfn format_from_style(\n    egui_style: &egui::Style,\n    emark_style: &easy_mark_parser::Style,\n) -> egui::text::TextFormat {\n    use egui::{Align, Color32, Stroke, TextStyle};\n\n    let color = if emark_style.strong || emark_style.heading {\n        egui_style.visuals.strong_text_color()\n    } else if emark_style.quoted {\n        egui_style.visuals.weak_text_color()\n    } else {\n        egui_style.visuals.text_color()\n    };\n\n    let text_style = if emark_style.heading {\n        TextStyle::Heading\n    } else if emark_style.code {\n        TextStyle::Monospace\n    } else if emark_style.small | emark_style.raised {\n        TextStyle::Small\n    } else {\n        TextStyle::Body\n    };\n\n    let background = if emark_style.code {\n        egui_style.visuals.code_bg_color\n    } else {\n        Color32::TRANSPARENT\n    };\n\n    let underline = if emark_style.underline {\n        Stroke::new(1.0, color)\n    } else {\n        Stroke::NONE\n    };\n\n    let strikethrough = if emark_style.strikethrough {\n        Stroke::new(1.0, color)\n    } else {\n        Stroke::NONE\n    };\n\n    let valign = if emark_style.raised {\n        Align::TOP\n    } else {\n        Align::BOTTOM\n    };\n\n    egui::text::TextFormat {\n        font_id: text_style.resolve(egui_style),\n        color,\n        background,\n        italics: emark_style.italics,\n        underline,\n        strikethrough,\n        valign,\n        ..Default::default()\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs",
    "content": "//! A parser for `EasyMark`: a very simple markup language.\n//!\n//! WARNING: `EasyMark` is subject to change.\n//\n//! # `EasyMark` design goals:\n//! 1. easy to parse\n//! 2. easy to learn\n//! 3. similar to markdown\n\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]\npub enum Item<'a> {\n    /// `\\n`\n    // TODO(emilk): add Style here so empty heading still uses up the right amount of space.\n    Newline,\n\n    /// Text\n    Text(Style, &'a str),\n\n    /// title, url\n    Hyperlink(Style, &'a str, &'a str),\n\n    /// leading space before e.g. a [`Self::BulletPoint`].\n    Indentation(usize),\n\n    /// >\n    QuoteIndent,\n\n    /// - a point well made.\n    BulletPoint,\n\n    /// 1. numbered list. The string is the number(s).\n    NumberedPoint(&'a str),\n\n    /// ---\n    Separator,\n\n    /// language, code\n    CodeBlock(&'a str, &'a str),\n}\n\n#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]\npub struct Style {\n    /// # heading (large text)\n    pub heading: bool,\n\n    /// > quoted (slightly dimmer color or other font style)\n    pub quoted: bool,\n\n    /// `code` (monospace, some other color)\n    pub code: bool,\n\n    /// self.strong* (emphasized, e.g. bold)\n    pub strong: bool,\n\n    /// _underline_\n    pub underline: bool,\n\n    /// ~strikethrough~\n    pub strikethrough: bool,\n\n    /// /italics/\n    pub italics: bool,\n\n    /// $small$\n    pub small: bool,\n\n    /// ^raised^\n    pub raised: bool,\n}\n\n/// Parser for the `EasyMark` markup language.\n///\n/// See the module-level documentation for details.\n///\n/// # Example:\n/// ```\n/// # use egui_demo_lib::easy_mark::parser::Parser;\n/// for item in Parser::new(\"Hello *world*!\") {\n/// }\n///\n/// ```\npub struct Parser<'a> {\n    /// The remainder of the input text\n    s: &'a str,\n\n    /// Are we at the start of a line?\n    start_of_line: bool,\n\n    /// Current self.style. Reset after a newline.\n    style: Style,\n}\n\nimpl<'a> Parser<'a> {\n    pub fn new(s: &'a str) -> Self {\n        Self {\n            s,\n            start_of_line: true,\n            style: Style::default(),\n        }\n    }\n\n    /// `1. `, `42. ` etc.\n    fn numbered_list(&mut self) -> Option<Item<'a>> {\n        let n_digits = self.s.chars().take_while(|c| c.is_ascii_digit()).count();\n        if n_digits > 0 && self.s.chars().skip(n_digits).take(2).eq(\". \".chars()) {\n            let number = &self.s[..n_digits];\n            self.s = &self.s[(n_digits + 2)..];\n            self.start_of_line = false;\n            return Some(Item::NumberedPoint(number));\n        }\n        None\n    }\n\n    // ```{language}\\n{code}```\n    fn code_block(&mut self) -> Option<Item<'a>> {\n        if let Some(language_start) = self.s.strip_prefix(\"```\")\n            && let Some(newline) = language_start.find('\\n')\n        {\n            let language = &language_start[..newline];\n            let code_start = &language_start[newline + 1..];\n            if let Some(end) = code_start.find(\"\\n```\") {\n                let code = &code_start[..end].trim();\n                self.s = &code_start[end + 4..];\n                self.start_of_line = false;\n                return Some(Item::CodeBlock(language, code));\n            } else {\n                self.s = \"\";\n                return Some(Item::CodeBlock(language, code_start));\n            }\n        }\n        None\n    }\n\n    // `code`\n    fn inline_code(&mut self) -> Option<Item<'a>> {\n        if let Some(rest) = self.s.strip_prefix('`') {\n            self.s = rest;\n            self.start_of_line = false;\n            self.style.code = true;\n            let rest_of_line = &self.s[..self.s.find('\\n').unwrap_or(self.s.len())];\n            if let Some(end) = rest_of_line.find('`') {\n                let item = Item::Text(self.style, &self.s[..end]);\n                self.s = &self.s[end + 1..];\n                self.style.code = false;\n                return Some(item);\n            } else {\n                let end = rest_of_line.len();\n                let item = Item::Text(self.style, rest_of_line);\n                self.s = &self.s[end..];\n                self.style.code = false;\n                return Some(item);\n            }\n        }\n        None\n    }\n\n    /// `<url>` or `[link](url)`\n    fn url(&mut self) -> Option<Item<'a>> {\n        if self.s.starts_with('<') {\n            let this_line = &self.s[..self.s.find('\\n').unwrap_or(self.s.len())];\n            if let Some(url_end) = this_line.find('>') {\n                let url = &self.s[1..url_end];\n                self.s = &self.s[url_end + 1..];\n                self.start_of_line = false;\n                return Some(Item::Hyperlink(self.style, url, url));\n            }\n        }\n\n        // [text](url)\n        if self.s.starts_with('[') {\n            let this_line = &self.s[..self.s.find('\\n').unwrap_or(self.s.len())];\n            if let Some(bracket_end) = this_line.find(']') {\n                let text = &this_line[1..bracket_end];\n                if this_line[bracket_end + 1..].starts_with('(')\n                    && let Some(parens_end) = this_line[bracket_end + 2..].find(')')\n                {\n                    let parens_end = bracket_end + 2 + parens_end;\n                    let url = &self.s[bracket_end + 2..parens_end];\n                    self.s = &self.s[parens_end + 1..];\n                    self.start_of_line = false;\n                    return Some(Item::Hyperlink(self.style, text, url));\n                }\n            }\n        }\n        None\n    }\n}\n\nimpl<'a> Iterator for Parser<'a> {\n    type Item = Item<'a>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        loop {\n            if self.s.is_empty() {\n                return None;\n            }\n\n            // \\n\n            if self.s.starts_with('\\n') {\n                self.s = &self.s[1..];\n                self.start_of_line = true;\n                self.style = Style::default();\n                return Some(Item::Newline);\n            }\n\n            // Ignore line break (continue on the same line)\n            if self.s.starts_with(\"\\\\\\n\") && self.s.len() >= 2 {\n                self.s = &self.s[2..];\n                self.start_of_line = false;\n                continue;\n            }\n\n            // \\ escape (to show e.g. a backtick)\n            if self.s.starts_with('\\\\') && self.s.len() >= 2 {\n                let text = &self.s[1..2];\n                self.s = &self.s[2..];\n                self.start_of_line = false;\n                return Some(Item::Text(self.style, text));\n            }\n\n            if self.start_of_line {\n                // leading space (indentation)\n                if self.s.starts_with(' ') {\n                    let length = self.s.find(|c| c != ' ').unwrap_or(self.s.len());\n                    self.s = &self.s[length..];\n                    self.start_of_line = true; // indentation doesn't count\n                    return Some(Item::Indentation(length));\n                }\n\n                // # Heading\n                if let Some(after) = self.s.strip_prefix(\"# \") {\n                    self.s = after;\n                    self.start_of_line = false;\n                    self.style.heading = true;\n                    continue;\n                }\n\n                // > quote\n                if let Some(after) = self.s.strip_prefix(\"> \") {\n                    self.s = after;\n                    self.start_of_line = true; // quote indentation doesn't count\n                    self.style.quoted = true;\n                    return Some(Item::QuoteIndent);\n                }\n\n                // - bullet point\n                if self.s.starts_with(\"- \") {\n                    self.s = &self.s[2..];\n                    self.start_of_line = false;\n                    return Some(Item::BulletPoint);\n                }\n\n                // `1. `, `42. ` etc.\n                if let Some(item) = self.numbered_list() {\n                    return Some(item);\n                }\n\n                // --- separator\n                if let Some(after) = self.s.strip_prefix(\"---\") {\n                    self.s = after.trim_start_matches('-'); // remove extra dashes\n                    self.s = self.s.strip_prefix('\\n').unwrap_or(self.s); // remove trailing newline\n                    self.start_of_line = false;\n                    return Some(Item::Separator);\n                }\n\n                // ```{language}\\n{code}```\n                if let Some(item) = self.code_block() {\n                    return Some(item);\n                }\n            }\n\n            // `code`\n            if let Some(item) = self.inline_code() {\n                return Some(item);\n            }\n\n            if let Some(rest) = self.s.strip_prefix('*') {\n                self.s = rest;\n                self.start_of_line = false;\n                self.style.strong = !self.style.strong;\n                continue;\n            }\n            if let Some(rest) = self.s.strip_prefix('_') {\n                self.s = rest;\n                self.start_of_line = false;\n                self.style.underline = !self.style.underline;\n                continue;\n            }\n            if let Some(rest) = self.s.strip_prefix('~') {\n                self.s = rest;\n                self.start_of_line = false;\n                self.style.strikethrough = !self.style.strikethrough;\n                continue;\n            }\n            if let Some(rest) = self.s.strip_prefix('/') {\n                self.s = rest;\n                self.start_of_line = false;\n                self.style.italics = !self.style.italics;\n                continue;\n            }\n            if let Some(rest) = self.s.strip_prefix('$') {\n                self.s = rest;\n                self.start_of_line = false;\n                self.style.small = !self.style.small;\n                continue;\n            }\n            if let Some(rest) = self.s.strip_prefix('^') {\n                self.s = rest;\n                self.start_of_line = false;\n                self.style.raised = !self.style.raised;\n                continue;\n            }\n\n            // `<url>` or `[link](url)`\n            if let Some(item) = self.url() {\n                return Some(item);\n            }\n\n            // Swallow everything up to the next special character:\n            let end = self\n                .s\n                .find(&['*', '`', '~', '_', '/', '$', '^', '\\\\', '<', '[', '\\n'][..])\n                .map_or_else(|| self.s.len(), |special| special.max(1));\n\n            let item = Item::Text(self.style, &self.s[..end]);\n            self.s = &self.s[end..];\n            self.start_of_line = false;\n            return Some(item);\n        }\n    }\n}\n\n#[test]\nfn test_easy_mark_parser() {\n    let items: Vec<_> = Parser::new(\"~strikethrough `code`~\").collect();\n    assert_eq!(\n        items,\n        vec![\n            Item::Text(\n                Style {\n                    strikethrough: true,\n                    ..Default::default()\n                },\n                \"strikethrough \"\n            ),\n            Item::Text(\n                Style {\n                    code: true,\n                    strikethrough: true,\n                    ..Default::default()\n                },\n                \"code\"\n            ),\n        ]\n    );\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/easy_mark/easy_mark_viewer.rs",
    "content": "use super::easy_mark_parser as easy_mark;\nuse egui::{\n    Align, Align2, Hyperlink, Layout, Response, RichText, Sense, Separator, Shape, TextStyle, Ui,\n    vec2,\n};\n\n/// Parse and display a VERY simple and small subset of Markdown.\npub fn easy_mark(ui: &mut Ui, easy_mark: &str) {\n    easy_mark_it(ui, easy_mark::Parser::new(easy_mark));\n}\n\npub fn easy_mark_it<'em>(ui: &mut Ui, items: impl Iterator<Item = easy_mark::Item<'em>>) {\n    let initial_size = vec2(\n        ui.available_width(),\n        ui.spacing().interact_size.y, // Assume there will be\n    );\n\n    let layout = Layout::left_to_right(Align::BOTTOM).with_main_wrap(true);\n\n    ui.allocate_ui_with_layout(initial_size, layout, |ui| {\n        ui.spacing_mut().item_spacing.x = 0.0;\n        let row_height = ui.text_style_height(&TextStyle::Body);\n        ui.set_row_height(row_height);\n\n        for item in items {\n            item_ui(ui, item);\n        }\n    });\n}\n\npub fn item_ui(ui: &mut Ui, item: easy_mark::Item<'_>) {\n    let row_height = ui.text_style_height(&TextStyle::Body);\n    let one_indent = row_height / 2.0;\n\n    match item {\n        easy_mark::Item::Newline => {\n            // ui.label(\"\\n\"); // too much spacing (paragraph spacing)\n            ui.allocate_exact_size(vec2(0.0, row_height), Sense::hover()); // make sure we take up some height\n            ui.end_row();\n            ui.set_row_height(row_height);\n        }\n\n        easy_mark::Item::Text(style, text) => {\n            let label = rich_text_from_style(text, &style);\n            if style.small && !style.raised {\n                ui.with_layout(Layout::left_to_right(Align::BOTTOM), |ui| {\n                    ui.set_min_height(row_height);\n                    ui.label(label);\n                });\n            } else {\n                ui.label(label);\n            }\n        }\n        easy_mark::Item::Hyperlink(style, text, url) => {\n            let label = rich_text_from_style(text, &style);\n            if style.small && !style.raised {\n                ui.with_layout(Layout::left_to_right(Align::BOTTOM), |ui| {\n                    ui.set_height(row_height);\n                    ui.add(Hyperlink::from_label_and_url(label, url));\n                });\n            } else {\n                ui.add(Hyperlink::from_label_and_url(label, url));\n            }\n        }\n\n        easy_mark::Item::Separator => {\n            ui.add(Separator::default().horizontal());\n        }\n        easy_mark::Item::Indentation(indent) => {\n            let indent = indent as f32 * one_indent;\n            ui.allocate_exact_size(vec2(indent, row_height), Sense::hover());\n        }\n        easy_mark::Item::QuoteIndent => {\n            let rect = ui\n                .allocate_exact_size(vec2(2.0 * one_indent, row_height), Sense::hover())\n                .0;\n            let rect = rect.expand2(ui.style().spacing.item_spacing * 0.5);\n            ui.painter().line_segment(\n                [rect.center_top(), rect.center_bottom()],\n                (1.0, ui.visuals().weak_text_color()),\n            );\n        }\n        easy_mark::Item::BulletPoint => {\n            ui.allocate_exact_size(vec2(one_indent, row_height), Sense::hover());\n            bullet_point(ui, one_indent);\n            ui.allocate_exact_size(vec2(one_indent, row_height), Sense::hover());\n        }\n        easy_mark::Item::NumberedPoint(number) => {\n            let width = 3.0 * one_indent;\n            numbered_point(ui, width, number);\n            ui.allocate_exact_size(vec2(one_indent, row_height), Sense::hover());\n        }\n        easy_mark::Item::CodeBlock(_language, code) => {\n            let where_to_put_background = ui.painter().add(Shape::Noop);\n            let mut rect = ui.monospace(code).rect;\n            rect = rect.expand(1.0); // looks better\n            rect.max.x = ui.max_rect().max.x;\n            let code_bg_color = ui.visuals().code_bg_color;\n            ui.painter().set(\n                where_to_put_background,\n                Shape::rect_filled(rect, 1.0, code_bg_color),\n            );\n        }\n    }\n}\n\nfn rich_text_from_style(text: &str, style: &easy_mark::Style) -> RichText {\n    let easy_mark::Style {\n        heading,\n        quoted,\n        code,\n        strong,\n        underline,\n        strikethrough,\n        italics,\n        small,\n        raised,\n    } = *style;\n\n    let small = small || raised; // Raised text is also smaller\n\n    let mut rich_text = RichText::new(text);\n    if heading && !small {\n        rich_text = rich_text.heading().strong();\n    }\n    if small && !heading {\n        rich_text = rich_text.small();\n    }\n    if code {\n        rich_text = rich_text.code();\n    }\n    if strong {\n        rich_text = rich_text.strong();\n    } else if quoted {\n        rich_text = rich_text.weak();\n    }\n    if underline {\n        rich_text = rich_text.underline();\n    }\n    if strikethrough {\n        rich_text = rich_text.strikethrough();\n    }\n    if italics {\n        rich_text = rich_text.italics();\n    }\n    if raised {\n        rich_text = rich_text.raised();\n    }\n    rich_text\n}\n\nfn bullet_point(ui: &mut Ui, width: f32) -> Response {\n    let row_height = ui.text_style_height(&TextStyle::Body);\n    let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover());\n    ui.painter().circle_filled(\n        rect.center(),\n        rect.height() / 8.0,\n        ui.visuals().strong_text_color(),\n    );\n    response\n}\n\nfn numbered_point(ui: &mut Ui, width: f32, number: &str) -> Response {\n    let font_id = TextStyle::Body.resolve(ui.style());\n    let row_height = ui.fonts_mut(|f| f.row_height(&font_id));\n    let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover());\n    let text = format!(\"{number}.\");\n    let text_color = ui.visuals().strong_text_color();\n    ui.painter().text(\n        rect.right_center(),\n        Align2::RIGHT_CENTER,\n        text,\n        font_id,\n        text_color,\n    );\n    response\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/easy_mark/mod.rs",
    "content": "//! Experimental markup language\n\nmod easy_mark_editor;\nmod easy_mark_highlighter;\npub mod easy_mark_parser;\nmod easy_mark_viewer;\n\npub use easy_mark_editor::EasyMarkEditor;\npub use easy_mark_highlighter::MemoizedEasymarkHighlighter;\npub use easy_mark_parser as parser;\npub use easy_mark_viewer::easy_mark;\n"
  },
  {
    "path": "crates/egui_demo_lib/src/lib.rs",
    "content": "//! Demo-code for showing how egui is used.\n//!\n//! This library can be used to test 3rd party egui integrations (see for instance <https://github.com/not-fl3/egui-miniquad/blob/master/examples/demo.rs>).\n//!\n//! The demo is also used in benchmarks and tests.\n//!\n//! ## Feature flags\n#![cfg_attr(feature = \"document-features\", doc = document_features::document_features!())]\n//!\n\n#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps\n\nmod demo;\npub mod easy_mark;\nmod rendering_test;\n\npub use demo::{Demo, DemoWindows, View, WidgetGallery};\npub use rendering_test::ColorTest;\n\n/// View some Rust code with syntax highlighting and selection.\npub(crate) fn rust_view_ui(ui: &mut egui::Ui, code: &str) {\n    let language = \"rs\";\n    let theme = egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style());\n    egui_extras::syntax_highlighting::code_view_ui(ui, &theme, code, language);\n}\n\n// ----------------------------------------------------------------------------\n\n/// Create a [`Hyperlink`](egui::Hyperlink) to this egui source code file on github.\n#[macro_export]\nmacro_rules! egui_github_link_file {\n    () => {\n        $crate::egui_github_link_file!(\"(source code)\")\n    };\n    ($label: expr) => {\n        egui::github_link_file!(\n            \"https://github.com/emilk/egui/blob/main/\",\n            egui::RichText::new($label).small()\n        )\n    };\n}\n\n/// Create a [`Hyperlink`](egui::Hyperlink) to this egui source code file and line on github.\n#[macro_export]\nmacro_rules! egui_github_link_file_line {\n    () => {\n        $crate::egui_github_link_file_line!(\"(source code)\")\n    };\n    ($label: expr) => {\n        egui::github_link_file_line!(\n            \"https://github.com/emilk/egui/blob/main/\",\n            egui::RichText::new($label).small()\n        )\n    };\n}\n\n// ----------------------------------------------------------------------------\n\npub const LOREM_IPSUM: &str = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\";\n\npub const LOREM_IPSUM_LONG: &str = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\nCurabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam various, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst.\";\n\n// ----------------------------------------------------------------------------\n\n#[test]\nfn test_egui_e2e() {\n    let mut demo_windows = crate::DemoWindows::default();\n    let ctx = egui::Context::default();\n    let raw_input = egui::RawInput::default();\n\n    const NUM_FRAMES: usize = 5;\n    for _ in 0..NUM_FRAMES {\n        let full_output = ctx.run_ui(raw_input.clone(), |ui| {\n            demo_windows.ui(ui);\n        });\n        let clipped_primitives = ctx.tessellate(full_output.shapes, full_output.pixels_per_point);\n        assert!(!clipped_primitives.is_empty());\n    }\n}\n\n#[test]\nfn test_egui_zero_window_size() {\n    let mut demo_windows = crate::DemoWindows::default();\n    let ctx = egui::Context::default();\n    let raw_input = egui::RawInput {\n        screen_rect: Some(egui::Rect::from_min_max(egui::Pos2::ZERO, egui::Pos2::ZERO)),\n        ..Default::default()\n    };\n\n    const NUM_FRAMES: usize = 5;\n    for _ in 0..NUM_FRAMES {\n        let full_output = ctx.run_ui(raw_input.clone(), |ui| {\n            demo_windows.ui(ui);\n        });\n        let clipped_primitives = ctx.tessellate(full_output.shapes, full_output.pixels_per_point);\n        assert!(\n            clipped_primitives.is_empty(),\n            \"There should be nothing to show, has at least one primitive with clip_rect: {:?}\",\n            clipped_primitives[0].clip_rect\n        );\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Detect narrow screens. This is used to show a simpler UI on mobile devices,\n/// especially for the web demo at <https://egui.rs>.\npub fn is_mobile(ctx: &egui::Context) -> bool {\n    let screen_size = ctx.content_rect().size();\n    screen_size.x < 550.0\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/src/rendering_test.rs",
    "content": "use std::collections::HashMap;\n\nuse egui::{\n    Align2, Color32, FontId, Image, Mesh, Pos2, Rect, Response, Rgba, RichText, Sense, Shape,\n    Stroke, TextureHandle, TextureOptions, Ui, Vec2, emath::GuiRounding as _, epaint, lerp, pos2,\n    vec2, widgets::color_picker::show_color,\n};\n\nconst GRADIENT_SIZE: Vec2 = vec2(256.0, 18.0);\n\nconst BLACK: Color32 = Color32::BLACK;\nconst GREEN: Color32 = Color32::GREEN;\nconst RED: Color32 = Color32::RED;\nconst TRANSPARENT: Color32 = Color32::TRANSPARENT;\nconst WHITE: Color32 = Color32::WHITE;\n\n/// A test for sanity-checking and diagnosing egui rendering backends.\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct ColorTest {\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    tex_mngr: TextureManager,\n    vertex_gradients: bool,\n    texture_gradients: bool,\n}\n\nimpl Default for ColorTest {\n    fn default() -> Self {\n        Self {\n            tex_mngr: Default::default(),\n            vertex_gradients: true,\n            texture_gradients: true,\n        }\n    }\n}\n\nimpl ColorTest {\n    pub fn ui(&mut self, ui: &mut Ui) {\n        ui.vertical_centered(|ui| {\n            ui.add(crate::egui_github_link_file!());\n        });\n\n        ui.horizontal_wrapped(|ui|{\n            ui.label(\"This is made to test that the egui rendering backend is set up correctly.\");\n            ui.add(egui::Label::new(\"❓\").sense(egui::Sense::click()))\n                .on_hover_text(\"The texture sampling should be sRGB-aware, and every other color operation should be done in gamma-space (sRGB). All colors should use pre-multiplied alpha\");\n        });\n\n        ui.separator();\n\n        pixel_test(ui);\n\n        ui.separator();\n\n        ui.collapsing(\"Color test\", |ui| {\n            self.color_test(ui);\n        });\n\n        ui.separator();\n\n        ui.heading(\"Text rendering\");\n\n        text_on_bg(ui, Color32::from_gray(200), Color32::from_gray(230)); // gray on gray\n        text_on_bg(ui, Color32::from_gray(140), Color32::from_gray(28)); // dark mode normal text\n\n        // Matches Mac Font book (useful for testing):\n        text_on_bg(ui, Color32::from_gray(39), Color32::from_gray(255));\n        text_on_bg(ui, Color32::from_gray(220), Color32::from_gray(30));\n\n        ui.separator();\n\n        blending_and_feathering_test(ui);\n    }\n\n    fn color_test(&mut self, ui: &mut Ui) {\n        ui.label(\"If the rendering is done right, all groups of gradients will look uniform.\");\n\n        ui.horizontal(|ui| {\n            ui.checkbox(&mut self.vertex_gradients, \"Vertex gradients\");\n            ui.checkbox(&mut self.texture_gradients, \"Texture gradients\");\n        });\n\n        ui.heading(\"sRGB color test\");\n        ui.label(\"Use a color picker to ensure this color is (255, 165, 0) / #ffa500\");\n        ui.scope(|ui| {\n            ui.spacing_mut().item_spacing.y = 0.0; // No spacing between gradients\n            let g = Gradient::one_color(Color32::from_rgb(255, 165, 0));\n            self.vertex_gradient(ui, \"orange rgb(255, 165, 0) - vertex\", WHITE, &g);\n            self.tex_gradient(ui, \"orange rgb(255, 165, 0) - texture\", WHITE, &g);\n        });\n\n        ui.separator();\n\n        ui.label(\"Test that vertex color times texture color is done in gamma space:\");\n        ui.scope(|ui| {\n            let tex_color = Color32::from_rgb(64, 128, 255);\n            let vertex_color = Color32::from_rgb(128, 196, 196);\n            let ground_truth = mul_color_gamma(tex_color, vertex_color);\n\n            ui.horizontal(|ui| {\n                let color_size = ui.spacing().interact_size;\n                ui.label(\"texture\");\n                show_color(ui, tex_color, color_size);\n                ui.label(\" * \");\n                show_color(ui, vertex_color, color_size);\n                ui.label(\" vertex color =\");\n            });\n\n            ui.spacing_mut().item_spacing.y = 0.0; // No spacing between gradients\n\n            {\n                let g = Gradient::one_color(ground_truth);\n                self.vertex_gradient(ui, \"Ground truth (vertices)\", WHITE, &g);\n                self.tex_gradient(ui, \"Ground truth (texture)\", WHITE, &g);\n            }\n\n            ui.horizontal(|ui| {\n                let g = Gradient::one_color(tex_color);\n                let tex = self.tex_mngr.get(ui.ctx(), &g);\n                let texel_offset = 0.5 / (g.0.len() as f32);\n                let uv = Rect::from_min_max(pos2(texel_offset, 0.0), pos2(1.0 - texel_offset, 1.0));\n                ui.add(\n                    Image::from_texture((tex.id(), GRADIENT_SIZE))\n                        .tint(vertex_color)\n                        .uv(uv),\n                )\n                .on_hover_text(format!(\"A texture that is {} texels wide\", g.0.len()));\n                ui.label(\"GPU result\");\n            });\n        });\n\n        ui.separator();\n\n        ui.label(\"Test that blending is done in gamma space:\");\n        ui.scope(|ui| {\n            let background = Color32::from_rgb(200, 60, 10);\n            let foreground = Color32::from_rgba_unmultiplied(108, 65, 200, 82);\n            let ground_truth = background.blend(foreground);\n\n            ui.horizontal(|ui| {\n                let color_size = ui.spacing().interact_size;\n                ui.label(\"Background:\");\n                show_color(ui, background, color_size);\n                ui.label(\", foreground: \");\n                show_color(ui, foreground, color_size);\n            });\n            ui.spacing_mut().item_spacing.y = 0.0; // No spacing between gradients\n            {\n                let g = Gradient::one_color(ground_truth);\n                self.vertex_gradient(ui, \"Ground truth (vertices)\", WHITE, &g);\n                self.tex_gradient(ui, \"Ground truth (texture)\", WHITE, &g);\n            }\n            {\n                let g = Gradient::one_color(foreground);\n                self.vertex_gradient(ui, \"Vertex blending\", background, &g);\n                self.tex_gradient(ui, \"Texture blending\", background, &g);\n            }\n        });\n\n        ui.separator();\n\n        // TODO(emilk): test color multiplication (image tint),\n        // to make sure vertex and texture color multiplication is done in gamma space.\n\n        ui.label(\"Gamma interpolation:\");\n        self.show_gradients(ui, WHITE, (RED, GREEN), Interpolation::Gamma);\n\n        ui.separator();\n\n        self.show_gradients(ui, RED, (TRANSPARENT, GREEN), Interpolation::Gamma);\n\n        ui.separator();\n\n        self.show_gradients(ui, WHITE, (TRANSPARENT, GREEN), Interpolation::Gamma);\n\n        ui.separator();\n\n        self.show_gradients(ui, BLACK, (BLACK, WHITE), Interpolation::Gamma);\n        ui.separator();\n        self.show_gradients(ui, WHITE, (BLACK, TRANSPARENT), Interpolation::Gamma);\n        ui.separator();\n        self.show_gradients(ui, BLACK, (TRANSPARENT, WHITE), Interpolation::Gamma);\n        ui.separator();\n\n        ui.label(\"Additive blending: add more and more blue to the red background:\");\n        self.show_gradients(\n            ui,\n            RED,\n            (TRANSPARENT, Color32::from_rgb_additive(0, 0, 255)),\n            Interpolation::Gamma,\n        );\n\n        ui.separator();\n\n        ui.label(\"Texture interpolation (texture sampling) should be in gamma space:\");\n        self.show_gradients(ui, WHITE, (RED, GREEN), Interpolation::Gamma);\n    }\n\n    fn show_gradients(\n        &mut self,\n        ui: &mut Ui,\n        bg_fill: Color32,\n        (left, right): (Color32, Color32),\n        interpolation: Interpolation,\n    ) {\n        let is_opaque = left.is_opaque() && right.is_opaque();\n\n        ui.horizontal(|ui| {\n            let color_size = ui.spacing().interact_size;\n            if !is_opaque {\n                ui.label(\"Background:\");\n                show_color(ui, bg_fill, color_size);\n            }\n            ui.label(\"gradient\");\n            show_color(ui, left, color_size);\n            ui.label(\"-\");\n            show_color(ui, right, color_size);\n        });\n\n        ui.scope(|ui| {\n            ui.spacing_mut().item_spacing.y = 0.0; // No spacing between gradients\n            if is_opaque {\n                let g = Gradient::ground_truth_gradient(left, right, interpolation);\n                self.vertex_gradient(ui, \"Ground Truth (CPU gradient) - vertices\", bg_fill, &g);\n                self.tex_gradient(ui, \"Ground Truth (CPU gradient) - texture\", bg_fill, &g);\n            } else {\n                let g = Gradient::ground_truth_gradient(left, right, interpolation)\n                    .with_bg_fill(bg_fill);\n                self.vertex_gradient(\n                    ui,\n                    \"Ground Truth (CPU gradient, CPU blending) - vertices\",\n                    bg_fill,\n                    &g,\n                );\n                self.tex_gradient(\n                    ui,\n                    \"Ground Truth (CPU gradient, CPU blending) - texture\",\n                    bg_fill,\n                    &g,\n                );\n                let g = Gradient::ground_truth_gradient(left, right, interpolation);\n                self.vertex_gradient(ui, \"CPU gradient, GPU blending - vertices\", bg_fill, &g);\n                self.tex_gradient(ui, \"CPU gradient, GPU blending - texture\", bg_fill, &g);\n            }\n\n            let g = Gradient::endpoints(left, right);\n\n            match interpolation {\n                Interpolation::Linear => {}\n                Interpolation::Gamma => {\n                    self.tex_gradient(ui, \"Texture of width 2 (test texture sampler)\", bg_fill, &g);\n\n                    // vertex shader uses gamma\n                    self.vertex_gradient(\n                        ui,\n                        \"Triangle mesh of width 2 (test vertex decode and interpolation)\",\n                        bg_fill,\n                        &g,\n                    );\n                }\n            }\n        });\n    }\n\n    fn tex_gradient(&mut self, ui: &mut Ui, label: &str, bg_fill: Color32, gradient: &Gradient) {\n        if !self.texture_gradients {\n            return;\n        }\n        ui.horizontal(|ui| {\n            let tex = self.tex_mngr.get(ui.ctx(), gradient);\n            let texel_offset = 0.5 / (gradient.0.len() as f32);\n            let uv = Rect::from_min_max(pos2(texel_offset, 0.0), pos2(1.0 - texel_offset, 1.0));\n            ui.add(\n                Image::from_texture((tex.id(), GRADIENT_SIZE))\n                    .bg_fill(bg_fill)\n                    .uv(uv),\n            )\n            .on_hover_text(format!(\n                \"A texture that is {} texels wide\",\n                gradient.0.len()\n            ));\n            ui.label(label);\n        });\n    }\n\n    fn vertex_gradient(&self, ui: &mut Ui, label: &str, bg_fill: Color32, gradient: &Gradient) {\n        if !self.vertex_gradients {\n            return;\n        }\n        ui.horizontal(|ui| {\n            vertex_gradient(ui, bg_fill, gradient).on_hover_text(format!(\n                \"A triangle mesh that is {} vertices wide\",\n                gradient.0.len()\n            ));\n            ui.label(label);\n        });\n    }\n}\n\nfn vertex_gradient(ui: &mut Ui, bg_fill: Color32, gradient: &Gradient) -> Response {\n    let (rect, response) = ui.allocate_at_least(GRADIENT_SIZE, Sense::hover());\n    let rect = rect.round_to_pixels(ui.pixels_per_point());\n    if bg_fill != Default::default() {\n        let mut mesh = Mesh::default();\n        mesh.add_colored_rect(rect, bg_fill);\n        ui.painter().add(Shape::mesh(mesh));\n    }\n    {\n        let n = gradient.0.len();\n        assert!(\n            n >= 2,\n            \"A gradient must have at least two colors, but this had {n}\"\n        );\n        let mut mesh = Mesh::default();\n        for (i, &color) in gradient.0.iter().enumerate() {\n            let t = i as f32 / (n as f32 - 1.0);\n            let x = lerp(rect.x_range(), t);\n            mesh.colored_vertex(pos2(x, rect.top()), color);\n            mesh.colored_vertex(pos2(x, rect.bottom()), color);\n            if i < n - 1 {\n                let i = i as u32;\n                mesh.add_triangle(2 * i, 2 * i + 1, 2 * i + 2);\n                mesh.add_triangle(2 * i + 1, 2 * i + 2, 2 * i + 3);\n            }\n        }\n        ui.painter().add(Shape::mesh(mesh));\n    }\n    response\n}\n\n#[derive(Clone, Copy)]\nenum Interpolation {\n    /// egui used to want Linear interpolation for some things, but now we're always in gamma space.\n    #[expect(unused)]\n    Linear,\n\n    Gamma,\n}\n\n#[derive(Clone, Hash, PartialEq, Eq)]\nstruct Gradient(pub Vec<Color32>);\n\nimpl Gradient {\n    pub fn one_color(srgba: Color32) -> Self {\n        Self(vec![srgba, srgba])\n    }\n\n    pub fn endpoints(left: Color32, right: Color32) -> Self {\n        Self(vec![left, right])\n    }\n\n    pub fn ground_truth_gradient(\n        left: Color32,\n        right: Color32,\n        interpolation: Interpolation,\n    ) -> Self {\n        match interpolation {\n            Interpolation::Linear => Self::ground_truth_linear_gradient(left, right),\n            Interpolation::Gamma => Self::ground_truth_gamma_gradient(left, right),\n        }\n    }\n\n    pub fn ground_truth_linear_gradient(left: Color32, right: Color32) -> Self {\n        let left = Rgba::from(left);\n        let right = Rgba::from(right);\n\n        let n = 255;\n        Self(\n            (0..=n)\n                .map(|i| {\n                    let t = i as f32 / n as f32;\n                    Color32::from(lerp(left..=right, t))\n                })\n                .collect(),\n        )\n    }\n\n    pub fn ground_truth_gamma_gradient(left: Color32, right: Color32) -> Self {\n        let n = 255;\n        Self(\n            (0..=n)\n                .map(|i| {\n                    let t = i as f32 / n as f32;\n                    left.lerp_to_gamma(right, t)\n                })\n                .collect(),\n        )\n    }\n\n    /// Do premultiplied alpha-aware blending of the gradient on top of the fill color\n    /// in gamma-space.\n    pub fn with_bg_fill(self, bg: Color32) -> Self {\n        Self(\n            self.0\n                .into_iter()\n                .map(|fg| {\n                    let a = fg.a() as f32 / 255.0;\n                    Color32::from_rgba_premultiplied(\n                        (bg[0] as f32 * (1.0 - a) + fg[0] as f32).round() as u8,\n                        (bg[1] as f32 * (1.0 - a) + fg[1] as f32).round() as u8,\n                        (bg[2] as f32 * (1.0 - a) + fg[2] as f32).round() as u8,\n                        (bg[3] as f32 * (1.0 - a) + fg[3] as f32).round() as u8,\n                    )\n                })\n                .collect(),\n        )\n    }\n\n    pub fn to_pixel_row(&self) -> Vec<Color32> {\n        self.0.clone()\n    }\n}\n\n#[derive(Default)]\nstruct TextureManager(HashMap<Gradient, TextureHandle>);\n\nimpl TextureManager {\n    fn get(&mut self, ctx: &egui::Context, gradient: &Gradient) -> &TextureHandle {\n        self.0.entry(gradient.clone()).or_insert_with(|| {\n            let pixels = gradient.to_pixel_row();\n            let width = pixels.len();\n            let height = 1;\n            ctx.load_texture(\n                \"color_test_gradient\",\n                epaint::ColorImage::new([width, height], pixels),\n                TextureOptions::LINEAR,\n            )\n        })\n    }\n}\n\n/// A visual test that the rendering is correctly aligned on the physical pixel grid.\n///\n/// Requires eyes and a magnifying glass to verify.\npub fn pixel_test(ui: &mut Ui) {\n    ui.heading(\"Pixel alignment test\");\n    ui.label(\"If anything is blurry, then everything will be blurry, including text.\");\n    ui.label(\"You might need a magnifying glass to check this test.\");\n\n    if cfg!(target_arch = \"wasm32\") {\n        ui.label(\"Make sure these test pass even when you zoom in/out and resize the browser.\");\n    }\n\n    ui.add_space(4.0);\n\n    pixel_test_lines(ui);\n\n    ui.add_space(4.0);\n\n    pixel_test_squares(ui);\n\n    ui.add_space(4.0);\n\n    pixel_test_strokes(ui);\n}\n\nfn pixel_test_strokes(ui: &mut Ui) {\n    ui.label(\"The strokes should align to the physical pixel grid.\");\n    let color = if ui.style().visuals.dark_mode {\n        egui::Color32::WHITE\n    } else {\n        egui::Color32::BLACK\n    };\n\n    let pixels_per_point = ui.pixels_per_point();\n\n    for thickness_pixels in 1..=3 {\n        let thickness_pixels = thickness_pixels as f32;\n        let thickness_points = thickness_pixels / pixels_per_point;\n        let num_squares = (pixels_per_point * 10.0).round().max(10.0) as u32;\n        let size_pixels = vec2(ui.min_size().x, num_squares as f32 + thickness_pixels * 2.0);\n        let size_points = size_pixels / pixels_per_point;\n        let (response, painter) = ui.allocate_painter(size_points, Sense::hover());\n\n        let mut cursor_pixel = Pos2::new(\n            response.rect.min.x * pixels_per_point + thickness_pixels,\n            response.rect.min.y * pixels_per_point + thickness_pixels,\n        )\n        .ceil();\n\n        let stroke = Stroke::new(thickness_points, color);\n        for size in 1..=num_squares {\n            let rect_points = Rect::from_min_size(\n                Pos2::new(cursor_pixel.x, cursor_pixel.y),\n                Vec2::splat(size as f32),\n            );\n            painter.rect_stroke(\n                rect_points / pixels_per_point,\n                0.0,\n                stroke,\n                egui::StrokeKind::Outside,\n            );\n            cursor_pixel.x += (1 + size) as f32 + thickness_pixels * 2.0;\n        }\n    }\n}\n\nfn pixel_test_squares(ui: &mut Ui) {\n    ui.label(\"The first square should be exactly one physical pixel big.\");\n    ui.label(\"They should be exactly one physical pixel apart.\");\n    ui.label(\"Each subsequent square should be one physical pixel larger than the previous.\");\n    ui.label(\"They should be perfectly aligned to the physical pixel grid.\");\n\n    let color = if ui.style().visuals.dark_mode {\n        egui::Color32::WHITE\n    } else {\n        egui::Color32::BLACK\n    };\n\n    let pixels_per_point = ui.pixels_per_point();\n\n    let num_squares = (pixels_per_point * 10.0).round().max(10.0) as u32;\n    let size_pixels = vec2(\n        ((num_squares + 1) * (num_squares + 2) / 2) as f32,\n        num_squares as f32,\n    );\n    let size_points = size_pixels / pixels_per_point + Vec2::splat(2.0);\n    let (response, painter) = ui.allocate_painter(size_points, Sense::hover());\n\n    let mut cursor_pixel = Pos2::new(\n        response.rect.min.x * pixels_per_point,\n        response.rect.min.y * pixels_per_point,\n    )\n    .ceil();\n    for size in 1..=num_squares {\n        let rect_points = Rect::from_min_size(\n            Pos2::new(cursor_pixel.x, cursor_pixel.y),\n            Vec2::splat(size as f32),\n        );\n        painter.rect_filled(rect_points / pixels_per_point, 0.0, color);\n        cursor_pixel.x += (1 + size) as f32;\n    }\n}\n\nfn pixel_test_lines(ui: &mut Ui) {\n    let pixels_per_point = ui.pixels_per_point();\n    let n = (96.0 * pixels_per_point) as usize;\n\n    ui.label(\"The lines should be exactly one physical pixel wide, one physical pixel apart.\");\n    ui.label(\"They should be perfectly white and black.\");\n\n    let hspace_px = pixels_per_point * 4.0;\n\n    let size_px = Vec2::new(2.0 * n as f32 + hspace_px, n as f32);\n    let size_points = size_px / pixels_per_point + Vec2::splat(2.0);\n    let (response, painter) = ui.allocate_painter(size_points, Sense::hover());\n\n    let mut cursor_px = Pos2::new(\n        response.rect.min.x * pixels_per_point,\n        response.rect.min.y * pixels_per_point,\n    )\n    .ceil();\n\n    // Vertical stripes:\n    for x in 0..n / 2 {\n        let rect_px = Rect::from_min_size(\n            Pos2::new(cursor_px.x + 2.0 * x as f32, cursor_px.y),\n            Vec2::new(1.0, n as f32),\n        );\n        painter.rect_filled(rect_px / pixels_per_point, 0.0, egui::Color32::WHITE);\n        let rect_px = rect_px.translate(vec2(1.0, 0.0));\n        painter.rect_filled(rect_px / pixels_per_point, 0.0, egui::Color32::BLACK);\n    }\n\n    cursor_px.x += n as f32 + hspace_px;\n\n    // Horizontal stripes:\n    for y in 0..n / 2 {\n        let rect_px = Rect::from_min_size(\n            Pos2::new(cursor_px.x, cursor_px.y + 2.0 * y as f32),\n            Vec2::new(n as f32, 1.0),\n        );\n        painter.rect_filled(rect_px / pixels_per_point, 0.0, egui::Color32::WHITE);\n        let rect_px = rect_px.translate(vec2(0.0, 1.0));\n        painter.rect_filled(rect_px / pixels_per_point, 0.0, egui::Color32::BLACK);\n    }\n}\n\nfn blending_and_feathering_test(ui: &mut Ui) {\n    ui.label(\"The left side shows how lines of different widths look.\");\n    ui.label(\"The right side tests text rendering at different opacities and sizes.\");\n    ui.label(\"The top and bottom images should look symmetrical in their intensities.\");\n\n    let size = vec2(512.0, 512.0);\n    let (response, painter) = ui.allocate_painter(size, Sense::hover());\n    let rect = response.rect;\n\n    let mut top_half = rect;\n    top_half.set_bottom(top_half.center().y);\n    painter.rect_filled(top_half, 0.0, Color32::BLACK);\n    paint_fine_lines_and_text(&painter, top_half, Color32::WHITE);\n\n    let mut bottom_half = rect;\n    bottom_half.set_top(bottom_half.center().y);\n    painter.rect_filled(bottom_half, 0.0, Color32::WHITE);\n    paint_fine_lines_and_text(&painter, bottom_half, Color32::BLACK);\n}\n\nfn text_on_bg(ui: &mut egui::Ui, fg: Color32, bg: Color32) {\n    assert!(\n        fg.is_opaque(),\n        \"Foreground color must be opaque, but was: {fg:?}\",\n    );\n    assert!(\n        bg.is_opaque(),\n        \"Background color must be opaque, but was: {bg:?}\",\n    );\n\n    ui.horizontal(|ui| {\n        ui.label(\n            RichText::from(\"▣ The quick brown fox jumps over the lazy dog and runs away.\")\n                .background_color(bg)\n                .color(fg),\n        );\n        ui.label(format!(\n            \"({} {} {}) on ({} {} {})\",\n            fg.r(),\n            fg.g(),\n            fg.b(),\n            bg.r(),\n            bg.g(),\n            bg.b(),\n        ));\n    });\n}\n\nfn paint_fine_lines_and_text(painter: &egui::Painter, mut rect: Rect, color: Color32) {\n    {\n        let mut y = 0.0;\n        for opacity in [1.00, 0.50, 0.25, 0.10, 0.05, 0.02, 0.01, 0.00] {\n            painter.text(\n                rect.center_top() + vec2(0.0, y),\n                Align2::LEFT_TOP,\n                format!(\"{:.0}% white\", 100.0 * opacity),\n                FontId::proportional(14.0),\n                Color32::WHITE.gamma_multiply(opacity),\n            );\n            painter.text(\n                rect.center_top() + vec2(80.0, y),\n                Align2::LEFT_TOP,\n                format!(\"{:.0}% gray\", 100.0 * opacity),\n                FontId::proportional(14.0),\n                Color32::GRAY.gamma_multiply(opacity),\n            );\n            painter.text(\n                rect.center_top() + vec2(160.0, y),\n                Align2::LEFT_TOP,\n                format!(\"{:.0}% black\", 100.0 * opacity),\n                FontId::proportional(14.0),\n                Color32::BLACK.gamma_multiply(opacity),\n            );\n            y += 20.0;\n        }\n\n        for font_size in [6.0, 7.0, 8.0, 9.0, 10.0, 12.0, 14.0] {\n            painter.text(\n                rect.center_top() + vec2(0.0, y),\n                Align2::LEFT_TOP,\n                format!(\n                    \"{font_size}px - The quick brown fox jumps over the lazy dog and runs away.\"\n                ),\n                FontId::proportional(font_size),\n                color,\n            );\n            y += font_size + 1.0;\n        }\n    }\n\n    rect.max.x = rect.center().x;\n\n    rect = rect.shrink(16.0);\n    for width in [0.05, 0.1, 0.25, 0.5, 1.0, 2.0, 4.0] {\n        painter.text(\n            rect.left_top(),\n            Align2::CENTER_CENTER,\n            width.to_string(),\n            FontId::monospace(12.0),\n            color,\n        );\n\n        painter.add(egui::epaint::CubicBezierShape::from_points_stroke(\n            [\n                rect.left_top() + vec2(16.0, 0.0),\n                rect.right_top(),\n                rect.right_center(),\n                rect.right_bottom(),\n            ],\n            false,\n            Color32::TRANSPARENT,\n            Stroke::new(width, color),\n        ));\n\n        rect.min.y += 24.0;\n        rect.max.x -= 24.0;\n    }\n\n    rect.min.y += 16.0;\n    painter.text(\n        rect.left_top(),\n        Align2::LEFT_CENTER,\n        \"transparent --> opaque\",\n        FontId::monospace(10.0),\n        color,\n    );\n    rect.min.y += 12.0;\n    let mut mesh = Mesh::default();\n    mesh.colored_vertex(rect.left_bottom(), Color32::TRANSPARENT);\n    mesh.colored_vertex(rect.left_top(), Color32::TRANSPARENT);\n    mesh.colored_vertex(rect.right_bottom(), color);\n    mesh.colored_vertex(rect.right_top(), color);\n    mesh.add_triangle(0, 1, 2);\n    mesh.add_triangle(1, 2, 3);\n    painter.add(mesh);\n}\n\nfn mul_color_gamma(left: Color32, right: Color32) -> Color32 {\n    Color32::from_rgba_premultiplied(\n        (left.r() as f32 * right.r() as f32 / 255.0).round() as u8,\n        (left.g() as f32 * right.g() as f32 / 255.0).round() as u8,\n        (left.b() as f32 * right.b() as f32 / 255.0).round() as u8,\n        (left.a() as f32 * right.a() as f32 / 255.0).round() as u8,\n    )\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::ColorTest;\n    use egui_kittest::SnapshotResults;\n    use egui_kittest::kittest::Queryable as _;\n\n    #[test]\n    pub fn rendering_test() {\n        let mut results = SnapshotResults::new();\n        for dpi in [1.0, 1.25, 1.5, 1.75, 1.6666667, 2.0] {\n            let mut color_test = ColorTest::default();\n            let mut harness = egui_kittest::Harness::builder()\n                .with_pixels_per_point(dpi)\n                .build_ui(|ui| {\n                    color_test.ui(ui);\n                });\n\n            {\n                // Expand color-test collapsing header. We accesskit-click since collapsing header\n                // might not be on screen at this point.\n                harness.get_by_label(\"Color test\").click_accesskit();\n                harness.run();\n            }\n\n            harness.fit_contents();\n\n            results.add(harness.try_snapshot(format!(\"rendering_test/dpi_{dpi:.2}\")));\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/tests/image_blending.rs",
    "content": "use egui::{hex_color, include_image};\nuse egui_kittest::Harness;\n\n#[test]\nfn test_image_blending() {\n    let mut results = egui_kittest::SnapshotResults::new();\n    for pixels_per_point in [1.0, 2.0] {\n        let mut harness = Harness::builder()\n            .with_pixels_per_point(pixels_per_point)\n            .build_ui(|ui| {\n                egui_extras::install_image_loaders(ui.ctx());\n                egui::Frame::new()\n                    .fill(hex_color!(\"#5981FF\"))\n                    .show(ui, |ui| {\n                        ui.add(\n                            egui::Image::new(include_image!(\"../data/ring.png\"))\n                                .max_height(18.0)\n                                .tint(egui::Color32::GRAY),\n                        );\n                    });\n            });\n        harness.run();\n        harness.fit_contents();\n        harness.snapshot(format!(\"image_blending/image_x{pixels_per_point}\"));\n        results.extend_harness(&mut harness);\n    }\n}\n"
  },
  {
    "path": "crates/egui_demo_lib/tests/misc.rs",
    "content": "use egui::{Color32, accesskit::Role};\nuse egui_kittest::{Harness, kittest::Queryable as _};\n\n#[test]\nfn test_kerning() {\n    let mut results = egui_kittest::SnapshotResults::new();\n    for pixels_per_point in [1.0, 2.0] {\n        for theme in [egui::Theme::Dark, egui::Theme::Light] {\n            let mut harness = Harness::builder()\n                .with_pixels_per_point(pixels_per_point)\n                .with_theme(theme)\n                .build_ui(|ui| {\n                    ui.label(\"Hello world!\");\n                    ui.label(\"Repeated characters: iiiiiiiiiiiii lllllllll mmmmmmmmmmmmmmmm\");\n                    ui.label(\"Thin spaces: −123 456 789\");\n                    ui.label(\"Ligature: fi :)\");\n                    ui.label(\"\\ttabbed\");\n                });\n            harness.run();\n            harness.fit_contents();\n            harness.snapshot(format!(\n                \"image_kerning/image_{theme}_x{pixels_per_point}\",\n                theme = match theme {\n                    egui::Theme::Dark => \"dark\",\n                    egui::Theme::Light => \"light\",\n                }\n            ));\n            results.extend_harness(&mut harness);\n        }\n    }\n}\n\n#[test]\nfn test_italics() {\n    let mut results = egui_kittest::SnapshotResults::new();\n    for pixels_per_point in [1.0, 2.0_f32.sqrt(), 2.0] {\n        for theme in [egui::Theme::Dark, egui::Theme::Light] {\n            let mut harness = Harness::builder()\n                .with_pixels_per_point(pixels_per_point)\n                .with_theme(theme)\n                .build_ui(|ui| {\n                    ui.label(egui::RichText::new(\"Small italics\").italics().small());\n                    ui.label(egui::RichText::new(\"Normal italics\").italics());\n                    ui.label(egui::RichText::new(\"Large italics\").italics().size(22.0));\n                });\n            harness.run();\n            harness.fit_contents();\n            harness.snapshot(format!(\n                \"italics/image_{theme}_x{pixels_per_point:.2}\",\n                theme = match theme {\n                    egui::Theme::Dark => \"dark\",\n                    egui::Theme::Light => \"light\",\n                }\n            ));\n            results.extend_harness(&mut harness);\n        }\n    }\n}\n\n#[test]\nfn test_text_selection() {\n    let mut results = egui_kittest::SnapshotResults::new();\n\n    for (test_idx, drag_start_x) in [0.2_f32, 0.9].into_iter().enumerate() {\n        let mut harness = Harness::builder().build_ui(|ui| {\n            let visuals = ui.visuals_mut();\n            visuals.selection.bg_fill = Color32::LIGHT_GREEN;\n            visuals.selection.stroke.color = Color32::RED;\n\n            ui.label(\"Some varied ☺ text :)\\nAnd it has a second line!\");\n        });\n        harness.run();\n        harness.fit_contents();\n\n        // Drag to select text:\n        let label = harness.get_by_role(Role::Label);\n        harness.drag_at(label.rect().lerp_inside([drag_start_x, 0.25]));\n        harness.drop_at(label.rect().lerp_inside([0.6, 0.75]));\n        harness.run();\n\n        harness.snapshot(format!(\"text_selection_{test_idx}\"));\n\n        results.extend_harness(&mut harness);\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/CHANGELOG.md",
    "content": "# Changelog for egui_extras\nAll notable changes to the `egui_extras` integration will be noted in this file.\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n## 0.33.3 - 2025-12-11\n* Bump `ehttp` to 0.6.0 [#7757](https://github.com/emilk/egui/pull/7757) by [@jprochazk](https://github.com/jprochazk)\n\n\n## 0.33.2 - 2025-11-13\nNothing new\n\n\n## 0.33.0 - 2025-10-09\n* Fix: use unique id for resize columns in `Table` [#7414](https://github.com/emilk/egui/pull/7414) by [@zezic](https://github.com/zezic)\n* Feat: Add serde serialization to SyntectSettings [#7506](https://github.com/emilk/egui/pull/7506) by [@bircni](https://github.com/bircni)\n* Make individual egui_extras image loaders public [#7551](https://github.com/emilk/egui/pull/7551) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)\n\n\n## 0.32.3 - 2025-09-12\n* Fix deadlock in `FileLoader` and `EhttpLoader` [#7515](https://github.com/emilk/egui/pull/7515) by [@emilk](https://github.com/emilk)\n\n\n## 0.32.2 - 2025-09-04\n* Fix memory leak when `forget_image` is called while loading [#7380](https://github.com/emilk/egui/pull/7380) by [@Vanadiae](https://github.com/Vanadiae)\n* Fix deadlock in `ImageLoader`, `FileLoader`, `EhttpLoader` [#7494](https://github.com/emilk/egui/pull/7494) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n\n## 0.32.1 - 2025-08-15\nNothing new\n\n\n## 0.32.0 - 2025-07-10 - Improved SVG support\n### ⭐ Added\n* Allow loading multi-MIME formats using the image_loader [#5769](https://github.com/emilk/egui/pull/5769) by [@MYDIH](https://github.com/MYDIH)\n* Make ImageLoader use background thread [#5394](https://github.com/emilk/egui/pull/5394) by [@bircni](https://github.com/bircni)\n* Add overline option for Table rows [#5637](https://github.com/emilk/egui/pull/5637) by [@akx](https://github.com/akx)\n* Support text in SVGs [#5979](https://github.com/emilk/egui/pull/5979) by [@cernec1999](https://github.com/cernec1999)\n* Enable setting DatePickerButton start and end year explicitly [#7061](https://github.com/emilk/egui/pull/7061) by [@zachbateman](https://github.com/zachbateman)\n* Support custom syntect settings in syntax highlighter [#7084](https://github.com/emilk/egui/pull/7084) by [@mkeeter](https://github.com/mkeeter)\n\n### 🔧 Changed\n* Use enum-map serde feature only when serde is enabled [#5748](https://github.com/emilk/egui/pull/5748) by [@tyssyt](https://github.com/tyssyt)\n* Better define the meaning of `SizeHint` [#7079](https://github.com/emilk/egui/pull/7079) by [@emilk](https://github.com/emilk)\n\n### 🔥 Removed\n* Remove things that have been deprecated for over a year [#7099](https://github.com/emilk/egui/pull/7099) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Refactor MIME type support detection in image loader to allow for deferred handling and appended encoding info [#5686](https://github.com/emilk/egui/pull/5686) by [@markusdd](https://github.com/markusdd)\n* Fix incorrect color fringe colors on SVG:s [#7069](https://github.com/emilk/egui/pull/7069) by [@emilk](https://github.com/emilk)\n* Fix sometimes blurry SVGs [#7071](https://github.com/emilk/egui/pull/7071) by [@emilk](https://github.com/emilk)\n* Fix crash in `egui_extras::FileLoader` after `forget_image` [#6995](https://github.com/emilk/egui/pull/6995) by [@bircni](https://github.com/bircni)\n\n\n## 0.31.1 - 2025-03-05\n* Fix image_loader for animated image types [#5688](https://github.com/emilk/egui/pull/5688) by [@BSteffaniak](https://github.com/BSteffaniak)\n\n\n## 0.31.0 - 2025-02-04\n* Animated WebP support [#5470](https://github.com/emilk/egui/pull/5470), [#5586](https://github.com/emilk/egui/pull/5586) by [@Aely0](https://github.com/Aely0)\n* Make image extension check case-insensitive [#5501](https://github.com/emilk/egui/pull/5501) by [@RyanBluth](https://github.com/RyanBluth)\n* Avoid allocations for loader cache lookup [#5584](https://github.com/emilk/egui/pull/5584) by [@mineichen](https://github.com/mineichen)\n\n\n## 0.30.0 - 2024-12-16\n* Use `Table::id_salt` on `ScrollArea` [#5282](https://github.com/emilk/egui/pull/5282) by [@jwhear](https://github.com/jwhear)\n* Use proper `image` crate URI and MIME support detection [#5324](https://github.com/emilk/egui/pull/5324) by [@xangelix](https://github.com/xangelix)\n* Support loading images with weird urls and improve error message [#5431](https://github.com/emilk/egui/pull/5431) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n\n## 0.29.1 - 2024-10-01 - Fix table interaction\n* Bug fix: click anywhere on a `Table` row to select it [#5193](https://github.com/emilk/egui/pull/5193) by [@emilk](https://github.com/emilk)\n\n\n## 0.29.0 - 2024-09-26\n### ⭐ Added\n* Add `TableRow::set_hovered` [#4820](https://github.com/emilk/egui/pull/4820) by [@addiswebb](https://github.com/addiswebb)\n* Add `TableBuilder::id_salt` [#5022](https://github.com/emilk/egui/pull/5022) by [@emilk](https://github.com/emilk)\n* Add `TableBuilder::animate_scrolling` [#5159](https://github.com/emilk/egui/pull/5159) by [@ecpost](https://github.com/ecpost)\n\n### 🔧 Changed\n* Truncate text in clipped `Table` columns [#5023](https://github.com/emilk/egui/pull/5023) by [@emilk](https://github.com/emilk)\n* Change default `max_scroll_height` of `egui::Table` to `f32::INFINITY` [#4817](https://github.com/emilk/egui/pull/4817) by [@abey79](https://github.com/abey79)\n* Return `ScrollAreaOutput` from `Table::body` [#4829](https://github.com/emilk/egui/pull/4829) by [@frederik-uni](https://github.com/frederik-uni)\n* Use `Style`'s font size in `egui_extras::syntax_highlighting` [#5090](https://github.com/emilk/egui/pull/5090) by [@lampsitter](https://github.com/lampsitter)\n\n### 🐛 Fixed\n* Make sure SVGs are crisp [#4823](https://github.com/emilk/egui/pull/4823) by [@AurevoirXavier](https://github.com/AurevoirXavier)\n* Fix file mime from path (wrong feature name) [#4933](https://github.com/emilk/egui/pull/4933) by [@rustbasic](https://github.com/rustbasic)\n* Fix compilation of `egui_extras` without `serde` feature [#5014](https://github.com/emilk/egui/pull/5014) by [@emilk](https://github.com/emilk)\n\n\n## 0.28.1 - 2024-07-05\n* Make `serde` a default (opt-out) feature of `egui_extras` [#4786](https://github.com/emilk/egui/pull/4786) by [@emilk](https://github.com/emilk)\n\n\n## 0.28.0 - 2024-07-03\n* Update `image` crate to 0.25 [#4160](https://github.com/emilk/egui/pull/4160) by [@emilk](https://github.com/emilk)\n* Set the `sizing_pass` flag in first frame of `egui_extras::Table` [#4613](https://github.com/emilk/egui/pull/4613) by [@emilk](https://github.com/emilk)\n* Make `serde` an opt-in feature [#4641](https://github.com/emilk/egui/pull/4641) by [@Dinnerbone](https://github.com/Dinnerbone)\n* GIF support [#4620](https://github.com/emilk/egui/pull/4620) by [@JustFrederik](https://github.com/JustFrederik)\n* Improve `egui_extras::Table` layout [#4755](https://github.com/emilk/egui/pull/4755) by [@emilk](https://github.com/emilk)\n* Improve the auto-sizing of `Table` [#4756](https://github.com/emilk/egui/pull/4756) by [@emilk](https://github.com/emilk)\n\n\n## 0.27.2 - 2024-04-02\n* Nothing new\n\n\n## 0.27.1 - 2024-03-29\n* Nothing new\n\n\n## 0.27.0 - 2024-03-26\n* Add scroll bar visibility option to `Table` widget [#3981](https://github.com/emilk/egui/pull/3981) (thanks [@richardhozak](https://github.com/richardhozak)!)\n* Update `ehttp` to 0.5 [#4055](https://github.com/emilk/egui/pull/4055)\n* Fix: assign a different id to each table cell, avoiding id clashes [#4076](https://github.com/emilk/egui/pull/4076)\n* Fix interaction with widgets inside selectable rows of `Table` [#4077](https://github.com/emilk/egui/pull/4077)\n* Fixed handling of `file://` protocol for images [#4107](https://github.com/emilk/egui/pull/4107) (thanks [@varphone](https://github.com/varphone)!)\n* Option to change date picker format [#4180](https://github.com/emilk/egui/pull/4180) (thanks [@zaaarf](https://github.com/zaaarf)!)\n* Added ability to disable highlighting of weekend days in `DatePickerPopup`. [#4151](https://github.com/emilk/egui/pull/4151) (thanks [@hiyosilver](https://github.com/hiyosilver)!)\n\n\n## 0.26.2 - 2024-02-14\n* Nothing new\n\n\n## 0.26.1 - 2024-02-11\n* Nothing new\n\n\n## 0.26.0 - 2024-02-05\n* Remove `unwrap`s in SVG scaling [#3826](https://github.com/emilk/egui/pull/3826) (thanks [@amPerl](https://github.com/amPerl)!)\n* Update to ehttp 0.4 [#3834](https://github.com/emilk/egui/pull/3834)\n* Fix `StripBuilder` not allocating its used space [#3957](https://github.com/emilk/egui/pull/3957) (thanks [@IVAN-MK7](https://github.com/IVAN-MK7)!)\n* Override text color with stroke selection color for selected cells [#3968](https://github.com/emilk/egui/pull/3968) (thanks [@njust](https://github.com/njust)!)\n\n\n## 0.25.0 - 2024-01-08\n* Implement table row selection and hover highlighting [#3347](https://github.com/emilk/egui/pull/3347) (thanks [@laurooyen](https://github.com/laurooyen)!)\n* Fix `egui_extras::Table` scrolling bug [#3690](https://github.com/emilk/egui/pull/3690) (thanks [@abey79](https://github.com/abey79)!)\n* Fix crash due to assertion during image loading from http [#3750](https://github.com/emilk/egui/pull/3750)\n* Update resvg dependency of egui_extras [#3719](https://github.com/emilk/egui/pull/3719) (thanks [@PingPongun](https://github.com/PingPongun)!)\n\n\n## 0.24.2 - 2023-12-08 - `Table` scroll bug fix\n* Fix `Table` scrolling bug [#3690](https://github.com/emilk/egui/pull/3690)\n\n\n## 0.24.1 - 2023-11-30\n* Add more years for datepicker [#3599](https://github.com/emilk/egui/pull/3599) (thanks [@vaqxai](https://github.com/vaqxai)!)\n\n\n## 0.24.0 - 2023-11-23\n* Fix Table stripe pattern when combining `row()` and `rows()` [#3442](https://github.com/emilk/egui/pull/3442) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)\n\n\n## 0.23.0 - 2023-09-27\n* `egui_extras::install_image_loaders` [#3297](https://github.com/emilk/egui/pull/3297) [#3315](https://github.com/emilk/egui/pull/3315) [#3328](https://github.com/emilk/egui/pull/3328) (thanks [@jprochazk](https://github.com/jprochazk)!)\n* Add syntax highlighting feature to `egui_extras` [#3333](https://github.com/emilk/egui/pull/3333) [#3388](https://github.com/emilk/egui/pull/3388)\n* Add `TableBuilder::drag_to_scroll` [#3100](https://github.com/emilk/egui/pull/3100) (thanks [@KYovchevski](https://github.com/KYovchevski)!)\n* Add opt-in `puffin` feature to `egui-extras` [#3307](https://github.com/emilk/egui/pull/3307)\n* Always depend on `log` crate [#3336](https://github.com/emilk/egui/pull/3336)\n* Fix not taking clipping into account when calculating column remainder [#3357](https://github.com/emilk/egui/pull/3357) (thanks [@daxpedda](https://github.com/daxpedda)!)\n\n## 0.22.0 - 2023-05-23\n- Add option to hide datepicker button calendar icon [#2910](https://github.com/emilk/egui/pull/2910) (thanks [@Barugon](https://github.com/Barugon)!)\n\n\n## 0.21.0 - 2023-02-08\n* Update to egui 0.21\n\n\n## 0.20.0 - 2022-12-08\n* Added `RetainedImage::from_svg_bytes_with_size` to be able to specify a size for SVGs to be rasterized at.\n* Lots of `Table` improvements ([#2369](https://github.com/emilk/egui/pull/2369)):\n    * Double-click column separators to auto-size the column.\n    * All `Table` now store state. You may see warnings about reused table ids. Use `ui.push_id` to fix this.\n    * `TableBuilder::column` takes a `Column` instead of a `Size`.\n    * `Column` controls default size, size range, resizing, and clipping of columns.\n    * `Column::auto` will pick a size automatically\n    * Added `Table::scroll_to_row`.\n    * Added `Table::min_scrolled_height` and `Table::max_scroll_height`.\n    * Added `TableBody::max_size`.\n    * `Table::scroll` renamed to `Table::vscroll`.\n    * `egui_extras::Strip` now has `clip: false` by default.\n    * Fix bugs when putting `Table` inside of a horizontal `ScrollArea`.\n    * Many other bug fixes.\n* Add `Table::auto_shrink` - set to `false` to expand table to fit its containing `Ui` ([#2371](https://github.com/emilk/egui/pull/2371)).\n* Added `TableBuilder::vertical_scroll_offset`: method to set vertical scroll offset position for a table ([#1946](https://github.com/emilk/egui/pull/1946)).\n\n\n## 0.19.0 - 2022-08-20\n* MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)).\n* You can now specify a texture filter for `RetainedImage` ([#1636](https://github.com/emilk/egui/pull/1636)).\n* Fixed uneven `Table` striping ([#1680](https://github.com/emilk/egui/pull/1680)).\n\n\n## 0.18.0 - 2022-04-30\n* Added `Strip`, `Table` and `DatePicker` ([#963](https://github.com/emilk/egui/pull/963)).\n* MSRV (Minimum Supported Rust Version) is now `1.60.0` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Renamed feature \"persistence\" to \"serde\" ([#1540](https://github.com/emilk/egui/pull/1540)).\n\n\n## 0.17.0 - 2022-02-22\n* `RetainedImage`: convenience for loading svg, png, jpeg etc and showing them in egui.\n"
  },
  {
    "path": "crates/egui_extras/Cargo.toml",
    "content": "[package]\nname = \"egui_extras\"\nversion.workspace = true\nauthors = [\n  \"Dominik Rössler <dominik@freshx.de>\",\n  \"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\",\n  \"René Rössler <rene@freshx.de>\",\n]\ndescription = \"Extra functionality and widgets for the egui GUI library\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui\"\nlicense.workspace = true\nreadme = \"README.md\"\nrepository = \"https://github.com/emilk/egui\"\ncategories = [\"gui\", \"game-development\"]\nkeywords = [\"gui\", \"imgui\", \"immediate\", \"portable\", \"gamedev\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"Cargo.toml\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[lib]\n\n\n[features]\ndefault = [\"dep:mime_guess2\"]\n\n## Shorthand for enabling all the different types of image loaders.\nall_loaders = [\"file\", \"http\", \"image\", \"svg\", \"gif\", \"webp\"]\n\n## Enable [`DatePickerButton`] widget.\ndatepicker = [\"chrono\"]\n\n## Add support for loading images from `file://` URIs.\nfile = [\"dep:mime_guess2\"]\n\n## Support loading gif images.\ngif = [\"image\", \"image/gif\"]\n\n## Support loading webp images.\nwebp = [\"image\", \"image/webp\"]\n\n## Add support for loading images via HTTP.\nhttp = [\"dep:ehttp\"]\n\n## Add support for loading images with the [`image`](https://docs.rs/image) crate.\n##\n## You also need to ALSO opt-in to the image formats you want to support, like so:\n## ```toml\n## image = { version = \"0.25\", features = [\"jpeg\", \"png\"] } # Add the types you want support for\n## ```\nimage = [\"dep:image\"]\n\n## Derive serde Serialize/Deserialize on stateful structs\nserde = [\"egui/serde\", \"enum-map/serde\", \"dep:serde\"]\n\n## Support loading svg images.\nsvg = [\"resvg\"]\n\n## Support rendering text in svg images.\nsvg_text = [\"svg\", \"resvg/text\", \"resvg/system-fonts\"]\n\n## Enable better syntax highlighting using [`syntect`](https://docs.rs/syntect).\nsyntect = [\"dep:syntect\"]\n\n\n[dependencies]\negui = { workspace = true, default-features = false }\n\nahash.workspace = true\nenum-map.workspace = true\nlog.workspace = true\nprofiling.workspace = true\n\n#! ### Optional dependencies\n\n# Serde for serializing state\nserde = { workspace = true, optional = true }\n\n# Date operations needed for datepicker widget\nchrono = { workspace = true, optional = true, features = [\"clock\", \"js-sys\", \"std\", \"wasmbind\"] }\n\n## Enable this when generating docs.\ndocument-features = { workspace = true, optional = true }\n\nimage = { workspace = true, optional = true }\n\n# file feature\nmime_guess2 = { workspace = true, optional = true }\n\n# syntax highlighting\nsyntect = { workspace = true, optional = true, features = [\"default-fancy\"] }\n\n# svg feature\nresvg = { workspace = true, optional = true }\n\n# http feature\nehttp = { workspace = true, optional = true }\n"
  },
  {
    "path": "crates/egui_extras/README.md",
    "content": "# egui_extras\n\n[![Latest version](https://img.shields.io/crates/v/egui_extras.svg)](https://crates.io/crates/egui_extras)\n[![Documentation](https://docs.rs/egui_extras/badge.svg)](https://docs.rs/egui_extras)\n[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\nThis is a crate that adds some features on top top of [`egui`](https://github.com/emilk/egui). This crate is for experimental features, and features that require big dependencies that do not belong in `egui`.\n\n## Images\nOne thing `egui_extras` is commonly used for is to install image loaders for `egui`:\n\n```toml\negui_extras = { version = \"*\", features = [\"all_loaders\"] }\nimage = { version = \"0.25\", features = [\"jpeg\", \"png\"] } # Add the types you want support for\n```\n\n```rs\negui_extras::install_image_loaders(egui_ctx);\n```\n"
  },
  {
    "path": "crates/egui_extras/src/datepicker/button.rs",
    "content": "use super::popup::DatePickerPopup;\nuse chrono::NaiveDate;\nuse egui::{Area, Button, Frame, InnerResponse, Key, Order, RichText, Ui, Widget};\nuse std::ops::RangeInclusive;\n\n#[derive(Default, Clone)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub(crate) struct DatePickerButtonState {\n    pub picker_visible: bool,\n}\n\n/// Shows a date, and will open a date picker popup when clicked.\npub struct DatePickerButton<'a> {\n    selection: &'a mut NaiveDate,\n    id_salt: Option<&'a str>,\n    combo_boxes: bool,\n    arrows: bool,\n    calendar: bool,\n    calendar_week: bool,\n    show_icon: bool,\n    format: String,\n    highlight_weekends: bool,\n    start_end_years: Option<RangeInclusive<i32>>,\n}\n\nimpl<'a> DatePickerButton<'a> {\n    pub fn new(selection: &'a mut NaiveDate) -> Self {\n        Self {\n            selection,\n            id_salt: None,\n            combo_boxes: true,\n            arrows: true,\n            calendar: true,\n            calendar_week: true,\n            show_icon: true,\n            format: \"%Y-%m-%d\".to_owned(),\n            highlight_weekends: true,\n            start_end_years: None,\n        }\n    }\n\n    /// Add id source.\n    /// Must be set if multiple date picker buttons are in the same Ui.\n    #[inline]\n    pub fn id_salt(mut self, id_salt: &'a str) -> Self {\n        self.id_salt = Some(id_salt);\n        self\n    }\n\n    /// Add id source.\n    /// Must be set if multiple date picker buttons are in the same Ui.\n    #[inline]\n    #[deprecated = \"Renamed id_salt\"]\n    pub fn id_source(self, id_salt: &'a str) -> Self {\n        self.id_salt(id_salt)\n    }\n\n    /// Show combo boxes in date picker popup. (Default: true)\n    #[inline]\n    pub fn combo_boxes(mut self, combo_boxes: bool) -> Self {\n        self.combo_boxes = combo_boxes;\n        self\n    }\n\n    /// Show arrows in date picker popup. (Default: true)\n    #[inline]\n    pub fn arrows(mut self, arrows: bool) -> Self {\n        self.arrows = arrows;\n        self\n    }\n\n    /// Show calendar in date picker popup. (Default: true)\n    #[inline]\n    pub fn calendar(mut self, calendar: bool) -> Self {\n        self.calendar = calendar;\n        self\n    }\n\n    /// Show calendar week in date picker popup. (Default: true)\n    #[inline]\n    pub fn calendar_week(mut self, week: bool) -> Self {\n        self.calendar_week = week;\n        self\n    }\n\n    /// Show the calendar icon on the button. (Default: true)\n    #[inline]\n    pub fn show_icon(mut self, show_icon: bool) -> Self {\n        self.show_icon = show_icon;\n        self\n    }\n\n    /// Change the format shown on the button. (Default: %Y-%m-%d)\n    /// See [`chrono::format::strftime`] for valid formats.\n    #[inline]\n    pub fn format(mut self, format: impl Into<String>) -> Self {\n        self.format = format.into();\n        self\n    }\n\n    /// Highlight weekend days. (Default: true)\n    #[inline]\n    pub fn highlight_weekends(mut self, highlight_weekends: bool) -> Self {\n        self.highlight_weekends = highlight_weekends;\n        self\n    }\n\n    /// Set the start and end years for the date picker. (Default: today's year - 100 to today's year + 10)\n    /// This will limit the years you can choose from in the dropdown to the specified range.\n    ///\n    /// For example, if you want to provide the range of years from 2000 to 2035, you can use:\n    /// `start_end_years(2000..=2035)`.\n    #[inline]\n    pub fn start_end_years(mut self, start_end_years: RangeInclusive<i32>) -> Self {\n        self.start_end_years = Some(start_end_years);\n        self\n    }\n}\n\nimpl Widget for DatePickerButton<'_> {\n    fn ui(self, ui: &mut Ui) -> egui::Response {\n        let id = ui.make_persistent_id(self.id_salt);\n        let mut button_state = ui\n            .data_mut(|data| data.get_persisted::<DatePickerButtonState>(id))\n            .unwrap_or_default();\n\n        let mut text = if self.show_icon {\n            RichText::new(format!(\"{} 📆\", self.selection.format(&self.format)))\n        } else {\n            RichText::new(format!(\"{}\", self.selection.format(&self.format)))\n        };\n        let visuals = ui.visuals().widgets.open;\n        if button_state.picker_visible {\n            text = text.color(visuals.text_color());\n        }\n        let mut button = Button::new(text);\n        if button_state.picker_visible {\n            button = button.fill(visuals.weak_bg_fill).stroke(visuals.bg_stroke);\n        }\n        let mut button_response = ui.add(button);\n        if button_response.clicked() {\n            button_state.picker_visible = true;\n            ui.data_mut(|data| data.insert_persisted(id, button_state.clone()));\n        }\n\n        if button_state.picker_visible {\n            let width = 333.0;\n            let mut pos = button_response.rect.left_bottom();\n            let width_with_padding = width\n                + ui.style().spacing.item_spacing.x\n                + ui.style().spacing.window_margin.leftf()\n                + ui.style().spacing.window_margin.rightf();\n            if pos.x + width_with_padding > ui.clip_rect().right() {\n                pos.x = button_response.rect.right() - width_with_padding;\n            }\n\n            // Check to make sure the calendar never is displayed out of window\n            pos.x = pos.x.max(ui.style().spacing.window_margin.leftf());\n\n            //TODO(elwerene): Better positioning\n\n            let InnerResponse {\n                inner: saved,\n                response: area_response,\n            } = Area::new(ui.make_persistent_id(self.id_salt))\n                .kind(egui::UiKind::Picker)\n                .order(Order::Foreground)\n                .fixed_pos(pos)\n                .show(ui.ctx(), |ui| {\n                    let frame = Frame::popup(ui.style());\n                    frame\n                        .show(ui, |ui| {\n                            ui.set_min_width(width);\n                            ui.set_max_width(width);\n\n                            DatePickerPopup {\n                                selection: self.selection,\n                                button_id: id,\n                                combo_boxes: self.combo_boxes,\n                                arrows: self.arrows,\n                                calendar: self.calendar,\n                                calendar_week: self.calendar_week,\n                                highlight_weekends: self.highlight_weekends,\n                                start_end_years: self.start_end_years,\n                            }\n                            .draw(ui)\n                        })\n                        .inner\n                });\n\n            if saved {\n                button_response.mark_changed();\n            }\n\n            // We don't want to close our popup if any other popup is open, since other popups would\n            // most likely be the combo boxes in the date picker.\n            let any_popup_open = ui.any_popup_open();\n            if !button_response.clicked()\n                && !any_popup_open\n                && (ui.input(|i| i.key_pressed(Key::Escape)) || area_response.clicked_elsewhere())\n            {\n                button_state.picker_visible = false;\n                ui.data_mut(|data| data.insert_persisted(id, button_state));\n            }\n        }\n\n        button_response\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/datepicker/mod.rs",
    "content": "#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps\n\nmod button;\nmod popup;\n\npub use button::DatePickerButton;\nuse chrono::{Datelike as _, Duration, NaiveDate, Weekday};\n\n#[derive(Debug)]\nstruct Week {\n    number: u8,\n    days: Vec<NaiveDate>,\n}\n\nfn month_data(year: i32, month: u32) -> Vec<Week> {\n    let first = NaiveDate::from_ymd_opt(year, month, 1).expect(\"Could not create NaiveDate\");\n    let mut start = first;\n    while start.weekday() != Weekday::Mon {\n        start = start.checked_sub_signed(Duration::days(1)).unwrap();\n    }\n    let mut weeks = vec![];\n    let mut week = vec![];\n    while start < first || start.month() == first.month() || start.weekday() != Weekday::Mon {\n        week.push(start);\n\n        if start.weekday() == Weekday::Sun {\n            weeks.push(Week {\n                number: start.iso_week().week() as u8,\n                days: std::mem::take(&mut week),\n            });\n        }\n        start = start.checked_add_signed(Duration::days(1)).unwrap();\n    }\n\n    weeks\n}\n"
  },
  {
    "path": "crates/egui_extras/src/datepicker/popup.rs",
    "content": "use chrono::{Datelike as _, NaiveDate, Weekday};\n\nuse egui::{Align, Button, Color32, ComboBox, Direction, Id, Layout, RichText, Ui, Vec2};\n\nuse super::{button::DatePickerButtonState, month_data};\n\nuse crate::{Column, Size, StripBuilder, TableBuilder};\n\n#[derive(Default, Clone)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nstruct DatePickerPopupState {\n    year: i32,\n    month: u32,\n    day: u32,\n    setup: bool,\n}\n\nimpl DatePickerPopupState {\n    fn last_day_of_month(&self) -> u32 {\n        let date: NaiveDate =\n            NaiveDate::from_ymd_opt(self.year, self.month, 1).expect(\"Could not create NaiveDate\");\n        date.with_day(31)\n            .map(|_| 31)\n            .or_else(|| date.with_day(30).map(|_| 30))\n            .or_else(|| date.with_day(29).map(|_| 29))\n            .unwrap_or(28)\n    }\n}\n\npub(crate) struct DatePickerPopup<'a> {\n    pub selection: &'a mut NaiveDate,\n    pub button_id: Id,\n    pub combo_boxes: bool,\n    pub arrows: bool,\n    pub calendar: bool,\n    pub calendar_week: bool,\n    pub highlight_weekends: bool,\n    pub start_end_years: Option<std::ops::RangeInclusive<i32>>,\n}\n\nimpl DatePickerPopup<'_> {\n    /// Returns `true` if user pressed `Save` button.\n    pub fn draw(&mut self, ui: &mut Ui) -> bool {\n        let id = ui.make_persistent_id(\"date_picker\");\n        let today = chrono::offset::Utc::now().date_naive();\n        let mut popup_state = ui\n            .data_mut(|data| data.get_persisted::<DatePickerPopupState>(id))\n            .unwrap_or_default();\n        if !popup_state.setup {\n            popup_state.year = self.selection.year();\n            popup_state.month = self.selection.month();\n            popup_state.day = self.selection.day();\n            popup_state.setup = true;\n            ui.data_mut(|data| data.insert_persisted(id, popup_state.clone()));\n        }\n\n        let weeks = month_data(popup_state.year, popup_state.month);\n        let (mut close, mut saved) = (false, false);\n        let height = 20.0;\n        let spacing = 2.0;\n        ui.spacing_mut().item_spacing = Vec2::splat(spacing);\n\n        ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); // Don't wrap any text\n\n        StripBuilder::new(ui)\n            .clip(false)\n            .sizes(\n                Size::exact(height),\n                match (self.combo_boxes, self.arrows) {\n                    (true, true) => 2,\n                    (true, false) | (false, true) => 1,\n                    (false, false) => 0,\n                },\n            )\n            .sizes(\n                Size::exact((spacing + height) * (weeks.len() + 1) as f32),\n                self.calendar as usize,\n            )\n            .size(Size::exact(height))\n            .vertical(|mut strip| {\n                if self.combo_boxes {\n                    strip.strip(|builder| {\n                        builder.sizes(Size::remainder(), 3).horizontal(|mut strip| {\n                            strip.cell(|ui| {\n                                ComboBox::from_id_salt(\"date_picker_year\")\n                                    .selected_text(popup_state.year.to_string())\n                                    .show_ui(ui, |ui| {\n                                        let (start_year, end_year) = match &self.start_end_years {\n                                            Some(range) => (*range.start(), *range.end()),\n                                            None => (today.year() - 100, today.year() + 10),\n                                        };\n                                        for year in start_year..=end_year {\n                                            if ui\n                                                .selectable_value(\n                                                    &mut popup_state.year,\n                                                    year,\n                                                    year.to_string(),\n                                                )\n                                                .changed()\n                                            {\n                                                popup_state.day = popup_state\n                                                    .day\n                                                    .min(popup_state.last_day_of_month());\n                                                ui.memory_mut(|mem| {\n                                                    mem.data\n                                                        .insert_persisted(id, popup_state.clone());\n                                                });\n                                            }\n                                        }\n                                    });\n                            });\n                            strip.cell(|ui| {\n                                ComboBox::from_id_salt(\"date_picker_month\")\n                                    .selected_text(month_name(popup_state.month))\n                                    .show_ui(ui, |ui| {\n                                        for month in 1..=12 {\n                                            if ui\n                                                .selectable_value(\n                                                    &mut popup_state.month,\n                                                    month,\n                                                    month_name(month),\n                                                )\n                                                .changed()\n                                            {\n                                                popup_state.day = popup_state\n                                                    .day\n                                                    .min(popup_state.last_day_of_month());\n                                                ui.memory_mut(|mem| {\n                                                    mem.data\n                                                        .insert_persisted(id, popup_state.clone());\n                                                });\n                                            }\n                                        }\n                                    });\n                            });\n                            strip.cell(|ui| {\n                                ComboBox::from_id_salt(\"date_picker_day\")\n                                    .selected_text(popup_state.day.to_string())\n                                    .show_ui(ui, |ui| {\n                                        for day in 1..=popup_state.last_day_of_month() {\n                                            if ui\n                                                .selectable_value(\n                                                    &mut popup_state.day,\n                                                    day,\n                                                    day.to_string(),\n                                                )\n                                                .changed()\n                                            {\n                                                ui.memory_mut(|mem| {\n                                                    mem.data\n                                                        .insert_persisted(id, popup_state.clone());\n                                                });\n                                            }\n                                        }\n                                    });\n                            });\n                        });\n                    });\n                }\n\n                if self.arrows {\n                    strip.strip(|builder| {\n                        builder.sizes(Size::remainder(), 6).horizontal(|mut strip| {\n                            strip.cell(|ui| {\n                                ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                                    if ui\n                                        .button(\"<<<\")\n                                        .on_hover_text(\"subtract one year\")\n                                        .clicked()\n                                    {\n                                        popup_state.year -= 1;\n                                        popup_state.day =\n                                            popup_state.day.min(popup_state.last_day_of_month());\n                                        ui.data_mut(|data| {\n                                            data.insert_persisted(id, popup_state.clone());\n                                        });\n                                    }\n                                });\n                            });\n                            strip.cell(|ui| {\n                                ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                                    if ui\n                                        .button(\"<<\")\n                                        .on_hover_text(\"subtract one month\")\n                                        .clicked()\n                                    {\n                                        popup_state.month -= 1;\n                                        if popup_state.month == 0 {\n                                            popup_state.month = 12;\n                                            popup_state.year -= 1;\n                                        }\n                                        popup_state.day =\n                                            popup_state.day.min(popup_state.last_day_of_month());\n                                        ui.data_mut(|data| {\n                                            data.insert_persisted(id, popup_state.clone());\n                                        });\n                                    }\n                                });\n                            });\n                            strip.cell(|ui| {\n                                ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                                    if ui.button(\"<\").on_hover_text(\"subtract one day\").clicked() {\n                                        popup_state.day -= 1;\n                                        if popup_state.day == 0 {\n                                            popup_state.month -= 1;\n                                            if popup_state.month == 0 {\n                                                popup_state.year -= 1;\n                                                popup_state.month = 12;\n                                            }\n                                            popup_state.day = popup_state.last_day_of_month();\n                                        }\n                                        ui.data_mut(|data| {\n                                            data.insert_persisted(id, popup_state.clone());\n                                        });\n                                    }\n                                });\n                            });\n                            strip.cell(|ui| {\n                                ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                                    if ui.button(\">\").on_hover_text(\"add one day\").clicked() {\n                                        popup_state.day += 1;\n                                        if popup_state.day > popup_state.last_day_of_month() {\n                                            popup_state.day = 1;\n                                            popup_state.month += 1;\n                                            if popup_state.month > 12 {\n                                                popup_state.month = 1;\n                                                popup_state.year += 1;\n                                            }\n                                        }\n                                        ui.data_mut(|data| {\n                                            data.insert_persisted(id, popup_state.clone());\n                                        });\n                                    }\n                                });\n                            });\n                            strip.cell(|ui| {\n                                ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                                    if ui.button(\">>\").on_hover_text(\"add one month\").clicked() {\n                                        popup_state.month += 1;\n                                        if popup_state.month > 12 {\n                                            popup_state.month = 1;\n                                            popup_state.year += 1;\n                                        }\n                                        popup_state.day =\n                                            popup_state.day.min(popup_state.last_day_of_month());\n                                        ui.data_mut(|data| {\n                                            data.insert_persisted(id, popup_state.clone());\n                                        });\n                                    }\n                                });\n                            });\n                            strip.cell(|ui| {\n                                ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                                    if ui.button(\">>>\").on_hover_text(\"add one year\").clicked() {\n                                        popup_state.year += 1;\n                                        popup_state.day =\n                                            popup_state.day.min(popup_state.last_day_of_month());\n                                        ui.data_mut(|data| {\n                                            data.insert_persisted(id, popup_state.clone());\n                                        });\n                                    }\n                                });\n                            });\n                        });\n                    });\n                }\n\n                if self.calendar {\n                    strip.cell(|ui| {\n                        ui.spacing_mut().item_spacing = Vec2::new(1.0, 2.0);\n                        TableBuilder::new(ui)\n                            .vscroll(false)\n                            .columns(Column::remainder(), if self.calendar_week { 8 } else { 7 })\n                            .header(height, |mut header| {\n                                if self.calendar_week {\n                                    header.col(|ui| {\n                                        ui.with_layout(\n                                            Layout::centered_and_justified(Direction::TopDown),\n                                            |ui| {\n                                                ui.label(\"Week\");\n                                            },\n                                        );\n                                    });\n                                }\n\n                                //TODO(elwerene): Locale\n                                for name in [\"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\", \"Su\"] {\n                                    header.col(|ui| {\n                                        ui.with_layout(\n                                            Layout::centered_and_justified(Direction::TopDown),\n                                            |ui| {\n                                                ui.label(name);\n                                            },\n                                        );\n                                    });\n                                }\n                            })\n                            .body(|mut body| {\n                                for week in weeks {\n                                    body.row(height, |mut row| {\n                                        if self.calendar_week {\n                                            row.col(|ui| {\n                                                ui.label(week.number.to_string());\n                                            });\n                                        }\n                                        for day in week.days {\n                                            row.col(|ui| {\n                                                ui.with_layout(\n                                                    Layout::top_down_justified(Align::Center),\n                                                    |ui| {\n                                                        let fill_color = if popup_state.year\n                                                            == day.year()\n                                                            && popup_state.month == day.month()\n                                                            && popup_state.day == day.day()\n                                                        {\n                                                            ui.visuals().selection.bg_fill\n                                                        } else if (day.weekday() == Weekday::Sat\n                                                            || day.weekday() == Weekday::Sun)\n                                                            && self.highlight_weekends\n                                                        {\n                                                            if ui.visuals().dark_mode {\n                                                                Color32::DARK_RED\n                                                            } else {\n                                                                Color32::LIGHT_RED\n                                                            }\n                                                        } else {\n                                                            ui.visuals().extreme_bg_color\n                                                        };\n\n                                                        let mut text_color = ui\n                                                            .visuals()\n                                                            .widgets\n                                                            .inactive\n                                                            .text_color();\n\n                                                        if day.month() != popup_state.month {\n                                                            text_color =\n                                                                text_color.linear_multiply(0.5);\n                                                        }\n\n                                                        let button_response = ui.add(\n                                                            Button::new(\n                                                                RichText::new(\n                                                                    day.day().to_string(),\n                                                                )\n                                                                .color(text_color),\n                                                            )\n                                                            .fill(fill_color),\n                                                        );\n\n                                                        if day == today {\n                                                            // Encircle today's date\n                                                            let stroke = ui\n                                                                .visuals()\n                                                                .widgets\n                                                                .inactive\n                                                                .fg_stroke;\n                                                            ui.painter().circle_stroke(\n                                                                button_response.rect.center(),\n                                                                8.0,\n                                                                stroke,\n                                                            );\n                                                        }\n\n                                                        if button_response.clicked() {\n                                                            popup_state.year = day.year();\n                                                            popup_state.month = day.month();\n                                                            popup_state.day = day.day();\n                                                            ui.data_mut(|data| {\n                                                                data.insert_persisted(\n                                                                    id,\n                                                                    popup_state.clone(),\n                                                                );\n                                                            });\n                                                        }\n                                                    },\n                                                );\n                                            });\n                                        }\n                                    });\n                                }\n                            });\n                    });\n                }\n\n                strip.strip(|builder| {\n                    builder.sizes(Size::remainder(), 3).horizontal(|mut strip| {\n                        strip.empty();\n                        strip.cell(|ui| {\n                            ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                                if ui.button(\"Cancel\").clicked() {\n                                    close = true;\n                                }\n                            });\n                        });\n                        strip.cell(|ui| {\n                            ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                                if ui.button(\"Save\").clicked() {\n                                    *self.selection = NaiveDate::from_ymd_opt(\n                                        popup_state.year,\n                                        popup_state.month,\n                                        popup_state.day,\n                                    )\n                                    .expect(\"Could not create NaiveDate\");\n                                    saved = true;\n                                    close = true;\n                                }\n                            });\n                        });\n                    });\n                });\n            });\n\n        if close {\n            popup_state.setup = false;\n            ui.data_mut(|data| {\n                data.insert_persisted(id, popup_state);\n                data.get_persisted_mut_or_default::<DatePickerButtonState>(self.button_id)\n                    .picker_visible = false;\n            });\n        }\n\n        saved && close\n    }\n}\n\nfn month_name(i: u32) -> &'static str {\n    match i {\n        1 => \"January\",\n        2 => \"February\",\n        3 => \"March\",\n        4 => \"April\",\n        5 => \"May\",\n        6 => \"June\",\n        7 => \"July\",\n        8 => \"August\",\n        9 => \"September\",\n        10 => \"October\",\n        11 => \"November\",\n        12 => \"December\",\n        _ => panic!(\"Unknown month: {i}\"),\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/image.rs",
    "content": "#[cfg(feature = \"svg\")]\nuse egui::SizeHint;\n\n// ----------------------------------------------------------------------------\n\n/// Load a (non-svg) image.\n///\n/// Requires the \"image\" feature. You must also opt-in to the image formats you need\n/// with e.g. `image = { version = \"0.25\", features = [\"jpeg\", \"png\"] }`.\n///\n/// # Errors\n/// On invalid image or unsupported image format.\n#[cfg(feature = \"image\")]\npub fn load_image_bytes(image_bytes: &[u8]) -> Result<egui::ColorImage, egui::load::LoadError> {\n    profiling::function_scope!();\n    let image = image::load_from_memory(image_bytes).map_err(|err| match err {\n        image::ImageError::Unsupported(err) => match err.kind() {\n            image::error::UnsupportedErrorKind::Format(format) => {\n                egui::load::LoadError::FormatNotSupported {\n                    detected_format: Some(format.to_string()),\n                }\n            }\n            _ => egui::load::LoadError::Loading(err.to_string()),\n        },\n        err => egui::load::LoadError::Loading(err.to_string()),\n    })?;\n    let size = [image.width() as _, image.height() as _];\n    let image_buffer = image.to_rgba8();\n    let pixels = image_buffer.as_flat_samples();\n\n    // TODO(emilk): if this is a PNG, looks for DPI info to calculate the source size,\n    // e.g. for screenshots taken on a high-DPI/retina display.\n\n    Ok(egui::ColorImage::from_rgba_unmultiplied(\n        size,\n        pixels.as_slice(),\n    ))\n}\n\n/// Load an SVG and rasterize it into an egui image.\n///\n/// Requires the \"svg\" feature.\n///\n/// # Errors\n/// On invalid image\n#[cfg(feature = \"svg\")]\npub fn load_svg_bytes(\n    svg_bytes: &[u8],\n    options: &resvg::usvg::Options<'_>,\n) -> Result<egui::ColorImage, String> {\n    load_svg_bytes_with_size(svg_bytes, Default::default(), options)\n}\n\n/// Load an SVG and rasterize it into an egui image with a scaling parameter.\n///\n/// Requires the \"svg\" feature.\n///\n/// # Errors\n/// On invalid image\n#[cfg(feature = \"svg\")]\npub fn load_svg_bytes_with_size(\n    svg_bytes: &[u8],\n    size_hint: SizeHint,\n    options: &resvg::usvg::Options<'_>,\n) -> Result<egui::ColorImage, String> {\n    use egui::Vec2;\n    use resvg::{\n        tiny_skia::Pixmap,\n        usvg::{Transform, Tree},\n    };\n\n    profiling::function_scope!();\n\n    let rtree = Tree::from_data(svg_bytes, options).map_err(|err| err.to_string())?;\n\n    let source_size = Vec2::new(rtree.size().width(), rtree.size().height());\n\n    let scaled_size = match size_hint {\n        SizeHint::Size {\n            width,\n            height,\n            maintain_aspect_ratio,\n        } => {\n            if maintain_aspect_ratio {\n                // As large as possible, without exceeding the given size:\n                let mut size = source_size;\n                size *= width as f32 / source_size.x;\n                if size.y > height as f32 {\n                    size *= height as f32 / size.y;\n                }\n                size\n            } else {\n                Vec2::new(width as _, height as _)\n            }\n        }\n        SizeHint::Height(h) => source_size * (h as f32 / source_size.y),\n        SizeHint::Width(w) => source_size * (w as f32 / source_size.x),\n        SizeHint::Scale(scale) => scale.into_inner() * source_size,\n    };\n\n    let scaled_size = scaled_size.round();\n    let (w, h) = (scaled_size.x as u32, scaled_size.y as u32);\n\n    let mut pixmap =\n        Pixmap::new(w, h).ok_or_else(|| format!(\"Failed to create SVG Pixmap of size {w}x{h}\"))?;\n\n    resvg::render(\n        &rtree,\n        Transform::from_scale(w as f32 / source_size.x, h as f32 / source_size.y),\n        &mut pixmap.as_mut(),\n    );\n\n    let image = egui::ColorImage::from_rgba_premultiplied([w as _, h as _], pixmap.data())\n        .with_source_size(source_size);\n\n    Ok(image)\n}\n"
  },
  {
    "path": "crates/egui_extras/src/layout.rs",
    "content": "use egui::{Id, Pos2, Rect, Response, Sense, Ui, UiBuilder, emath::GuiRounding as _};\n\n#[derive(Clone, Copy)]\npub(crate) enum CellSize {\n    /// Absolute size in points\n    Absolute(f32),\n\n    /// Take all available space\n    Remainder,\n}\n\n/// Cells are positioned in two dimensions, cells go in one direction and form lines.\n///\n/// In a strip there's only one line which goes in the direction of the strip:\n///\n/// In a horizontal strip, a [`StripLayout`] with horizontal [`CellDirection`] is used.\n/// Its cells go from left to right inside this [`StripLayout`].\n///\n/// In a table there's a [`StripLayout`] for each table row with a horizontal [`CellDirection`].\n/// Its cells go from left to right. And the lines go from top to bottom.\npub(crate) enum CellDirection {\n    /// Cells go from left to right.\n    Horizontal,\n\n    /// Cells go from top to bottom.\n    Vertical,\n}\n\n/// Flags used by [`StripLayout::add`].\n#[derive(Clone, Copy, Default)]\npub(crate) struct StripLayoutFlags {\n    pub(crate) clip: bool,\n    pub(crate) striped: bool,\n    pub(crate) hovered: bool,\n    pub(crate) selected: bool,\n    pub(crate) overline: bool,\n\n    /// Used when we want to accurately measure the size of this cell.\n    pub(crate) sizing_pass: bool,\n}\n\n/// Positions cells in [`CellDirection`] and starts a new line on [`StripLayout::end_line`]\npub struct StripLayout<'l> {\n    pub(crate) ui: &'l mut Ui,\n    direction: CellDirection,\n    pub(crate) rect: Rect,\n    pub(crate) cursor: Pos2,\n\n    /// Keeps track of the max used position,\n    /// so we know how much space we used.\n    max: Pos2,\n\n    cell_layout: egui::Layout,\n    sense: Sense,\n}\n\nimpl<'l> StripLayout<'l> {\n    pub(crate) fn new(\n        ui: &'l mut Ui,\n        direction: CellDirection,\n        cell_layout: egui::Layout,\n        sense: Sense,\n    ) -> Self {\n        let rect = ui.available_rect_before_wrap();\n        let pos = rect.left_top();\n\n        Self {\n            ui,\n            direction,\n            rect,\n            cursor: pos,\n            max: pos,\n            cell_layout,\n            sense,\n        }\n    }\n\n    fn cell_rect(&self, width: &CellSize, height: &CellSize) -> Rect {\n        Rect {\n            min: self.cursor,\n            max: Pos2 {\n                x: match width {\n                    CellSize::Absolute(width) => self.cursor.x + width,\n                    CellSize::Remainder => self.rect.right(),\n                },\n                y: match height {\n                    CellSize::Absolute(height) => self.cursor.y + height,\n                    CellSize::Remainder => self.rect.bottom(),\n                },\n            },\n        }\n    }\n\n    fn set_pos(&mut self, rect: Rect) {\n        self.max.x = self.max.x.max(rect.right());\n        self.max.y = self.max.y.max(rect.bottom());\n\n        match self.direction {\n            CellDirection::Horizontal => {\n                self.cursor.x = rect.right() + self.ui.spacing().item_spacing.x;\n            }\n            CellDirection::Vertical => {\n                self.cursor.y = rect.bottom() + self.ui.spacing().item_spacing.y;\n            }\n        }\n    }\n\n    pub(crate) fn empty(&mut self, width: CellSize, height: CellSize) {\n        self.set_pos(self.cell_rect(&width, &height));\n    }\n\n    /// This is the innermost part of [`crate::Table`] and [`crate::Strip`].\n    ///\n    /// Return the used space (`min_rect`) plus the [`Response`] of the whole cell.\n    pub(crate) fn add(\n        &mut self,\n        flags: StripLayoutFlags,\n        width: CellSize,\n        height: CellSize,\n        child_ui_id_salt: Id,\n        add_cell_contents: impl FnOnce(&mut Ui),\n    ) -> (Rect, Response) {\n        let max_rect = self.cell_rect(&width, &height);\n\n        // Make sure we don't have a gap in the stripe/frame/selection background:\n        let item_spacing = self.ui.spacing().item_spacing;\n        let gapless_rect = max_rect.expand2(0.5 * item_spacing).round_ui();\n\n        if flags.striped {\n            self.ui.painter().rect_filled(\n                gapless_rect,\n                egui::CornerRadius::ZERO,\n                self.ui.visuals().faint_bg_color,\n            );\n        }\n\n        if flags.selected {\n            self.ui.painter().rect_filled(\n                gapless_rect,\n                egui::CornerRadius::ZERO,\n                self.ui.visuals().selection.bg_fill,\n            );\n        }\n\n        if flags.hovered && !flags.selected && self.sense.interactive() {\n            self.ui.painter().rect_filled(\n                gapless_rect,\n                egui::CornerRadius::ZERO,\n                self.ui.visuals().widgets.hovered.bg_fill,\n            );\n        }\n\n        let mut child_ui = self.cell(flags, max_rect, child_ui_id_salt, add_cell_contents);\n\n        let used_rect = child_ui.min_rect();\n\n        // Make sure we catch clicks etc on the _whole_ cell:\n        child_ui.set_min_size(max_rect.size());\n\n        let allocation_rect = if self.ui.is_sizing_pass() {\n            used_rect\n        } else if flags.clip {\n            max_rect\n        } else {\n            max_rect | used_rect\n        };\n\n        self.set_pos(allocation_rect);\n\n        self.ui.advance_cursor_after_rect(allocation_rect);\n\n        let response = child_ui.response();\n\n        (used_rect, response)\n    }\n\n    /// only needed for layouts with multiple lines, like [`Table`](crate::Table).\n    pub fn end_line(&mut self) {\n        match self.direction {\n            CellDirection::Horizontal => {\n                self.cursor.y = self.max.y + self.ui.spacing().item_spacing.y;\n                self.cursor.x = self.rect.left();\n            }\n            CellDirection::Vertical => {\n                self.cursor.x = self.max.x + self.ui.spacing().item_spacing.x;\n                self.cursor.y = self.rect.top();\n            }\n        }\n    }\n\n    /// Skip a lot of space.\n    pub(crate) fn skip_space(&mut self, delta: egui::Vec2) {\n        let before = self.cursor;\n        self.cursor += delta;\n        let rect = Rect::from_two_pos(before, self.cursor);\n        self.ui.allocate_rect(rect, Sense::hover());\n    }\n\n    /// Return the Ui to which the contents where added\n    fn cell(\n        &mut self,\n        flags: StripLayoutFlags,\n        max_rect: Rect,\n        child_ui_id_salt: egui::Id,\n        add_cell_contents: impl FnOnce(&mut Ui),\n    ) -> Ui {\n        let mut ui_builder = UiBuilder::new()\n            .id_salt(child_ui_id_salt)\n            .ui_stack_info(egui::UiStackInfo::new(egui::UiKind::TableCell))\n            .max_rect(max_rect)\n            .layout(self.cell_layout)\n            .sense(self.sense);\n        if flags.sizing_pass {\n            ui_builder = ui_builder.sizing_pass();\n        }\n\n        let mut child_ui = self.ui.new_child(ui_builder);\n\n        if flags.clip {\n            let margin = egui::Vec2::splat(self.ui.visuals().clip_rect_margin);\n            let margin = margin.min(0.5 * self.ui.spacing().item_spacing);\n            let clip_rect = max_rect.expand2(margin);\n            child_ui.shrink_clip_rect(clip_rect);\n\n            if !child_ui.is_sizing_pass() {\n                // Better to truncate (if we can), rather than hard clipping:\n                child_ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Truncate);\n            }\n        }\n\n        if flags.selected {\n            let stroke_color = child_ui.style().visuals.selection.stroke.color;\n            child_ui.style_mut().visuals.override_text_color = Some(stroke_color);\n        }\n\n        if flags.overline {\n            child_ui.painter().hline(\n                max_rect.x_range(),\n                max_rect.top(),\n                child_ui.visuals().widgets.noninteractive.bg_stroke,\n            );\n        }\n\n        add_cell_contents(&mut child_ui);\n\n        child_ui\n    }\n\n    /// Allocate the rect in [`Self::ui`] so that the scrollview knows about our size\n    pub fn allocate_rect(&mut self) -> Response {\n        let mut rect = self.rect;\n        rect.set_right(self.max.x);\n        rect.set_bottom(self.max.y);\n\n        self.ui.allocate_rect(rect, Sense::hover())\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/lib.rs",
    "content": "//! This is a crate that adds some features on top top of [`egui`](https://github.com/emilk/egui).\n//!\n//! This crate are for experimental features, and features that require big dependencies that does not belong in `egui`.\n//!\n//! ## Feature flags\n#![cfg_attr(feature = \"document-features\", doc = document_features::document_features!())]\n//!\n\n#![expect(clippy::manual_range_contains)]\n\n#[cfg(feature = \"chrono\")]\nmod datepicker;\n\npub mod syntax_highlighting;\n\n#[doc(hidden)]\npub mod image;\nmod layout;\npub mod loaders;\nmod sizing;\nmod strip;\nmod table;\n\n#[cfg(feature = \"chrono\")]\npub use crate::datepicker::DatePickerButton;\n\npub(crate) use crate::layout::StripLayout;\npub use crate::sizing::Size;\npub use crate::strip::*;\npub use crate::table::*;\n\npub use loaders::install_image_loaders;\n\n// ---------------------------------------------------------------------------\n\n/// Panic in debug builds, log otherwise.\nmacro_rules! log_or_panic {\n    ($fmt: literal) => {$crate::log_or_panic!($fmt,)};\n    ($fmt: literal, $($arg: tt)*) => {{\n        if cfg!(debug_assertions) {\n            panic!($fmt, $($arg)*);\n        } else {\n            log::error!($fmt, $($arg)*);\n        }\n    }};\n}\npub(crate) use log_or_panic;\n"
  },
  {
    "path": "crates/egui_extras/src/loaders/file_loader.rs",
    "content": "use ahash::HashMap;\nuse egui::{\n    load::{Bytes, BytesLoadResult, BytesLoader, BytesPoll, LoadError},\n    mutex::Mutex,\n};\nuse std::{sync::Arc, task::Poll, thread};\n\n#[derive(Clone)]\nstruct File {\n    bytes: Arc<[u8]>,\n    mime: Option<String>,\n}\n\ntype Entry = Poll<Result<File, String>>;\n\n#[derive(Default)]\npub struct FileLoader {\n    /// Cache for loaded files\n    cache: Arc<Mutex<HashMap<String, Entry>>>,\n}\n\nimpl FileLoader {\n    pub const ID: &'static str = egui::generate_loader_id!(FileLoader);\n}\n\nconst PROTOCOL: &str = \"file://\";\n\n/// Remove the leading slash from the path if the target OS is Windows.\n///\n/// This is because Windows paths are not supposed to start with a slash.\n/// For example, `file:///C:/path/to/file` is a valid URI, but `/C:/path/to/file` is not a valid path.\n#[inline]\nfn trim_extra_slash(s: &str) -> &str {\n    if cfg!(target_os = \"windows\") {\n        s.trim_start_matches('/')\n    } else {\n        s\n    }\n}\n\nimpl BytesLoader for FileLoader {\n    fn id(&self) -> &str {\n        Self::ID\n    }\n\n    fn load(&self, ctx: &egui::Context, uri: &str) -> BytesLoadResult {\n        // File loader only supports the `file` protocol.\n        let Some(path) = uri.strip_prefix(PROTOCOL).map(trim_extra_slash) else {\n            return Err(LoadError::NotSupported);\n        };\n\n        let mut cache = self.cache.lock();\n        if let Some(entry) = cache.get(uri).cloned() {\n            // `path` has either begun loading, is loaded, or has failed to load.\n            match entry {\n                Poll::Ready(Ok(file)) => Ok(BytesPoll::Ready {\n                    size: None,\n                    bytes: Bytes::Shared(file.bytes),\n                    mime: file.mime,\n                }),\n                Poll::Ready(Err(err)) => Err(LoadError::Loading(err)),\n                Poll::Pending => Ok(BytesPoll::Pending { size: None }),\n            }\n        } else {\n            log::trace!(\"started loading {uri:?}\");\n            // We need to load the file at `path`.\n\n            // Set the file to `pending` until we finish loading it.\n            let path = path.to_owned();\n            cache.insert(uri.to_owned(), Poll::Pending);\n            drop(cache);\n\n            // Spawn a thread to read the file, so that we don't block the render for too long.\n            thread::Builder::new()\n                .name(format!(\"egui_extras::FileLoader::load({uri:?})\"))\n                .spawn({\n                    let ctx = ctx.clone();\n                    let cache = Arc::clone(&self.cache);\n                    let uri = uri.to_owned();\n                    move || {\n                        let result = match std::fs::read(&path) {\n                            Ok(bytes) => {\n                                #[cfg(feature = \"file\")]\n                                let mime = mime_guess2::from_path(&path)\n                                    .first_raw()\n                                    .map(|v| v.to_owned());\n\n                                #[cfg(not(feature = \"file\"))]\n                                let mime = None;\n\n                                Ok(File {\n                                    bytes: bytes.into(),\n                                    mime,\n                                })\n                            }\n                            Err(err) => Err(err.to_string()),\n                        };\n                        let repaint = {\n                            let mut cache = cache.lock();\n                            if let std::collections::hash_map::Entry::Occupied(mut entry) = cache.entry(uri.clone()) {\n                                let entry = entry.get_mut();\n                                *entry = Poll::Ready(result);\n                                log::trace!(\"Finished loading {uri:?}\");\n                                true\n                            } else {\n                                log::trace!(\"Canceled loading {uri:?}\\nNote: This can happen if `forget_image` is called while the image is still loading.\");\n                                false\n                            }\n                        };\n                        // We may not lock Context while the cache lock is held (see ImageLoader::load\n                        // for details).\n                        if repaint {\n                            ctx.request_repaint();\n                        }\n                    }\n                })\n                .expect(\"failed to spawn thread\");\n\n            Ok(BytesPoll::Pending { size: None })\n        }\n    }\n\n    fn forget(&self, uri: &str) {\n        let _ = self.cache.lock().remove(uri);\n    }\n\n    fn forget_all(&self) {\n        self.cache.lock().clear();\n    }\n\n    fn byte_size(&self) -> usize {\n        self.cache\n            .lock()\n            .values()\n            .map(|entry| match entry {\n                Poll::Ready(Ok(file)) => {\n                    file.bytes.len() + file.mime.as_ref().map_or(0, |m| m.len())\n                }\n                Poll::Ready(Err(err)) => err.len(),\n                _ => 0,\n            })\n            .sum()\n    }\n\n    fn has_pending(&self) -> bool {\n        self.cache.lock().values().any(|entry| entry.is_pending())\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/loaders/gif_loader.rs",
    "content": "use ahash::HashMap;\nuse egui::{\n    ColorImage, FrameDurations, Id, decode_animated_image_uri, has_gif_magic_header,\n    load::{BytesPoll, ImageLoadResult, ImageLoader, ImagePoll, LoadError, SizeHint},\n    mutex::Mutex,\n};\nuse image::AnimationDecoder as _;\nuse std::{io::Cursor, mem::size_of, sync::Arc, time::Duration};\n\n/// Array of Frames and the duration for how long each frame should be shown\n#[derive(Debug, Clone)]\npub struct AnimatedImage {\n    frames: Vec<Arc<ColorImage>>,\n    frame_durations: FrameDurations,\n}\n\nimpl AnimatedImage {\n    fn load_gif(data: &[u8]) -> Result<Self, String> {\n        let decoder = image::codecs::gif::GifDecoder::new(Cursor::new(data))\n            .map_err(|err| format!(\"Failed to decode gif: {err}\"))?;\n        let mut images = vec![];\n        let mut durations = vec![];\n        for frame in decoder.into_frames() {\n            let frame = frame.map_err(|err| format!(\"Failed to decode gif: {err}\"))?;\n            let img = frame.buffer();\n            let pixels = img.as_flat_samples();\n\n            let delay: Duration = frame.delay().into();\n            images.push(Arc::new(ColorImage::from_rgba_unmultiplied(\n                [img.width() as usize, img.height() as usize],\n                pixels.as_slice(),\n            )));\n            durations.push(delay);\n        }\n        Ok(Self {\n            frames: images,\n            frame_durations: FrameDurations::new(durations),\n        })\n    }\n}\n\nimpl AnimatedImage {\n    pub fn byte_len(&self) -> usize {\n        size_of::<Self>()\n            + self\n                .frames\n                .iter()\n                .map(|image| {\n                    image.pixels.len() * size_of::<egui::Color32>() + size_of::<Duration>()\n                })\n                .sum::<usize>()\n    }\n\n    /// Gets image at index\n    pub fn get_image(&self, index: usize) -> Arc<ColorImage> {\n        Arc::clone(&self.frames[index % self.frames.len()])\n    }\n}\ntype Entry = Result<Arc<AnimatedImage>, String>;\n\n#[derive(Default)]\npub struct GifLoader {\n    cache: Mutex<HashMap<String, Entry>>,\n}\n\nimpl GifLoader {\n    pub const ID: &'static str = egui::generate_loader_id!(GifLoader);\n}\n\nimpl ImageLoader for GifLoader {\n    fn id(&self) -> &str {\n        Self::ID\n    }\n\n    fn load(&self, ctx: &egui::Context, frame_uri: &str, _: SizeHint) -> ImageLoadResult {\n        let (image_uri, frame_index) =\n            decode_animated_image_uri(frame_uri).map_err(|_err| LoadError::NotSupported)?;\n        let mut cache = self.cache.lock();\n        if let Some(entry) = cache.get(image_uri).cloned() {\n            match entry {\n                Ok(image) => Ok(ImagePoll::Ready {\n                    image: image.get_image(frame_index),\n                }),\n                Err(err) => Err(LoadError::Loading(err)),\n            }\n        } else {\n            match ctx.try_load_bytes(image_uri) {\n                Ok(BytesPoll::Ready { bytes, .. }) => {\n                    if !has_gif_magic_header(&bytes) {\n                        return Err(LoadError::NotSupported);\n                    }\n                    log::trace!(\"started loading {image_uri:?}\");\n                    let result = AnimatedImage::load_gif(&bytes).map(Arc::new);\n                    if let Ok(v) = &result {\n                        ctx.data_mut(|data| {\n                            *data.get_temp_mut_or_default(Id::new(image_uri)) =\n                                v.frame_durations.clone();\n                        });\n                    }\n                    log::trace!(\"finished loading {image_uri:?}\");\n                    cache.insert(image_uri.into(), result.clone());\n                    match result {\n                        Ok(image) => Ok(ImagePoll::Ready {\n                            image: image.get_image(frame_index),\n                        }),\n                        Err(err) => Err(LoadError::Loading(err)),\n                    }\n                }\n                Ok(BytesPoll::Pending { size }) => Ok(ImagePoll::Pending { size }),\n                Err(err) => Err(err),\n            }\n        }\n    }\n\n    fn forget(&self, uri: &str) {\n        let _ = self.cache.lock().remove(uri);\n    }\n\n    fn forget_all(&self) {\n        self.cache.lock().clear();\n    }\n\n    fn byte_size(&self) -> usize {\n        self.cache\n            .lock()\n            .values()\n            .map(|v| match v {\n                Ok(v) => v.byte_len(),\n                Err(e) => e.len(),\n            })\n            .sum()\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/loaders/http_loader.rs",
    "content": "use ahash::HashMap;\nuse egui::{\n    load::{Bytes, BytesLoadResult, BytesLoader, BytesPoll, LoadError},\n    mutex::Mutex,\n};\nuse std::{sync::Arc, task::Poll};\n\n#[derive(Clone)]\nstruct File {\n    bytes: Arc<[u8]>,\n    mime: Option<String>,\n}\n\nimpl File {\n    fn from_response(uri: &str, response: ehttp::Response) -> Result<Self, String> {\n        if !response.ok {\n            match response.text() {\n                Some(response_text) => {\n                    return Err(format!(\n                        \"failed to load {uri:?}: {} {} {response_text}\",\n                        response.status, response.status_text\n                    ));\n                }\n                None => {\n                    return Err(format!(\n                        \"failed to load {uri:?}: {} {}\",\n                        response.status, response.status_text\n                    ));\n                }\n            }\n        }\n\n        let mime = response.content_type().map(|v| v.to_owned());\n        let bytes = response.bytes.into();\n\n        Ok(Self { bytes, mime })\n    }\n}\n\ntype Entry = Poll<Result<File, String>>;\n\n#[derive(Default)]\npub struct EhttpLoader {\n    cache: Arc<Mutex<HashMap<String, Entry>>>,\n}\n\nimpl EhttpLoader {\n    pub const ID: &'static str = egui::generate_loader_id!(EhttpLoader);\n}\n\nconst PROTOCOLS: &[&str] = &[\"http://\", \"https://\"];\n\nfn starts_with_one_of(s: &str, prefixes: &[&str]) -> bool {\n    prefixes.iter().any(|prefix| s.starts_with(prefix))\n}\n\nimpl BytesLoader for EhttpLoader {\n    fn id(&self) -> &str {\n        Self::ID\n    }\n\n    fn load(&self, ctx: &egui::Context, uri: &str) -> BytesLoadResult {\n        if !starts_with_one_of(uri, PROTOCOLS) {\n            return Err(LoadError::NotSupported);\n        }\n\n        let mut cache = self.cache.lock();\n        if let Some(entry) = cache.get(uri).cloned() {\n            match entry {\n                Poll::Ready(Ok(file)) => Ok(BytesPoll::Ready {\n                    size: None,\n                    bytes: Bytes::Shared(file.bytes),\n                    mime: file.mime,\n                }),\n                Poll::Ready(Err(err)) => Err(LoadError::Loading(err)),\n                Poll::Pending => Ok(BytesPoll::Pending { size: None }),\n            }\n        } else {\n            log::trace!(\"started loading {uri:?}\");\n\n            let uri = uri.to_owned();\n            cache.insert(uri.clone(), Poll::Pending);\n            drop(cache);\n\n            ehttp::fetch(ehttp::Request::get(uri.clone()), {\n                let ctx = ctx.clone();\n                let cache = Arc::clone(&self.cache);\n                move |response| {\n                    let result = match response {\n                        Ok(response) => File::from_response(&uri, response),\n                        Err(err) => {\n                            // Log details; return summary\n                            log::error!(\"Failed to load {uri:?}: {err}\");\n                            Err(format!(\"Failed to load {uri:?}\"))\n                        }\n                    };\n                    let repaint = {\n                        let mut cache = cache.lock();\n                        if let std::collections::hash_map::Entry::Occupied(mut entry) =\n                            cache.entry(uri.clone())\n                        {\n                            let entry = entry.get_mut();\n                            *entry = Poll::Ready(result);\n                            log::trace!(\"Finished loading {uri:?}\");\n                            true\n                        } else {\n                            log::trace!(\n                                \"Canceled loading {uri:?}\\nNote: This can happen if `forget_image` is called while the image is still loading.\"\n                            );\n                            false\n                        }\n                    };\n                    // We may not lock Context while the cache lock is held (see ImageLoader::load\n                    // for details).\n                    if repaint {\n                        ctx.request_repaint();\n                    }\n                }\n            });\n\n            Ok(BytesPoll::Pending { size: None })\n        }\n    }\n\n    fn forget(&self, uri: &str) {\n        let _ = self.cache.lock().remove(uri);\n    }\n\n    fn forget_all(&self) {\n        self.cache.lock().clear();\n    }\n\n    fn byte_size(&self) -> usize {\n        self.cache\n            .lock()\n            .values()\n            .map(|entry| match entry {\n                Poll::Ready(Ok(file)) => {\n                    file.bytes.len() + file.mime.as_ref().map_or(0, |m| m.len())\n                }\n                Poll::Ready(Err(err)) => err.len(),\n                _ => 0,\n            })\n            .sum()\n    }\n\n    fn has_pending(&self) -> bool {\n        self.cache.lock().values().any(|entry| entry.is_pending())\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/loaders/image_loader.rs",
    "content": "use ahash::HashMap;\nuse egui::{\n    ColorImage, decode_animated_image_uri,\n    load::{Bytes, BytesPoll, ImageLoadResult, ImageLoader, ImagePoll, LoadError, SizeHint},\n    mutex::Mutex,\n};\nuse image::ImageFormat;\nuse std::{mem::size_of, path::Path, sync::Arc, task::Poll};\n\n#[cfg(not(target_arch = \"wasm32\"))]\nuse std::thread;\n\ntype Entry = Poll<Result<Arc<ColorImage>, String>>;\n\n#[derive(Default)]\npub struct ImageCrateLoader {\n    cache: Arc<Mutex<HashMap<String, Entry>>>,\n}\n\nimpl ImageCrateLoader {\n    pub const ID: &'static str = egui::generate_loader_id!(ImageCrateLoader);\n}\n\nfn is_supported_uri(uri: &str) -> bool {\n    let Some(ext) = Path::new(uri)\n        .extension()\n        .and_then(|ext| ext.to_str().map(|ext| ext.to_lowercase()))\n    else {\n        // `true` because if there's no extension, assume that we support it\n        return true;\n    };\n\n    // Uses only the enabled image crate features\n    ImageFormat::from_extension(ext).is_some_and(|format| format.reading_enabled())\n}\n\nfn is_supported_mime(mime: &str) -> bool {\n    // some mime types e.g. reflect binary files or mark the content as a download, which\n    // may be a valid image or not, in this case, defer the decision on the format guessing\n    // or the image crate and return true here\n    let mimes_to_defer = [\n        \"application/octet-stream\",\n        \"application/x-msdownload\",\n        \"application/force-download\",\n    ];\n    for m in &mimes_to_defer {\n        // use contains instead of direct equality, as e.g. encoding info might be appended\n        if mime.contains(m) {\n            return true;\n        }\n    }\n\n    // Some servers may return a media type with an optional parameter, e.g. \"image/jpeg; charset=utf-8\".\n    let (mime_type, _) = mime.split_once(';').unwrap_or((mime, \"\"));\n\n    // Uses only the enabled image crate features\n    ImageFormat::from_mime_type(mime_type).is_some_and(|format| format.reading_enabled())\n}\n\nimpl ImageLoader for ImageCrateLoader {\n    fn id(&self) -> &str {\n        Self::ID\n    }\n\n    fn load(&self, ctx: &egui::Context, uri: &str, _: SizeHint) -> ImageLoadResult {\n        // three stages of guessing if we support loading the image:\n        // 1. URI extension (only done for files)\n        // 2. Mime from `BytesPoll::Ready`\n        // 3. image::guess_format (used internally by image::load_from_memory)\n\n        // TODO(lucasmerlin): Egui currently changes all URIs for webp and gif files to include\n        // the frame index (#0), which breaks if the animated image loader is disabled.\n        // We work around this by removing the frame index from the URI here\n        let uri = decode_animated_image_uri(uri).map_or(uri, |(uri, _frame_index)| uri);\n\n        // (1)\n        if uri.starts_with(\"file://\") && !is_supported_uri(uri) {\n            return Err(LoadError::NotSupported);\n        }\n\n        #[cfg(not(target_arch = \"wasm32\"))]\n        #[expect(clippy::unnecessary_wraps)] // needed here to match other return types\n        fn load_image(\n            ctx: &egui::Context,\n            uri: &str,\n            cache: &Arc<Mutex<HashMap<String, Entry>>>,\n            bytes: &Bytes,\n        ) -> ImageLoadResult {\n            let uri = uri.to_owned();\n            cache.lock().insert(uri.clone(), Poll::Pending);\n\n            // Do the image parsing on a bg thread\n            thread::Builder::new()\n                .name(format!(\"egui_extras::ImageLoader::load({uri:?})\"))\n                .spawn({\n                    let ctx = ctx.clone();\n                    let cache = Arc::clone(cache);\n\n                    let uri = uri.clone();\n                    let bytes = bytes.clone();\n                    move || {\n                        log::trace!(\"ImageLoader - started loading {uri:?}\");\n                        let result = crate::image::load_image_bytes(&bytes)\n                            .map(Arc::new)\n                            .map_err(|err| err.to_string());\n                        let repaint = {\n                            let mut cache = cache.lock();\n\n                            if let std::collections::hash_map::Entry::Occupied(mut entry) = cache.entry(uri.clone()) {\n                                let entry = entry.get_mut();\n                                *entry = Poll::Ready(result);\n                                log::trace!(\"ImageLoader - finished loading {uri:?}\");\n                                true\n                            } else {\n                                log::trace!(\"ImageLoader - canceled loading {uri:?}\\nNote: This can happen if `forget_image` is called while the image is still loading.\");\n                                false\n                            }\n                        };\n                        // We may not lock Context while the cache lock is held, since this can\n                        // deadlock.\n                        // Example deadlock scenario:\n                        // - loader thread: lock cache\n                        // - main thread: lock ctx (e.g. in `Context::has_pending_images`)\n                        // - loader thread: try to lock ctx (in `request_repaint`)\n                        // - main thread: try to lock cache (from `Self::has_pending`)\n                        if repaint {\n                            ctx.request_repaint();\n                        }\n                    }\n                })\n                .expect(\"failed to spawn thread\");\n\n            Ok(ImagePoll::Pending { size: None })\n        }\n\n        #[cfg(target_arch = \"wasm32\")]\n        fn load_image(\n            _ctx: &egui::Context,\n            uri: &str,\n            cache: &Arc<Mutex<HashMap<String, Entry>>>,\n            bytes: &Bytes,\n        ) -> ImageLoadResult {\n            let mut cache_lock = cache.lock();\n            log::trace!(\"started loading {uri:?}\");\n            let result = crate::image::load_image_bytes(bytes)\n                .map(Arc::new)\n                .map_err(|err| err.to_string());\n            log::trace!(\"finished loading {uri:?}\");\n            cache_lock.insert(uri.into(), std::task::Poll::Ready(result.clone()));\n            match result {\n                Ok(image) => Ok(ImagePoll::Ready { image }),\n                Err(err) => Err(LoadError::Loading(err)),\n            }\n        }\n\n        let entry = self.cache.lock().get(uri).cloned();\n        if let Some(entry) = entry {\n            match entry {\n                Poll::Ready(Ok(image)) => Ok(ImagePoll::Ready { image }),\n                Poll::Ready(Err(err)) => Err(LoadError::Loading(err)),\n                Poll::Pending => Ok(ImagePoll::Pending { size: None }),\n            }\n        } else {\n            match ctx.try_load_bytes(uri) {\n                Ok(BytesPoll::Ready { bytes, mime, .. }) => {\n                    // (2)\n                    if let Some(mime) = mime\n                        && !is_supported_mime(&mime)\n                    {\n                        return Err(LoadError::FormatNotSupported {\n                            detected_format: Some(mime),\n                        });\n                    }\n                    load_image(ctx, uri, &self.cache, &bytes)\n                }\n                Ok(BytesPoll::Pending { size }) => Ok(ImagePoll::Pending { size }),\n                Err(err) => Err(err),\n            }\n        }\n    }\n\n    fn forget(&self, uri: &str) {\n        let _ = self.cache.lock().remove(uri);\n    }\n\n    fn forget_all(&self) {\n        self.cache.lock().clear();\n    }\n\n    fn byte_size(&self) -> usize {\n        self.cache\n            .lock()\n            .values()\n            .map(|result| match result {\n                Poll::Ready(Ok(image)) => image.pixels.len() * size_of::<egui::Color32>(),\n                Poll::Ready(Err(err)) => err.len(),\n                Poll::Pending => 0,\n            })\n            .sum()\n    }\n\n    fn has_pending(&self) -> bool {\n        self.cache.lock().values().any(|result| result.is_pending())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn check_support() {\n        assert!(is_supported_uri(\"https://test.png\"));\n        assert!(is_supported_uri(\"test.jpeg\"));\n        assert!(is_supported_uri(\"http://test.gif\"));\n        assert!(is_supported_uri(\"file://test\"));\n        assert!(!is_supported_uri(\"test.svg\"));\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/loaders/svg_loader.rs",
    "content": "use std::{\n    mem::size_of,\n    sync::{\n        Arc,\n        atomic::{AtomicU64, Ordering::Relaxed},\n    },\n};\n\nuse ahash::HashMap;\n\nuse egui::{\n    ColorImage,\n    load::{BytesPoll, ImageLoadResult, ImageLoader, ImagePoll, LoadError, SizeHint},\n    mutex::Mutex,\n};\n\nstruct Entry {\n    last_used: AtomicU64,\n    result: Result<Arc<ColorImage>, String>,\n}\n\npub struct SvgLoader {\n    pass_index: AtomicU64,\n    cache: Mutex<HashMap<String, HashMap<SizeHint, Entry>>>,\n    options: resvg::usvg::Options<'static>,\n}\n\nimpl SvgLoader {\n    pub const ID: &'static str = egui::generate_loader_id!(SvgLoader);\n}\n\nfn is_supported(uri: &str) -> bool {\n    uri.ends_with(\".svg\")\n}\n\nimpl Default for SvgLoader {\n    fn default() -> Self {\n        // opt is mutated when `svg_text` feature flag is enabled\n        #[allow(clippy::allow_attributes, unused_mut)]\n        let mut options = resvg::usvg::Options::default();\n\n        #[cfg(feature = \"svg_text\")]\n        options.fontdb_mut().load_system_fonts();\n\n        Self {\n            pass_index: AtomicU64::new(0),\n            cache: Mutex::new(HashMap::default()),\n            options,\n        }\n    }\n}\n\nimpl ImageLoader for SvgLoader {\n    fn id(&self) -> &str {\n        Self::ID\n    }\n\n    fn load(&self, ctx: &egui::Context, uri: &str, size_hint: SizeHint) -> ImageLoadResult {\n        if !is_supported(uri) {\n            return Err(LoadError::NotSupported);\n        }\n\n        let mut cache = self.cache.lock();\n        let bucket = cache.entry(uri.to_owned()).or_default();\n\n        if let Some(entry) = bucket.get(&size_hint) {\n            entry\n                .last_used\n                .store(self.pass_index.load(Relaxed), Relaxed);\n            match entry.result.clone() {\n                Ok(image) => Ok(ImagePoll::Ready { image }),\n                Err(err) => Err(LoadError::Loading(err)),\n            }\n        } else {\n            match ctx.try_load_bytes(uri) {\n                Ok(BytesPoll::Ready { bytes, .. }) => {\n                    log::trace!(\"Started loading {uri:?}\");\n                    let result =\n                        crate::image::load_svg_bytes_with_size(&bytes, size_hint, &self.options)\n                            .map(Arc::new);\n\n                    log::trace!(\"Finished loading {uri:?}\");\n                    bucket.insert(\n                        size_hint,\n                        Entry {\n                            last_used: AtomicU64::new(self.pass_index.load(Relaxed)),\n                            result: result.clone(),\n                        },\n                    );\n                    match result {\n                        Ok(image) => Ok(ImagePoll::Ready { image }),\n                        Err(err) => Err(LoadError::Loading(err)),\n                    }\n                }\n                Ok(BytesPoll::Pending { size }) => Ok(ImagePoll::Pending { size }),\n                Err(err) => Err(err),\n            }\n        }\n    }\n\n    fn forget(&self, uri: &str) {\n        self.cache.lock().retain(|key, _| key != uri);\n    }\n\n    fn forget_all(&self) {\n        self.cache.lock().clear();\n    }\n\n    fn byte_size(&self) -> usize {\n        self.cache\n            .lock()\n            .values()\n            .flat_map(|bucket| bucket.values())\n            .map(|entry| match &entry.result {\n                Ok(image) => image.pixels.len() * size_of::<egui::Color32>(),\n                Err(err) => err.len(),\n            })\n            .sum()\n    }\n\n    fn end_pass(&self, pass_index: u64) {\n        self.pass_index.store(pass_index, Relaxed);\n        let mut cache = self.cache.lock();\n        cache.retain(|_key, bucket| {\n            if 2 <= bucket.len() {\n                // There are multiple images of the same URI (e.g. SVGs of different scales).\n                // This could be because someone has an SVG in a resizable container,\n                // and so we get a lot of different sizes of it.\n                // This could wast RAM, so we remove the ones that are not used in this frame.\n                bucket.retain(|_, texture| pass_index <= texture.last_used.load(Relaxed) + 1);\n            }\n            !bucket.is_empty()\n        });\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn check_support() {\n        // inverse of same test in `image_loader.rs`\n        assert!(!is_supported(\"https://test.png\"));\n        assert!(!is_supported(\"test.jpeg\"));\n        assert!(!is_supported(\"http://test.gif\"));\n        assert!(!is_supported(\"test.webp\"));\n        assert!(!is_supported(\"file://test\"));\n        assert!(is_supported(\"test.svg\"));\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/loaders/webp_loader.rs",
    "content": "use ahash::HashMap;\nuse egui::{\n    ColorImage, FrameDurations, Id, decode_animated_image_uri, has_webp_header,\n    load::{BytesPoll, ImageLoadResult, ImageLoader, ImagePoll, LoadError, SizeHint},\n    mutex::Mutex,\n};\nuse image::{AnimationDecoder as _, ColorType, ImageDecoder as _, Rgba, codecs::webp::WebPDecoder};\nuse std::{io::Cursor, mem::size_of, sync::Arc, time::Duration};\n\n#[derive(Clone)]\nenum WebP {\n    Static(Arc<ColorImage>),\n    Animated(AnimatedImage),\n}\n\nimpl WebP {\n    fn load(data: &[u8]) -> Result<Self, String> {\n        let mut decoder = WebPDecoder::new(Cursor::new(data))\n            .map_err(|error| format!(\"WebP decode failure ({error})\"))?;\n\n        if decoder.has_animation() {\n            decoder\n                .set_background_color(Rgba([0, 0, 0, 0]))\n                .map_err(|error| {\n                    format!(\"Failure to set default background color for animated WebP ({error})\")\n                })?;\n\n            let mut images = vec![];\n            let mut durations = vec![];\n\n            for frame in decoder.into_frames() {\n                let frame =\n                    frame.map_err(|error| format!(\"WebP frame decode failure ({error})\"))?;\n                let image = frame.buffer();\n                let pixels = image.as_flat_samples();\n\n                images.push(Arc::new(ColorImage::from_rgba_unmultiplied(\n                    [image.width() as usize, image.height() as usize],\n                    pixels.as_slice(),\n                )));\n\n                let delay: Duration = frame.delay().into();\n                durations.push(delay);\n            }\n            Ok(Self::Animated(AnimatedImage {\n                frames: images,\n                frame_durations: FrameDurations::new(durations),\n            }))\n        } else {\n            // color_type() of WebPDecoder only returns Rgb8/Rgba8 variants of ColorType\n            let create_image = match decoder.color_type() {\n                ColorType::Rgb8 => ColorImage::from_rgb,\n                ColorType::Rgba8 => ColorImage::from_rgba_unmultiplied,\n                unreachable => {\n                    return Err(format!(\n                        \"Unreachable WebP color type, expected Rgb8/Rgba8, got {unreachable:?}\"\n                    ));\n                }\n            };\n\n            let (width, height) = decoder.dimensions();\n            let size = decoder.total_bytes() as usize;\n\n            let mut data = vec![0; size];\n            decoder\n                .read_image(&mut data)\n                .map_err(|error| format!(\"WebP image read failure ({error})\"))?;\n\n            Ok(Self::Static(Arc::new(create_image(\n                [width as usize, height as usize],\n                &data,\n            ))))\n        }\n    }\n\n    fn get_image(&self, frame_index: usize) -> Arc<ColorImage> {\n        match self {\n            Self::Static(image) => Arc::clone(image),\n            Self::Animated(animation) => animation.get_image_by_index(frame_index),\n        }\n    }\n\n    pub fn byte_len(&self) -> usize {\n        size_of::<Self>()\n            + match self {\n                Self::Static(image) => image.pixels.len() * size_of::<egui::Color32>(),\n                Self::Animated(animation) => animation.byte_len(),\n            }\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct AnimatedImage {\n    frames: Vec<Arc<ColorImage>>,\n    frame_durations: FrameDurations,\n}\n\nimpl AnimatedImage {\n    pub fn byte_len(&self) -> usize {\n        size_of::<Self>()\n            + self\n                .frames\n                .iter()\n                .map(|image| {\n                    image.pixels.len() * size_of::<egui::Color32>() + size_of::<Duration>()\n                })\n                .sum::<usize>()\n    }\n\n    pub fn get_image_by_index(&self, index: usize) -> Arc<ColorImage> {\n        Arc::clone(&self.frames[index % self.frames.len()])\n    }\n}\n\ntype Entry = Result<WebP, String>;\n\n#[derive(Default)]\npub struct WebPLoader {\n    cache: Mutex<HashMap<String, Entry>>,\n}\n\nimpl WebPLoader {\n    pub const ID: &'static str = egui::generate_loader_id!(WebPLoader);\n}\n\nimpl ImageLoader for WebPLoader {\n    fn id(&self) -> &str {\n        Self::ID\n    }\n\n    fn load(&self, ctx: &egui::Context, frame_uri: &str, _: SizeHint) -> ImageLoadResult {\n        let (image_uri, frame_index) =\n            decode_animated_image_uri(frame_uri).map_err(|_error| LoadError::NotSupported)?;\n\n        let mut cache = self.cache.lock();\n        if let Some(entry) = cache.get(image_uri).cloned() {\n            match entry {\n                Ok(image) => Ok(ImagePoll::Ready {\n                    image: image.get_image(frame_index),\n                }),\n                Err(error) => Err(LoadError::Loading(error)),\n            }\n        } else {\n            match ctx.try_load_bytes(image_uri) {\n                Ok(BytesPoll::Ready { bytes, .. }) => {\n                    if !has_webp_header(&bytes) {\n                        return Err(LoadError::NotSupported);\n                    }\n\n                    log::trace!(\"started loading {image_uri:?}\");\n\n                    let result = WebP::load(&bytes);\n\n                    if let Ok(WebP::Animated(animated_image)) = &result {\n                        ctx.data_mut(|data| {\n                            *data.get_temp_mut_or_default(Id::new(image_uri)) =\n                                animated_image.frame_durations.clone();\n                        });\n                    }\n\n                    log::trace!(\"finished loading {image_uri:?}\");\n\n                    cache.insert(image_uri.into(), result.clone());\n\n                    match result {\n                        Ok(image) => Ok(ImagePoll::Ready {\n                            image: image.get_image(frame_index),\n                        }),\n                        Err(error) => Err(LoadError::Loading(error)),\n                    }\n                }\n                Ok(BytesPoll::Pending { size }) => Ok(ImagePoll::Pending { size }),\n                Err(error) => Err(error),\n            }\n        }\n    }\n\n    fn forget(&self, uri: &str) {\n        let _ = self.cache.lock().remove(uri);\n    }\n\n    fn forget_all(&self) {\n        self.cache.lock().clear();\n    }\n\n    fn byte_size(&self) -> usize {\n        self.cache\n            .lock()\n            .values()\n            .map(|entry| match entry {\n                Ok(entry_value) => entry_value.byte_len(),\n                Err(error) => error.len(),\n            })\n            .sum()\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/loaders.rs",
    "content": "// TODO(jprochazk): automatic cache eviction\n\n/// Installs a set of image loaders.\n///\n/// Calling this enables the use of [`egui::Image`] and [`egui::Ui::image`].\n///\n/// ⚠ This will do nothing and you won't see any images unless you also enable some feature flags on `egui_extras`:\n///\n/// - `file` feature: `file://` loader on non-Wasm targets\n/// - `http` feature: `http(s)://` loader\n/// - `image` feature: Loader of png, jpeg etc using the [`image`] crate\n/// - `svg` feature: `.svg` loader\n///\n/// Calling this multiple times on the same [`egui::Context`] is safe.\n/// It will never install duplicate loaders.\n///\n/// - If you just want to be able to load `file://` and `http://` URIs, enable the `all_loaders` feature.\n/// - The supported set of image formats is configured by adding the [`image`](https://crates.io/crates/image)\n///   crate as your direct dependency, and enabling features on it:\n///\n/// ```toml,ignore\n/// egui_extras = { version = \"*\", features = [\"all_loaders\"] }\n/// image = { version = \"0.25\", features = [\"jpeg\", \"png\"] } # Add the types you want support for\n/// ```\n///\n/// ⚠ You have to configure both the supported loaders in `egui_extras` _and_ the supported image formats\n/// in `image` to get any output!\n///\n/// ## Loader-specific information\n///\n/// ⚠ The exact way bytes, images, and textures are loaded is subject to change,\n/// but the supported protocols and file extensions are not.\n///\n/// The `file` loader is a [`BytesLoader`][`egui::load::BytesLoader`].\n/// It will attempt to load `file://` URIs, and infer the content type from the extension.\n/// The path will be passed to [`std::fs::read`] after trimming the `file://` prefix,\n/// and is resolved the same way as with `std::fs::read(path)`:\n/// - Relative paths are relative to the current working directory\n/// - Absolute paths are left as is.\n///\n/// The `http` loader is a [`BytesLoader`][`egui::load::BytesLoader`].\n/// It will attempt to load `http://` and `https://` URIs, and infer the content type from the `Content-Type` header.\n///\n/// The `image` loader is an [`ImageLoader`][`egui::load::ImageLoader`].\n/// It will attempt to load any URI with any extension other than `svg`.\n/// It will also try to load any URI without an extension.\n/// The content type specified by [`BytesPoll::Ready::mime`][`egui::load::BytesPoll::Ready::mime`] always takes precedence.\n/// This means that even if the URI has a `png` extension, and the `png` image format is enabled, if the content type is\n/// not one of the supported and enabled image formats, the loader will return [`LoadError::NotSupported`][`egui::load::LoadError::NotSupported`],\n/// allowing a different loader to attempt to load the image.\n///\n/// The `svg` loader is an [`ImageLoader`][`egui::load::ImageLoader`].\n/// It will attempt to load any URI with an `svg` extension. It will _not_ attempt to load a URI without an extension.\n/// The content type specified by [`BytesPoll::Ready::mime`][`egui::load::BytesPoll::Ready::mime`] always takes precedence,\n/// and must include `svg` for it to be considered supported. For example, `image/svg+xml` would be loaded by the `svg` loader.\n///\n/// See [`egui::load`] for more information about how loaders work.\npub fn install_image_loaders(ctx: &egui::Context) {\n    #[cfg(all(not(target_arch = \"wasm32\"), feature = \"file\"))]\n    if !ctx.is_loader_installed(self::file_loader::FileLoader::ID) {\n        ctx.add_bytes_loader(std::sync::Arc::new(self::file_loader::FileLoader::default()));\n        log::trace!(\"installed FileLoader\");\n    }\n\n    #[cfg(feature = \"http\")]\n    if !ctx.is_loader_installed(self::http_loader::EhttpLoader::ID) {\n        ctx.add_bytes_loader(std::sync::Arc::new(\n            self::http_loader::EhttpLoader::default(),\n        ));\n        log::trace!(\"installed EhttpLoader\");\n    }\n\n    #[cfg(feature = \"image\")]\n    if !ctx.is_loader_installed(self::image_loader::ImageCrateLoader::ID) {\n        ctx.add_image_loader(std::sync::Arc::new(\n            self::image_loader::ImageCrateLoader::default(),\n        ));\n        log::trace!(\"installed ImageCrateLoader\");\n    }\n\n    #[cfg(feature = \"gif\")]\n    if !ctx.is_loader_installed(self::gif_loader::GifLoader::ID) {\n        ctx.add_image_loader(std::sync::Arc::new(self::gif_loader::GifLoader::default()));\n        log::trace!(\"installed GifLoader\");\n    }\n\n    #[cfg(feature = \"webp\")]\n    if !ctx.is_loader_installed(self::webp_loader::WebPLoader::ID) {\n        ctx.add_image_loader(std::sync::Arc::new(self::webp_loader::WebPLoader::default()));\n        log::trace!(\"installed WebPLoader\");\n    }\n\n    #[cfg(feature = \"svg\")]\n    if !ctx.is_loader_installed(self::svg_loader::SvgLoader::ID) {\n        ctx.add_image_loader(std::sync::Arc::new(self::svg_loader::SvgLoader::default()));\n        log::trace!(\"installed SvgLoader\");\n    }\n\n    #[cfg(all(\n        any(target_arch = \"wasm32\", not(feature = \"file\")),\n        not(feature = \"http\"),\n        not(feature = \"image\"),\n        not(feature = \"svg\")\n    ))]\n    log::warn!(\"`install_image_loaders` was called, but no loaders are enabled\");\n\n    let _ = ctx;\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\npub mod file_loader;\n\n#[cfg(feature = \"http\")]\npub mod http_loader;\n\n#[cfg(feature = \"gif\")]\npub mod gif_loader;\n#[cfg(feature = \"image\")]\npub mod image_loader;\n#[cfg(feature = \"svg\")]\npub mod svg_loader;\n#[cfg(feature = \"webp\")]\npub mod webp_loader;\n"
  },
  {
    "path": "crates/egui_extras/src/sizing.rs",
    "content": "use egui::Rangef;\n\n/// Size hint for table column/strip cell.\n#[derive(Clone, Debug, Copy)]\npub enum Size {\n    /// Absolute size in points, with a given range of allowed sizes to resize within.\n    Absolute { initial: f32, range: Rangef },\n\n    /// Relative size relative to all available space.\n    Relative { fraction: f32, range: Rangef },\n\n    /// Multiple remainders each get the same space.\n    Remainder { range: Rangef },\n}\n\nimpl Size {\n    /// Exactly this big, with no room for resize.\n    pub fn exact(points: f32) -> Self {\n        Self::Absolute {\n            initial: points,\n            range: Rangef::new(points, points),\n        }\n    }\n\n    /// Initially this big, but can resize.\n    pub fn initial(points: f32) -> Self {\n        Self::Absolute {\n            initial: points,\n            range: Rangef::new(0.0, f32::INFINITY),\n        }\n    }\n\n    /// Relative size relative to all available space. Values must be in range `0.0..=1.0`.\n    pub fn relative(fraction: f32) -> Self {\n        debug_assert!(\n            0.0 <= fraction && fraction <= 1.0,\n            \"fraction should be in the range [0, 1], but was {fraction}\"\n        );\n        Self::Relative {\n            fraction,\n            range: Rangef::new(0.0, f32::INFINITY),\n        }\n    }\n\n    /// Multiple remainders each get the same space.\n    pub fn remainder() -> Self {\n        Self::Remainder {\n            range: Rangef::new(0.0, f32::INFINITY),\n        }\n    }\n\n    /// Won't shrink below this size (in points).\n    #[inline]\n    pub fn at_least(mut self, minimum: f32) -> Self {\n        self.range_mut().min = minimum;\n        self\n    }\n\n    /// Won't grow above this size (in points).\n    #[inline]\n    pub fn at_most(mut self, maximum: f32) -> Self {\n        self.range_mut().max = maximum;\n        self\n    }\n\n    #[inline]\n    pub fn with_range(mut self, range: Rangef) -> Self {\n        *self.range_mut() = range;\n        self\n    }\n\n    /// Allowed range of movement (in points), if in a resizable [`Table`](crate::table::Table).\n    pub fn range(self) -> Rangef {\n        match self {\n            Self::Absolute { range, .. }\n            | Self::Relative { range, .. }\n            | Self::Remainder { range, .. } => range,\n        }\n    }\n\n    pub fn range_mut(&mut self) -> &mut Rangef {\n        match self {\n            Self::Absolute { range, .. }\n            | Self::Relative { range, .. }\n            | Self::Remainder { range, .. } => range,\n        }\n    }\n\n    #[inline]\n    pub fn is_absolute(&self) -> bool {\n        matches!(self, Self::Absolute { .. })\n    }\n\n    #[inline]\n    pub fn is_relative(&self) -> bool {\n        matches!(self, Self::Relative { .. })\n    }\n\n    #[inline]\n    pub fn is_remainder(&self) -> bool {\n        matches!(self, Self::Remainder { .. })\n    }\n}\n\n#[derive(Clone, Default)]\npub struct Sizing {\n    pub(crate) sizes: Vec<Size>,\n}\n\nimpl Sizing {\n    pub fn add(&mut self, size: Size) {\n        self.sizes.push(size);\n    }\n\n    pub fn to_lengths(&self, length: f32, spacing: f32) -> Vec<f32> {\n        if self.sizes.is_empty() {\n            return vec![];\n        }\n\n        let mut num_remainders = 0;\n        let sum_non_remainder = self\n            .sizes\n            .iter()\n            .map(|&size| match size {\n                Size::Absolute { initial, .. } => initial,\n                Size::Relative { fraction, range } => {\n                    assert!(\n                        0.0 <= fraction && fraction <= 1.0,\n                        \"fraction should be in the range [0, 1], but was {fraction}\"\n                    );\n                    range.clamp(length * fraction)\n                }\n                Size::Remainder { .. } => {\n                    num_remainders += 1;\n                    0.0\n                }\n            })\n            .sum::<f32>()\n            + spacing * (self.sizes.len() - 1) as f32;\n\n        let avg_remainder_length = if num_remainders == 0 {\n            0.0\n        } else {\n            let mut remainder_length = length - sum_non_remainder;\n            let avg_remainder_length = 0.0f32.max(remainder_length / num_remainders as f32).floor();\n            for &size in &self.sizes {\n                if let Size::Remainder { range } = size\n                    && avg_remainder_length < range.min\n                {\n                    remainder_length -= range.min;\n                    num_remainders -= 1;\n                }\n            }\n            if num_remainders > 0 {\n                0.0f32.max(remainder_length / num_remainders as f32)\n            } else {\n                0.0\n            }\n        };\n\n        self.sizes\n            .iter()\n            .map(|&size| match size {\n                Size::Absolute { initial, .. } => initial,\n                Size::Relative { fraction, range } => range.clamp(length * fraction),\n                Size::Remainder { range } => range.clamp(avg_remainder_length),\n            })\n            .collect()\n    }\n}\n\nimpl From<Vec<Size>> for Sizing {\n    fn from(sizes: Vec<Size>) -> Self {\n        Self { sizes }\n    }\n}\n\n#[test]\nfn test_sizing() {\n    let sizing: Sizing = vec![].into();\n    assert_eq!(sizing.to_lengths(50.0, 0.0), Vec::<f32>::new());\n\n    let sizing: Sizing = vec![Size::remainder().at_least(20.0), Size::remainder()].into();\n    assert_eq!(sizing.to_lengths(50.0, 0.0), vec![25.0, 25.0]);\n    assert_eq!(sizing.to_lengths(30.0, 0.0), vec![20.0, 10.0]);\n    assert_eq!(sizing.to_lengths(20.0, 0.0), vec![20.0, 0.0]);\n    assert_eq!(sizing.to_lengths(10.0, 0.0), vec![20.0, 0.0]);\n    assert_eq!(sizing.to_lengths(20.0, 10.0), vec![20.0, 0.0]);\n    assert_eq!(sizing.to_lengths(30.0, 10.0), vec![20.0, 0.0]);\n    assert_eq!(sizing.to_lengths(40.0, 10.0), vec![20.0, 10.0]);\n    assert_eq!(sizing.to_lengths(110.0, 10.0), vec![50.0, 50.0]);\n\n    let sizing: Sizing = vec![Size::relative(0.5).at_least(10.0), Size::exact(10.0)].into();\n    assert_eq!(sizing.to_lengths(50.0, 0.0), vec![25.0, 10.0]);\n    assert_eq!(sizing.to_lengths(30.0, 0.0), vec![15.0, 10.0]);\n    assert_eq!(sizing.to_lengths(20.0, 0.0), vec![10.0, 10.0]);\n    assert_eq!(sizing.to_lengths(10.0, 0.0), vec![10.0, 10.0]);\n}\n"
  },
  {
    "path": "crates/egui_extras/src/strip.rs",
    "content": "use crate::{\n    Size,\n    layout::{CellDirection, CellSize, StripLayout, StripLayoutFlags},\n    sizing::Sizing,\n};\nuse egui::{Response, Ui};\n\n/// Builder for creating a new [`Strip`].\n///\n/// This can be used to do dynamic layouts.\n///\n/// In contrast to normal egui behavior, strip cells do *not* grow with its children!\n///\n/// First use [`Self::size`] and [`Self::sizes`] to allocate space for the rows or columns will follow.\n/// Then build the strip with [`Self::horizontal`]/[`Self::vertical`], and add 'cells'\n/// to it using [`Strip::cell`]. The number of cells MUST match the number of pre-allocated sizes.\n///\n/// ### Example\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// use egui_extras::{StripBuilder, Size};\n/// StripBuilder::new(ui)\n///     .size(Size::remainder().at_least(100.0)) // top cell\n///     .size(Size::exact(40.0)) // bottom cell\n///     .vertical(|mut strip| {\n///         // Add the top 'cell'\n///         strip.cell(|ui| {\n///             ui.label(\"Fixed\");\n///         });\n///         // We add a nested strip in the bottom cell:\n///         strip.strip(|builder| {\n///             builder.sizes(Size::remainder(), 2).horizontal(|mut strip| {\n///                 strip.cell(|ui| {\n///                     ui.label(\"Top Left\");\n///                 });\n///                 strip.cell(|ui| {\n///                     ui.label(\"Top Right\");\n///                 });\n///             });\n///         });\n///     });\n/// # });\n/// ```\npub struct StripBuilder<'a> {\n    ui: &'a mut Ui,\n    sizing: Sizing,\n    clip: bool,\n    cell_layout: egui::Layout,\n    sense: egui::Sense,\n}\n\nimpl<'a> StripBuilder<'a> {\n    /// Create new strip builder.\n    pub fn new(ui: &'a mut Ui) -> Self {\n        let cell_layout = *ui.layout();\n        Self {\n            ui,\n            sizing: Default::default(),\n            clip: false,\n            cell_layout,\n            sense: egui::Sense::hover(),\n        }\n    }\n\n    /// Should we clip the contents of each cell? Default: `false`.\n    #[inline]\n    pub fn clip(mut self, clip: bool) -> Self {\n        self.clip = clip;\n        self\n    }\n\n    /// What layout should we use for the individual cells?\n    #[inline]\n    pub fn cell_layout(mut self, cell_layout: egui::Layout) -> Self {\n        self.cell_layout = cell_layout;\n        self\n    }\n\n    /// What should strip cells sense for? Default: [`egui::Sense::hover()`].\n    #[inline]\n    pub fn sense(mut self, sense: egui::Sense) -> Self {\n        self.sense = sense;\n        self\n    }\n\n    /// Allocate space for one column/row.\n    #[inline]\n    pub fn size(mut self, size: Size) -> Self {\n        self.sizing.add(size);\n        self\n    }\n\n    /// Allocate space for several columns/rows at once.\n    #[inline]\n    pub fn sizes(mut self, size: Size, count: usize) -> Self {\n        for _ in 0..count {\n            self.sizing.add(size);\n        }\n        self\n    }\n\n    /// Build horizontal strip: Cells are positions from left to right.\n    /// Takes the available horizontal width, so there can't be anything right of the strip or the container will grow slowly!\n    ///\n    /// Returns a [`egui::Response`] for hover events.\n    pub fn horizontal<F>(self, strip: F) -> Response\n    where\n        F: for<'b> FnOnce(Strip<'a, 'b>),\n    {\n        let widths = self.sizing.to_lengths(\n            self.ui.available_rect_before_wrap().width(),\n            self.ui.spacing().item_spacing.x,\n        );\n        let mut layout = StripLayout::new(\n            self.ui,\n            CellDirection::Horizontal,\n            self.cell_layout,\n            self.sense,\n        );\n        strip(Strip {\n            layout: &mut layout,\n            direction: CellDirection::Horizontal,\n            clip: self.clip,\n            sizes: widths,\n            size_index: 0,\n        });\n        layout.allocate_rect()\n    }\n\n    /// Build vertical strip: Cells are positions from top to bottom.\n    /// Takes the full available vertical height, so there can't be anything below of the strip or the container will grow slowly!\n    ///\n    /// Returns a [`egui::Response`] for hover events.\n    pub fn vertical<F>(self, strip: F) -> Response\n    where\n        F: for<'b> FnOnce(Strip<'a, 'b>),\n    {\n        let heights = self.sizing.to_lengths(\n            self.ui.available_rect_before_wrap().height(),\n            self.ui.spacing().item_spacing.y,\n        );\n        let mut layout = StripLayout::new(\n            self.ui,\n            CellDirection::Vertical,\n            self.cell_layout,\n            self.sense,\n        );\n        strip(Strip {\n            layout: &mut layout,\n            direction: CellDirection::Vertical,\n            clip: self.clip,\n            sizes: heights,\n            size_index: 0,\n        });\n        layout.allocate_rect()\n    }\n}\n\n/// A Strip of cells which go in one direction. Each cell has a fixed size.\n/// In contrast to normal egui behavior, strip cells do *not* grow with its children!\npub struct Strip<'a, 'b> {\n    layout: &'b mut StripLayout<'a>,\n    direction: CellDirection,\n    clip: bool,\n    sizes: Vec<f32>,\n    size_index: usize,\n}\n\nimpl Strip<'_, '_> {\n    #[cfg_attr(debug_assertions, track_caller)]\n    fn next_cell_size(&mut self) -> (CellSize, CellSize) {\n        let size = if let Some(size) = self.sizes.get(self.size_index) {\n            self.size_index += 1;\n            *size\n        } else {\n            crate::log_or_panic!(\n                \"Added more `Strip` cells than were pre-allocated ({} pre-allocated)\",\n                self.sizes.len()\n            );\n            8.0 // anything will look wrong, so pick something that is obviously wrong\n        };\n\n        match self.direction {\n            CellDirection::Horizontal => (CellSize::Absolute(size), CellSize::Remainder),\n            CellDirection::Vertical => (CellSize::Remainder, CellSize::Absolute(size)),\n        }\n    }\n\n    /// Add cell contents.\n    #[cfg_attr(debug_assertions, track_caller)]\n    pub fn cell(&mut self, add_contents: impl FnOnce(&mut Ui)) {\n        let (width, height) = self.next_cell_size();\n        let flags = StripLayoutFlags {\n            clip: self.clip,\n            ..Default::default()\n        };\n        self.layout.add(\n            flags,\n            width,\n            height,\n            egui::Id::new(self.size_index),\n            add_contents,\n        );\n    }\n\n    /// Add an empty cell.\n    #[cfg_attr(debug_assertions, track_caller)]\n    pub fn empty(&mut self) {\n        let (width, height) = self.next_cell_size();\n        self.layout.empty(width, height);\n    }\n\n    /// Add a strip as cell.\n    pub fn strip(&mut self, strip_builder: impl FnOnce(StripBuilder<'_>)) {\n        let clip = self.clip;\n        self.cell(|ui| {\n            strip_builder(StripBuilder::new(ui).clip(clip));\n        });\n    }\n}\n\nimpl Drop for Strip<'_, '_> {\n    fn drop(&mut self) {\n        while self.size_index < self.sizes.len() {\n            self.empty();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/syntax_highlighting.rs",
    "content": "//! Syntax highlighting for code.\n//!\n//! Turn on the `syntect` feature for great syntax highlighting of any language.\n//! Otherwise, a very simple fallback will be used, that works okish for C, C++, Rust, and Python.\n\nuse egui::TextStyle;\nuse egui::text::LayoutJob;\n\n/// View some code with syntax highlighting and selection.\npub fn code_view_ui(\n    ui: &mut egui::Ui,\n    theme: &CodeTheme,\n    code: &str,\n    language: &str,\n) -> egui::Response {\n    let layout_job = highlight(ui.ctx(), ui.style(), theme, code, language);\n    ui.add(egui::Label::new(layout_job).selectable(true))\n}\n\n/// Add syntax highlighting to a code string.\n///\n/// The results are memoized, so you can call this every frame without performance penalty.\npub fn highlight(\n    ctx: &egui::Context,\n    style: &egui::Style,\n    theme: &CodeTheme,\n    code: &str,\n    language: &str,\n) -> LayoutJob {\n    highlight_inner(ctx, style, theme, code, language, None)\n}\n\n/// Add syntax highlighting to a code string, with custom `syntect` settings\n///\n/// The results are memoized, so you can call this every frame without performance penalty.\n///\n/// The `syntect` settings are memoized by *address*, so a stable reference should\n/// be used to avoid unnecessary recomputation.\n#[cfg(feature = \"syntect\")]\npub fn highlight_with(\n    ctx: &egui::Context,\n    style: &egui::Style,\n    theme: &CodeTheme,\n    code: &str,\n    language: &str,\n    settings: &SyntectSettings,\n) -> LayoutJob {\n    highlight_inner(\n        ctx,\n        style,\n        theme,\n        code,\n        language,\n        Some(HighlightSettings(settings)),\n    )\n}\n\nfn highlight_inner(\n    ctx: &egui::Context,\n    style: &egui::Style,\n    theme: &CodeTheme,\n    code: &str,\n    language: &str,\n    settings: Option<HighlightSettings<'_>>,\n) -> LayoutJob {\n    // We take in both context and style so that in situations where ui is not available such as when\n    // performing it at a separate thread (ctx, ctx.global_style()) can be used and when ui is available\n    // (ui.ctx(), ui.style()) can be used\n\n    #[expect(non_local_definitions)]\n    impl\n        egui::cache::ComputerMut<\n            (&egui::FontId, &CodeTheme, &str, &str, HighlightSettings<'_>),\n            LayoutJob,\n        > for Highlighter\n    {\n        fn compute(\n            &mut self,\n            (font_id, theme, code, lang, settings): (\n                &egui::FontId,\n                &CodeTheme,\n                &str,\n                &str,\n                HighlightSettings<'_>,\n            ),\n        ) -> LayoutJob {\n            Self::highlight(font_id.clone(), theme, code, lang, settings)\n        }\n    }\n\n    type HighlightCache = egui::cache::FrameCache<LayoutJob, Highlighter>;\n\n    let font_id = style\n        .override_font_id\n        .clone()\n        .unwrap_or_else(|| TextStyle::Monospace.resolve(style));\n\n    // Private type, so that users can't interfere with it in the `IdTypeMap`\n    #[cfg(feature = \"syntect\")]\n    #[derive(Clone, Default)]\n    struct PrivateSettings(std::sync::Arc<SyntectSettings>);\n\n    // Dummy private settings, to minimize code changes without `syntect`\n    #[cfg(not(feature = \"syntect\"))]\n    #[derive(Clone, Default)]\n    struct PrivateSettings(std::sync::Arc<()>);\n\n    ctx.memory_mut(|mem| {\n        let settings = settings.unwrap_or_else(|| {\n            HighlightSettings(\n                &mem.data\n                    .get_temp_mut_or_default::<PrivateSettings>(egui::Id::NULL)\n                    .0,\n            )\n        });\n        mem.caches\n            .cache::<HighlightCache>()\n            .get((&font_id, theme, code, language, settings))\n            .clone()\n    })\n}\n\nfn monospace_font_size(style: &egui::Style) -> f32 {\n    TextStyle::Monospace.resolve(style).size\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg(not(feature = \"syntect\"))]\n#[derive(Clone, Copy, PartialEq, enum_map::Enum)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nenum TokenType {\n    Comment,\n    Keyword,\n    Literal,\n    StringLiteral,\n    Punctuation,\n    Whitespace,\n}\n\n#[cfg(feature = \"syntect\")]\n#[derive(Clone, Copy, Hash, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nenum SyntectTheme {\n    Base16EightiesDark,\n    Base16MochaDark,\n    Base16OceanDark,\n    Base16OceanLight,\n    InspiredGitHub,\n    SolarizedDark,\n    SolarizedLight,\n}\n\n#[cfg(feature = \"syntect\")]\nimpl SyntectTheme {\n    fn all() -> impl ExactSizeIterator<Item = Self> {\n        [\n            Self::Base16EightiesDark,\n            Self::Base16MochaDark,\n            Self::Base16OceanDark,\n            Self::Base16OceanLight,\n            Self::InspiredGitHub,\n            Self::SolarizedDark,\n            Self::SolarizedLight,\n        ]\n        .iter()\n        .copied()\n    }\n\n    fn name(&self) -> &'static str {\n        match self {\n            Self::Base16EightiesDark => \"Base16 Eighties (dark)\",\n            Self::Base16MochaDark => \"Base16 Mocha (dark)\",\n            Self::Base16OceanDark => \"Base16 Ocean (dark)\",\n            Self::Base16OceanLight => \"Base16 Ocean (light)\",\n            Self::InspiredGitHub => \"InspiredGitHub (light)\",\n            Self::SolarizedDark => \"Solarized (dark)\",\n            Self::SolarizedLight => \"Solarized (light)\",\n        }\n    }\n\n    fn syntect_key_name(&self) -> &'static str {\n        match self {\n            Self::Base16EightiesDark => \"base16-eighties.dark\",\n            Self::Base16MochaDark => \"base16-mocha.dark\",\n            Self::Base16OceanDark => \"base16-ocean.dark\",\n            Self::Base16OceanLight => \"base16-ocean.light\",\n            Self::InspiredGitHub => \"InspiredGitHub\",\n            Self::SolarizedDark => \"Solarized (dark)\",\n            Self::SolarizedLight => \"Solarized (light)\",\n        }\n    }\n\n    pub fn is_dark(&self) -> bool {\n        match self {\n            Self::Base16EightiesDark\n            | Self::Base16MochaDark\n            | Self::Base16OceanDark\n            | Self::SolarizedDark => true,\n\n            Self::Base16OceanLight | Self::InspiredGitHub | Self::SolarizedLight => false,\n        }\n    }\n}\n\n/// A selected color theme.\n#[derive(Clone, Hash, PartialEq)]\n#[cfg_attr(\n    feature = \"serde\",\n    derive(serde::Deserialize, serde::Serialize),\n    serde(default)\n)]\npub struct CodeTheme {\n    dark_mode: bool,\n\n    #[cfg(feature = \"syntect\")]\n    syntect_theme: SyntectTheme,\n    #[cfg(feature = \"syntect\")]\n    font_id: egui::FontId,\n\n    #[cfg(not(feature = \"syntect\"))]\n    formats: enum_map::EnumMap<TokenType, egui::TextFormat>,\n}\n\nimpl Default for CodeTheme {\n    fn default() -> Self {\n        Self::dark(12.0)\n    }\n}\n\nimpl CodeTheme {\n    pub fn is_dark(&self) -> bool {\n        self.dark_mode\n    }\n\n    /// Selects either dark or light theme based on the given style.\n    pub fn from_style(style: &egui::Style) -> Self {\n        let font_id = style\n            .override_font_id\n            .clone()\n            .unwrap_or_else(|| TextStyle::Monospace.resolve(style));\n\n        if style.visuals.dark_mode {\n            Self::dark_with_font_id(font_id)\n        } else {\n            Self::light_with_font_id(font_id)\n        }\n    }\n\n    /// ### Example\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// use egui_extras::syntax_highlighting::CodeTheme;\n    /// let theme = CodeTheme::dark(12.0);\n    /// # });\n    /// ```\n    pub fn dark(font_size: f32) -> Self {\n        Self::dark_with_font_id(egui::FontId::monospace(font_size))\n    }\n\n    /// ### Example\n    ///\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// use egui_extras::syntax_highlighting::CodeTheme;\n    /// let theme = CodeTheme::light(12.0);\n    /// # });\n    /// ```\n    pub fn light(font_size: f32) -> Self {\n        Self::light_with_font_id(egui::FontId::monospace(font_size))\n    }\n\n    /// Load code theme from egui memory.\n    ///\n    /// There is one dark and one light theme stored at any one time.\n    pub fn from_memory(ctx: &egui::Context, style: &egui::Style) -> Self {\n        #![expect(clippy::needless_return)]\n\n        let (id, default) = if style.visuals.dark_mode {\n            (egui::Id::new(\"dark\"), Self::dark as fn(f32) -> Self)\n        } else {\n            (egui::Id::new(\"light\"), Self::light as fn(f32) -> Self)\n        };\n\n        #[cfg(feature = \"serde\")]\n        {\n            return ctx.data_mut(|d| {\n                d.get_persisted(id)\n                    .unwrap_or_else(|| default(monospace_font_size(style)))\n            });\n        }\n\n        #[cfg(not(feature = \"serde\"))]\n        {\n            return ctx.data_mut(|d| {\n                d.get_temp(id)\n                    .unwrap_or_else(|| default(monospace_font_size(style)))\n            });\n        }\n    }\n\n    /// Store theme to egui memory.\n    ///\n    /// There is one dark and one light theme stored at any one time.\n    pub fn store_in_memory(self, ctx: &egui::Context) {\n        let id = if ctx.global_style().visuals.dark_mode {\n            egui::Id::new(\"dark\")\n        } else {\n            egui::Id::new(\"light\")\n        };\n\n        #[cfg(feature = \"serde\")]\n        ctx.data_mut(|d| d.insert_persisted(id, self));\n\n        #[cfg(not(feature = \"serde\"))]\n        ctx.data_mut(|d| d.insert_temp(id, self));\n    }\n}\n\n#[cfg(feature = \"syntect\")]\nimpl CodeTheme {\n    /// Change the font size\n    pub fn with_font_size(&self, font_size: f32) -> Self {\n        Self {\n            dark_mode: self.dark_mode,\n            syntect_theme: self.syntect_theme,\n            font_id: egui::FontId::monospace(font_size),\n        }\n    }\n\n    /// Change the `font_id` of the theme\n    pub fn with_font_id(&self, font_id: egui::FontId) -> Self {\n        Self {\n            dark_mode: self.dark_mode,\n            syntect_theme: self.syntect_theme,\n            font_id,\n        }\n    }\n\n    fn dark_with_font_id(font_id: egui::FontId) -> Self {\n        Self {\n            dark_mode: true,\n            syntect_theme: SyntectTheme::Base16MochaDark,\n            font_id,\n        }\n    }\n\n    fn light_with_font_id(font_id: egui::FontId) -> Self {\n        Self {\n            dark_mode: false,\n            syntect_theme: SyntectTheme::SolarizedLight,\n            font_id,\n        }\n    }\n\n    /// Show UI for changing the color theme.\n    pub fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.horizontal(|ui| {\n            ui.selectable_value(&mut self.dark_mode, true, \"🌙 Dark theme\")\n                .on_hover_text(\"Use the dark mode theme\");\n\n            ui.selectable_value(&mut self.dark_mode, false, \"☀ Light theme\")\n                .on_hover_text(\"Use the light mode theme\");\n        });\n        let current_theme_is_dark = self.is_dark();\n        for theme in SyntectTheme::all().filter(|t| t.is_dark() == current_theme_is_dark) {\n            ui.radio_value(&mut self.syntect_theme, theme, theme.name());\n        }\n    }\n}\n\n#[cfg(not(feature = \"syntect\"))]\nimpl CodeTheme {\n    // The syntect version takes it by value. This could be avoided by specializing the from_style\n    // function, but at the cost of more code duplication.\n    #[expect(clippy::needless_pass_by_value)]\n    fn dark_with_font_id(font_id: egui::FontId) -> Self {\n        #![expect(clippy::mem_forget)]\n        use egui::{Color32, TextFormat};\n        Self {\n            dark_mode: true,\n            formats: enum_map::enum_map![\n                TokenType::Comment => TextFormat::simple(font_id.clone(), Color32::from_gray(120)),\n                TokenType::Keyword => TextFormat::simple(font_id.clone(), Color32::from_rgb(255, 100, 100)),\n                TokenType::Literal => TextFormat::simple(font_id.clone(), Color32::from_rgb(87, 165, 171)),\n                TokenType::StringLiteral => TextFormat::simple(font_id.clone(), Color32::from_rgb(109, 147, 226)),\n                TokenType::Punctuation => TextFormat::simple(font_id.clone(), Color32::LIGHT_GRAY),\n                TokenType::Whitespace => TextFormat::simple(font_id.clone(), Color32::TRANSPARENT),\n            ],\n        }\n    }\n\n    // The syntect version takes it by value\n    #[expect(clippy::needless_pass_by_value)]\n    fn light_with_font_id(font_id: egui::FontId) -> Self {\n        #![expect(clippy::mem_forget)]\n        use egui::{Color32, TextFormat};\n        Self {\n            dark_mode: false,\n            #[cfg(not(feature = \"syntect\"))]\n            formats: enum_map::enum_map![\n                TokenType::Comment => TextFormat::simple(font_id.clone(), Color32::GRAY),\n                TokenType::Keyword => TextFormat::simple(font_id.clone(), Color32::from_rgb(235, 0, 0)),\n                TokenType::Literal => TextFormat::simple(font_id.clone(), Color32::from_rgb(153, 134, 255)),\n                TokenType::StringLiteral => TextFormat::simple(font_id.clone(), Color32::from_rgb(37, 203, 105)),\n                TokenType::Punctuation => TextFormat::simple(font_id.clone(), Color32::DARK_GRAY),\n                TokenType::Whitespace => TextFormat::simple(font_id.clone(), Color32::TRANSPARENT),\n            ],\n        }\n    }\n\n    /// Show UI for changing the color theme.\n    pub fn ui(&mut self, ui: &mut egui::Ui) {\n        ui.horizontal_top(|ui| {\n            let selected_id = egui::Id::NULL;\n\n            #[cfg(feature = \"serde\")]\n            let mut selected_tt: TokenType =\n                ui.data_mut(|d| *d.get_persisted_mut_or(selected_id, TokenType::Comment));\n\n            #[cfg(not(feature = \"serde\"))]\n            let mut selected_tt: TokenType =\n                ui.data_mut(|d| *d.get_temp_mut_or(selected_id, TokenType::Comment));\n\n            ui.vertical(|ui| {\n                ui.set_width(150.0);\n                ui.horizontal(|ui| {\n                    ui.selectable_value(&mut self.dark_mode, true, \"🌙 Dark theme\")\n                        .on_hover_text(\"Use the dark mode theme\");\n\n                    ui.selectable_value(&mut self.dark_mode, false, \"☀ Light theme\")\n                        .on_hover_text(\"Use the light mode theme\");\n                });\n                ui.scope(|ui| {\n                    for (tt, tt_name) in [\n                        (TokenType::Comment, \"// comment\"),\n                        (TokenType::Keyword, \"keyword\"),\n                        (TokenType::Literal, \"literal\"),\n                        (TokenType::StringLiteral, \"\\\"string literal\\\"\"),\n                        (TokenType::Punctuation, \"punctuation ;\"),\n                        // (TokenType::Whitespace, \"whitespace\"),\n                    ] {\n                        let format = &mut self.formats[tt];\n                        ui.style_mut().override_font_id = Some(format.font_id.clone());\n                        ui.visuals_mut().override_text_color = Some(format.color);\n                        ui.radio_value(&mut selected_tt, tt, tt_name);\n                    }\n                });\n\n                let reset_value = if self.dark_mode {\n                    Self::dark(monospace_font_size(ui.style()))\n                } else {\n                    Self::light(monospace_font_size(ui.style()))\n                };\n\n                if ui\n                    .add_enabled(*self != reset_value, egui::Button::new(\"Reset theme\"))\n                    .clicked()\n                {\n                    *self = reset_value;\n                }\n            });\n\n            ui.add_space(16.0);\n\n            #[cfg(feature = \"serde\")]\n            ui.data_mut(|d| d.insert_persisted(selected_id, selected_tt));\n            #[cfg(not(feature = \"serde\"))]\n            ui.data_mut(|d| d.insert_temp(selected_id, selected_tt));\n\n            egui::Frame::group(ui.style())\n                .inner_margin(egui::Vec2::splat(2.0))\n                .show(ui, |ui| {\n                    // ui.group(|ui| {\n                    ui.style_mut().override_text_style = Some(egui::TextStyle::Small);\n                    ui.spacing_mut().slider_width = 128.0; // Controls color picker size\n                    egui::widgets::color_picker::color_picker_color32(\n                        ui,\n                        &mut self.formats[selected_tt].color,\n                        egui::color_picker::Alpha::Opaque,\n                    );\n                });\n        });\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg(feature = \"syntect\")]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct SyntectSettings {\n    pub ps: syntect::parsing::SyntaxSet,\n    pub ts: syntect::highlighting::ThemeSet,\n}\n\n#[cfg(feature = \"syntect\")]\nimpl Default for SyntectSettings {\n    fn default() -> Self {\n        profiling::function_scope!();\n        Self {\n            ps: syntect::parsing::SyntaxSet::load_defaults_newlines(),\n            ts: syntect::highlighting::ThemeSet::load_defaults(),\n        }\n    }\n}\n\n/// Highlight settings are memoized by reference address, rather than value\n#[cfg(feature = \"syntect\")]\n#[derive(Copy, Clone)]\nstruct HighlightSettings<'a>(&'a SyntectSettings);\n\n#[cfg(not(feature = \"syntect\"))]\n#[derive(Copy, Clone)]\nstruct HighlightSettings<'a>(&'a ());\n\nimpl std::hash::Hash for HighlightSettings<'_> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        std::ptr::hash(self.0, state);\n    }\n}\n\n#[derive(Default)]\nstruct Highlighter;\n\nimpl Highlighter {\n    fn highlight(\n        font_id: egui::FontId,\n        theme: &CodeTheme,\n        code: &str,\n        lang: &str,\n        settings: HighlightSettings<'_>,\n    ) -> LayoutJob {\n        Self::highlight_impl(theme, code, lang, settings).unwrap_or_else(|| {\n            // Fallback:\n            LayoutJob::simple(\n                code.into(),\n                font_id,\n                if theme.dark_mode {\n                    egui::Color32::LIGHT_GRAY\n                } else {\n                    egui::Color32::DARK_GRAY\n                },\n                f32::INFINITY,\n            )\n        })\n    }\n\n    #[cfg(feature = \"syntect\")]\n    fn highlight_impl(\n        theme: &CodeTheme,\n        text: &str,\n        language: &str,\n        highlighter: HighlightSettings<'_>,\n    ) -> Option<LayoutJob> {\n        profiling::function_scope!();\n        use syntect::easy::HighlightLines;\n        use syntect::highlighting::FontStyle;\n        use syntect::util::LinesWithEndings;\n\n        let syntax = highlighter\n            .0\n            .ps\n            .find_syntax_by_name(language)\n            .or_else(|| highlighter.0.ps.find_syntax_by_extension(language))?;\n\n        let syn_theme = theme.syntect_theme.syntect_key_name();\n        let mut h = HighlightLines::new(syntax, &highlighter.0.ts.themes[syn_theme]);\n\n        use egui::text::{LayoutSection, TextFormat};\n\n        let mut job = LayoutJob {\n            text: text.into(),\n            ..Default::default()\n        };\n\n        for line in LinesWithEndings::from(text) {\n            for (style, range) in h.highlight_line(line, &highlighter.0.ps).ok()? {\n                let fg = style.foreground;\n                let text_color = egui::Color32::from_rgb(fg.r, fg.g, fg.b);\n                let italics = style.font_style.contains(FontStyle::ITALIC);\n                let underline = style.font_style.contains(FontStyle::ITALIC);\n                let underline = if underline {\n                    egui::Stroke::new(1.0, text_color)\n                } else {\n                    egui::Stroke::NONE\n                };\n                job.sections.push(LayoutSection {\n                    leading_space: 0.0,\n                    byte_range: as_byte_range(text, range),\n                    format: TextFormat {\n                        font_id: theme.font_id.clone(),\n                        color: text_color,\n                        italics,\n                        underline,\n                        ..Default::default()\n                    },\n                });\n            }\n        }\n\n        Some(job)\n    }\n}\n\n#[cfg(feature = \"syntect\")]\nfn as_byte_range(whole: &str, range: &str) -> std::ops::Range<usize> {\n    let whole_start = whole.as_ptr() as usize;\n    let range_start = range.as_ptr() as usize;\n    assert!(\n        whole_start <= range_start,\n        \"range must be within whole, but was {range}\"\n    );\n    assert!(\n        range_start + range.len() <= whole_start + whole.len(),\n        \"range_start + range length must be smaller than whole_start + whole length, but was {}\",\n        range_start + range.len()\n    );\n    let offset = range_start - whole_start;\n    offset..(offset + range.len())\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg(not(feature = \"syntect\"))]\nimpl Highlighter {\n    fn highlight_impl(\n        theme: &CodeTheme,\n        mut text: &str,\n        language: &str,\n        _settings: HighlightSettings<'_>,\n    ) -> Option<LayoutJob> {\n        profiling::function_scope!();\n\n        let language = Language::new(language)?;\n\n        // Extremely simple syntax highlighter for when we compile without syntect\n\n        let mut job = LayoutJob::default();\n\n        while !text.is_empty() {\n            if language.double_slash_comments && text.starts_with(\"//\")\n                || language.hash_comments && text.starts_with('#')\n            {\n                let end = text.find('\\n').unwrap_or(text.len());\n                job.append(&text[..end], 0.0, theme.formats[TokenType::Comment].clone());\n                text = &text[end..];\n            } else if text.starts_with('\"') {\n                let end = text[1..]\n                    .find('\"')\n                    .map(|i| i + 2)\n                    .or_else(|| text.find('\\n'))\n                    .unwrap_or(text.len());\n                job.append(\n                    &text[..end],\n                    0.0,\n                    theme.formats[TokenType::StringLiteral].clone(),\n                );\n                text = &text[end..];\n            } else if text.starts_with(|c: char| c.is_ascii_alphanumeric()) {\n                let end = text[1..]\n                    .find(|c: char| !c.is_ascii_alphanumeric())\n                    .map_or_else(|| text.len(), |i| i + 1);\n                let word = &text[..end];\n                let tt = if language.is_keyword(word) {\n                    TokenType::Keyword\n                } else {\n                    TokenType::Literal\n                };\n                job.append(word, 0.0, theme.formats[tt].clone());\n                text = &text[end..];\n            } else if text.starts_with(|c: char| c.is_ascii_whitespace()) {\n                let end = text[1..]\n                    .find(|c: char| !c.is_ascii_whitespace())\n                    .map_or_else(|| text.len(), |i| i + 1);\n                job.append(\n                    &text[..end],\n                    0.0,\n                    theme.formats[TokenType::Whitespace].clone(),\n                );\n                text = &text[end..];\n            } else {\n                let mut it = text.char_indices();\n                it.next();\n                let end = it.next().map_or(text.len(), |(idx, _chr)| idx);\n                job.append(\n                    &text[..end],\n                    0.0,\n                    theme.formats[TokenType::Punctuation].clone(),\n                );\n                text = &text[end..];\n            }\n        }\n\n        Some(job)\n    }\n}\n\n#[cfg(not(feature = \"syntect\"))]\nstruct Language {\n    /// `// comment`\n    double_slash_comments: bool,\n\n    /// `# comment`\n    hash_comments: bool,\n\n    keywords: std::collections::BTreeSet<&'static str>,\n}\n\n#[cfg(not(feature = \"syntect\"))]\nimpl Language {\n    fn new(language: &str) -> Option<Self> {\n        match language.to_lowercase().as_str() {\n            \"c\" | \"h\" | \"hpp\" | \"cpp\" | \"c++\" => Some(Self::cpp()),\n            \"py\" | \"python\" => Some(Self::python()),\n            \"rs\" | \"rust\" => Some(Self::rust()),\n            \"toml\" => Some(Self::toml()),\n            _ => {\n                None // unsupported language\n            }\n        }\n    }\n\n    fn is_keyword(&self, word: &str) -> bool {\n        self.keywords.contains(word)\n    }\n\n    fn cpp() -> Self {\n        Self {\n            double_slash_comments: true,\n            hash_comments: false,\n            keywords: [\n                \"alignas\",\n                \"alignof\",\n                \"and_eq\",\n                \"and\",\n                \"asm\",\n                \"atomic_cancel\",\n                \"atomic_commit\",\n                \"atomic_noexcept\",\n                \"auto\",\n                \"bitand\",\n                \"bitor\",\n                \"bool\",\n                \"break\",\n                \"case\",\n                \"catch\",\n                \"char\",\n                \"char16_t\",\n                \"char32_t\",\n                \"char8_t\",\n                \"class\",\n                \"co_await\",\n                \"co_return\",\n                \"co_yield\",\n                \"compl\",\n                \"concept\",\n                \"const_cast\",\n                \"const\",\n                \"consteval\",\n                \"constexpr\",\n                \"constinit\",\n                \"continue\",\n                \"decltype\",\n                \"default\",\n                \"delete\",\n                \"do\",\n                \"double\",\n                \"dynamic_cast\",\n                \"else\",\n                \"enum\",\n                \"explicit\",\n                \"export\",\n                \"extern\",\n                \"false\",\n                \"float\",\n                \"for\",\n                \"friend\",\n                \"goto\",\n                \"if\",\n                \"inline\",\n                \"int\",\n                \"long\",\n                \"mutable\",\n                \"namespace\",\n                \"new\",\n                \"noexcept\",\n                \"not_eq\",\n                \"not\",\n                \"nullptr\",\n                \"operator\",\n                \"or_eq\",\n                \"or\",\n                \"private\",\n                \"protected\",\n                \"public\",\n                \"reflexpr\",\n                \"register\",\n                \"reinterpret_cast\",\n                \"requires\",\n                \"return\",\n                \"short\",\n                \"signed\",\n                \"sizeof\",\n                \"static_assert\",\n                \"static_cast\",\n                \"static\",\n                \"struct\",\n                \"switch\",\n                \"synchronized\",\n                \"template\",\n                \"this\",\n                \"thread_local\",\n                \"throw\",\n                \"true\",\n                \"try\",\n                \"typedef\",\n                \"typeid\",\n                \"typename\",\n                \"union\",\n                \"unsigned\",\n                \"using\",\n                \"virtual\",\n                \"void\",\n                \"volatile\",\n                \"wchar_t\",\n                \"while\",\n                \"xor_eq\",\n                \"xor\",\n            ]\n            .into_iter()\n            .collect(),\n        }\n    }\n\n    fn python() -> Self {\n        Self {\n            double_slash_comments: false,\n            hash_comments: true,\n            keywords: [\n                \"and\", \"as\", \"assert\", \"break\", \"class\", \"continue\", \"def\", \"del\", \"elif\", \"else\",\n                \"except\", \"False\", \"finally\", \"for\", \"from\", \"global\", \"if\", \"import\", \"in\", \"is\",\n                \"lambda\", \"None\", \"nonlocal\", \"not\", \"or\", \"pass\", \"raise\", \"return\", \"True\",\n                \"try\", \"while\", \"with\", \"yield\",\n            ]\n            .into_iter()\n            .collect(),\n        }\n    }\n\n    fn rust() -> Self {\n        Self {\n            double_slash_comments: true,\n            hash_comments: false,\n            keywords: [\n                \"as\", \"async\", \"await\", \"break\", \"const\", \"continue\", \"crate\", \"dyn\", \"else\",\n                \"enum\", \"extern\", \"false\", \"fn\", \"for\", \"if\", \"impl\", \"in\", \"let\", \"loop\", \"match\",\n                \"mod\", \"move\", \"mut\", \"pub\", \"ref\", \"return\", \"self\", \"Self\", \"static\", \"struct\",\n                \"super\", \"trait\", \"true\", \"type\", \"unsafe\", \"use\", \"where\", \"while\",\n            ]\n            .into_iter()\n            .collect(),\n        }\n    }\n\n    fn toml() -> Self {\n        Self {\n            double_slash_comments: false,\n            hash_comments: true,\n            keywords: Default::default(),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_extras/src/table.rs",
    "content": "//! Table view with (optional) fixed header and scrolling body.\n//! Cell widths are precalculated with given size hints so we can have tables like this:\n//! | fixed size | all available space/minimum | 30% of available width | fixed size |\n//! Takes all available height, so if you want something below the table, put it in a strip.\n\nuse egui::{\n    Align, Id, NumExt as _, Rangef, Rect, Response, ScrollArea, Ui, Vec2, Vec2b,\n    scroll_area::{ScrollAreaOutput, ScrollBarVisibility, ScrollSource},\n};\n\nuse crate::{\n    StripLayout,\n    layout::{CellDirection, CellSize, StripLayoutFlags},\n};\n\n// -----------------------------------------------------------------=----------\n\n#[derive(Clone, Copy, Debug, PartialEq)]\nenum InitialColumnSize {\n    /// Absolute size in points\n    Absolute(f32),\n\n    /// Base on content\n    Automatic(f32),\n\n    /// Take all available space\n    Remainder,\n}\n\n/// Specifies the properties of a column, like its width range.\n#[derive(Clone, Copy, Debug, PartialEq)]\npub struct Column {\n    initial_width: InitialColumnSize,\n\n    width_range: Rangef,\n\n    /// Clip contents if too narrow?\n    clip: bool,\n\n    resizable: Option<bool>,\n\n    /// If set, we should accurately measure the size of this column this frame\n    /// so that we can correctly auto-size it. This is done as a `sizing_pass`.\n    auto_size_this_frame: bool,\n}\n\nimpl Column {\n    /// Automatically sized based on content.\n    ///\n    /// If you have many thousands of rows and are therefore using [`TableBody::rows`]\n    /// or [`TableBody::heterogeneous_rows`], then the automatic size will only be based\n    /// on the currently visible rows.\n    pub fn auto() -> Self {\n        Self::auto_with_initial_suggestion(100.0)\n    }\n\n    /// Automatically sized.\n    ///\n    /// The given fallback is a loose suggestion, that may be used to wrap\n    /// cell contents, if they contain a wrapping layout.\n    /// In most cases though, the given value is ignored.\n    pub fn auto_with_initial_suggestion(suggested_width: f32) -> Self {\n        Self::new(InitialColumnSize::Automatic(suggested_width))\n    }\n\n    /// With this initial width.\n    pub fn initial(width: f32) -> Self {\n        Self::new(InitialColumnSize::Absolute(width))\n    }\n\n    /// Always this exact width, never shrink or grow.\n    pub fn exact(width: f32) -> Self {\n        Self::new(InitialColumnSize::Absolute(width))\n            .range(width..=width)\n            .clip(true)\n    }\n\n    /// Take all the space remaining after the other columns have\n    /// been sized.\n    ///\n    /// If you have multiple [`Column::remainder`] they all\n    /// share the remaining space equally.\n    pub fn remainder() -> Self {\n        Self::new(InitialColumnSize::Remainder)\n    }\n\n    fn new(initial_width: InitialColumnSize) -> Self {\n        Self {\n            initial_width,\n            width_range: Rangef::new(0.0, f32::INFINITY),\n            resizable: None,\n            clip: false,\n            auto_size_this_frame: false,\n        }\n    }\n\n    /// Can this column be resized by dragging the column separator?\n    ///\n    /// If you don't call this, the fallback value of\n    /// [`TableBuilder::resizable`] is used (which by default is `false`).\n    #[inline]\n    pub fn resizable(mut self, resizable: bool) -> Self {\n        self.resizable = Some(resizable);\n        self\n    }\n\n    /// If `true`: Allow the column to shrink enough to clip the contents.\n    /// If `false`: The column will always be wide enough to contain all its content.\n    ///\n    /// Clipping can make sense if you expect a column to contain a lot of things,\n    /// and you don't want it too take up too much space.\n    /// If you turn on clipping you should also consider calling [`Self::at_least`].\n    ///\n    /// Default: `false`.\n    #[inline]\n    pub fn clip(mut self, clip: bool) -> Self {\n        self.clip = clip;\n        self\n    }\n\n    /// Won't shrink below this width (in points).\n    ///\n    /// Default: 0.0\n    #[inline]\n    pub fn at_least(mut self, minimum: f32) -> Self {\n        self.width_range.min = minimum;\n        self\n    }\n\n    /// Won't grow above this width (in points).\n    ///\n    /// Default: [`f32::INFINITY`]\n    #[inline]\n    pub fn at_most(mut self, maximum: f32) -> Self {\n        self.width_range.max = maximum;\n        self\n    }\n\n    /// Allowed range of movement (in points), if in a resizable [`Table`].\n    #[inline]\n    pub fn range(mut self, range: impl Into<Rangef>) -> Self {\n        self.width_range = range.into();\n        self\n    }\n\n    /// If set, the column will be automatically sized based on the content this frame.\n    ///\n    /// Do not set this every frame, just on a specific action.\n    #[inline]\n    pub fn auto_size_this_frame(mut self, auto_size_this_frame: bool) -> Self {\n        self.auto_size_this_frame = auto_size_this_frame;\n        self\n    }\n\n    fn is_auto(&self) -> bool {\n        match self.initial_width {\n            InitialColumnSize::Automatic(_) => true,\n            InitialColumnSize::Absolute(_) | InitialColumnSize::Remainder => false,\n        }\n    }\n}\n\nfn to_sizing(columns: &[Column]) -> crate::sizing::Sizing {\n    use crate::Size;\n\n    let mut sizing = crate::sizing::Sizing::default();\n    for column in columns {\n        let size = match column.initial_width {\n            InitialColumnSize::Absolute(width) => Size::exact(width),\n            InitialColumnSize::Automatic(suggested_width) => Size::initial(suggested_width),\n            InitialColumnSize::Remainder => Size::remainder(),\n        }\n        .with_range(column.width_range);\n        sizing.add(size);\n    }\n    sizing\n}\n\n// -----------------------------------------------------------------=----------\n\nstruct TableScrollOptions {\n    vscroll: bool,\n    drag_to_scroll: bool,\n    stick_to_bottom: bool,\n    scroll_to_row: Option<(usize, Option<Align>)>,\n    scroll_offset_y: Option<f32>,\n    min_scrolled_height: f32,\n    max_scroll_height: f32,\n    auto_shrink: Vec2b,\n    scroll_bar_visibility: ScrollBarVisibility,\n    animated: bool,\n}\n\nimpl Default for TableScrollOptions {\n    fn default() -> Self {\n        Self {\n            vscroll: true,\n            drag_to_scroll: true,\n            stick_to_bottom: false,\n            scroll_to_row: None,\n            scroll_offset_y: None,\n            min_scrolled_height: 200.0,\n            max_scroll_height: f32::INFINITY,\n            auto_shrink: Vec2b::TRUE,\n            scroll_bar_visibility: ScrollBarVisibility::VisibleWhenNeeded,\n            animated: true,\n        }\n    }\n}\n\n// -----------------------------------------------------------------=----------\n\n/// Builder for a [`Table`] with (optional) fixed header and scrolling body.\n///\n/// You must pre-allocate all columns with [`Self::column`]/[`Self::columns`].\n///\n/// If you have multiple [`Table`]:s in the same [`Ui`]\n/// you will need to give them unique id:s by with [`Self::id_salt`].\n///\n/// ### Example\n/// ```\n/// # egui::__run_test_ui(|ui| {\n/// use egui_extras::{TableBuilder, Column};\n/// TableBuilder::new(ui)\n///     .column(Column::auto().resizable(true))\n///     .column(Column::remainder())\n///     .header(20.0, |mut header| {\n///         header.col(|ui| {\n///             ui.heading(\"First column\");\n///         });\n///         header.col(|ui| {\n///             ui.heading(\"Second column\");\n///         });\n///     })\n///     .body(|mut body| {\n///         body.row(30.0, |mut row| {\n///             row.col(|ui| {\n///                 ui.label(\"Hello\");\n///             });\n///             row.col(|ui| {\n///                 ui.button(\"world!\");\n///             });\n///         });\n///     });\n/// # });\n/// ```\npub struct TableBuilder<'a> {\n    ui: &'a mut Ui,\n    id_salt: Id,\n    columns: Vec<Column>,\n    striped: Option<bool>,\n    resizable: bool,\n    cell_layout: egui::Layout,\n    scroll_options: TableScrollOptions,\n    sense: egui::Sense,\n}\n\nimpl<'a> TableBuilder<'a> {\n    pub fn new(ui: &'a mut Ui) -> Self {\n        let cell_layout = *ui.layout();\n        Self {\n            ui,\n            id_salt: Id::new(\"__table_state\"),\n            columns: Default::default(),\n            striped: None,\n            resizable: false,\n            cell_layout,\n            scroll_options: Default::default(),\n            sense: egui::Sense::hover(),\n        }\n    }\n\n    /// Give this table a unique id within the parent [`Ui`].\n    ///\n    /// This is required if you have multiple tables in the same [`Ui`].\n    #[inline]\n    #[deprecated = \"Renamed id_salt\"]\n    pub fn id_source(self, id_salt: impl std::hash::Hash) -> Self {\n        self.id_salt(id_salt)\n    }\n\n    /// Give this table a unique id within the parent [`Ui`].\n    ///\n    /// This is required if you have multiple tables in the same [`Ui`].\n    #[inline]\n    pub fn id_salt(mut self, id_salt: impl std::hash::Hash) -> Self {\n        self.id_salt = Id::new(id_salt);\n        self\n    }\n\n    /// Enable striped row background for improved readability.\n    ///\n    /// Default is whatever is in [`egui::Visuals::striped`].\n    #[inline]\n    pub fn striped(mut self, striped: bool) -> Self {\n        self.striped = Some(striped);\n        self\n    }\n\n    /// What should table cells sense for? (default: [`egui::Sense::hover()`]).\n    #[inline]\n    pub fn sense(mut self, sense: egui::Sense) -> Self {\n        self.sense = sense;\n        self\n    }\n\n    /// Make the columns resizable by dragging.\n    ///\n    /// You can set this for individual columns with [`Column::resizable`].\n    /// [`Self::resizable`] is used as a fallback for any column for which you don't call\n    /// [`Column::resizable`].\n    ///\n    /// If the _last_ column is [`Column::remainder`], then it won't be resizable\n    /// (and instead use up the remainder).\n    ///\n    /// Default is `false`.\n    #[inline]\n    pub fn resizable(mut self, resizable: bool) -> Self {\n        self.resizable = resizable;\n        self\n    }\n\n    /// Enable vertical scrolling in body (default: `true`)\n    #[inline]\n    pub fn vscroll(mut self, vscroll: bool) -> Self {\n        self.scroll_options.vscroll = vscroll;\n        self\n    }\n\n    /// Enables scrolling the table's contents using mouse drag (default: `true`).\n    ///\n    /// See [`ScrollArea::drag_to_scroll`] for more.\n    #[inline]\n    pub fn drag_to_scroll(mut self, drag_to_scroll: bool) -> Self {\n        self.scroll_options.drag_to_scroll = drag_to_scroll;\n        self\n    }\n\n    /// Should the scroll handle stick to the bottom position even as the content size changes\n    /// dynamically? The scroll handle remains stuck until manually changed, and will become stuck\n    /// once again when repositioned to the bottom. Default: `false`.\n    #[inline]\n    pub fn stick_to_bottom(mut self, stick: bool) -> Self {\n        self.scroll_options.stick_to_bottom = stick;\n        self\n    }\n\n    /// Set a row to scroll to.\n    ///\n    /// `align` specifies if the row should be positioned in the top, center, or bottom of the view\n    /// (using [`Align::TOP`], [`Align::Center`] or [`Align::BOTTOM`]).\n    /// If `align` is `None`, the table will scroll just enough to bring the cursor into view.\n    ///\n    /// See also: [`Self::vertical_scroll_offset`].\n    #[inline]\n    pub fn scroll_to_row(mut self, row: usize, align: Option<Align>) -> Self {\n        self.scroll_options.scroll_to_row = Some((row, align));\n        self\n    }\n\n    /// Set the vertical scroll offset position, in points.\n    ///\n    /// See also: [`Self::scroll_to_row`].\n    #[inline]\n    pub fn vertical_scroll_offset(mut self, offset: f32) -> Self {\n        self.scroll_options.scroll_offset_y = Some(offset);\n        self\n    }\n\n    /// The minimum height of a vertical scroll area which requires scroll bars.\n    ///\n    /// The scroll area will only become smaller than this if the content is smaller than this\n    /// (and so we don't require scroll bars).\n    ///\n    /// Default: `200.0`.\n    #[inline]\n    pub fn min_scrolled_height(mut self, min_scrolled_height: f32) -> Self {\n        self.scroll_options.min_scrolled_height = min_scrolled_height;\n        self\n    }\n\n    /// Don't make the scroll area higher than this (add scroll-bars instead!).\n    ///\n    /// In other words: add scroll-bars when this height is reached.\n    /// Default: `800.0`.\n    #[inline]\n    pub fn max_scroll_height(mut self, max_scroll_height: f32) -> Self {\n        self.scroll_options.max_scroll_height = max_scroll_height;\n        self\n    }\n\n    /// For each axis (x,y):\n    /// * If true, add blank space outside the table, keeping the table small.\n    /// * If false, add blank space inside the table, expanding the table to fit the containing ui.\n    ///\n    /// Default: `true`.\n    ///\n    /// See [`ScrollArea::auto_shrink`] for more.\n    #[inline]\n    pub fn auto_shrink(mut self, auto_shrink: impl Into<Vec2b>) -> Self {\n        self.scroll_options.auto_shrink = auto_shrink.into();\n        self\n    }\n\n    /// Set the visibility of both horizontal and vertical scroll bars.\n    ///\n    /// With `ScrollBarVisibility::VisibleWhenNeeded` (default), the scroll bar will be visible only when needed.\n    #[inline]\n    pub fn scroll_bar_visibility(mut self, scroll_bar_visibility: ScrollBarVisibility) -> Self {\n        self.scroll_options.scroll_bar_visibility = scroll_bar_visibility;\n        self\n    }\n\n    /// Should the scroll area animate `scroll_to_*` functions?\n    ///\n    /// Default: `true`.\n    #[inline]\n    pub fn animate_scrolling(mut self, animated: bool) -> Self {\n        self.scroll_options.animated = animated;\n        self\n    }\n\n    /// What layout should we use for the individual cells?\n    #[inline]\n    pub fn cell_layout(mut self, cell_layout: egui::Layout) -> Self {\n        self.cell_layout = cell_layout;\n        self\n    }\n\n    /// Allocate space for one column.\n    #[inline]\n    pub fn column(mut self, column: Column) -> Self {\n        self.columns.push(column);\n        self\n    }\n\n    /// Allocate space for several columns at once.\n    #[inline]\n    pub fn columns(mut self, column: Column, count: usize) -> Self {\n        for _ in 0..count {\n            self.columns.push(column);\n        }\n        self\n    }\n\n    fn available_width(&self) -> f32 {\n        self.ui.available_rect_before_wrap().width()\n            - (self.scroll_options.vscroll as i32 as f32)\n                * self.ui.spacing().scroll.allocated_width()\n    }\n\n    /// Reset all column widths.\n    pub fn reset(&self) {\n        let state_id = self.ui.id().with(self.id_salt);\n        TableState::reset(self.ui, state_id);\n    }\n\n    /// Create a header row which always stays visible and at the top\n    pub fn header(self, height: f32, add_header_row: impl FnOnce(TableRow<'_, '_>)) -> Table<'a> {\n        let available_width = self.available_width();\n\n        let Self {\n            ui,\n            id_salt,\n            mut columns,\n            striped,\n            resizable,\n            cell_layout,\n            scroll_options,\n            sense,\n        } = self;\n\n        for (i, column) in columns.iter_mut().enumerate() {\n            let column_resize_id = ui.id().with(\"resize_column\").with(i);\n            if let Some(response) = ui.ctx().read_response(column_resize_id)\n                && response.double_clicked()\n            {\n                column.auto_size_this_frame = true;\n            }\n        }\n\n        let striped = striped.unwrap_or_else(|| ui.visuals().striped);\n\n        let state_id = ui.id().with(id_salt);\n\n        let (is_sizing_pass, state) =\n            TableState::load(ui, state_id, resizable, &columns, available_width);\n\n        let mut max_used_widths = vec![0.0; columns.len()];\n        let table_top = ui.cursor().top();\n\n        let mut ui_builder = egui::UiBuilder::new();\n        if is_sizing_pass {\n            ui_builder = ui_builder.sizing_pass();\n        }\n        ui.scope_builder(ui_builder, |ui| {\n            let mut layout = StripLayout::new(ui, CellDirection::Horizontal, cell_layout, sense);\n            let mut response: Option<Response> = None;\n            add_header_row(TableRow {\n                layout: &mut layout,\n                columns: &columns,\n                widths: &state.column_widths,\n                max_used_widths: &mut max_used_widths,\n                row_index: 0,\n                col_index: 0,\n                height,\n                striped: false,\n                hovered: false,\n                selected: false,\n                overline: false,\n                response: &mut response,\n            });\n            layout.allocate_rect();\n        });\n\n        Table {\n            ui,\n            table_top,\n            state_id,\n            columns,\n            available_width,\n            state,\n            max_used_widths,\n            is_sizing_pass,\n            resizable,\n            striped,\n            cell_layout,\n            scroll_options,\n            sense,\n        }\n    }\n\n    /// Create table body without a header row\n    pub fn body<F>(self, add_body_contents: F) -> ScrollAreaOutput<()>\n    where\n        F: for<'b> FnOnce(TableBody<'b>),\n    {\n        let available_width = self.available_width();\n\n        let Self {\n            ui,\n            id_salt,\n            columns,\n            striped,\n            resizable,\n            cell_layout,\n            scroll_options,\n            sense,\n        } = self;\n\n        let striped = striped.unwrap_or_else(|| ui.visuals().striped);\n\n        let state_id = ui.id().with(id_salt);\n\n        let (is_sizing_pass, state) =\n            TableState::load(ui, state_id, resizable, &columns, available_width);\n\n        let max_used_widths = vec![0.0; columns.len()];\n        let table_top = ui.cursor().top();\n\n        Table {\n            ui,\n            table_top,\n            state_id,\n            columns,\n            available_width,\n            state,\n            max_used_widths,\n            is_sizing_pass,\n            resizable,\n            striped,\n            cell_layout,\n            scroll_options,\n            sense,\n        }\n        .body(add_body_contents)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\nstruct TableState {\n    column_widths: Vec<f32>,\n\n    /// If known from previous frame\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    max_used_widths: Vec<f32>,\n}\n\nimpl TableState {\n    /// Return true if we should do a sizing pass.\n    fn load(\n        ui: &Ui,\n        state_id: egui::Id,\n        resizable: bool,\n        columns: &[Column],\n        available_width: f32,\n    ) -> (bool, Self) {\n        let rect = Rect::from_min_size(ui.available_rect_before_wrap().min, Vec2::ZERO);\n        ui.ctx().check_for_id_clash(state_id, rect, \"Table\");\n\n        #[cfg(feature = \"serde\")]\n        let state = ui.data_mut(|d| d.get_persisted::<Self>(state_id));\n        #[cfg(not(feature = \"serde\"))]\n        let state = ui.data_mut(|d| d.get_temp::<Self>(state_id));\n\n        // Make sure that the stored widths aren't out-dated:\n        let state = state.filter(|state| state.column_widths.len() == columns.len());\n\n        let is_sizing_pass =\n            ui.is_sizing_pass() || state.is_none() && columns.iter().any(|c| c.is_auto());\n\n        let mut state = state.unwrap_or_else(|| {\n            let initial_widths =\n                to_sizing(columns).to_lengths(available_width, ui.spacing().item_spacing.x);\n            Self {\n                column_widths: initial_widths,\n                max_used_widths: Default::default(),\n            }\n        });\n\n        if !is_sizing_pass && state.max_used_widths.len() == columns.len() {\n            // Make sure any non-resizable `remainder` columns are updated\n            // to take up the remainder of the current available width.\n            // Also handles changing item spacing.\n            let mut sizing = crate::sizing::Sizing::default();\n            for ((prev_width, max_used), column) in state\n                .column_widths\n                .iter()\n                .zip(&state.max_used_widths)\n                .zip(columns)\n            {\n                use crate::Size;\n\n                let column_resizable = column.resizable.unwrap_or(resizable);\n                let size = if column_resizable {\n                    // Resiable columns keep their width:\n                    Size::exact(*prev_width)\n                } else {\n                    match column.initial_width {\n                        InitialColumnSize::Absolute(width) => Size::exact(width),\n                        InitialColumnSize::Automatic(_) => Size::exact(*prev_width),\n                        InitialColumnSize::Remainder => Size::remainder(),\n                    }\n                    .at_least(column.width_range.min.max(*max_used))\n                    .at_most(column.width_range.max)\n                };\n                sizing.add(size);\n            }\n            state.column_widths = sizing.to_lengths(available_width, ui.spacing().item_spacing.x);\n        }\n\n        (is_sizing_pass, state)\n    }\n\n    fn store(self, ui: &egui::Ui, state_id: egui::Id) {\n        #![expect(clippy::needless_return)]\n        #[cfg(feature = \"serde\")]\n        {\n            return ui.data_mut(|d| d.insert_persisted(state_id, self));\n        }\n        #[cfg(not(feature = \"serde\"))]\n        {\n            return ui.data_mut(|d| d.insert_temp(state_id, self));\n        }\n    }\n\n    fn reset(ui: &egui::Ui, state_id: egui::Id) {\n        ui.data_mut(|d| d.remove::<Self>(state_id));\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Table struct which can construct a [`TableBody`].\n///\n/// Is created by [`TableBuilder`] by either calling [`TableBuilder::body`] or after creating a header row with [`TableBuilder::header`].\npub struct Table<'a> {\n    ui: &'a mut Ui,\n    table_top: f32,\n    state_id: egui::Id,\n    columns: Vec<Column>,\n    available_width: f32,\n    state: TableState,\n\n    /// Accumulated maximum used widths for each column.\n    max_used_widths: Vec<f32>,\n\n    /// During the sizing pass we calculate the width of columns with [`Column::auto`].\n    is_sizing_pass: bool,\n    resizable: bool,\n    striped: bool,\n    cell_layout: egui::Layout,\n\n    scroll_options: TableScrollOptions,\n\n    sense: egui::Sense,\n}\n\nimpl Table<'_> {\n    /// Access the contained [`egui::Ui`].\n    ///\n    /// You can use this to e.g. modify the [`egui::Style`] with [`egui::Ui::style_mut`].\n    pub fn ui_mut(&mut self) -> &mut egui::Ui {\n        self.ui\n    }\n\n    /// Create table body after adding a header row\n    pub fn body<F>(self, add_body_contents: F) -> ScrollAreaOutput<()>\n    where\n        F: for<'b> FnOnce(TableBody<'b>),\n    {\n        let Table {\n            ui,\n            table_top,\n            state_id,\n            columns,\n            resizable,\n            mut available_width,\n            mut state,\n            mut max_used_widths,\n            is_sizing_pass,\n            striped,\n            cell_layout,\n            scroll_options,\n            sense,\n        } = self;\n\n        let TableScrollOptions {\n            vscroll,\n            drag_to_scroll,\n            stick_to_bottom,\n            scroll_to_row,\n            scroll_offset_y,\n            min_scrolled_height,\n            max_scroll_height,\n            auto_shrink,\n            scroll_bar_visibility,\n            animated,\n        } = scroll_options;\n\n        let cursor_position = ui.cursor().min;\n\n        let mut scroll_area = ScrollArea::new([false, vscroll])\n            .id_salt(state_id.with(\"__scroll_area\"))\n            .scroll_source(ScrollSource {\n                drag: drag_to_scroll,\n                ..Default::default()\n            })\n            .stick_to_bottom(stick_to_bottom)\n            .min_scrolled_height(min_scrolled_height)\n            .max_height(max_scroll_height)\n            .auto_shrink(auto_shrink)\n            .scroll_bar_visibility(scroll_bar_visibility)\n            .animated(animated);\n\n        if let Some(scroll_offset_y) = scroll_offset_y {\n            scroll_area = scroll_area.vertical_scroll_offset(scroll_offset_y);\n        }\n\n        let columns_ref = &columns;\n        let widths_ref = &state.column_widths;\n        let max_used_widths_ref = &mut max_used_widths;\n\n        let scroll_area_out = scroll_area.show(ui, move |ui| {\n            let mut scroll_to_y_range = None;\n\n            let clip_rect = ui.clip_rect();\n\n            let mut ui_builder = egui::UiBuilder::new();\n            if is_sizing_pass {\n                ui_builder = ui_builder.sizing_pass();\n            }\n            ui.scope_builder(ui_builder, |ui| {\n                let hovered_row_index_id = self.state_id.with(\"__table_hovered_row\");\n                let hovered_row_index =\n                    ui.data_mut(|data| data.remove_temp::<usize>(hovered_row_index_id));\n\n                let layout = StripLayout::new(ui, CellDirection::Horizontal, cell_layout, sense);\n\n                add_body_contents(TableBody {\n                    layout,\n                    columns: columns_ref,\n                    widths: widths_ref,\n                    max_used_widths: max_used_widths_ref,\n                    striped,\n                    row_index: 0,\n                    y_range: clip_rect.y_range(),\n                    scroll_to_row: scroll_to_row.map(|(r, _)| r),\n                    scroll_to_y_range: &mut scroll_to_y_range,\n                    hovered_row_index,\n                    hovered_row_index_id,\n                });\n\n                if scroll_to_row.is_some() && scroll_to_y_range.is_none() {\n                    // TableBody::row didn't find the correct row, so scroll to the bottom:\n                    scroll_to_y_range = Some(Rangef::new(f32::INFINITY, f32::INFINITY));\n                }\n            });\n\n            if let Some(y_range) = scroll_to_y_range {\n                let x = 0.0; // ignored, we only have vertical scrolling\n                let rect = egui::Rect::from_x_y_ranges(x..=x, y_range);\n                let align = scroll_to_row.and_then(|(_, a)| a);\n                ui.scroll_to_rect(rect, align);\n            }\n        });\n\n        let bottom = ui.min_rect().bottom();\n\n        let spacing_x = ui.spacing().item_spacing.x;\n        let mut x = cursor_position.x - spacing_x * 0.5;\n        for (i, column_width) in state.column_widths.iter_mut().enumerate() {\n            let column = &columns[i];\n            let column_is_resizable = column.resizable.unwrap_or(resizable);\n            let width_range = column.width_range;\n\n            let is_last_column = i + 1 == columns.len();\n            if is_last_column\n                && column.initial_width == InitialColumnSize::Remainder\n                && !ui.is_sizing_pass()\n            {\n                // If the last column is 'remainder', then let it fill the remainder!\n                let eps = 0.1; // just to avoid some rounding errors.\n                *column_width = available_width - eps;\n                if !column.clip {\n                    *column_width = column_width.at_least(max_used_widths[i]);\n                }\n                *column_width = width_range.clamp(*column_width);\n                break;\n            }\n\n            if ui.is_sizing_pass() {\n                if column.clip {\n                    // If we clip, we don't need to be as wide as the max used width\n                    *column_width = column_width.min(max_used_widths[i]);\n                } else {\n                    *column_width = max_used_widths[i];\n                }\n            } else if !column.clip {\n                // Unless we clip we don't want to shrink below the\n                // size that was actually used:\n                *column_width = column_width.at_least(max_used_widths[i]);\n            }\n            *column_width = width_range.clamp(*column_width);\n\n            x += *column_width + spacing_x;\n\n            if column.is_auto() && (is_sizing_pass || !column_is_resizable) {\n                *column_width = width_range.clamp(max_used_widths[i]);\n            } else if column_is_resizable {\n                let column_resize_id = state_id.with(\"resize_column\").with(i);\n\n                let mut p0 = egui::pos2(x, table_top);\n                let mut p1 = egui::pos2(x, bottom);\n                let line_rect = egui::Rect::from_min_max(p0, p1)\n                    .expand(ui.style().interaction.resize_grab_radius_side);\n\n                let resize_response =\n                    ui.interact(line_rect, column_resize_id, egui::Sense::click_and_drag());\n\n                if column.auto_size_this_frame {\n                    // Auto-size: resize to what is needed.\n                    *column_width = width_range.clamp(max_used_widths[i]);\n                } else if resize_response.dragged()\n                    && let Some(pointer) = ui.ctx().pointer_latest_pos()\n                {\n                    let mut new_width = *column_width + pointer.x - x;\n                    if !column.clip {\n                        // Unless we clip we don't want to shrink below the\n                        // size that was actually used.\n                        // However, we still want to allow content that shrinks when you try\n                        // to make the column less wide, so we allow some small shrinkage each frame:\n                        // big enough to allow shrinking over time, small enough not to look ugly when\n                        // shrinking fails. This is a bit of a HACK around immediate mode.\n                        let max_shrinkage_per_frame = 8.0;\n                        new_width =\n                            new_width.at_least(max_used_widths[i] - max_shrinkage_per_frame);\n                    }\n                    new_width = width_range.clamp(new_width);\n\n                    let x = x - *column_width + new_width;\n                    (p0.x, p1.x) = (x, x);\n\n                    *column_width = new_width;\n                }\n\n                let dragging_something_else =\n                    ui.input(|i| i.pointer.any_down() || i.pointer.any_pressed());\n                let resize_hover = resize_response.hovered() && !dragging_something_else;\n\n                if resize_hover || resize_response.dragged() {\n                    ui.set_cursor_icon(egui::CursorIcon::ResizeColumn);\n                }\n\n                let stroke = if resize_response.dragged() {\n                    ui.style().visuals.widgets.active.bg_stroke\n                } else if resize_hover {\n                    ui.style().visuals.widgets.hovered.bg_stroke\n                } else {\n                    // ui.visuals().widgets.inactive.bg_stroke\n                    ui.visuals().widgets.noninteractive.bg_stroke\n                };\n\n                ui.painter().line_segment([p0, p1], stroke);\n            }\n\n            available_width -= *column_width + spacing_x;\n        }\n\n        state.max_used_widths = max_used_widths;\n\n        state.store(ui, state_id);\n        scroll_area_out\n    }\n}\n\n/// The body of a table.\n///\n/// Is created by calling `body` on a [`Table`] (after adding a header row) or [`TableBuilder`] (without a header row).\npub struct TableBody<'a> {\n    layout: StripLayout<'a>,\n\n    columns: &'a [Column],\n\n    /// Current column widths.\n    widths: &'a [f32],\n\n    /// Accumulated maximum used widths for each column.\n    max_used_widths: &'a mut [f32],\n\n    striped: bool,\n    row_index: usize,\n    y_range: Rangef,\n\n    /// Look for this row to scroll to.\n    scroll_to_row: Option<usize>,\n\n    /// If we find the correct row to scroll to,\n    /// this is set to the y-range of the row.\n    scroll_to_y_range: &'a mut Option<Rangef>,\n\n    hovered_row_index: Option<usize>,\n\n    /// Used to store the hovered row index between frames.\n    hovered_row_index_id: egui::Id,\n}\n\nimpl<'a> TableBody<'a> {\n    /// Access the contained [`egui::Ui`].\n    ///\n    /// You can use this to e.g. modify the [`egui::Style`] with [`egui::Ui::style_mut`].\n    pub fn ui_mut(&mut self) -> &mut egui::Ui {\n        self.layout.ui\n    }\n\n    /// Where in screen-space is the table body?\n    pub fn max_rect(&self) -> Rect {\n        self.layout\n            .rect\n            .translate(egui::vec2(0.0, self.scroll_offset_y()))\n    }\n\n    fn scroll_offset_y(&self) -> f32 {\n        self.y_range.min - self.layout.rect.top()\n    }\n\n    /// Return a vector containing all column widths for this table body.\n    ///\n    /// This is primarily meant for use with [`TableBody::heterogeneous_rows`] in cases where row\n    /// heights are expected to according to the width of one or more cells -- for example, if text\n    /// is wrapped rather than clipped within the cell.\n    pub fn widths(&self) -> &[f32] {\n        self.widths\n    }\n\n    /// Add a single row with the given height.\n    ///\n    /// ⚠️ It is much more performant to use [`Self::rows`] or [`Self::heterogeneous_rows`],\n    /// as those functions will only render the visible rows.\n    pub fn row(&mut self, height: f32, add_row_content: impl FnOnce(TableRow<'a, '_>)) {\n        let mut response: Option<Response> = None;\n        let top_y = self.layout.cursor.y;\n        add_row_content(TableRow {\n            layout: &mut self.layout,\n            columns: self.columns,\n            widths: self.widths,\n            max_used_widths: self.max_used_widths,\n            row_index: self.row_index,\n            col_index: 0,\n            height,\n            striped: self.striped && self.row_index.is_multiple_of(2),\n            hovered: self.hovered_row_index == Some(self.row_index),\n            selected: false,\n            overline: false,\n            response: &mut response,\n        });\n        self.capture_hover_state(&response, self.row_index);\n        let bottom_y = self.layout.cursor.y;\n\n        if Some(self.row_index) == self.scroll_to_row {\n            *self.scroll_to_y_range = Some(Rangef::new(top_y, bottom_y));\n        }\n\n        self.row_index += 1;\n    }\n\n    /// Add many rows with same height.\n    ///\n    /// Is a lot more performant than adding each individual row as non visible rows must not be rendered.\n    ///\n    /// If you need many rows with different heights, use [`Self::heterogeneous_rows`] instead.\n    ///\n    /// ### Example\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// use egui_extras::{TableBuilder, Column};\n    /// TableBuilder::new(ui)\n    ///     .column(Column::remainder().at_least(100.0))\n    ///     .body(|mut body| {\n    ///         let row_height = 18.0;\n    ///         let num_rows = 10_000;\n    ///         body.rows(row_height, num_rows, |mut row| {\n    ///             let row_index = row.index();\n    ///             row.col(|ui| {\n    ///                 ui.label(format!(\"First column of row {row_index}\"));\n    ///             });\n    ///         });\n    ///     });\n    /// # });\n    /// ```\n    pub fn rows(\n        mut self,\n        row_height_sans_spacing: f32,\n        total_rows: usize,\n        mut add_row_content: impl FnMut(TableRow<'_, '_>),\n    ) {\n        let spacing = self.layout.ui.spacing().item_spacing;\n        let row_height_with_spacing = row_height_sans_spacing + spacing.y;\n\n        if let Some(scroll_to_row) = self.scroll_to_row {\n            let scroll_to_row = scroll_to_row.at_most(total_rows.saturating_sub(1)) as f32;\n            *self.scroll_to_y_range = Some(Rangef::new(\n                self.layout.cursor.y + scroll_to_row * row_height_with_spacing,\n                self.layout.cursor.y + (scroll_to_row + 1.0) * row_height_with_spacing,\n            ));\n        }\n\n        let scroll_offset_y = self\n            .scroll_offset_y()\n            .min(total_rows as f32 * row_height_with_spacing);\n        let max_height = self.y_range.span();\n        let mut min_row = 0;\n\n        if scroll_offset_y > 0.0 {\n            min_row = (scroll_offset_y / row_height_with_spacing).floor() as usize;\n            self.add_buffer(min_row as f32 * row_height_with_spacing);\n        }\n\n        let max_row =\n            ((scroll_offset_y + max_height) / row_height_with_spacing).ceil() as usize + 1;\n        let max_row = max_row.min(total_rows);\n\n        for row_index in min_row..max_row {\n            let mut response: Option<Response> = None;\n            add_row_content(TableRow {\n                layout: &mut self.layout,\n                columns: self.columns,\n                widths: self.widths,\n                max_used_widths: self.max_used_widths,\n                row_index,\n                col_index: 0,\n                height: row_height_sans_spacing,\n                striped: self.striped && (row_index + self.row_index).is_multiple_of(2),\n                hovered: self.hovered_row_index == Some(row_index),\n                selected: false,\n                overline: false,\n                response: &mut response,\n            });\n            self.capture_hover_state(&response, row_index);\n        }\n\n        if total_rows - max_row > 0 {\n            let skip_height = (total_rows - max_row) as f32 * row_height_with_spacing;\n            self.add_buffer(skip_height - spacing.y);\n        }\n    }\n\n    /// Add rows with varying heights.\n    ///\n    /// This takes a very slight performance hit compared to [`TableBody::rows`] due to the need to\n    /// iterate over all row heights in order to calculate the virtual table height above and below the\n    /// visible region, but it is many orders of magnitude more performant than adding individual\n    /// heterogeneously-sized rows using [`TableBody::row`] at the cost of the additional complexity\n    /// that comes with pre-calculating row heights and representing them as an iterator.\n    ///\n    /// ### Example\n    /// ```\n    /// # egui::__run_test_ui(|ui| {\n    /// use egui_extras::{TableBuilder, Column};\n    /// TableBuilder::new(ui)\n    ///     .column(Column::remainder().at_least(100.0))\n    ///     .body(|mut body| {\n    ///         let row_heights: Vec<f32> = vec![60.0, 18.0, 31.0, 240.0];\n    ///         body.heterogeneous_rows(row_heights.into_iter(), |mut row| {\n    ///             let row_index = row.index();\n    ///             let thick = row_index % 6 == 0;\n    ///             row.col(|ui| {\n    ///                 ui.centered_and_justified(|ui| {\n    ///                     ui.label(row_index.to_string());\n    ///                 });\n    ///             });\n    ///         });\n    ///     });\n    /// # });\n    /// ```\n    pub fn heterogeneous_rows(\n        mut self,\n        heights: impl Iterator<Item = f32>,\n        mut add_row_content: impl FnMut(TableRow<'_, '_>),\n    ) {\n        let spacing = self.layout.ui.spacing().item_spacing;\n        let mut enumerated_heights = heights.enumerate();\n\n        let max_height = self.y_range.span();\n        let scroll_offset_y = self.scroll_offset_y() as f64;\n\n        let scroll_to_y_range_offset = self.layout.cursor.y as f64;\n\n        let mut cursor_y: f64 = 0.0;\n\n        // Skip the invisible rows, and populate the first non-virtual row.\n        for (row_index, row_height) in &mut enumerated_heights {\n            let old_cursor_y = cursor_y;\n            cursor_y += (row_height + spacing.y) as f64;\n\n            if Some(row_index) == self.scroll_to_row {\n                *self.scroll_to_y_range = Some(Rangef::new(\n                    (scroll_to_y_range_offset + old_cursor_y) as f32,\n                    (scroll_to_y_range_offset + cursor_y) as f32,\n                ));\n            }\n\n            if cursor_y >= scroll_offset_y {\n                // This row is visible:\n                self.add_buffer(old_cursor_y as f32); // skip all the invisible rows\n                let mut response: Option<Response> = None;\n                add_row_content(TableRow {\n                    layout: &mut self.layout,\n                    columns: self.columns,\n                    widths: self.widths,\n                    max_used_widths: self.max_used_widths,\n                    row_index,\n                    col_index: 0,\n                    height: row_height,\n                    striped: self.striped && (row_index + self.row_index).is_multiple_of(2),\n                    hovered: self.hovered_row_index == Some(row_index),\n                    selected: false,\n                    overline: false,\n                    response: &mut response,\n                });\n                self.capture_hover_state(&response, row_index);\n                break;\n            }\n        }\n\n        // populate visible rows:\n        for (row_index, row_height) in &mut enumerated_heights {\n            let top_y = cursor_y;\n            let mut response: Option<Response> = None;\n            add_row_content(TableRow {\n                layout: &mut self.layout,\n                columns: self.columns,\n                widths: self.widths,\n                max_used_widths: self.max_used_widths,\n                row_index,\n                col_index: 0,\n                height: row_height,\n                striped: self.striped && (row_index + self.row_index).is_multiple_of(2),\n                hovered: self.hovered_row_index == Some(row_index),\n                overline: false,\n                selected: false,\n                response: &mut response,\n            });\n            self.capture_hover_state(&response, row_index);\n            cursor_y += (row_height + spacing.y) as f64;\n\n            if Some(row_index) == self.scroll_to_row {\n                *self.scroll_to_y_range = Some(Rangef::new(\n                    (scroll_to_y_range_offset + top_y) as f32,\n                    (scroll_to_y_range_offset + cursor_y) as f32,\n                ));\n            }\n\n            if cursor_y > scroll_offset_y + max_height as f64 {\n                break;\n            }\n        }\n\n        // calculate height below the visible table range:\n        let mut height_below_visible: f64 = 0.0;\n        for (row_index, row_height) in enumerated_heights {\n            height_below_visible += (row_height + spacing.y) as f64;\n\n            let top_y = cursor_y;\n            cursor_y += (row_height + spacing.y) as f64;\n            if Some(row_index) == self.scroll_to_row {\n                *self.scroll_to_y_range = Some(Rangef::new(\n                    (scroll_to_y_range_offset + top_y) as f32,\n                    (scroll_to_y_range_offset + cursor_y) as f32,\n                ));\n            }\n        }\n\n        if self.scroll_to_row.is_some() && self.scroll_to_y_range.is_none() {\n            // Catch desire to scroll past the end:\n            *self.scroll_to_y_range =\n                Some(Rangef::point((scroll_to_y_range_offset + cursor_y) as f32));\n        }\n\n        if height_below_visible > 0.0 {\n            // we need to add a buffer to allow the table to\n            // accurately calculate the scrollbar position\n            self.add_buffer(height_below_visible as f32);\n        }\n    }\n\n    // Create a table row buffer of the given height to represent the non-visible portion of the\n    // table.\n    fn add_buffer(&mut self, height: f32) {\n        self.layout.skip_space(egui::vec2(0.0, height));\n    }\n\n    // Capture the hover information for the just created row. This is used in the next render\n    // to ensure that the entire row is highlighted.\n    fn capture_hover_state(&self, response: &Option<Response>, row_index: usize) {\n        let is_row_hovered = response.as_ref().is_some_and(|r| r.hovered());\n        if is_row_hovered {\n            self.layout\n                .ui\n                .data_mut(|data| data.insert_temp(self.hovered_row_index_id, row_index));\n        }\n    }\n}\n\nimpl Drop for TableBody<'_> {\n    fn drop(&mut self) {\n        self.layout.allocate_rect();\n    }\n}\n\n/// The row of a table.\n/// Is created by [`TableRow`] for each created [`TableBody::row`] or each visible row in rows created by calling [`TableBody::rows`].\npub struct TableRow<'a, 'b> {\n    layout: &'b mut StripLayout<'a>,\n    columns: &'b [Column],\n    widths: &'b [f32],\n\n    /// grows during building with the maximum widths\n    max_used_widths: &'b mut [f32],\n\n    row_index: usize,\n    col_index: usize,\n    height: f32,\n\n    striped: bool,\n    hovered: bool,\n    selected: bool,\n    overline: bool,\n\n    response: &'b mut Option<Response>,\n}\n\nimpl TableRow<'_, '_> {\n    /// Add the contents of a column on this row (i.e. a cell).\n    ///\n    /// Returns the used space (`min_rect`) plus the [`Response`] of the whole cell.\n    #[cfg_attr(debug_assertions, track_caller)]\n    pub fn col(&mut self, add_cell_contents: impl FnOnce(&mut Ui)) -> (Rect, Response) {\n        let col_index = self.col_index;\n\n        let clip = self.columns.get(col_index).is_some_and(|c| c.clip);\n        let auto_size_this_frame = self\n            .columns\n            .get(col_index)\n            .is_some_and(|c| c.auto_size_this_frame);\n\n        let width = if let Some(width) = self.widths.get(col_index) {\n            self.col_index += 1;\n            *width\n        } else {\n            crate::log_or_panic!(\n                \"Added more `Table` columns than were pre-allocated ({} pre-allocated)\",\n                self.widths.len()\n            );\n            8.0 // anything will look wrong, so pick something that is obviously wrong\n        };\n\n        let width = CellSize::Absolute(width);\n        let height = CellSize::Absolute(self.height);\n\n        let flags = StripLayoutFlags {\n            clip,\n            striped: self.striped,\n            hovered: self.hovered,\n            selected: self.selected,\n            overline: self.overline,\n            sizing_pass: auto_size_this_frame || self.layout.ui.is_sizing_pass(),\n        };\n\n        let (used_rect, response) = self.layout.add(\n            flags,\n            width,\n            height,\n            egui::Id::new((self.row_index, col_index)),\n            add_cell_contents,\n        );\n\n        if let Some(max_w) = self.max_used_widths.get_mut(col_index) {\n            *max_w = max_w.max(used_rect.width());\n        }\n\n        *self.response = Some(\n            self.response\n                .as_ref()\n                .map_or_else(|| response.clone(), |r| r.union(response.clone())),\n        );\n\n        (used_rect, response)\n    }\n\n    /// Set the selection highlight state for cells added after a call to this function.\n    #[inline]\n    pub fn set_selected(&mut self, selected: bool) {\n        self.selected = selected;\n    }\n\n    /// Set the hovered highlight state for cells added after a call to this function.\n    #[inline]\n    pub fn set_hovered(&mut self, hovered: bool) {\n        self.hovered = hovered;\n    }\n\n    /// Set the overline state for this row. The overline is a line above the row,\n    /// usable for e.g. visually grouping rows.\n    #[inline]\n    pub fn set_overline(&mut self, overline: bool) {\n        self.overline = overline;\n    }\n\n    /// Returns a union of the [`Response`]s of the cells added to the row up to this point.\n    ///\n    /// You need to add at least one row to the table before calling this function.\n    pub fn response(&self) -> Response {\n        self.response\n            .clone()\n            .expect(\"Should only be called after `col`\")\n    }\n\n    /// Returns the index of the row.\n    #[inline]\n    pub fn index(&self) -> usize {\n        self.row_index\n    }\n\n    /// Returns the index of the column. Incremented after a column is added.\n    #[inline]\n    pub fn col_index(&self) -> usize {\n        self.col_index\n    }\n}\n\nimpl Drop for TableRow<'_, '_> {\n    #[inline]\n    fn drop(&mut self) {\n        self.layout.end_line();\n    }\n}\n"
  },
  {
    "path": "crates/egui_glow/CHANGELOG.md",
    "content": "# Changelog for egui_glow\nAll notable changes to the `egui_glow` integration will be noted in this file.\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n\n## 0.33.3 - 2025-12-11\nNothing new\n\n\n## 0.33.2 - 2025-11-13\nNothing new\n\n\n## 0.33.0 - 2025-10-09\n* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)\n\n\n## 0.32.3 - 2025-09-12\nNothing new\n\n\n## 0.32.2 - 2025-09-04\nNothing new\n\n\n## 0.32.1 - 2025-08-15\nNothing new\n\n\n## 0.32.0 - 2025-07-10\n* Fix text distortion on mobile devices/browsers with `glow` backend [#6893](https://github.com/emilk/egui/pull/6893) by [@wareya](https://github.com/wareya)\n* Improve texture filtering by doing it in gamma space [#7311](https://github.com/emilk/egui/pull/7311) by [@emilk](https://github.com/emilk)\n\n\n## 0.31.1 - 2025-03-05\nNothing new\n\n\n## 0.31.0 - 2025-02-04\nNothing new\n\n\n## 0.30.0 - 2024-12-16\n* Update glow to 0.16 [#5395](https://github.com/emilk/egui/pull/5395) by [@sagudev](https://github.com/sagudev)\n\n\n## 0.29.1 - 2024-10-01\nNothing new\n\n\n## 0.29.0 - 2024-09-26 - `glow` 0.14\n* Update `glow` to 0.14 [#4952](https://github.com/emilk/egui/pull/4952) by [@bircni](https://github.com/bircni)\n* Introduce dithering to reduce banding [#4497](https://github.com/emilk/egui/pull/4497) by [@jwagner](https://github.com/jwagner)\n* Fix missing `winit` feature in `egui_glow` [#4916](https://github.com/emilk/egui/pull/4916) by [@bash](https://github.com/bash)\n* Add support for mipmap textures [#5146](https://github.com/emilk/egui/pull/5146) by [@nolanderc](https://github.com/nolanderc)\n\n\n## 0.28.1 - 2024-07-05\nNothing new\n\n\n## 0.28.0 - 2024-07-03\n* Enable `egui_glow`'s `winit` feature on Wasm (#4420) [#4421](https://github.com/emilk/egui/pull/4421) by [@simon-frankau](https://github.com/simon-frankau)\n\n\n## 0.27.2 - 2024-04-02\n* Nothing new\n\n\n## 0.27.1 - 2024-03-29\n* Nothing new\n\n\n## 0.27.0 - 2024-03-26\n* Only disable sRGB framebuffer on supported platforms [#3994](https://github.com/emilk/egui/pull/3994) (thanks [@Nopey](https://github.com/Nopey)!)\n* Update memoffset to 0.9.0, arboard to 3.3.1, and remove egui_glow's needless dependency on pure_glow's deps  [#4036](https://github.com/emilk/egui/pull/4036) (thanks [@Nopey](https://github.com/Nopey)!)\n\n\n## 0.26.2 - 2024-02-14\n* Update memoffset to 0.9.0, arboard to 3.3.1, and remove egui_glow's needless dependency on pure_glow's deps  [#4036](https://github.com/emilk/egui/pull/4036) (thanks [@Nopey](https://github.com/Nopey)!)\n\n\n## 0.26.1 - 2024-02-11\n* Only disable sRGB framebuffer on supported platforms [#3994](https://github.com/emilk/egui/pull/3994) (thanks [@Nopey](https://github.com/Nopey)!)\n\n\n## 0.26.0 - 2024-02-05\n* Add `x11` and `wayland` features [#3909](https://github.com/emilk/egui/pull/3909) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n\n\n## 0.25.0 - 2024-01-08\n* Update to glow 0.13 [#3715](https://github.com/emilk/egui/pull/3715)\n* Make glow `Send + Sync` again [#3646](https://github.com/emilk/egui/pull/3646) (thanks [@surban](https://github.com/surban)!)\n\n\n## 0.24.1 - 2023-11-30\n* Improve a docstring\n\n\n## 0.24.0 - 2023-11-23\n* Change `Arc<glow::Context>` to `Rc<glow::Context>` [#3598](https://github.com/emilk/egui/pull/3598)\n* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)\n* Clamp viewport values [#3604](https://github.com/emilk/egui/pull/3604) (thanks [@Wumpf](https://github.com/Wumpf)!)\n\n\n## 0.23.0 - 2023-09-27\n* Update `egui`\n\n\n## 0.22.0 - 2023-05-23\n* Update `egui`\n\n\n## 0.21.0 - 2023-02-08\n* Update to `glow` 0.12 ([#2695](https://github.com/emilk/egui/pull/2695)).\n* Remove the `screen_reader` feature ([#2669](https://github.com/emilk/egui/pull/2669)).\n\n\n## 0.20.1 - 2022-12-11\n* Fix [docs.rs](https://docs.rs/egui_glow) build ([#2420](https://github.com/emilk/egui/pull/2420)).\n\n\n## 0.20.0 - 2022-12-08\n* Allow empty textures.\n* Added `shader_version` variable on `EguiGlow::new` for easier cross compiling on different OpenGL | ES targets ([#1993](https://github.com/emilk/egui/pull/1993)).\n\n\n## 0.19.0 - 2022-08-20\n* MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)).\n* `EguiGlow::new` now takes an `EventLoopWindowTarget<E>` instead of a `winit::Window` ([#1634](https://github.com/emilk/egui/pull/1634)).\n* Use `Arc` for `glow::Context` instead of `Rc` ([#1640](https://github.com/emilk/egui/pull/1640)).\n* Fixed `glClear` on WebGL1 ([#1658](https://github.com/emilk/egui/pull/1658)).\n* Add `Painter::intermediate_fbo` which tells callbacks where to render. This is only needed if the callbacks use their own FBO:s and need to know what to restore to.\n\n\n## 0.18.1 - 2022-05-05\n* Remove calls to `gl.get_error` in release builds to speed up rendering ([#1583](https://github.com/emilk/egui/pull/1583)).\n\n\n## 0.18.0 - 2022-04-30\n* Improved logging on rendering failures.\n* Added new `NativeOptions`: `vsync`, `multisampling`, `depth_buffer`, `stencil_buffer`.\n* Fixed potential scale bug when DPI scaling changes (e.g. when dragging a  window between different displays) ([#1441](https://github.com/emilk/egui/pull/1441)).\n* MSRV (Minimum Supported Rust Version) is now `1.60.0` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* `clipboard`, `links`, `winit` are now all opt-in features ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Added new feature `puffin` to add [`puffin profiler`](https://github.com/EmbarkStudios/puffin) scopes ([#1483](https://github.com/emilk/egui/pull/1483)).\n* Removed the features `dark-light`, `default_fonts` and `persistence` ([#1542](https://github.com/emilk/egui/pull/1542)).\n\n\n## 0.17.0 - 2022-02-22\n* `EguiGlow::run` no longer returns the shapes to paint, but stores them internally until you call `EguiGlow::paint` ([#1110](https://github.com/emilk/egui/pull/1110)).\n* Added `set_texture_filter` method to `Painter` ([#1041](https://github.com/emilk/egui/pull/1041)).\n* Fixed failure to run in Chrome ([#1092](https://github.com/emilk/egui/pull/1092)).\n* `EguiGlow::new` and `EguiGlow::paint` now takes `&winit::Window` ([#1151](https://github.com/emilk/egui/pull/1151)).\n* Automatically detect and apply dark or light mode from system ([#1045](https://github.com/emilk/egui/pull/1045)).\n\n\n## 0.16.0 - 2021-12-29\n* Made winit/glutin an optional dependency ([#868](https://github.com/emilk/egui/pull/868)).\n* Simplified `EguiGlow` interface ([#871](https://github.com/emilk/egui/pull/871)).\n* Removed `EguiGlow::is_quit_event` ([#881](https://github.com/emilk/egui/pull/881)).\n* Updated `glutin` to 0.28 ([#930](https://github.com/emilk/egui/pull/930)).\n* Changed the `Painter` interface slightly ([#999](https://github.com/emilk/egui/pull/999)).\n\n\n## 0.15.0 - 2021-10-24\n`egui_glow` has been newly created, with feature parity to `egui_glium`.\n\nAs `glow` is a set of lower-level bindings to OpenGL, this crate is potentially less stable than `egui_glium`,\nbut hopefully this will one day replace `egui_glium` as the default backend for `eframe`.\n"
  },
  {
    "path": "crates/egui_glow/Cargo.toml",
    "content": "[package]\nname = \"egui_glow\"\nversion.workspace = true\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\ndescription = \"Bindings for using egui natively using the glow library\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui/tree/main/crates/egui_glow\"\nlicense.workspace = true\nreadme = \"README.md\"\nrepository = \"https://github.com/emilk/egui/tree/main/crates/egui_glow\"\ncategories = [\"gui\", \"game-development\"]\nkeywords = [\"glow\", \"egui\", \"gui\", \"gamedev\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"Cargo.toml\", \"src/shader/*.glsl\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[features]\ndefault = []\n\n## For the `winit` integration:\n## enable cut/copy/paste to os clipboard.\n##\n## if disabled a clipboard will be simulated so you can still copy/paste within the egui app.\nclipboard = [\"egui-winit?/clipboard\"]\n\n## For the `winit` integration:\n## enable opening links in a browser when an egui hyperlink is clicked.\nlinks = [\"egui-winit?/links\"]\n\n## Enable [`winit`](https://docs.rs/winit) integration. On Linux, requires either `wayland` or `x11`\nwinit = [\"egui-winit\", \"dep:winit\"]\n\n## Enables Wayland support for winit.\nwayland = [\"winit?/wayland\"]\n\n## Enables x11 support for winit.\nx11 = [\"winit?/x11\"]\n\n\n[dependencies]\negui = { workspace = true, default-features = false, features = [\"bytemuck\"] }\negui-winit = { workspace = true, optional = true, default-features = false }\n\nbytemuck.workspace = true\nglow.workspace = true\nlog.workspace = true\nmemoffset.workspace = true\nprofiling.workspace = true\n\n#! ### Optional dependencies\n## Enable this when generating docs.\ndocument-features = { workspace = true, optional = true }\n\n# Native:\nwinit = { workspace = true, optional = true, default-features = false, features = [\"rwh_06\"] }\n\n# Web:\n[target.'cfg(target_arch = \"wasm32\")'.dependencies]\nweb-sys = { workspace = true, features = [\"console\"] }\nwasm-bindgen.workspace = true\n\n\n[dev-dependencies]\nglutin = { workspace = true, default-features = true } # examples/pure_glow\nglutin-winit = { workspace = true, default-features = true }\n\n[[example]]\nname = \"pure_glow\"\nrequired-features = [\"winit\", \"egui/default_fonts\"]\n"
  },
  {
    "path": "crates/egui_glow/README.md",
    "content": "# egui_glow\n\n[![Latest version](https://img.shields.io/crates/v/egui_glow.svg)](https://crates.io/crates/egui_glow)\n[![Documentation](https://docs.rs/egui_glow/badge.svg)](https://docs.rs/egui_glow)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\nThis crates provides bindings between [`egui`](https://github.com/emilk/egui) and [glow](https://crates.io/crates/glow) which allows you to:\n* Render egui using glow on both native and web.\n* Write cross platform native egui apps (with the `winit` feature).\n\nTo write web apps using `glow` you can use [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe) (which uses `egui_glow` for rendering).\n\nTo use on Linux, first run:\n\n```\nsudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev\n```\n\nThis crate optionally depends on [`egui-winit`](https://github.com/emilk/egui/tree/main/crates/egui-winit).\n\nTest the example with:\n\n``` sh\ncargo run -p egui_glow --example pure_glow --features=winit,egui/default_fonts\n```\n"
  },
  {
    "path": "crates/egui_glow/examples/pure_glow.rs",
    "content": "//! Example how to use pure `egui_glow`.\n\n#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs, clippy::unwrap_used)] // it's an example\n#![expect(clippy::undocumented_unsafe_blocks)]\n#![expect(unsafe_code)]\n\nuse std::num::NonZeroU32;\nuse std::sync::Arc;\n\nuse egui_winit::winit;\nuse winit::raw_window_handle::HasWindowHandle as _;\n\n/// The majority of `GlutinWindowContext` is taken from `eframe`\nstruct GlutinWindowContext {\n    window: winit::window::Window,\n    gl_context: glutin::context::PossiblyCurrentContext,\n    gl_display: glutin::display::Display,\n    gl_surface: glutin::surface::Surface<glutin::surface::WindowSurface>,\n}\n\nimpl GlutinWindowContext {\n    // refactor this function to use `glutin-winit` crate eventually.\n    // preferably add android support at the same time.\n    #[expect(unsafe_code)]\n    unsafe fn new(event_loop: &winit::event_loop::ActiveEventLoop) -> Self {\n        use glutin::context::NotCurrentGlContext as _;\n        use glutin::display::GetGlDisplay as _;\n        use glutin::display::GlDisplay as _;\n        use glutin::prelude::GlSurface as _;\n        let winit_window_builder = winit::window::WindowAttributes::default()\n            .with_resizable(true)\n            .with_inner_size(winit::dpi::LogicalSize {\n                width: 800.0,\n                height: 600.0,\n            })\n            .with_title(\"egui_glow example\") // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279\n            .with_visible(false);\n\n        let config_template_builder = glutin::config::ConfigTemplateBuilder::new()\n            .prefer_hardware_accelerated(None)\n            .with_depth_size(0)\n            .with_stencil_size(0)\n            .with_transparency(false);\n\n        log::debug!(\"trying to get gl_config\");\n        let (mut window, gl_config) =\n            glutin_winit::DisplayBuilder::new() // let glutin-winit helper crate handle the complex parts of opengl context creation\n                .with_preference(glutin_winit::ApiPreference::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150\n                .with_window_attributes(Some(winit_window_builder.clone()))\n                .build(\n                    event_loop,\n                    config_template_builder,\n                    |mut config_iterator| {\n                        config_iterator.next().expect(\n                            \"failed to find a matching configuration for creating glutin config\",\n                        )\n                    },\n                )\n                .expect(\"failed to create gl_config\");\n        let gl_display = gl_config.display();\n        log::debug!(\"found gl_config: {:?}\", &gl_config);\n\n        let raw_window_handle = window.as_ref().map(|w| {\n            w.window_handle()\n                .expect(\"failed to get window handle\")\n                .as_raw()\n        });\n        log::debug!(\"raw window handle: {raw_window_handle:?}\");\n        let context_attributes =\n            glutin::context::ContextAttributesBuilder::new().build(raw_window_handle);\n        // by default, glutin will try to create a core opengl context. but, if it is not available, try to create a gl-es context using this fallback attributes\n        let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new()\n            .with_context_api(glutin::context::ContextApi::Gles(None))\n            .build(raw_window_handle);\n        let not_current_gl_context = unsafe {\n            gl_display\n                    .create_context(&gl_config, &context_attributes)\n                    .unwrap_or_else(|_| {\n                        log::debug!(\"failed to create gl_context with attributes: {:?}. retrying with fallback context attributes: {:?}\",\n                            &context_attributes,\n                            &fallback_context_attributes);\n                        gl_config\n                            .display()\n                            .create_context(&gl_config, &fallback_context_attributes)\n                            .expect(\"failed to create context even with fallback attributes\")\n                    })\n        };\n\n        // this is where the window is created, if it has not been created while searching for suitable gl_config\n        let window = window.take().unwrap_or_else(|| {\n            log::debug!(\"window doesn't exist yet. creating one now with finalize_window\");\n            glutin_winit::finalize_window(event_loop, winit_window_builder.clone(), &gl_config)\n                .expect(\"failed to finalize glutin window\")\n        });\n        let (width, height): (u32, u32) = window.inner_size().into();\n        let width = NonZeroU32::new(width).unwrap_or(NonZeroU32::MIN);\n        let height = NonZeroU32::new(height).unwrap_or(NonZeroU32::MIN);\n        let surface_attributes =\n            glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new()\n                .build(\n                    window\n                        .window_handle()\n                        .expect(\"failed to get window handle\")\n                        .as_raw(),\n                    width,\n                    height,\n                );\n        log::debug!(\n            \"creating surface with attributes: {:?}\",\n            &surface_attributes\n        );\n        let gl_surface = unsafe {\n            gl_display\n                .create_window_surface(&gl_config, &surface_attributes)\n                .unwrap()\n        };\n        log::debug!(\"surface created successfully: {gl_surface:?}.making context current\");\n        let gl_context = not_current_gl_context.make_current(&gl_surface).unwrap();\n\n        gl_surface\n            .set_swap_interval(\n                &gl_context,\n                glutin::surface::SwapInterval::Wait(NonZeroU32::MIN),\n            )\n            .unwrap();\n\n        Self {\n            window,\n            gl_context,\n            gl_display,\n            gl_surface,\n        }\n    }\n\n    fn window(&self) -> &winit::window::Window {\n        &self.window\n    }\n\n    fn resize(&self, physical_size: winit::dpi::PhysicalSize<u32>) {\n        use glutin::surface::GlSurface as _;\n        self.gl_surface.resize(\n            &self.gl_context,\n            physical_size.width.try_into().unwrap(),\n            physical_size.height.try_into().unwrap(),\n        );\n    }\n\n    fn swap_buffers(&self) -> glutin::error::Result<()> {\n        use glutin::surface::GlSurface as _;\n        self.gl_surface.swap_buffers(&self.gl_context)\n    }\n\n    fn get_proc_address(&self, addr: &std::ffi::CStr) -> *const std::ffi::c_void {\n        use glutin::display::GlDisplay as _;\n        self.gl_display.get_proc_address(addr)\n    }\n}\n\n#[derive(Debug)]\npub enum UserEvent {\n    Redraw(std::time::Duration),\n}\n\nstruct GlowApp {\n    proxy: winit::event_loop::EventLoopProxy<UserEvent>,\n    gl_window: Option<GlutinWindowContext>,\n    gl: Option<Arc<glow::Context>>,\n    egui_glow: Option<egui_glow::EguiGlow>,\n    repaint_delay: std::time::Duration,\n    clear_color: [f32; 3],\n}\n\nimpl GlowApp {\n    fn new(proxy: winit::event_loop::EventLoopProxy<UserEvent>) -> Self {\n        Self {\n            proxy,\n            gl_window: None,\n            gl: None,\n            egui_glow: None,\n            repaint_delay: std::time::Duration::MAX,\n            clear_color: [0.1, 0.1, 0.1],\n        }\n    }\n}\n\nimpl winit::application::ApplicationHandler<UserEvent> for GlowApp {\n    fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {\n        let (gl_window, gl) = create_display(event_loop);\n        let gl = std::sync::Arc::new(gl);\n        gl_window.window().set_visible(true);\n\n        let egui_glow = egui_glow::EguiGlow::new(event_loop, Arc::clone(&gl), None, None, true);\n\n        let event_loop_proxy = egui::mutex::Mutex::new(self.proxy.clone());\n        egui_glow\n            .egui_ctx\n            .set_request_repaint_callback(move |info| {\n                event_loop_proxy\n                    .lock()\n                    .send_event(UserEvent::Redraw(info.delay))\n                    .expect(\"Cannot send event\");\n            });\n        self.gl_window = Some(gl_window);\n        self.gl = Some(gl);\n        self.egui_glow = Some(egui_glow);\n    }\n\n    fn window_event(\n        &mut self,\n        event_loop: &winit::event_loop::ActiveEventLoop,\n        _window_id: winit::window::WindowId,\n        event: winit::event::WindowEvent,\n    ) {\n        let mut redraw = || {\n            let mut quit = false;\n\n            self.egui_glow\n                .as_mut()\n                .unwrap()\n                .run(self.gl_window.as_mut().unwrap().window(), |ui| {\n                    egui::Panel::left(\"my_side_panel\").show_inside(ui, |ui| {\n                        ui.heading(\"Hello World!\");\n                        if ui.button(\"Quit\").clicked() {\n                            quit = true;\n                        }\n\n                        ui.color_edit_button_rgb(self.clear_color.as_mut().try_into().unwrap());\n                    });\n                });\n\n            if quit {\n                event_loop.exit();\n            } else {\n                event_loop.set_control_flow(if self.repaint_delay.is_zero() {\n                    self.gl_window.as_mut().unwrap().window().request_redraw();\n                    winit::event_loop::ControlFlow::Poll\n                } else if let Some(repaint_after_instant) =\n                    std::time::Instant::now().checked_add(self.repaint_delay)\n                {\n                    winit::event_loop::ControlFlow::WaitUntil(repaint_after_instant)\n                } else {\n                    winit::event_loop::ControlFlow::Wait\n                });\n            }\n\n            {\n                unsafe {\n                    use glow::HasContext as _;\n                    self.gl.as_mut().unwrap().clear_color(\n                        self.clear_color[0],\n                        self.clear_color[1],\n                        self.clear_color[2],\n                        1.0,\n                    );\n                    self.gl.as_mut().unwrap().clear(glow::COLOR_BUFFER_BIT);\n                }\n\n                // draw things behind egui here\n\n                self.egui_glow\n                    .as_mut()\n                    .unwrap()\n                    .paint(self.gl_window.as_mut().unwrap().window());\n\n                // draw things on top of egui here\n\n                self.gl_window.as_mut().unwrap().swap_buffers().unwrap();\n                self.gl_window.as_mut().unwrap().window().set_visible(true);\n            }\n        };\n\n        use winit::event::WindowEvent;\n        if matches!(event, WindowEvent::CloseRequested | WindowEvent::Destroyed) {\n            event_loop.exit();\n            return;\n        }\n\n        if matches!(event, WindowEvent::RedrawRequested) {\n            redraw();\n            return;\n        }\n\n        if let winit::event::WindowEvent::Resized(physical_size) = &event {\n            self.gl_window.as_mut().unwrap().resize(*physical_size);\n        }\n\n        let event_response = self\n            .egui_glow\n            .as_mut()\n            .unwrap()\n            .on_window_event(self.gl_window.as_mut().unwrap().window(), &event);\n\n        if event_response.repaint {\n            self.gl_window.as_mut().unwrap().window().request_redraw();\n        }\n    }\n\n    fn user_event(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop, event: UserEvent) {\n        match event {\n            UserEvent::Redraw(delay) => self.repaint_delay = delay,\n        }\n    }\n\n    fn new_events(\n        &mut self,\n        _event_loop: &winit::event_loop::ActiveEventLoop,\n        cause: winit::event::StartCause,\n    ) {\n        if let winit::event::StartCause::ResumeTimeReached { .. } = &cause {\n            self.gl_window.as_mut().unwrap().window().request_redraw();\n        }\n    }\n\n    fn exiting(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {\n        self.egui_glow.as_mut().unwrap().destroy();\n    }\n}\n\nfn main() {\n    let event_loop = winit::event_loop::EventLoop::<UserEvent>::with_user_event()\n        .build()\n        .unwrap();\n    let proxy = event_loop.create_proxy();\n\n    let mut app = GlowApp::new(proxy);\n    event_loop.run_app(&mut app).expect(\"failed to run app\");\n}\n\nfn create_display(\n    event_loop: &winit::event_loop::ActiveEventLoop,\n) -> (GlutinWindowContext, glow::Context) {\n    let glutin_window_context = unsafe { GlutinWindowContext::new(event_loop) };\n    let gl = unsafe {\n        glow::Context::from_loader_function(|s| {\n            let s = std::ffi::CString::new(s)\n                .expect(\"failed to construct C string from string for gl proc address\");\n\n            glutin_window_context.get_proc_address(&s)\n        })\n    };\n\n    (glutin_window_context, gl)\n}\n"
  },
  {
    "path": "crates/egui_glow/src/lib.rs",
    "content": "//! [`egui`] bindings for [`glow`](https://github.com/grovesNL/glow).\n//!\n//! The main type you want to look at is [`Painter`].\n//!\n//! If you are writing an app, you may want to look at [`eframe`](https://docs.rs/eframe) instead.\n//!\n//! ## Feature flags\n#![cfg_attr(feature = \"document-features\", doc = document_features::document_features!())]\n//!\n\n#![expect(clippy::undocumented_unsafe_blocks)]\n\npub mod painter;\npub use glow;\npub use painter::{CallbackFn, Painter, PainterError};\nmod misc_util;\nmod shader_version;\nmod vao;\n\npub use shader_version::ShaderVersion;\n\n#[cfg(feature = \"winit\")]\npub mod winit;\n#[cfg(feature = \"winit\")]\npub use winit::*;\n\n/// Check for OpenGL error and report it using `log::error`.\n///\n/// Only active in debug builds!\n///\n/// ``` no_run\n/// # let glow_context = todo!();\n/// use egui_glow::check_for_gl_error;\n/// check_for_gl_error!(glow_context);\n/// check_for_gl_error!(glow_context, \"during painting\");\n/// ```\n#[macro_export]\nmacro_rules! check_for_gl_error {\n    ($gl: expr) => {{\n        if cfg!(debug_assertions) {\n            $crate::check_for_gl_error_impl($gl, file!(), line!(), \"\")\n        }\n    }};\n    ($gl: expr, $context: literal) => {{\n        if cfg!(debug_assertions) {\n            $crate::check_for_gl_error_impl($gl, file!(), line!(), $context)\n        }\n    }};\n}\n\n/// Check for OpenGL error and report it using `log::error`.\n///\n/// WARNING: slow! Only use during setup!\n///\n/// ``` no_run\n/// # let glow_context = todo!();\n/// use egui_glow::check_for_gl_error_even_in_release;\n/// check_for_gl_error_even_in_release!(glow_context);\n/// check_for_gl_error_even_in_release!(glow_context, \"during painting\");\n/// ```\n#[macro_export]\nmacro_rules! check_for_gl_error_even_in_release {\n    ($gl: expr) => {{ $crate::check_for_gl_error_impl($gl, file!(), line!(), \"\") }};\n    ($gl: expr, $context: literal) => {{ $crate::check_for_gl_error_impl($gl, file!(), line!(), $context) }};\n}\n\n#[doc(hidden)]\npub fn check_for_gl_error_impl(gl: &glow::Context, file: &str, line: u32, context: &str) {\n    use glow::HasContext as _;\n    #[expect(unsafe_code)]\n    let error_code = unsafe { gl.get_error() };\n    if error_code != glow::NO_ERROR {\n        let error_str = match error_code {\n            glow::INVALID_ENUM => \"GL_INVALID_ENUM\",\n            glow::INVALID_VALUE => \"GL_INVALID_VALUE\",\n            glow::INVALID_OPERATION => \"GL_INVALID_OPERATION\",\n            glow::STACK_OVERFLOW => \"GL_STACK_OVERFLOW\",\n            glow::STACK_UNDERFLOW => \"GL_STACK_UNDERFLOW\",\n            glow::OUT_OF_MEMORY => \"GL_OUT_OF_MEMORY\",\n            glow::INVALID_FRAMEBUFFER_OPERATION => \"GL_INVALID_FRAMEBUFFER_OPERATION\",\n            glow::CONTEXT_LOST => \"GL_CONTEXT_LOST\",\n            0x8031 => \"GL_TABLE_TOO_LARGE1\",\n            0x9242 => \"CONTEXT_LOST_WEBGL\",\n            _ => \"<unknown>\",\n        };\n\n        if context.is_empty() {\n            log::error!(\n                \"GL error, at {file}:{line}: {error_str} (0x{error_code:X}). Please file a bug at https://github.com/emilk/egui/issues\"\n            );\n        } else {\n            log::error!(\n                \"GL error, at {file}:{line} ({context}): {error_str} (0x{error_code:X}). Please file a bug at https://github.com/emilk/egui/issues\"\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_glow/src/misc_util.rs",
    "content": "#![expect(unsafe_code)]\n\nuse glow::HasContext as _;\n\npub(crate) unsafe fn compile_shader(\n    gl: &glow::Context,\n    shader_type: u32,\n    source: &str,\n) -> Result<glow::Shader, String> {\n    unsafe {\n        let shader = gl.create_shader(shader_type)?;\n\n        gl.shader_source(shader, source);\n\n        gl.compile_shader(shader);\n\n        if gl.get_shader_compile_status(shader) {\n            Ok(shader)\n        } else {\n            Err(gl.get_shader_info_log(shader))\n        }\n    }\n}\n\npub(crate) unsafe fn link_program<'a, T: IntoIterator<Item = &'a glow::Shader>>(\n    gl: &glow::Context,\n    shaders: T,\n) -> Result<glow::Program, String> {\n    unsafe {\n        let program = gl.create_program()?;\n\n        for shader in shaders {\n            gl.attach_shader(program, *shader);\n        }\n\n        gl.link_program(program);\n\n        if gl.get_program_link_status(program) {\n            Ok(program)\n        } else {\n            Err(gl.get_program_info_log(program))\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_glow/src/painter.rs",
    "content": "#![expect(clippy::unwrap_used)]\n#![expect(unsafe_code)]\n\nuse std::{collections::HashMap, sync::Arc};\n\nuse egui::{\n    emath::Rect,\n    epaint::{Mesh, PaintCallbackInfo, Primitive, Vertex},\n};\nuse glow::HasContext as _;\nuse memoffset::offset_of;\n\nuse crate::check_for_gl_error;\nuse crate::misc_util::{compile_shader, link_program};\nuse crate::shader_version::ShaderVersion;\nuse crate::vao;\n\n/// Re-exported [`glow::Context`].\npub use glow::Context;\n\nconst VERT_SRC: &str = include_str!(\"shader/vertex.glsl\");\nconst FRAG_SRC: &str = include_str!(\"shader/fragment.glsl\");\n\ntrait TextureFilterExt {\n    fn glow_code(&self, mipmap: Option<egui::TextureFilter>) -> u32;\n}\n\nimpl TextureFilterExt for egui::TextureFilter {\n    fn glow_code(&self, mipmap: Option<egui::TextureFilter>) -> u32 {\n        match (self, mipmap) {\n            (Self::Linear, None) => glow::LINEAR,\n            (Self::Nearest, None) => glow::NEAREST,\n            (Self::Linear, Some(Self::Linear)) => glow::LINEAR_MIPMAP_LINEAR,\n            (Self::Nearest, Some(Self::Linear)) => glow::NEAREST_MIPMAP_LINEAR,\n            (Self::Linear, Some(Self::Nearest)) => glow::LINEAR_MIPMAP_NEAREST,\n            (Self::Nearest, Some(Self::Nearest)) => glow::NEAREST_MIPMAP_NEAREST,\n        }\n    }\n}\n\ntrait TextureWrapModeExt {\n    fn glow_code(&self) -> u32;\n}\n\nimpl TextureWrapModeExt for egui::TextureWrapMode {\n    fn glow_code(&self) -> u32 {\n        match self {\n            Self::ClampToEdge => glow::CLAMP_TO_EDGE,\n            Self::Repeat => glow::REPEAT,\n            Self::MirroredRepeat => glow::MIRRORED_REPEAT,\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct PainterError(String);\n\nimpl std::error::Error for PainterError {}\n\nimpl std::fmt::Display for PainterError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"OpenGL: {}\", self.0)\n    }\n}\n\nimpl From<String> for PainterError {\n    #[inline]\n    fn from(value: String) -> Self {\n        Self(value)\n    }\n}\n\n/// An OpenGL painter using [`glow`].\n///\n/// This is responsible for painting egui and managing egui textures.\n/// You can access the underlying [`glow::Context`] with [`Self::gl`].\n///\n/// This struct must be destroyed with [`Painter::destroy`] before dropping, to ensure OpenGL\n/// objects have been properly deleted and are not leaked.\n///\n/// NOTE: all egui viewports share the same painter.\npub struct Painter {\n    gl: Arc<glow::Context>,\n\n    max_texture_side: usize,\n\n    program: glow::Program,\n    u_screen_size: glow::UniformLocation,\n    u_sampler: glow::UniformLocation,\n    is_webgl_1: bool,\n    vao: crate::vao::VertexArrayObject,\n    srgb_textures: bool,\n    supports_srgb_framebuffer: bool,\n    vbo: glow::Buffer,\n    element_array_buffer: glow::Buffer,\n\n    textures: HashMap<egui::TextureId, glow::Texture>,\n\n    next_native_tex_id: u64,\n\n    /// Stores outdated OpenGL textures that are yet to be deleted\n    textures_to_destroy: Vec<glow::Texture>,\n\n    /// Used to make sure we are destroyed correctly.\n    destroyed: bool,\n}\n\n/// A callback function that can be used to compose an [`egui::PaintCallback`] for custom rendering\n/// with [`glow`].\n///\n/// The callback is passed, the [`egui::PaintCallbackInfo`] and the [`Painter`] which can be used to\n/// access the OpenGL context.\n///\n/// # Example\n///\n/// See the [`custom3d_glow`](https://github.com/emilk/egui/blob/main/crates/egui_demo_app/src/apps/custom3d_wgpu.rs) demo source for a detailed usage example.\npub struct CallbackFn {\n    f: Box<dyn Fn(PaintCallbackInfo, &Painter) + Sync + Send>,\n}\n\nimpl CallbackFn {\n    pub fn new<F: Fn(PaintCallbackInfo, &Painter) + Sync + Send + 'static>(callback: F) -> Self {\n        let f = Box::new(callback);\n        Self { f }\n    }\n}\n\nimpl Painter {\n    /// Create painter.\n    ///\n    /// Set `pp_fb_extent` to the framebuffer size to enable `sRGB` support on OpenGL ES and WebGL.\n    ///\n    /// Set `shader_prefix` if you want to turn on shader workaround e.g. `\"#define APPLY_BRIGHTENING_GAMMA\\n\"`\n    /// (see <https://github.com/emilk/egui/issues/794>).\n    ///\n    /// # Errors\n    /// will return `Err` below cases\n    /// * failed to compile shader\n    /// * failed to create postprocess on webgl with `sRGB` support\n    /// * failed to create buffer\n    pub fn new(\n        gl: Arc<glow::Context>,\n        shader_prefix: &str,\n        shader_version: Option<ShaderVersion>,\n        dithering: bool,\n    ) -> Result<Self, PainterError> {\n        profiling::function_scope!();\n        crate::check_for_gl_error_even_in_release!(&gl, \"before Painter::new\");\n\n        // some useful debug info. all three of them are present in gl 1.1.\n        unsafe {\n            let version = gl.get_parameter_string(glow::VERSION);\n            let renderer = gl.get_parameter_string(glow::RENDERER);\n            let vendor = gl.get_parameter_string(glow::VENDOR);\n            log::debug!(\n                \"\\nopengl version: {version}\\nopengl renderer: {renderer}\\nopengl vendor: {vendor}\"\n            );\n        }\n\n        #[cfg(not(target_arch = \"wasm32\"))]\n        if gl.version().major < 2 {\n            // this checks on desktop that we are not using opengl 1.1 microsoft sw rendering context.\n            // ShaderVersion::get fn will segfault due to SHADING_LANGUAGE_VERSION (added in gl2.0)\n            return Err(PainterError(\"egui_glow requires opengl 2.0+. \".to_owned()));\n        }\n\n        let max_texture_side = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as usize;\n        let shader_version = shader_version.unwrap_or_else(|| ShaderVersion::get(&gl));\n        let is_webgl_1 = shader_version == ShaderVersion::Es100;\n        let shader_version_declaration = shader_version.version_declaration();\n        log::debug!(\"Shader header: {shader_version_declaration:?}.\");\n\n        let supported_extensions = gl.supported_extensions();\n        log::trace!(\"OpenGL extensions: {supported_extensions:?}\");\n        let srgb_textures = false; // egui wants normal sRGB-unaware textures\n\n        let supports_srgb_framebuffer = !cfg!(target_arch = \"wasm32\")\n            && supported_extensions.iter().any(|extension| {\n                // {GL,GLX,WGL}_ARB_framebuffer_sRGB, …\n                extension.ends_with(\"ARB_framebuffer_sRGB\")\n            });\n        log::debug!(\"SRGB framebuffer Support: {supports_srgb_framebuffer}\");\n\n        unsafe {\n            let vert = compile_shader(\n                &gl,\n                glow::VERTEX_SHADER,\n                &format!(\n                    \"{}\\n#define NEW_SHADER_INTERFACE {}\\n{}\\n{}\",\n                    shader_version_declaration,\n                    shader_version.is_new_shader_interface() as i32,\n                    shader_prefix,\n                    VERT_SRC\n                ),\n            )?;\n            let frag = compile_shader(\n                &gl,\n                glow::FRAGMENT_SHADER,\n                &format!(\n                    \"{}\\n#define NEW_SHADER_INTERFACE {}\\n#define DITHERING {}\\n{}\\n{}\",\n                    shader_version_declaration,\n                    shader_version.is_new_shader_interface() as i32,\n                    dithering as i32,\n                    shader_prefix,\n                    FRAG_SRC\n                ),\n            )?;\n            let program = link_program(&gl, [vert, frag].iter())?;\n            gl.detach_shader(program, vert);\n            gl.detach_shader(program, frag);\n            gl.delete_shader(vert);\n            gl.delete_shader(frag);\n            let u_screen_size = gl.get_uniform_location(program, \"u_screen_size\").unwrap();\n            let u_sampler = gl.get_uniform_location(program, \"u_sampler\").unwrap();\n\n            let vbo = gl.create_buffer()?;\n\n            let a_pos_loc = gl.get_attrib_location(program, \"a_pos\").unwrap();\n            let a_tc_loc = gl.get_attrib_location(program, \"a_tc\").unwrap();\n            let a_srgba_loc = gl.get_attrib_location(program, \"a_srgba\").unwrap();\n\n            let stride = std::mem::size_of::<Vertex>() as i32;\n            let buffer_infos = vec![\n                vao::BufferInfo {\n                    location: a_pos_loc,\n                    vector_size: 2,\n                    data_type: glow::FLOAT,\n                    normalized: false,\n                    stride,\n                    offset: offset_of!(Vertex, pos) as i32,\n                },\n                vao::BufferInfo {\n                    location: a_tc_loc,\n                    vector_size: 2,\n                    data_type: glow::FLOAT,\n                    normalized: false,\n                    stride,\n                    offset: offset_of!(Vertex, uv) as i32,\n                },\n                vao::BufferInfo {\n                    location: a_srgba_loc,\n                    vector_size: 4,\n                    data_type: glow::UNSIGNED_BYTE,\n                    normalized: false,\n                    stride,\n                    offset: offset_of!(Vertex, color) as i32,\n                },\n            ];\n            let vao = crate::vao::VertexArrayObject::new(&gl, vbo, buffer_infos);\n\n            let element_array_buffer = gl.create_buffer()?;\n\n            crate::check_for_gl_error_even_in_release!(&gl, \"after Painter::new\");\n\n            Ok(Self {\n                gl,\n                max_texture_side,\n                program,\n                u_screen_size,\n                u_sampler,\n                is_webgl_1,\n                vao,\n                srgb_textures,\n                supports_srgb_framebuffer,\n                vbo,\n                element_array_buffer,\n                textures: Default::default(),\n                next_native_tex_id: 1 << 32,\n                textures_to_destroy: Vec::new(),\n                destroyed: false,\n            })\n        }\n    }\n\n    /// Access the shared glow context.\n    pub fn gl(&self) -> &Arc<glow::Context> {\n        &self.gl\n    }\n\n    pub fn max_texture_side(&self) -> usize {\n        self.max_texture_side\n    }\n\n    /// The framebuffer we use as an intermediate render target,\n    /// or `None` if we are painting to the screen framebuffer directly.\n    ///\n    /// This is the framebuffer that is bound when [`egui::Shape::Callback`] is called,\n    /// and is where any callbacks should ultimately render onto.\n    ///\n    /// So if in a [`egui::Shape::Callback`] you need to use an offscreen FBO, you should\n    /// then restore to this afterwards with\n    /// `gl.bind_framebuffer(glow::FRAMEBUFFER, painter.intermediate_fbo());`\n    #[expect(clippy::unused_self)]\n    pub fn intermediate_fbo(&self) -> Option<glow::Framebuffer> {\n        // We don't currently ever render to an offscreen buffer,\n        // but we may want to start to in order to do anti-aliasing on web, for instance.\n        None\n    }\n\n    unsafe fn prepare_painting(\n        &mut self,\n        [width_in_pixels, height_in_pixels]: [u32; 2],\n        pixels_per_point: f32,\n    ) {\n        unsafe {\n            self.gl.enable(glow::SCISSOR_TEST);\n            // egui outputs mesh in both winding orders\n            self.gl.disable(glow::CULL_FACE);\n            self.gl.disable(glow::DEPTH_TEST);\n\n            self.gl.color_mask(true, true, true, true);\n\n            self.gl.enable(glow::BLEND);\n            self.gl\n                .blend_equation_separate(glow::FUNC_ADD, glow::FUNC_ADD);\n            self.gl.blend_func_separate(\n                // egui outputs colors with premultiplied alpha:\n                glow::ONE,\n                glow::ONE_MINUS_SRC_ALPHA,\n                // Less important, but this is technically the correct alpha blend function\n                // when you want to make use of the framebuffer alpha (for screenshots, compositing, etc).\n                glow::ONE_MINUS_DST_ALPHA,\n                glow::ONE,\n            );\n\n            if self.supports_srgb_framebuffer {\n                self.gl.disable(glow::FRAMEBUFFER_SRGB);\n                check_for_gl_error!(&self.gl, \"FRAMEBUFFER_SRGB\");\n            }\n\n            let width_in_points = width_in_pixels as f32 / pixels_per_point;\n            let height_in_points = height_in_pixels as f32 / pixels_per_point;\n\n            self.gl\n                .viewport(0, 0, width_in_pixels as i32, height_in_pixels as i32);\n            self.gl.use_program(Some(self.program));\n\n            self.gl\n                .uniform_2_f32(Some(&self.u_screen_size), width_in_points, height_in_points);\n            self.gl.uniform_1_i32(Some(&self.u_sampler), 0);\n            self.gl.active_texture(glow::TEXTURE0);\n\n            self.vao.bind(&self.gl);\n            self.gl\n                .bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.element_array_buffer));\n        }\n\n        check_for_gl_error!(&self.gl, \"prepare_painting\");\n    }\n\n    pub fn clear(&self, screen_size_in_pixels: [u32; 2], clear_color: [f32; 4]) {\n        clear(&self.gl, screen_size_in_pixels, clear_color);\n    }\n\n    /// You are expected to have cleared the color buffer before calling this.\n    pub fn paint_and_update_textures(\n        &mut self,\n        screen_size_px: [u32; 2],\n        pixels_per_point: f32,\n        clipped_primitives: &[egui::ClippedPrimitive],\n        textures_delta: &egui::TexturesDelta,\n    ) {\n        profiling::function_scope!();\n\n        for (id, image_delta) in &textures_delta.set {\n            self.set_texture(*id, image_delta);\n        }\n\n        self.paint_primitives(screen_size_px, pixels_per_point, clipped_primitives);\n\n        for &id in &textures_delta.free {\n            self.free_texture(id);\n        }\n    }\n\n    /// Main entry-point for painting a frame.\n    ///\n    /// You should call `target.clear_color(..)` before\n    /// and `target.finish()` after this.\n    ///\n    /// The following OpenGL features will be set:\n    /// - Scissor test will be enabled\n    /// - Cull face will be disabled\n    /// - Blend will be enabled\n    ///\n    /// The scissor area and blend parameters will be changed.\n    ///\n    /// As well as this, the following objects will be unset:\n    /// - Vertex Buffer\n    /// - Element Buffer\n    /// - Texture (and active texture will be set to 0)\n    /// - Program\n    ///\n    /// Please be mindful of these effects when integrating into your program, and also be mindful\n    /// of the effects your program might have on this code. Look at the source if in doubt.\n    pub fn paint_primitives(\n        &mut self,\n        screen_size_px: [u32; 2],\n        pixels_per_point: f32,\n        clipped_primitives: &[egui::ClippedPrimitive],\n    ) {\n        profiling::function_scope!();\n        self.assert_not_destroyed();\n\n        unsafe { self.prepare_painting(screen_size_px, pixels_per_point) };\n\n        for egui::ClippedPrimitive {\n            clip_rect,\n            primitive,\n        } in clipped_primitives\n        {\n            set_clip_rect(&self.gl, screen_size_px, pixels_per_point, *clip_rect);\n\n            match primitive {\n                Primitive::Mesh(mesh) => {\n                    self.paint_mesh(mesh);\n                }\n                Primitive::Callback(callback) => {\n                    if callback.rect.is_positive() {\n                        profiling::scope!(\"callback\");\n\n                        let info = egui::PaintCallbackInfo {\n                            viewport: callback.rect,\n                            clip_rect: *clip_rect,\n                            pixels_per_point,\n                            screen_size_px,\n                        };\n\n                        let viewport_px = info.viewport_in_pixels();\n                        unsafe {\n                            self.gl.viewport(\n                                viewport_px.left_px,\n                                viewport_px.from_bottom_px,\n                                viewport_px.width_px,\n                                viewport_px.height_px,\n                            );\n                        }\n\n                        if let Some(callback) = callback.callback.downcast_ref::<CallbackFn>() {\n                            (callback.f)(info, self);\n                        } else {\n                            log::warn!(\n                                \"Warning: Unsupported render callback. Expected egui_glow::CallbackFn\"\n                            );\n                        }\n\n                        check_for_gl_error!(&self.gl, \"callback\");\n\n                        // Restore state:\n                        unsafe { self.prepare_painting(screen_size_px, pixels_per_point) };\n                    }\n                }\n            }\n        }\n\n        unsafe {\n            self.vao.unbind(&self.gl);\n            self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);\n\n            self.gl.disable(glow::SCISSOR_TEST);\n\n            check_for_gl_error!(&self.gl, \"painting\");\n        }\n    }\n\n    #[inline(never)] // Easier profiling\n    fn paint_mesh(&mut self, mesh: &Mesh) {\n        debug_assert!(mesh.is_valid(), \"Mesh is not valid\");\n        if let Some(texture) = self.texture(mesh.texture_id) {\n            unsafe {\n                self.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vbo));\n                self.gl.buffer_data_u8_slice(\n                    glow::ARRAY_BUFFER,\n                    bytemuck::cast_slice(&mesh.vertices),\n                    glow::STREAM_DRAW,\n                );\n\n                self.gl\n                    .bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.element_array_buffer));\n                self.gl.buffer_data_u8_slice(\n                    glow::ELEMENT_ARRAY_BUFFER,\n                    bytemuck::cast_slice(&mesh.indices),\n                    glow::STREAM_DRAW,\n                );\n\n                self.gl.bind_texture(glow::TEXTURE_2D, Some(texture));\n            }\n\n            unsafe {\n                self.gl.draw_elements(\n                    glow::TRIANGLES,\n                    mesh.indices.len() as i32,\n                    glow::UNSIGNED_INT,\n                    0,\n                );\n            }\n\n            check_for_gl_error!(&self.gl, \"paint_mesh\");\n        } else {\n            log::warn!(\"Failed to find texture {:?}\", mesh.texture_id);\n        }\n    }\n\n    // ------------------------------------------------------------------------\n\n    pub fn set_texture(&mut self, tex_id: egui::TextureId, delta: &egui::epaint::ImageDelta) {\n        profiling::function_scope!();\n\n        self.assert_not_destroyed();\n\n        let glow_texture = *self\n            .textures\n            .entry(tex_id)\n            .or_insert_with(|| unsafe { self.gl.create_texture().unwrap() });\n        unsafe {\n            self.gl.bind_texture(glow::TEXTURE_2D, Some(glow_texture));\n        }\n\n        match &delta.image {\n            egui::ImageData::Color(image) => {\n                assert_eq!(\n                    image.width() * image.height(),\n                    image.pixels.len(),\n                    \"Mismatch between texture size and texel count\"\n                );\n\n                let data: &[u8] = bytemuck::cast_slice(image.pixels.as_ref());\n\n                self.upload_texture_srgb(delta.pos, image.size, delta.options, data);\n            }\n        }\n    }\n\n    fn upload_texture_srgb(\n        &mut self,\n        pos: Option<[usize; 2]>,\n        [w, h]: [usize; 2],\n        options: egui::TextureOptions,\n        data: &[u8],\n    ) {\n        profiling::function_scope!();\n        assert_eq!(\n            data.len(),\n            w * h * 4,\n            \"Mismatch between texture size and texel count, by {}\",\n            data.len() % (w * h * 4)\n        );\n        assert!(\n            w <= self.max_texture_side && h <= self.max_texture_side,\n            \"Got a texture image of size {}x{}, but the maximum supported texture side is only {}\",\n            w,\n            h,\n            self.max_texture_side\n        );\n\n        unsafe {\n            self.gl.tex_parameter_i32(\n                glow::TEXTURE_2D,\n                glow::TEXTURE_MAG_FILTER,\n                options.magnification.glow_code(None) as i32,\n            );\n            self.gl.tex_parameter_i32(\n                glow::TEXTURE_2D,\n                glow::TEXTURE_MIN_FILTER,\n                options.minification.glow_code(options.mipmap_mode) as i32,\n            );\n\n            self.gl.tex_parameter_i32(\n                glow::TEXTURE_2D,\n                glow::TEXTURE_WRAP_S,\n                options.wrap_mode.glow_code() as i32,\n            );\n            self.gl.tex_parameter_i32(\n                glow::TEXTURE_2D,\n                glow::TEXTURE_WRAP_T,\n                options.wrap_mode.glow_code() as i32,\n            );\n            check_for_gl_error!(&self.gl, \"tex_parameter\");\n\n            let (internal_format, src_format) = if self.is_webgl_1 {\n                let format = if self.srgb_textures {\n                    glow::SRGB_ALPHA\n                } else {\n                    glow::RGBA\n                };\n                (format, format)\n            } else if self.srgb_textures {\n                (glow::SRGB8_ALPHA8, glow::RGBA)\n            } else {\n                (glow::RGBA8, glow::RGBA)\n            };\n\n            self.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);\n\n            let level = 0;\n            if let Some([x, y]) = pos {\n                profiling::scope!(\"gl.tex_sub_image_2d\");\n                self.gl.tex_sub_image_2d(\n                    glow::TEXTURE_2D,\n                    level,\n                    x as _,\n                    y as _,\n                    w as _,\n                    h as _,\n                    src_format,\n                    glow::UNSIGNED_BYTE,\n                    glow::PixelUnpackData::Slice(Some(data)),\n                );\n                check_for_gl_error!(&self.gl, \"tex_sub_image_2d\");\n            } else {\n                let border = 0;\n                profiling::scope!(\"gl.tex_image_2d\");\n                self.gl.tex_image_2d(\n                    glow::TEXTURE_2D,\n                    level,\n                    internal_format as _,\n                    w as _,\n                    h as _,\n                    border,\n                    src_format,\n                    glow::UNSIGNED_BYTE,\n                    glow::PixelUnpackData::Slice(Some(data)),\n                );\n                check_for_gl_error!(&self.gl, \"tex_image_2d\");\n            }\n\n            if options.mipmap_mode.is_some() {\n                self.gl.generate_mipmap(glow::TEXTURE_2D);\n                check_for_gl_error!(&self.gl, \"generate_mipmap\");\n            }\n        }\n    }\n\n    pub fn free_texture(&mut self, tex_id: egui::TextureId) {\n        if let Some(old_tex) = self.textures.remove(&tex_id) {\n            unsafe { self.gl.delete_texture(old_tex) };\n        }\n    }\n\n    /// Get the [`glow::Texture`] bound to a [`egui::TextureId`].\n    pub fn texture(&self, texture_id: egui::TextureId) -> Option<glow::Texture> {\n        self.textures.get(&texture_id).copied()\n    }\n\n    pub fn register_native_texture(&mut self, native: glow::Texture) -> egui::TextureId {\n        self.assert_not_destroyed();\n        let id = egui::TextureId::User(self.next_native_tex_id);\n        self.next_native_tex_id += 1;\n        self.textures.insert(id, native);\n        id\n    }\n\n    pub fn replace_native_texture(&mut self, id: egui::TextureId, replacing: glow::Texture) {\n        if let Some(old_tex) = self.textures.insert(id, replacing) {\n            self.textures_to_destroy.push(old_tex);\n        }\n    }\n\n    pub fn read_screen_rgba(&self, [w, h]: [u32; 2]) -> egui::ColorImage {\n        profiling::function_scope!();\n\n        let mut pixels = vec![0_u8; (w * h * 4) as usize];\n        unsafe {\n            self.gl.read_pixels(\n                0,\n                0,\n                w as _,\n                h as _,\n                glow::RGBA,\n                glow::UNSIGNED_BYTE,\n                glow::PixelPackData::Slice(Some(&mut pixels)),\n            );\n        }\n        let mut flipped = Vec::with_capacity((w * h * 4) as usize);\n        for row in pixels.chunks_exact((w * 4) as usize).rev() {\n            flipped.extend_from_slice(bytemuck::cast_slice(row));\n        }\n        egui::ColorImage::new([w as usize, h as usize], flipped)\n    }\n\n    pub fn read_screen_rgb(&self, [w, h]: [u32; 2]) -> Vec<u8> {\n        profiling::function_scope!();\n        let mut pixels = vec![0_u8; (w * h * 3) as usize];\n        unsafe {\n            self.gl.read_pixels(\n                0,\n                0,\n                w as _,\n                h as _,\n                glow::RGB,\n                glow::UNSIGNED_BYTE,\n                glow::PixelPackData::Slice(Some(&mut pixels)),\n            );\n        }\n        pixels\n    }\n\n    unsafe fn destroy_gl(&self) {\n        unsafe {\n            self.gl.delete_program(self.program);\n            #[expect(clippy::iter_over_hash_type)]\n            for tex in self.textures.values() {\n                self.gl.delete_texture(*tex);\n            }\n            self.gl.delete_buffer(self.vbo);\n            self.gl.delete_buffer(self.element_array_buffer);\n            for t in &self.textures_to_destroy {\n                self.gl.delete_texture(*t);\n            }\n        }\n    }\n\n    /// This function must be called before [`Painter`] is dropped, as [`Painter`] has some OpenGL objects\n    /// that should be deleted.\n    pub fn destroy(&mut self) {\n        if !self.destroyed {\n            unsafe {\n                self.destroy_gl();\n            }\n            self.destroyed = true;\n        }\n    }\n\n    fn assert_not_destroyed(&self) {\n        assert!(!self.destroyed, \"the egui glow has already been destroyed!\");\n    }\n}\n\npub fn clear(gl: &glow::Context, screen_size_in_pixels: [u32; 2], clear_color: [f32; 4]) {\n    profiling::function_scope!();\n    unsafe {\n        gl.disable(glow::SCISSOR_TEST);\n\n        gl.viewport(\n            0,\n            0,\n            screen_size_in_pixels[0] as i32,\n            screen_size_in_pixels[1] as i32,\n        );\n        gl.clear_color(\n            clear_color[0],\n            clear_color[1],\n            clear_color[2],\n            clear_color[3],\n        );\n        gl.clear(glow::COLOR_BUFFER_BIT);\n    }\n}\n\nimpl Drop for Painter {\n    fn drop(&mut self) {\n        if !self.destroyed {\n            log::warn!(\n                \"You forgot to call destroy() on the egui glow painter. Resources will leak!\"\n            );\n        }\n    }\n}\n\nfn set_clip_rect(\n    gl: &glow::Context,\n    [width_px, height_px]: [u32; 2],\n    pixels_per_point: f32,\n    clip_rect: Rect,\n) {\n    // Transform clip rect to physical pixels:\n    let clip_min_x = pixels_per_point * clip_rect.min.x;\n    let clip_min_y = pixels_per_point * clip_rect.min.y;\n    let clip_max_x = pixels_per_point * clip_rect.max.x;\n    let clip_max_y = pixels_per_point * clip_rect.max.y;\n\n    // Round to integer:\n    let clip_min_x = clip_min_x.round() as i32;\n    let clip_min_y = clip_min_y.round() as i32;\n    let clip_max_x = clip_max_x.round() as i32;\n    let clip_max_y = clip_max_y.round() as i32;\n\n    // Clamp:\n    let clip_min_x = clip_min_x.clamp(0, width_px as i32);\n    let clip_min_y = clip_min_y.clamp(0, height_px as i32);\n    let clip_max_x = clip_max_x.clamp(clip_min_x, width_px as i32);\n    let clip_max_y = clip_max_y.clamp(clip_min_y, height_px as i32);\n\n    unsafe {\n        gl.scissor(\n            clip_min_x,\n            height_px as i32 - clip_max_y,\n            clip_max_x - clip_min_x,\n            clip_max_y - clip_min_y,\n        );\n    }\n}\n"
  },
  {
    "path": "crates/egui_glow/src/shader/fragment.glsl",
    "content": "#ifdef GL_ES\n    // To avoid weird distortion issues when rendering text etc, we want highp if possible.\n    // But apparently some devices don't support it, so we have to check first.\n    #if defined(GL_FRAGMENT_PRECISION_HIGH) && GL_FRAGMENT_PRECISION_HIGH == 1\n        precision highp float;\n    #else\n        precision mediump float;\n    #endif\n#endif\n\nuniform sampler2D u_sampler;\n\n#if NEW_SHADER_INTERFACE\n    in vec4 v_rgba_in_gamma;\n    in vec2 v_tc;\n    out vec4 f_color;\n    // a dirty hack applied to support webGL2\n    #define gl_FragColor f_color\n    #define texture2D texture\n#else\n    varying vec4 v_rgba_in_gamma;\n    varying vec2 v_tc;\n#endif\n\n// -----------------------------------------------\n// Adapted from\n// https://www.shadertoy.com/view/llVGzG\n// Originally presented in:\n// Jimenez 2014, \"Next Generation Post-Processing in Call of Duty\"\n//\n// A good overview can be found in\n// https://blog.demofox.org/2022/01/01/interleaved-gradient-noise-a-different-kind-of-low-discrepancy-sequence/\n// via https://github.com/rerun-io/rerun/\nfloat interleaved_gradient_noise(vec2 n) {\n    float f = 0.06711056 * n.x + 0.00583715 * n.y;\n    return fract(52.9829189 * fract(f));\n}\n\nvec3 dither_interleaved(vec3 rgb, float levels) {\n    float noise = interleaved_gradient_noise(gl_FragCoord.xy);\n    // scale down the noise slightly to ensure flat colors aren't getting dithered\n    noise = (noise - 0.5) * 0.95;\n    return rgb + noise / (levels - 1.0);\n}\n\nvoid main() {\n    vec4 texture_in_gamma = texture2D(u_sampler, v_tc);\n\n    // We multiply the colors in gamma space, because that's the only way to get text to look right.\n    vec4 frag_color_gamma = v_rgba_in_gamma * texture_in_gamma;\n\n    // Dither the float color down to eight bits to reduce banding.\n    // This step is optional for egui backends.\n#if DITHERING\n    frag_color_gamma.rgb = dither_interleaved(frag_color_gamma.rgb, 256.);\n#endif\n    gl_FragColor = frag_color_gamma;\n}\n"
  },
  {
    "path": "crates/egui_glow/src/shader/vertex.glsl",
    "content": "#if NEW_SHADER_INTERFACE\n    #define I in\n    #define O out\n    #define V(x) x\n#else\n    #define I attribute\n    #define O varying\n    #define V(x) vec3(x)\n#endif\n\n#ifdef GL_ES\n    // To avoid weird distortion issues when rendering text etc, we want highp if possible.\n    // But apparently some devices don't support it, so we have to check first.\n    #if defined(GL_FRAGMENT_PRECISION_HIGH) && GL_FRAGMENT_PRECISION_HIGH == 1\n        precision highp float;\n    #else\n        precision mediump float;\n    #endif\n#endif\n\nuniform vec2 u_screen_size;\nI vec2 a_pos;\nI vec4 a_srgba; // 0-255 sRGB\nI vec2 a_tc;\nO vec4 v_rgba_in_gamma;\nO vec2 v_tc;\n\nvoid main() {\n    gl_Position = vec4(\n                      2.0 * a_pos.x / u_screen_size.x - 1.0,\n                      1.0 - 2.0 * a_pos.y / u_screen_size.y,\n                      0.0,\n                      1.0);\n    v_rgba_in_gamma = a_srgba / 255.0;\n    v_tc = a_tc;\n}\n"
  },
  {
    "path": "crates/egui_glow/src/shader_version.rs",
    "content": "#![expect(clippy::undocumented_unsafe_blocks)]\n#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps\n#![expect(unsafe_code)]\n\nuse std::convert::TryInto as _;\n\n/// Helper for parsing and interpreting the OpenGL shader version.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum ShaderVersion {\n    Gl120,\n\n    /// OpenGL 1.4 or later\n    Gl140,\n\n    /// e.g. WebGL1\n    Es100,\n\n    /// e.g. WebGL2\n    Es300,\n}\n\nimpl ShaderVersion {\n    pub fn get(gl: &glow::Context) -> Self {\n        use glow::HasContext as _;\n        let shading_lang_string =\n            unsafe { gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) };\n        let shader_version = Self::parse(&shading_lang_string);\n        log::debug!(\"Shader version: {shader_version:?} ({shading_lang_string:?}).\");\n        shader_version\n    }\n\n    #[inline]\n    pub(crate) fn parse(glsl_ver: &str) -> Self {\n        let start = glsl_ver.find(|c| char::is_ascii_digit(&c)).unwrap();\n        let es = glsl_ver[..start].contains(\" ES \");\n        let ver = glsl_ver[start..]\n            .split_once(' ')\n            .map_or(&glsl_ver[start..], |x| x.0);\n        let [maj, min]: [u8; 2] = ver\n            .splitn(3, '.')\n            .take(2)\n            .map(|x| x.parse().unwrap_or_default())\n            .collect::<Vec<u8>>()\n            .try_into()\n            .unwrap();\n        if es {\n            if maj >= 3 { Self::Es300 } else { Self::Es100 }\n        } else if maj > 1 || (maj == 1 && min >= 40) {\n            Self::Gl140\n        } else {\n            Self::Gl120\n        }\n    }\n\n    /// Goes on top of the shader.\n    pub fn version_declaration(&self) -> &'static str {\n        match self {\n            Self::Gl120 => \"#version 120\\n\",\n            Self::Gl140 => \"#version 140\\n\",\n            Self::Es100 => \"#version 100\\n\",\n            Self::Es300 => \"#version 300 es\\n\",\n        }\n    }\n\n    /// If true, use `in/out`. If `false`, use `varying` and `gl_FragColor`.\n    pub fn is_new_shader_interface(&self) -> bool {\n        match self {\n            Self::Gl120 | Self::Es100 => false,\n            Self::Es300 | Self::Gl140 => true,\n        }\n    }\n\n    pub fn is_embedded(&self) -> bool {\n        match self {\n            Self::Gl120 | Self::Gl140 => false,\n            Self::Es100 | Self::Es300 => true,\n        }\n    }\n}\n\n#[test]\nfn test_shader_version() {\n    use ShaderVersion::{Es100, Es300, Gl120, Gl140};\n    for (s, v) in [\n        (\"1.2 OpenGL foo bar\", Gl120),\n        (\"3.0\", Gl140),\n        (\"0.0\", Gl120),\n        (\"OpenGL ES GLSL 3.00 (WebGL2)\", Es300),\n        (\"OpenGL ES GLSL 1.00 (WebGL)\", Es100),\n        (\"OpenGL ES GLSL ES 1.00 foo bar\", Es100),\n        (\"WebGL GLSL ES 3.00 foo bar\", Es300),\n        (\"WebGL GLSL ES 3.00\", Es300),\n        (\"WebGL GLSL ES 1.0 foo bar\", Es100),\n    ] {\n        assert_eq!(ShaderVersion::parse(s), v);\n    }\n}\n"
  },
  {
    "path": "crates/egui_glow/src/vao.rs",
    "content": "#![expect(unsafe_code)]\n#![expect(clippy::unwrap_used)]\n\nuse glow::HasContext as _;\n\nuse crate::check_for_gl_error;\n\n// ----------------------------------------------------------------------------\n\n#[derive(Debug)]\npub(crate) struct BufferInfo {\n    pub location: u32, //\n    pub vector_size: i32,\n    pub data_type: u32, //GL_FLOAT,GL_UNSIGNED_BYTE\n    pub normalized: bool,\n    pub stride: i32,\n    pub offset: i32,\n}\n\n// ----------------------------------------------------------------------------\n\n/// Wrapper around either Emulated VAO or GL's VAO.\npub(crate) struct VertexArrayObject {\n    // If `None`, we emulate VAO:s.\n    vao: Option<crate::glow::VertexArray>,\n    vbo: glow::Buffer,\n    buffer_infos: Vec<BufferInfo>,\n}\n\nimpl VertexArrayObject {\n    pub(crate) unsafe fn new(\n        gl: &glow::Context,\n        vbo: glow::Buffer,\n        buffer_infos: Vec<BufferInfo>,\n    ) -> Self {\n        let vao = if supports_vao(gl) {\n            unsafe {\n                let vao = gl.create_vertex_array().unwrap();\n                check_for_gl_error!(gl, \"create_vertex_array\");\n\n                // Store state in the VAO:\n                gl.bind_vertex_array(Some(vao));\n                gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));\n\n                for attribute in &buffer_infos {\n                    gl.vertex_attrib_pointer_f32(\n                        attribute.location,\n                        attribute.vector_size,\n                        attribute.data_type,\n                        attribute.normalized,\n                        attribute.stride,\n                        attribute.offset,\n                    );\n                    check_for_gl_error!(gl, \"vertex_attrib_pointer_f32\");\n                    gl.enable_vertex_attrib_array(attribute.location);\n                    check_for_gl_error!(gl, \"enable_vertex_attrib_array\");\n                }\n\n                gl.bind_vertex_array(None);\n\n                Some(vao)\n            }\n        } else {\n            log::debug!(\"VAO not supported\");\n            None\n        };\n\n        Self {\n            vao,\n            vbo,\n            buffer_infos,\n        }\n    }\n\n    pub(crate) unsafe fn bind(&self, gl: &glow::Context) {\n        unsafe {\n            if let Some(vao) = self.vao {\n                gl.bind_vertex_array(Some(vao));\n                check_for_gl_error!(gl, \"bind_vertex_array\");\n            } else {\n                gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vbo));\n                check_for_gl_error!(gl, \"bind_buffer\");\n\n                for attribute in &self.buffer_infos {\n                    gl.vertex_attrib_pointer_f32(\n                        attribute.location,\n                        attribute.vector_size,\n                        attribute.data_type,\n                        attribute.normalized,\n                        attribute.stride,\n                        attribute.offset,\n                    );\n                    check_for_gl_error!(gl, \"vertex_attrib_pointer_f32\");\n                    gl.enable_vertex_attrib_array(attribute.location);\n                    check_for_gl_error!(gl, \"enable_vertex_attrib_array\");\n                }\n            }\n        }\n    }\n\n    pub(crate) unsafe fn unbind(&self, gl: &glow::Context) {\n        unsafe {\n            if self.vao.is_some() {\n                gl.bind_vertex_array(None);\n            } else {\n                gl.bind_buffer(glow::ARRAY_BUFFER, None);\n                for attribute in &self.buffer_infos {\n                    gl.disable_vertex_attrib_array(attribute.location);\n                }\n            }\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nfn supports_vao(gl: &glow::Context) -> bool {\n    const WEBGL_PREFIX: &str = \"WebGL \";\n    const OPENGL_ES_PREFIX: &str = \"OpenGL ES \";\n\n    let version_string = unsafe { gl.get_parameter_string(glow::VERSION) };\n    log::debug!(\"GL version: {version_string:?}.\");\n\n    // Examples:\n    // * \"WebGL 2.0 (OpenGL ES 3.0 Chromium)\"\n    // * \"WebGL 2.0\"\n\n    if let Some(pos) = version_string.rfind(WEBGL_PREFIX) {\n        let version_str = &version_string[pos + WEBGL_PREFIX.len()..];\n        if version_str.contains(\"1.0\") {\n            // need to test OES_vertex_array_object .\n            let supported_extensions = gl.supported_extensions();\n            log::debug!(\"Supported OpenGL extensions: {supported_extensions:?}\");\n            supported_extensions.contains(\"OES_vertex_array_object\")\n                || supported_extensions.contains(\"GL_OES_vertex_array_object\")\n        } else {\n            true\n        }\n    } else if version_string.contains(OPENGL_ES_PREFIX) {\n        // glow targets es2.0+ so we don't concern about OpenGL ES-CM,OpenGL ES-CL\n        if version_string.contains(\"2.0\") {\n            // need to test OES_vertex_array_object .\n            let supported_extensions = gl.supported_extensions();\n            log::debug!(\"Supported OpenGL extensions: {supported_extensions:?}\");\n            supported_extensions.contains(\"OES_vertex_array_object\")\n                || supported_extensions.contains(\"GL_OES_vertex_array_object\")\n        } else {\n            true\n        }\n    } else {\n        // from OpenGL 3 vao into core\n        if version_string.starts_with('2') {\n            // I found APPLE_vertex_array_object , GL_ATI_vertex_array_object ,ARB_vertex_array_object\n            // but APPLE's and ATI's very old extension.\n            let supported_extensions = gl.supported_extensions();\n            log::debug!(\"Supported OpenGL extensions: {supported_extensions:?}\");\n            supported_extensions.contains(\"ARB_vertex_array_object\")\n                || supported_extensions.contains(\"GL_ARB_vertex_array_object\")\n        } else {\n            true\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_glow/src/winit.rs",
    "content": "pub use egui_winit::{self, EventResponse};\n\nuse egui::{ViewportId, ViewportOutput};\nuse egui_winit::winit;\n\nuse crate::shader_version::ShaderVersion;\n\n/// Use [`egui`] from a [`glow`] app based on [`winit`].\npub struct EguiGlow {\n    pub egui_ctx: egui::Context,\n    pub egui_winit: egui_winit::State,\n    pub painter: crate::Painter,\n\n    viewport_info: egui::ViewportInfo,\n\n    // output from the last update:\n    shapes: Vec<egui::epaint::ClippedShape>,\n    pixels_per_point: f32,\n    textures_delta: egui::TexturesDelta,\n}\n\nimpl EguiGlow {\n    /// For automatic shader version detection set `shader_version` to `None`.\n    pub fn new(\n        event_loop: &winit::event_loop::ActiveEventLoop,\n        gl: std::sync::Arc<glow::Context>,\n        shader_version: Option<ShaderVersion>,\n        native_pixels_per_point: Option<f32>,\n        dithering: bool,\n    ) -> Self {\n        #[expect(clippy::unwrap_used)] // TODO(emilk): return error instead of unwrap\n        let painter = crate::Painter::new(gl, \"\", shader_version, dithering)\n            .map_err(|err| {\n                log::error!(\"error occurred in initializing painter:\\n{err}\");\n            })\n            .unwrap();\n\n        let egui_ctx = egui::Context::default();\n\n        let egui_winit = egui_winit::State::new(\n            egui_ctx.clone(),\n            ViewportId::ROOT,\n            event_loop,\n            native_pixels_per_point,\n            event_loop.system_theme(),\n            Some(painter.max_texture_side()),\n        );\n\n        Self {\n            egui_ctx,\n            egui_winit,\n            painter,\n            viewport_info: Default::default(),\n            shapes: Default::default(),\n            pixels_per_point: native_pixels_per_point.unwrap_or(1.0),\n            textures_delta: Default::default(),\n        }\n    }\n\n    pub fn on_window_event(\n        &mut self,\n        window: &winit::window::Window,\n        event: &winit::event::WindowEvent,\n    ) -> EventResponse {\n        self.egui_winit.on_window_event(window, event)\n    }\n\n    /// Call [`Self::paint`] later to paint.\n    pub fn run(&mut self, window: &winit::window::Window, run_ui: impl FnMut(&mut egui::Ui)) {\n        let raw_input = self.egui_winit.take_egui_input(window);\n\n        let egui::FullOutput {\n            platform_output,\n            textures_delta,\n            shapes,\n            pixels_per_point,\n            viewport_output,\n        } = self.egui_ctx.run_ui(raw_input, run_ui);\n\n        if viewport_output.len() > 1 {\n            log::warn!(\"Multiple viewports not yet supported by EguiGlow\");\n        }\n        for (_, ViewportOutput { commands, .. }) in viewport_output {\n            let mut actions_requested = Default::default();\n            egui_winit::process_viewport_commands(\n                &self.egui_ctx,\n                &mut self.viewport_info,\n                commands,\n                window,\n                &mut actions_requested,\n            );\n            for action in actions_requested {\n                log::warn!(\"{action:?} not yet supported by EguiGlow\");\n            }\n        }\n\n        self.egui_winit\n            .handle_platform_output(window, platform_output);\n\n        self.shapes = shapes;\n        self.pixels_per_point = pixels_per_point;\n        self.textures_delta.append(textures_delta);\n    }\n\n    /// Paint the results of the last call to [`Self::run`].\n    pub fn paint(&mut self, window: &winit::window::Window) {\n        let shapes = std::mem::take(&mut self.shapes);\n        let mut textures_delta = std::mem::take(&mut self.textures_delta);\n\n        for (id, image_delta) in textures_delta.set {\n            self.painter.set_texture(id, &image_delta);\n        }\n\n        let pixels_per_point = self.pixels_per_point;\n        let clipped_primitives = self.egui_ctx.tessellate(shapes, pixels_per_point);\n        let dimensions: [u32; 2] = window.inner_size().into();\n        self.painter\n            .paint_primitives(dimensions, pixels_per_point, &clipped_primitives);\n\n        for id in textures_delta.free.drain(..) {\n            self.painter.free_texture(id);\n        }\n    }\n\n    /// Call to release the allocated graphics resources.\n    pub fn destroy(&mut self) {\n        self.painter.destroy();\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/CHANGELOG.md",
    "content": "# Changelog for egui_kittest\nAll notable changes to the `egui_kittest` crate will be noted in this file.\n\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n## 0.33.3 - 2025-12-11\n* Enforce consistent snapshot updates [#7744](https://github.com/emilk/egui/pull/7744) by [@lucasmerlin](https://github.com/lucasmerlin)\n* `kittest`: add drag-and-drop helpers [#7690](https://github.com/emilk/egui/pull/7690) by [@emilk](https://github.com/emilk)\n\n\n## 0.33.2 - 2025-11-13\nNothing new\n\n\n## 0.33.1 - 2025-10-15\n* Add `egui_kittest::HarnessBuilder::with_options` [#7638](https://github.com/emilk/egui/pull/7638) by [@emilk](https://github.com/emilk)\n\n\n## 0.33.0 - 2025-10-09\n### ⭐ Added\n* Kittest: Add `UPDATE_SNAPSHOTS=force` [#7508](https://github.com/emilk/egui/pull/7508) by [@emilk](https://github.com/emilk)\n* Add `egui_kittest::HarnessBuilder::with_os` and set the default to `Nix` [#7493](https://github.com/emilk/egui/pull/7493) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `Harness::debug_open_snapshot` helper [#7590](https://github.com/emilk/egui/pull/7590) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🔧 Changed\n* Include popups and tooltips in `Harness::fit_contents` [#7556](https://github.com/emilk/egui/pull/7556) by [@oxkitsune](https://github.com/oxkitsune)\n* Adjust when we write .diff and .new snapshot images [#7571](https://github.com/emilk/egui/pull/7571) by [@emilk](https://github.com/emilk)\n* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)\n* `Harness`: Add `remove_cursor`,  `event` and `event_modifiers` [#7607](https://github.com/emilk/egui/pull/7607) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Use software texture filtering in kittest [#7602](https://github.com/emilk/egui/pull/7602) by [@emilk](https://github.com/emilk)\n* Write .new.png file if snapshot is missing [#7610](https://github.com/emilk/egui/pull/7610) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🔥 Removed\n* Remove deprecated `Harness::wgpu_snapshot` and related fns [#7504](https://github.com/emilk/egui/pull/7504) by [@bircni](https://github.com/bircni)\n\n\n## 0.32.3 - 2025-09-12\nNothing new\n\n\n## 0.32.2 - 2025-09-04\n* Allow masking widgets in kittest snapshots [#7467](https://github.com/emilk/egui/pull/7467) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n\n## 0.32.1 - 2025-08-15\n* Fix `UPDATE_SNAPSHOTS`: only update if we didn't pass the test [#7455](https://github.com/emilk/egui/pull/7455) by [@emilk](https://github.com/emilk)\n\n\n## 0.32.0 - 2025-07-10\n### ⭐ Added\n* Add `ImageLoader::has_pending` and `wait_for_pending_images` [#7030](https://github.com/emilk/egui/pull/7030) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Create custom `egui_kittest::Node` [#7138](https://github.com/emilk/egui/pull/7138) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `HarnessBuilder::theme` [#7289](https://github.com/emilk/egui/pull/7289) by [@emilk](https://github.com/emilk)\n* Add support for scrolling via accesskit / kittest [#7286](https://github.com/emilk/egui/pull/7286) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `failed_pixel_count_threshold` [#7092](https://github.com/emilk/egui/pull/7092) by [@bircni](https://github.com/bircni)\n\n### 🔧 Changed\n* More ergonomic functions taking `Impl Into<String>` [#7307](https://github.com/emilk/egui/pull/7307) by [@emlik](https://github.com/emilk)\n* Update kittest to 0.2 [#7332](https://github.com/emilk/egui/pull/7332) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n\n## 0.31.1 - 2025-03-05\n* Fix modifiers not working in kittest [#5693](https://github.com/emilk/egui/pull/5693) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Enable all features for egui_kittest docs [#5711](https://github.com/emilk/egui/pull/5711) by [@YgorSouza](https://github.com/YgorSouza)\n* Run a frame per queued event in egui_kittest [#5704](https://github.com/emilk/egui/pull/5704) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add guidelines for image comparison tests [#5714](https://github.com/emilk/egui/pull/5714) by [@Wumpf](https://github.com/Wumpf)\n\n\n## 0.31.0 - 2025-02-04\n### ⭐ Added\n* Add `Harness::new_eframe` and `TestRenderer` trait [#5539](https://github.com/emilk/egui/pull/5539) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Change `Harness::run` to run until no more repaints are requested [#5580](https://github.com/emilk/egui/pull/5580) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Add `SnapshotResults` struct to `egui_kittest` [#5672](https://github.com/emilk/egui/pull/5672) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🔧 Changed\n* Extend `WgpuSetup`, `egui_kittest` now prefers software rasterizers for testing [#5506](https://github.com/emilk/egui/pull/5506) by [@Wumpf](https://github.com/Wumpf)\n* Write `.old.png` files when updating images [#5578](https://github.com/emilk/egui/pull/5578) by [@emilk](https://github.com/emilk)\n* Succeed and keep going when `UPDATE_SNAPSHOTS` is set [#5649](https://github.com/emilk/egui/pull/5649) by [@emilk](https://github.com/emilk)\n\n\n## 0.30.0 - 2024-12-16 - Initial relrease\n* Support for egui 0.30.0\n* Automate clicks and text input\n* Automatic screenshot testing with wgpu\n"
  },
  {
    "path": "crates/egui_kittest/Cargo.toml",
    "content": "[package]\nname = \"egui_kittest\"\nversion.workspace = true\nauthors = [\"Lucas Meurer <hi@lucasmerlin.me>\", \"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\ndescription = \"Testing library for egui based on kittest and AccessKit\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui\"\nlicense.workspace = true\nreadme = \"./README.md\"\nrepository = \"https://github.com/emilk/egui\"\ncategories = [\"gui\", \"development-tools::testing\", \"accessibility\"]\nkeywords = [\"gui\", \"immediate\", \"egui\", \"testing\", \"accesskit\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"Cargo.toml\"]\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[features]\n## Adds a wgpu-based test renderer.\nwgpu = [\"dep:egui-wgpu\", \"dep:pollster\", \"dep:image\", \"dep:wgpu\", \"eframe?/wgpu\"]\n\n## Adds a dify-based image snapshot utility.\nsnapshot = [\"dep:dify\", \"dep:image\", \"dep:open\", \"dep:tempfile\", \"image/png\"]\n\n## Allows testing eframe::App\neframe = [\"dep:eframe\", \"eframe/accesskit\"]\n\n# This is just so it compiles with `--all-features` on Linux\nx11 = [\"eframe?/x11\"]\n\n\n[dependencies]\negui.workspace = true\neframe = { workspace = true, optional = true }\nkittest.workspace = true\nserde.workspace = true\ntoml = {workspace = true, features = [\"parse\", \"serde\"] }\n\n# wgpu dependencies\negui-wgpu = { workspace = true, optional = true }\npollster = { workspace = true, optional = true }\nimage = { workspace = true, optional = true }\n# Enable DX12 because it always comes with a software rasterizer.\nwgpu = { workspace = true, features = [\"metal\", \"dx12\", \"vulkan\", \"gles\"], optional = true }\n\n# snapshot dependencies\ndify = { workspace = true, optional = true }\n\n# Enable this when generating docs.\ndocument-features = { workspace = true, optional = true }\n\n# Native dependencies:\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\nopen = { workspace = true, optional = true }\ntempfile = { workspace = true, optional = true }\n\n[dev-dependencies]\negui = { workspace = true, features = [\"default_fonts\"] }\nimage = { workspace = true, features = [\"png\"] }\negui_extras = { workspace = true, features = [\"image\", \"http\"] }\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/egui_kittest/README.md",
    "content": "# egui_kittest\n\n[![Latest version](https://img.shields.io/crates/v/egui_kittest.svg)](https://crates.io/crates/egui_kittest)\n[![Documentation](https://docs.rs/egui_kittest/badge.svg)](https://docs.rs/egui_kittest)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\nUi testing library for egui, based on [kittest](https://github.com/rerun-io/kittest) (an [AccessKit](https://github.com/AccessKit/accesskit) based testing library).\n\n## Example usage\n```rust\nuse egui::accesskit::Toggled;\nuse egui_kittest::{Harness, kittest::{Queryable, NodeT}};\n\nlet mut checked = false;\nlet app = |ui: &mut egui::Ui| {\n    ui.checkbox(&mut checked, \"Check me!\");\n};\n\nlet mut harness = Harness::new_ui(app);\n\nlet checkbox = harness.get_by_label(\"Check me!\");\nassert_eq!(checkbox.accesskit_node().toggled(), Some(Toggled::False));\ncheckbox.click();\n\nharness.run();\n\nlet checkbox = harness.get_by_label(\"Check me!\");\nassert_eq!(checkbox.accesskit_node().toggled(), Some(Toggled::True));\n\n// Shrink the window size to the smallest size possible\nharness.fit_contents();\n\n// You can even render the ui and do image snapshot tests\n#[cfg(all(feature = \"wgpu\", feature = \"snapshot\"))]\nharness.snapshot(\"readme_example\");\n```\n\n## Configuration\n\nYou can configure test settings via a `kittest.toml` file in your workspace root.\nAll possible settings and their defaults:\n```toml\n# path to the snapshot directory\noutput_path = \"tests/snapshots\"\n\n# default threshold for image comparison tests\nthreshold = 0.6\n\n# default failed_pixel_count_threshold\nfailed_pixel_count_threshold = 0\n\n[windows]\nthreshold = 0.6\nfailed_pixel_count_threshold = 0\n\n[macos]\nthreshold = 0.6\nfailed_pixel_count_threshold = 0\n\n[linux]\nthreshold = 0.6\nfailed_pixel_count_threshold = 0\n```\n\n## Snapshot testing\nThere is a snapshot testing feature. To create snapshot tests, enable the `snapshot` and `wgpu` features.\nOnce enabled, you can call `Harness::snapshot` to render the ui and save the image to the `tests/snapshots` directory.\n\nTo update the snapshots, run your tests with `UPDATE_SNAPSHOTS=true`, so e.g. `UPDATE_SNAPSHOTS=true cargo test`.\nRunning with `UPDATE_SNAPSHOTS=true` will cause the tests to succeed.\nThis is so that you can set `UPDATE_SNAPSHOTS=true` and update all tests, without `cargo test` failing on the first failing crate.\n\n`UPDATE_SNAPSHOTS=true` will only update the images of _failing_ tests.\nIf you want to update all snapshot images, even those that are within error margins,\nrun with `UPDATE_SNAPSHOTS=force`.\n\nIf you want to have multiple snapshots in the same test, it makes sense to collect the results in a `SnapshotResults`\n([look here](https://github.com/emilk/egui/blob/d1fcd740ded5d69016c993a502b52e67f5d492d7/crates/egui_demo_lib/src/demo/demo_app_windows.rs#L387-L420) for an example).\nThis way they can all be updated at the same time.\n\nYou should add the following to your `.gitignore`:\n```gitignore\n**/tests/snapshots/**/*.diff.png\n**/tests/snapshots/**/*.new.png\n```\n\n### Guidelines for writing snapshot tests\n\n* Whenever **possible** prefer regular Rust tests or `insta` snapshot tests over image comparison tests because…\n  * …compared to regular Rust tests, they can be relatively slow to run\n  * …they are brittle since unrelated side effects (like a change in color) can cause the test to fail\n  * …images take up repo space\n* images should…\n  * …be checked in or otherwise be available (egui uses [git LFS](https://git-lfs.com/) files for this purpose)\n  * …depict exactly what's tested and nothing else\n  * …have a low resolution to avoid growth in repo size\n  * …have a low comparison threshold to avoid the test passing despite unwanted differences (the default threshold should be fine for most usecases!)\n\n### What to do when CI / another computer produces a different image?\n\nThe default tolerance settings should be fine for almost all gui comparison tests.\nHowever, especially when you're using custom rendering, you may observe images difference with different setups leading to unexpected test failures.\n\nFirst check whether the difference is due to a change in enabled rendering features, potentially due to difference in hardware (/software renderer) capabilities.\nGenerally you should carefully enforcing the same set of features for all test runs, but this may happen nonetheless.\n\nOnce you validated that the differences are miniscule and hard to avoid, you can try to _carefully_ adjust the comparison tolerance setting (`SnapshotOptions::threshold`, TODO([#5683](https://github.com/emilk/egui/issues/5683)): as well as number of pixels allowed to differ) for the specific test.\n\n⚠️ **WARNING** ⚠️\nPicking too high tolerances may mean that you are missing actual test failures.\nIt is recommended to manually verify that the tests still break under the right circumstances as expected after adjusting the tolerances.\n\n---\n\nIn order to avoid image differences, it can be useful to form an understanding of how they occur in the first place.\n\nDiscrepancies can be caused by a variety of implementation details that depend on the concrete GPU, OS, rendering backend (Metal/Vulkan/DX12 etc.) or graphics driver (even between different versions of the same driver).\n\nCommon issues include:\n* multi-sample anti-aliasing\n  * sample placement and sample resolve steps are implementation defined\n  * alpha-to-coverage algorithm/pattern can wary wildly between implementations\n* texture filtering\n  * different implementations may apply different optimizations *even* for simple linear texture filtering\n* out of bounds texture access (via `textureLoad`)\n  * implementations are free to return indeterminate values instead of clamping\n* floating point evaluation, for details see [WGSL spec § 15.7. Floating Point Evaluation](https://www.w3.org/TR/WGSL/#floating-point-evaluation). Notably:\n  * rounding mode may be inconsistent\n  * floating point math \"optimizations\" may occur\n    * depending on output shading language, different arithmetic optimizations may be performed upon floating point operations even if they change the result\n  * floating point denormal flush\n    * even on modern implementations, denormal float values may be flushed to zero\n  * `NaN`/`Inf` handling\n    * whenever the result of a function should yield `NaN`/`Inf`, implementations may free to yield an indeterminate value instead\n  * builtin-function function precision & error handling (trigonometric functions and others)\n* [partial derivatives (dpdx/dpdx)](https://www.w3.org/TR/WGSL/#dpdx-builtin)\n  * implementations are free to use either `dpdxFine` or `dpdxCoarse`\n* [...]\n\nFrom this follow a few simple recommendations (these may or may not apply as they may impose unwanted restrictions on your rendering setup):\n* avoid enabling mult-sample anti-aliasing whenever it's not explicitly tested or needed\n* do not rely on NaN, Inf and denormal float values\n* consider dedicated test paths for texture sampling\n* prefer explicit partial derivative functions\n"
  },
  {
    "path": "crates/egui_kittest/src/app_kind.rs",
    "content": "use egui::Frame;\n\ntype AppKindContextState<'a, State> = Box<dyn FnMut(&egui::Context, &mut State) + 'a>;\ntype AppKindUiState<'a, State> = Box<dyn FnMut(&mut egui::Ui, &mut State) + 'a>;\ntype AppKindContext<'a> = Box<dyn FnMut(&egui::Context) + 'a>;\ntype AppKindUi<'a> = Box<dyn FnMut(&mut egui::Ui) + 'a>;\n\n/// In order to access the [`eframe::App`] trait from the generic `State`, we store a function pointer\n/// here that will return the dyn trait from the struct. In the builder we have the correct where\n/// clause to be able to create this.\n/// Later we can use it anywhere to get the [`eframe::App`] from the `State`.\n#[cfg(feature = \"eframe\")]\ntype AppKindEframe<'a, State> = (fn(&mut State) -> &mut dyn eframe::App, eframe::Frame);\n\npub(crate) enum AppKind<'a, State> {\n    Context(AppKindContext<'a>),\n    Ui(AppKindUi<'a>),\n    ContextState(AppKindContextState<'a, State>),\n    UiState(AppKindUiState<'a, State>),\n    #[cfg(feature = \"eframe\")]\n    Eframe(AppKindEframe<'a, State>),\n}\n\nimpl<State> AppKind<'_, State> {\n    pub fn run(\n        &mut self,\n        ui: &mut egui::Ui,\n        state: &mut State,\n        sizing_pass: bool,\n    ) -> Option<egui::Response> {\n        match self {\n            AppKind::Context(f) => {\n                debug_assert!(!sizing_pass, \"Context closures cannot do a sizing pass\");\n                f(ui);\n                None\n            }\n            AppKind::ContextState(f) => {\n                debug_assert!(!sizing_pass, \"Context closures cannot do a sizing pass\");\n                f(ui, state);\n                None\n            }\n            #[cfg(feature = \"eframe\")]\n            AppKind::Eframe((get_app, frame)) => {\n                let app = get_app(state);\n\n                app.logic(ui, frame);\n\n                #[expect(deprecated)]\n                app.update(ui, frame);\n\n                app.ui(ui, frame);\n\n                None\n            }\n            kind_ui => Some(kind_ui.run_ui(ui, state, sizing_pass)),\n        }\n    }\n\n    fn run_ui(\n        &mut self,\n        ui: &mut egui::Ui,\n        state: &mut State,\n        sizing_pass: bool,\n    ) -> egui::Response {\n        let mut builder = egui::UiBuilder::new();\n        if sizing_pass {\n            builder.sizing_pass = true;\n        }\n        ui.scope_builder(builder, |ui| {\n            Frame::central_panel(ui.style())\n                // Only set outer margin, so we show no frame for tests with only free-floating windows/popups:\n                .outer_margin(8.0)\n                .inner_margin(0.0)\n                .show(ui, |ui| match self {\n                    AppKind::Ui(f) => f(ui),\n                    AppKind::UiState(f) => f(ui, state),\n                    _ => unreachable!(\n                        \"run_ui should only be called with AppKind::Ui or AppKind UiState\"\n                    ),\n                });\n        })\n        .response\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/src/builder.rs",
    "content": "use crate::app_kind::AppKind;\nuse crate::{Harness, LazyRenderer, TestRenderer};\nuse egui::{Pos2, Rect, Vec2};\nuse std::marker::PhantomData;\n\n/// Builder for [`Harness`].\n#[must_use]\npub struct HarnessBuilder<State = ()> {\n    pub(crate) screen_rect: Rect,\n    pub(crate) pixels_per_point: f32,\n    pub(crate) theme: egui::Theme,\n    pub(crate) os: egui::os::OperatingSystem,\n    pub(crate) max_steps: u64,\n    pub(crate) step_dt: f32,\n    pub(crate) state: PhantomData<State>,\n    pub(crate) renderer: Box<dyn TestRenderer>,\n    pub(crate) wait_for_pending_images: bool,\n\n    #[cfg(feature = \"snapshot\")]\n    pub(crate) default_snapshot_options: crate::SnapshotOptions,\n}\n\nimpl<State> Default for HarnessBuilder<State> {\n    fn default() -> Self {\n        Self {\n            screen_rect: Rect::from_min_size(Pos2::ZERO, Vec2::new(800.0, 600.0)),\n            pixels_per_point: 1.0,\n            theme: egui::Theme::Dark,\n            state: PhantomData,\n            renderer: Box::new(LazyRenderer::default()),\n            max_steps: 4,\n            step_dt: 1.0 / 4.0,\n            wait_for_pending_images: true,\n            os: egui::os::OperatingSystem::Nix,\n\n            #[cfg(feature = \"snapshot\")]\n            default_snapshot_options: crate::SnapshotOptions::default(),\n        }\n    }\n}\n\nimpl<State> HarnessBuilder<State> {\n    /// Set the size of the window.\n    #[inline]\n    pub fn with_size(mut self, size: impl Into<Vec2>) -> Self {\n        let size = size.into();\n        self.screen_rect.set_width(size.x);\n        self.screen_rect.set_height(size.y);\n        self\n    }\n\n    /// Set the `pixels_per_point` of the window.\n    #[inline]\n    pub fn with_pixels_per_point(mut self, pixels_per_point: f32) -> Self {\n        self.pixels_per_point = pixels_per_point;\n        self\n    }\n\n    /// Set the desired theme (dark or light).\n    #[inline]\n    pub fn with_theme(mut self, theme: egui::Theme) -> Self {\n        self.theme = theme;\n        self\n    }\n\n    /// Set the default options used for snapshot tests on this harness.\n    #[cfg(feature = \"snapshot\")]\n    #[inline]\n    pub fn with_options(mut self, options: crate::SnapshotOptions) -> Self {\n        self.default_snapshot_options = options;\n        self\n    }\n\n    /// Override the [`egui::os::OperatingSystem`] reported to egui.\n    ///\n    /// This affects e.g. the way shortcuts are displayed. So for snapshot tests,\n    /// it makes sense to set this to a specific OS, so snapshots don't change when running\n    /// the same tests on different OSes.\n    ///\n    /// Default is [`egui::os::OperatingSystem::Nix`].\n    /// Use [`egui::os::OperatingSystem::from_target_os()`] to use the current OS (this restores\n    /// eguis default behavior).\n    #[inline]\n    pub fn with_os(mut self, os: egui::os::OperatingSystem) -> Self {\n        self.os = os;\n        self\n    }\n\n    /// Set the maximum number of steps to run when calling [`Harness::run`].\n    ///\n    /// Default is 4.\n    /// With the default `step_dt`, this means 1 second of simulation.\n    #[inline]\n    pub fn with_max_steps(mut self, max_steps: u64) -> Self {\n        self.max_steps = max_steps;\n        self\n    }\n\n    /// Set the time delta for a single [`Harness::step`].\n    ///\n    /// Default is 1.0 / 4.0 (4fps).\n    /// The default is low so we don't waste cpu waiting for animations.\n    #[inline]\n    pub fn with_step_dt(mut self, step_dt: f32) -> Self {\n        self.step_dt = step_dt;\n        self\n    }\n\n    /// Should we wait for pending images?\n    ///\n    /// If `true`, [`Harness::run`] and related methods will check if there are pending images\n    /// (via [`egui::Context::has_pending_images`]) and sleep for [`Self::with_step_dt`] up to\n    /// [`Self::with_max_steps`] times.\n    ///\n    /// Default: `true`\n    #[inline]\n    pub fn with_wait_for_pending_images(mut self, wait_for_pending_images: bool) -> Self {\n        self.wait_for_pending_images = wait_for_pending_images;\n        self\n    }\n\n    /// Set the [`TestRenderer`] to use for rendering.\n    ///\n    /// By default, a [`LazyRenderer`] is used.\n    #[inline]\n    pub fn renderer(mut self, renderer: impl TestRenderer + 'static) -> Self {\n        self.renderer = Box::new(renderer);\n        self\n    }\n\n    /// Enable wgpu rendering with a default setup suitable for testing.\n    ///\n    /// This sets up a [`crate::wgpu::WgpuTestRenderer`] with the default setup.\n    #[cfg(feature = \"wgpu\")]\n    pub fn wgpu(self) -> Self {\n        self.renderer(crate::wgpu::WgpuTestRenderer::default())\n    }\n\n    /// Enable wgpu rendering with the given setup.\n    #[cfg(feature = \"wgpu\")]\n    pub fn wgpu_setup(self, setup: egui_wgpu::WgpuSetup) -> Self {\n        self.renderer(crate::wgpu::WgpuTestRenderer::from_setup(setup))\n    }\n\n    /// Create a new Harness with the given app closure and a state.\n    ///\n    /// The app closure will immediately be called once to create the initial ui.\n    ///\n    /// If you don't need to create Windows / Panels, you can use [`HarnessBuilder::build_ui`] instead.\n    ///\n    /// # Example\n    /// ```rust\n    /// # use egui::CentralPanel;\n    /// # use egui_kittest::{Harness, kittest::Queryable};\n    /// let checked = false;\n    /// let mut harness = Harness::builder()\n    ///     .with_size(egui::Vec2::new(300.0, 200.0))\n    ///     .build_state(|ctx, checked| {\n    ///         CentralPanel::default().show(ctx, |ui| {\n    ///             ui.checkbox(checked, \"Check me!\");\n    ///         });\n    ///     }, checked);\n    ///\n    /// harness.get_by_label(\"Check me!\").click();\n    /// harness.run();\n    ///\n    /// assert_eq!(*harness.state(), true);\n    /// ```\n    #[track_caller]\n    #[deprecated = \"use `build_ui_state` instead\"]\n    pub fn build_state<'a>(\n        self,\n        app: impl FnMut(&egui::Context, &mut State) + 'a,\n        state: State,\n    ) -> Harness<'a, State> {\n        Harness::from_builder(self, AppKind::ContextState(Box::new(app)), state, None)\n    }\n\n    /// Create a new Harness with the given ui closure and a state.\n    ///\n    /// The ui closure will immediately be called once to create the initial ui.\n    ///\n    /// If you need to create Windows / Panels, you can use [`HarnessBuilder::build`] instead.\n    ///\n    /// # Example\n    /// ```rust\n    /// # use egui_kittest::{Harness, kittest::Queryable};\n    /// let mut checked = false;\n    /// let mut harness = Harness::builder()\n    ///     .with_size(egui::Vec2::new(300.0, 200.0))\n    ///     .build_ui_state(|ui, checked| {\n    ///        ui.checkbox(checked, \"Check me!\");\n    ///     }, checked);\n    ///\n    /// harness.get_by_label(\"Check me!\").click();\n    /// harness.run();\n    ///\n    /// assert_eq!(*harness.state(), true);\n    /// ```\n    #[track_caller]\n    pub fn build_ui_state<'a>(\n        self,\n        app: impl FnMut(&mut egui::Ui, &mut State) + 'a,\n        state: State,\n    ) -> Harness<'a, State> {\n        Harness::from_builder(self, AppKind::UiState(Box::new(app)), state, None)\n    }\n\n    /// Create a new [Harness] from the given eframe creation closure.\n    /// The app can be accessed via the [`Harness::state`] / [`Harness::state_mut`] methods.\n    #[cfg(feature = \"eframe\")]\n    #[track_caller]\n    pub fn build_eframe<'a>(\n        self,\n        build: impl FnOnce(&mut eframe::CreationContext<'a>) -> State,\n    ) -> Harness<'a, State>\n    where\n        State: eframe::App,\n    {\n        let ctx = egui::Context::default();\n\n        let mut cc = eframe::CreationContext::_new_kittest(ctx.clone());\n        let mut frame = eframe::Frame::_new_kittest();\n\n        self.renderer.setup_eframe(&mut cc, &mut frame);\n\n        let app = build(&mut cc);\n\n        let kind = AppKind::Eframe((|state| state, frame));\n        Harness::from_builder(self, kind, app, Some(ctx))\n    }\n}\n\nimpl HarnessBuilder {\n    /// Create a new Harness with the given app closure.\n    ///\n    /// The app closure will immediately be called once to create the initial ui.\n    ///\n    /// If you don't need to create Windows / Panels, you can use [`HarnessBuilder::build_ui`] instead.\n    ///\n    /// # Example\n    /// ```rust\n    /// # use egui::CentralPanel;\n    /// # use egui_kittest::{Harness, kittest::Queryable};\n    /// let mut harness = Harness::builder()\n    ///     .with_size(egui::Vec2::new(300.0, 200.0))\n    ///     .build(|ctx| {\n    ///         CentralPanel::default().show(ctx, |ui| {\n    ///             ui.label(\"Hello, world!\");\n    ///         });\n    ///     });\n    /// ```\n    #[must_use]\n    #[track_caller]\n    #[deprecated = \"use `build_ui` instead\"]\n    pub fn build<'a>(self, app: impl FnMut(&egui::Context) + 'a) -> Harness<'a> {\n        Harness::from_builder(self, AppKind::Context(Box::new(app)), (), None)\n    }\n\n    /// Create a new Harness with the given ui closure.\n    ///\n    /// The ui closure will immediately be called once to create the initial ui.\n    ///\n    /// If you need to create Windows / Panels, you can use [`HarnessBuilder::build`] instead.\n    ///\n    /// # Example\n    /// ```rust\n    /// # use egui_kittest::{Harness, kittest::Queryable};\n    /// let mut harness = Harness::builder()\n    ///     .with_size(egui::Vec2::new(300.0, 200.0))\n    ///     .build_ui(|ui| {\n    ///         ui.label(\"Hello, world!\");\n    ///     });\n    /// ```\n    #[must_use]\n    #[track_caller]\n    pub fn build_ui<'a>(self, app: impl FnMut(&mut egui::Ui) + 'a) -> Harness<'a> {\n        Harness::from_builder(self, AppKind::Ui(Box::new(app)), (), None)\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/src/config.rs",
    "content": "#![cfg(feature = \"snapshot\")]\n\nuse std::io;\nuse std::path::PathBuf;\n\n/// Configuration for `egui_kittest`.\n///\n/// It's loaded once (per process) by searching for a `kittest.toml` file in the project root\n/// (the directory containing `Cargo.lock`).\n#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\n#[serde(default, deny_unknown_fields)]\npub struct Config {\n    /// The output path for image snapshots.\n    ///\n    /// Default is \"tests/snapshots\" (relative to the working directory / crate root).\n    output_path: PathBuf,\n\n    /// The per-pixel threshold.\n    ///\n    /// Default is 0.6.\n    threshold: f32,\n\n    /// The number of pixels that can differ before the test is considered failed.\n    ///\n    /// Default is 0.\n    failed_pixel_count_threshold: usize,\n\n    windows: OsConfig,\n    mac: OsConfig,\n    linux: OsConfig,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            output_path: PathBuf::from(\"tests/snapshots\"),\n            threshold: 0.6,\n            failed_pixel_count_threshold: 0,\n            windows: Default::default(),\n            mac: Default::default(),\n            linux: Default::default(),\n        }\n    }\n}\n#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]\n#[serde(default, deny_unknown_fields)]\npub struct OsConfig {\n    /// Override the per-pixel threshold for this OS.\n    threshold: Option<f32>,\n\n    /// Override the failed pixel count threshold for this OS.\n    failed_pixel_count_threshold: Option<usize>,\n}\n\nfn find_kittest_toml() -> io::Result<std::path::PathBuf> {\n    let mut current_dir = std::env::current_dir()?;\n\n    loop {\n        let current_kittest = current_dir.join(\"kittest.toml\");\n        // Check if Cargo.toml exists in this directory\n        if current_kittest.exists() {\n            return Ok(current_kittest);\n        }\n\n        // Move up one directory\n        if !current_dir.pop() {\n            return Err(io::Error::new(\n                io::ErrorKind::NotFound,\n                \"kittest.toml not found\",\n            ));\n        }\n    }\n}\n\nfn load_config() -> Config {\n    if let Ok(config_path) = find_kittest_toml() {\n        match std::fs::read_to_string(&config_path) {\n            Ok(config_str) => match toml::from_str(&config_str) {\n                Ok(config) => config,\n                Err(e) => panic!(\"Failed to parse {}: {e}\", &config_path.display()),\n            },\n            Err(err) => {\n                panic!(\"Failed to read {}: {}\", config_path.display(), err);\n            }\n        }\n    } else {\n        Config::default()\n    }\n}\n\n/// Get the global configuration.\n///\n/// See [`Config::global`] for details.\npub fn config() -> &'static Config {\n    Config::global()\n}\n\nimpl Config {\n    /// Get or load the global configuration.\n    ///\n    /// This is either\n    ///  - Based on a `kittest.toml`, found by searching from the current working directory\n    ///    (for tests that is the crate root) upwards.\n    ///  - The default [Config], if no `kittest.toml` is found.\n    pub fn global() -> &'static Self {\n        static INSTANCE: std::sync::LazyLock<Config> = std::sync::LazyLock::new(load_config);\n        &INSTANCE\n    }\n\n    /// The output path for image snapshots.\n    ///\n    /// Default is \"tests/snapshots\".\n    pub fn output_path(&self) -> PathBuf {\n        self.output_path.clone()\n    }\n}\n\n#[cfg(feature = \"snapshot\")]\nimpl Config {\n    pub fn os_threshold(&self) -> crate::OsThreshold<f32> {\n        let fallback = self.threshold;\n        crate::OsThreshold {\n            windows: self.windows.threshold.unwrap_or(fallback),\n            macos: self.mac.threshold.unwrap_or(fallback),\n            linux: self.linux.threshold.unwrap_or(fallback),\n            fallback,\n        }\n    }\n\n    pub fn os_failed_pixel_count_threshold(&self) -> crate::OsThreshold<usize> {\n        let fallback = self.failed_pixel_count_threshold;\n        crate::OsThreshold {\n            windows: self\n                .windows\n                .failed_pixel_count_threshold\n                .unwrap_or(fallback),\n            macos: self.mac.failed_pixel_count_threshold.unwrap_or(fallback),\n            linux: self.linux.failed_pixel_count_threshold.unwrap_or(fallback),\n            fallback,\n        }\n    }\n\n    /// The threshold.\n    ///\n    /// Default is 1.0.\n    pub fn threshold(&self) -> f32 {\n        self.os_threshold().threshold()\n    }\n\n    /// The number of pixels that can differ before the test is considered failed.\n    ///\n    /// Default is 0.\n    pub fn failed_pixel_count_threshold(&self) -> usize {\n        self.os_failed_pixel_count_threshold().threshold()\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/src/lib.rs",
    "content": "#![cfg_attr(doc, doc = include_str!(\"../README.md\"))]\n//!\n//! ## Feature flags\n#![cfg_attr(feature = \"document-features\", doc = document_features::document_features!())]\n#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps\n\nmod builder;\n#[cfg(feature = \"snapshot\")]\nmod snapshot;\n\n#[cfg(feature = \"snapshot\")]\npub use crate::snapshot::*;\n\nmod app_kind;\nmod config;\nmod node;\nmod renderer;\n#[cfg(feature = \"wgpu\")]\nmod texture_to_image;\n#[cfg(feature = \"wgpu\")]\npub mod wgpu;\n\n// re-exports:\npub use {\n    self::{builder::*, node::*, renderer::*},\n    kittest,\n};\n\nuse std::{\n    fmt::{Debug, Display, Formatter},\n    time::Duration,\n};\n\nuse egui::{\n    Color32, Key, Modifiers, PointerButton, Pos2, Rect, RepaintCause, Shape, Vec2, ViewportId,\n    epaint::{ClippedShape, RectShape},\n    style::ScrollAnimation,\n};\nuse kittest::Queryable;\n\nuse crate::app_kind::AppKind;\n\n#[derive(Debug, Clone)]\npub struct ExceededMaxStepsError {\n    pub max_steps: u64,\n    pub repaint_causes: Vec<RepaintCause>,\n}\n\nimpl Display for ExceededMaxStepsError {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        write!(\n            f,\n            \"Harness::run exceeded max_steps ({}). If your expect your ui to keep repainting \\\n            (e.g. when showing a spinner) call Harness::step or Harness::run_steps instead.\\\n            \\nRepaint causes: {:#?}\",\n            self.max_steps, self.repaint_causes,\n        )\n    }\n}\n\n/// The test Harness. This contains everything needed to run the test.\n///\n/// Create a new Harness using [`Harness::new`] or [`Harness::builder`].\n///\n/// The [Harness] has a optional generic state that can be used to pass data to the app / ui closure.\n/// In _most cases_ it should be fine to just store the state in the closure itself.\n/// The state functions are useful if you need to access the state after the harness has been created.\n///\n/// Some egui style options are changed from the defaults:\n/// - The cursor blinking is disabled\n/// - The scroll animation is disabled\npub struct Harness<'a, State = ()> {\n    pub ctx: egui::Context,\n    input: egui::RawInput,\n    kittest: kittest::State,\n    output: egui::FullOutput,\n    app: AppKind<'a, State>,\n    response: Option<egui::Response>,\n    state: State,\n    renderer: Box<dyn TestRenderer>,\n    max_steps: u64,\n    step_dt: f32,\n    wait_for_pending_images: bool,\n    queued_events: EventQueue,\n\n    #[cfg(feature = \"snapshot\")]\n    default_snapshot_options: SnapshotOptions,\n    #[cfg(feature = \"snapshot\")]\n    snapshot_results: SnapshotResults,\n}\n\nimpl<State> Debug for Harness<'_, State> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        self.kittest.fmt(f)\n    }\n}\n\nimpl<'a, State> Harness<'a, State> {\n    #[track_caller]\n    pub(crate) fn from_builder(\n        builder: HarnessBuilder<State>,\n        mut app: AppKind<'a, State>,\n        mut state: State,\n        ctx: Option<egui::Context>,\n    ) -> Self {\n        let HarnessBuilder {\n            screen_rect,\n            pixels_per_point,\n            theme,\n            os,\n            max_steps,\n            step_dt,\n            state: _,\n            mut renderer,\n            wait_for_pending_images,\n\n            #[cfg(feature = \"snapshot\")]\n            default_snapshot_options,\n        } = builder;\n        let ctx = ctx.unwrap_or_default();\n        ctx.set_theme(theme);\n        ctx.set_os(os);\n        ctx.enable_accesskit();\n        ctx.all_styles_mut(|style| {\n            // Disable cursor blinking so it doesn't interfere with snapshots\n            style.visuals.text_cursor.blink = false;\n            style.scroll_animation = ScrollAnimation::none();\n            style.animation_time = 0.0;\n        });\n        let mut input = egui::RawInput {\n            screen_rect: Some(screen_rect),\n            ..Default::default()\n        };\n        let viewport = input.viewports.get_mut(&ViewportId::ROOT).unwrap();\n        viewport.native_pixels_per_point = Some(pixels_per_point);\n\n        let mut response = None;\n\n        // We need to run egui for a single frame so that the AccessKit state can be initialized\n        // and users can immediately start querying for widgets.\n        let mut output = ctx.run_ui(input.clone(), |ui| {\n            response = app.run(ui, &mut state, false);\n        });\n\n        renderer.handle_delta(&output.textures_delta);\n\n        let mut harness = Self {\n            app,\n            ctx,\n            input,\n            kittest: kittest::State::new(\n                output\n                    .platform_output\n                    .accesskit_update\n                    .take()\n                    .expect(\"AccessKit was disabled\"),\n            ),\n            output,\n            response,\n            state,\n            renderer,\n            max_steps,\n            step_dt,\n            wait_for_pending_images,\n            queued_events: Default::default(),\n\n            #[cfg(feature = \"snapshot\")]\n            default_snapshot_options,\n\n            #[cfg(feature = \"snapshot\")]\n            snapshot_results: SnapshotResults::default(),\n        };\n        // Run the harness until it is stable, ensuring that all Areas are shown and animations are done\n        harness.run_ok();\n        harness\n    }\n\n    /// Create a [`Harness`] via a [`HarnessBuilder`].\n    pub fn builder() -> HarnessBuilder<State> {\n        HarnessBuilder::default()\n    }\n\n    /// Create a new Harness with the given app closure and a state.\n    ///\n    /// The app closure will immediately be called once to create the initial ui.\n    ///\n    /// If you don't need to create Windows / Panels, you can use [`Harness::new_ui`] instead.\n    ///\n    /// If you e.g. want to customize the size of the window, you can use [`Harness::builder`].\n    ///\n    /// # Example\n    /// ```rust\n    /// # use egui::CentralPanel;\n    /// # use egui_kittest::{Harness, kittest::Queryable};\n    /// let mut checked = false;\n    /// let mut harness = Harness::new_state(|ctx, checked| {\n    ///     CentralPanel::default().show(ctx, |ui| {\n    ///         ui.checkbox(checked, \"Check me!\");\n    ///     });\n    /// }, checked);\n    ///\n    /// harness.get_by_label(\"Check me!\").click();\n    /// harness.run();\n    ///\n    /// assert_eq!(*harness.state(), true);\n    /// ```\n    #[track_caller]\n    #[deprecated = \"use `new_ui_state` instead\"]\n    pub fn new_state(app: impl FnMut(&egui::Context, &mut State) + 'a, state: State) -> Self {\n        #[expect(deprecated)]\n        Self::builder().build_state(app, state)\n    }\n\n    /// Create a new Harness with the given ui closure and a state.\n    ///\n    /// The ui closure will immediately be called once to create the initial ui.\n    ///\n    /// If you need to create Windows / Panels, you can use [`Harness::new`] instead.\n    ///\n    /// If you e.g. want to customize the size of the ui, you can use [`Harness::builder`].\n    ///\n    /// # Example\n    /// ```rust\n    /// # use egui_kittest::{Harness, kittest::Queryable};\n    /// let mut checked = false;\n    /// let mut harness = Harness::new_ui_state(|ui, checked| {\n    ///     ui.checkbox(checked, \"Check me!\");\n    /// }, checked);\n    ///\n    /// harness.get_by_label(\"Check me!\").click();\n    /// harness.run();\n    ///\n    /// assert_eq!(*harness.state(), true);\n    /// ```\n    #[track_caller]\n    pub fn new_ui_state(app: impl FnMut(&mut egui::Ui, &mut State) + 'a, state: State) -> Self {\n        Self::builder().build_ui_state(app, state)\n    }\n\n    /// Create a new [Harness] from the given eframe creation closure.\n    #[cfg(feature = \"eframe\")]\n    #[track_caller]\n    pub fn new_eframe(builder: impl FnOnce(&mut eframe::CreationContext<'a>) -> State) -> Self\n    where\n        State: eframe::App,\n    {\n        Self::builder().build_eframe(builder)\n    }\n\n    /// Set the size of the window.\n    /// Note: If you only want to set the size once at the beginning,\n    /// prefer using [`HarnessBuilder::with_size`].\n    #[inline]\n    pub fn set_size(&mut self, size: Vec2) -> &mut Self {\n        self.input.screen_rect = Some(Rect::from_min_size(Pos2::ZERO, size));\n        self\n    }\n\n    /// Set the `pixels_per_point` of the window.\n    /// Note: If you only want to set the `pixels_per_point` once at the beginning,\n    /// prefer using [`HarnessBuilder::with_pixels_per_point`].\n    #[inline]\n    pub fn set_pixels_per_point(&mut self, pixels_per_point: f32) -> &mut Self {\n        self.ctx.set_pixels_per_point(pixels_per_point);\n        self\n    }\n\n    /// Run a frame for each queued event (or a single frame if there are no events).\n    /// This will call the app closure with each queued event and\n    /// update the Harness.\n    pub fn step(&mut self) {\n        let events = std::mem::take(&mut *self.queued_events.lock());\n        if events.is_empty() {\n            self._step(false);\n        }\n        for event in events {\n            match event {\n                EventType::Event(event) => {\n                    self.input.events.push(event);\n                }\n                EventType::Modifiers(modifiers) => {\n                    self.input.modifiers = modifiers;\n                }\n            }\n            self._step(false);\n        }\n    }\n\n    /// Run a single step. This will not process any events.\n    fn _step(&mut self, sizing_pass: bool) {\n        self.input.predicted_dt = self.step_dt;\n\n        let mut output = self.ctx.run_ui(self.input.take(), |ui| {\n            self.response = self.app.run(ui, &mut self.state, sizing_pass);\n        });\n        self.kittest.update(\n            output\n                .platform_output\n                .accesskit_update\n                .take()\n                .expect(\"AccessKit was disabled\"),\n        );\n        self.renderer.handle_delta(&output.textures_delta);\n        self.output = output;\n    }\n\n    /// Calculate the rect that includes all popups and tooltips.\n    fn compute_total_rect_with_popups(&self) -> Option<Rect> {\n        // Start with the standard response rect\n        let mut used = if let Some(response) = self.response.as_ref() {\n            response.rect\n        } else {\n            return None;\n        };\n\n        // Add all visible areas from other orders (popups, tooltips, etc.)\n        self.ctx.memory(|mem| {\n            mem.areas()\n                .visible_layer_ids()\n                .into_iter()\n                .filter(|layer_id| layer_id.order != egui::Order::Background)\n                .filter_map(|layer_id| mem.area_rect(layer_id.id))\n                .for_each(|area_rect| used |= area_rect);\n        });\n\n        Some(used)\n    }\n\n    /// Resize the test harness to fit the contents. This only works when creating the Harness via\n    /// [`Harness::new_ui`] / [`Harness::new_ui_state`] or\n    /// [`HarnessBuilder::build_ui`] / [`HarnessBuilder::build_ui_state`].\n    pub fn fit_contents(&mut self) {\n        self._step(true);\n\n        // Calculate size including all content (main UI + popups + tooltips)\n        if let Some(rect) = self.compute_total_rect_with_popups() {\n            self.set_size(rect.size());\n        }\n\n        self.run_ok();\n    }\n\n    /// Run until\n    /// - all animations are done\n    /// - no more repaints are requested\n    ///\n    /// Returns the number of frames that were run.\n    ///\n    /// # Panics\n    /// Panics if the number of steps exceeds the maximum number of steps set\n    /// in [`HarnessBuilder::with_max_steps`].\n    ///\n    /// See also:\n    /// - [`Harness::try_run`].\n    /// - [`Harness::try_run_realtime`].\n    /// - [`Harness::run_ok`].\n    /// - [`Harness::step`].\n    /// - [`Harness::run_steps`].\n    #[track_caller]\n    pub fn run(&mut self) -> u64 {\n        match self.try_run() {\n            Ok(steps) => steps,\n            Err(err) => {\n                panic!(\"{err}\");\n            }\n        }\n    }\n\n    fn _try_run(&mut self, sleep: bool) -> Result<u64, ExceededMaxStepsError> {\n        let mut steps = 0;\n        loop {\n            steps += 1;\n            self.step();\n\n            let wait_for_images = self.wait_for_pending_images && self.ctx.has_pending_images();\n\n            // We only care about immediate repaints\n            if self.root_viewport_output().repaint_delay != Duration::ZERO && !wait_for_images {\n                break;\n            } else if sleep || wait_for_images {\n                std::thread::sleep(Duration::from_secs_f32(self.step_dt));\n            }\n            if steps > self.max_steps {\n                return Err(ExceededMaxStepsError {\n                    max_steps: self.max_steps,\n                    repaint_causes: self.ctx.repaint_causes(),\n                });\n            }\n        }\n        Ok(steps)\n    }\n\n    /// Run until\n    /// - all animations are done\n    /// - no more repaints are requested\n    /// - the maximum number of steps is reached (See [`HarnessBuilder::with_max_steps`])\n    ///\n    /// Returns the number of steps that were run.\n    ///\n    /// # Errors\n    /// Returns an error if the maximum number of steps is exceeded.\n    ///\n    /// See also:\n    /// - [`Harness::run`].\n    /// - [`Harness::run_ok`].\n    /// - [`Harness::step`].\n    /// - [`Harness::run_steps`].\n    /// - [`Harness::try_run_realtime`].\n    pub fn try_run(&mut self) -> Result<u64, ExceededMaxStepsError> {\n        self._try_run(false)\n    }\n\n    /// Run until\n    /// - all animations are done\n    /// - no more repaints are requested\n    /// - the maximum number of steps is reached (See [`HarnessBuilder::with_max_steps`])\n    ///\n    /// Returns the number of steps that were run, or None if the maximum number of steps was exceeded.\n    ///\n    /// See also:\n    /// - [`Harness::run`].\n    /// - [`Harness::try_run`].\n    /// - [`Harness::step`].\n    /// - [`Harness::run_steps`].\n    /// - [`Harness::try_run_realtime`].\n    pub fn run_ok(&mut self) -> Option<u64> {\n        self.try_run().ok()\n    }\n\n    /// Run multiple frames, sleeping for [`HarnessBuilder::with_step_dt`] between frames.\n    ///\n    /// This is useful to e.g. wait for an async operation to complete (e.g. loading of images).\n    /// Runs until\n    /// - all animations are done\n    /// - no more repaints are requested\n    /// - the maximum number of steps is reached (See [`HarnessBuilder::with_max_steps`])\n    ///\n    /// Returns the number of steps that were run.\n    ///\n    /// # Errors\n    /// Returns an error if the maximum number of steps is exceeded.\n    ///\n    /// See also:\n    /// - [`Harness::run`].\n    /// - [`Harness::run_ok`].\n    /// - [`Harness::step`].\n    /// - [`Harness::run_steps`].\n    /// - [`Harness::try_run`].\n    pub fn try_run_realtime(&mut self) -> Result<u64, ExceededMaxStepsError> {\n        self._try_run(true)\n    }\n\n    /// Run a number of steps.\n    /// Equivalent to calling [`Harness::step`] x times.\n    pub fn run_steps(&mut self, steps: usize) {\n        for _ in 0..steps {\n            self.step();\n        }\n    }\n\n    /// Access the [`egui::RawInput`] for the next frame.\n    pub fn input(&self) -> &egui::RawInput {\n        &self.input\n    }\n\n    /// Access the [`egui::RawInput`] for the next frame mutably.\n    pub fn input_mut(&mut self) -> &mut egui::RawInput {\n        &mut self.input\n    }\n\n    /// Access the [`egui::FullOutput`] for the last frame.\n    pub fn output(&self) -> &egui::FullOutput {\n        &self.output\n    }\n\n    /// Access the [`kittest::State`].\n    pub fn kittest_state(&self) -> &kittest::State {\n        &self.kittest\n    }\n\n    /// Access the state.\n    pub fn state(&self) -> &State {\n        &self.state\n    }\n\n    /// Access the state mutably.\n    pub fn state_mut(&mut self) -> &mut State {\n        &mut self.state\n    }\n\n    /// Queue an event to be processed in the next frame.\n    pub fn event(&self, event: egui::Event) {\n        self.queued_events.lock().push(EventType::Event(event));\n    }\n\n    /// Queue an event with modifiers.\n    ///\n    /// Queues the modifiers to be pressed, then the event, then the modifiers to be released.\n    pub fn event_modifiers(&self, event: egui::Event, modifiers: Modifiers) {\n        let mut queue = self.queued_events.lock();\n        queue.push(EventType::Modifiers(modifiers));\n        queue.push(EventType::Event(event));\n        queue.push(EventType::Modifiers(Modifiers::default()));\n    }\n\n    fn modifiers(&self, modifiers: Modifiers) {\n        self.queued_events\n            .lock()\n            .push(EventType::Modifiers(modifiers));\n    }\n\n    pub fn key_down(&self, key: egui::Key) {\n        self.event(egui::Event::Key {\n            key,\n            pressed: true,\n            modifiers: Modifiers::default(),\n            repeat: false,\n            physical_key: None,\n        });\n    }\n\n    pub fn key_down_modifiers(&self, modifiers: Modifiers, key: egui::Key) {\n        self.event_modifiers(\n            egui::Event::Key {\n                key,\n                pressed: true,\n                modifiers,\n                repeat: false,\n                physical_key: None,\n            },\n            modifiers,\n        );\n    }\n\n    pub fn key_up(&self, key: egui::Key) {\n        self.event(egui::Event::Key {\n            key,\n            pressed: false,\n            modifiers: Modifiers::default(),\n            repeat: false,\n            physical_key: None,\n        });\n    }\n\n    pub fn key_up_modifiers(&self, modifiers: Modifiers, key: egui::Key) {\n        self.event_modifiers(\n            egui::Event::Key {\n                key,\n                pressed: false,\n                modifiers,\n                repeat: false,\n                physical_key: None,\n            },\n            modifiers,\n        );\n    }\n\n    /// Press the given keys in combination.\n    ///\n    /// For e.g. [`Key::A`] + [`Key::B`] this would generate:\n    /// - Press [`Key::A`]\n    /// - Press [`Key::B`]\n    /// - Release [`Key::B`]\n    /// - Release [`Key::A`]\n    pub fn key_combination(&self, keys: &[Key]) {\n        for key in keys {\n            self.key_down(*key);\n        }\n        for key in keys.iter().rev() {\n            self.key_up(*key);\n        }\n    }\n\n    /// Press the given keys in combination, with modifiers.\n    ///\n    /// For e.g. [`Modifiers::COMMAND`] + [`Key::A`] + [`Key::B`] this would generate:\n    /// - Press [`Modifiers::COMMAND`]\n    /// - Press [`Key::A`]\n    /// - Press [`Key::B`]\n    /// - Release [`Key::B`]\n    /// - Release [`Key::A`]\n    /// - Release [`Modifiers::COMMAND`]\n    pub fn key_combination_modifiers(&self, modifiers: Modifiers, keys: &[Key]) {\n        self.modifiers(modifiers);\n\n        for pressed in [true, false] {\n            for key in keys {\n                self.event(egui::Event::Key {\n                    key: *key,\n                    pressed,\n                    modifiers,\n                    repeat: false,\n                    physical_key: None,\n                });\n            }\n        }\n\n        self.modifiers(Modifiers::default());\n    }\n\n    /// Press a key.\n    ///\n    /// This will create a key down event and a key up event.\n    pub fn key_press(&self, key: egui::Key) {\n        self.key_combination(&[key]);\n    }\n\n    /// Press a key with modifiers.\n    ///\n    /// This will\n    /// - set the modifiers\n    /// - create a key down event\n    /// - create a key up event\n    /// - reset the modifiers\n    pub fn key_press_modifiers(&self, modifiers: Modifiers, key: egui::Key) {\n        self.key_combination_modifiers(modifiers, &[key]);\n    }\n\n    /// Move mouse cursor to this position.\n    pub fn hover_at(&self, pos: egui::Pos2) {\n        self.event(egui::Event::PointerMoved(pos));\n    }\n\n    /// Start dragging from a position.\n    pub fn drag_at(&self, pos: egui::Pos2) {\n        self.event(egui::Event::PointerButton {\n            pos,\n            button: PointerButton::Primary,\n            pressed: true,\n            modifiers: Modifiers::NONE,\n        });\n    }\n\n    /// Stop dragging and remove cursor.\n    pub fn drop_at(&self, pos: egui::Pos2) {\n        self.event(egui::Event::PointerButton {\n            pos,\n            button: PointerButton::Primary,\n            pressed: false,\n            modifiers: Modifiers::NONE,\n        });\n        self.remove_cursor();\n    }\n\n    /// Remove the cursor from the screen.\n    ///\n    /// Will fire a [`egui::Event::PointerGone`] event.\n    ///\n    /// If you click a button and then take a snapshot, the button will be shown as hovered.\n    /// If you don't want that, you can call this method after clicking.\n    pub fn remove_cursor(&self) {\n        self.event(egui::Event::PointerGone);\n    }\n\n    /// Mask something. Useful for snapshot tests.\n    ///\n    /// Call this _after_ [`Self::run`] and before [`Self::snapshot`].\n    /// This will add a [`RectShape`] to the output shapes, for the current frame.\n    /// Will be overwritten on the next call to [`Self::run`].\n    pub fn mask(&mut self, rect: Rect) {\n        self.output.shapes.push(ClippedShape {\n            clip_rect: Rect::EVERYTHING,\n            shape: Shape::Rect(RectShape::filled(rect, 0.0, Color32::MAGENTA)),\n        });\n    }\n\n    /// Render the last output to an image.\n    ///\n    /// # Errors\n    /// Returns an error if the rendering fails.\n    #[cfg(any(feature = \"wgpu\", feature = \"snapshot\"))]\n    pub fn render(&mut self) -> Result<image::RgbaImage, String> {\n        let mut output = self.output.clone();\n\n        if let Some(mouse_pos) = self.ctx.input(|i| i.pointer.hover_pos()) {\n            // Paint a mouse cursor:\n            let triangle = vec![\n                mouse_pos,\n                mouse_pos + egui::vec2(16.0, 8.0),\n                mouse_pos + egui::vec2(8.0, 16.0),\n            ];\n\n            output.shapes.push(ClippedShape {\n                clip_rect: self.ctx.content_rect(),\n                shape: egui::epaint::PathShape::convex_polygon(\n                    triangle,\n                    Color32::WHITE,\n                    egui::Stroke::new(1.0, Color32::BLACK),\n                )\n                .into(),\n            });\n        }\n\n        self.renderer.render(&self.ctx, &output)\n    }\n\n    /// Get the root viewport output\n    fn root_viewport_output(&self) -> &egui::ViewportOutput {\n        self.output\n            .viewport_output\n            .get(&ViewportId::ROOT)\n            .expect(\"Missing root viewport\")\n    }\n\n    /// The root node of the test harness.\n    pub fn root(&self) -> Node<'_> {\n        Node {\n            accesskit_node: self.kittest.root(),\n            queue: &self.queued_events,\n        }\n    }\n\n    #[deprecated = \"Use `Harness::root` instead.\"]\n    pub fn node(&self) -> Node<'_> {\n        self.root()\n    }\n}\n\n/// Utilities for stateless harnesses.\nimpl<'a> Harness<'a> {\n    /// Create a new Harness with the given app closure.\n    /// Use the [`Harness::run`], [`Harness::step`], etc... methods to run the app.\n    ///\n    /// The app closure will immediately be called once to create the initial ui.\n    ///\n    /// If you don't need to create Windows / Panels, you can use [`Harness::new_ui`] instead.\n    ///\n    /// If you e.g. want to customize the size of the window, you can use [`Harness::builder`].\n    ///\n    /// # Example\n    /// ```rust\n    /// # use egui::CentralPanel;\n    /// # use egui_kittest::Harness;\n    /// let mut harness = Harness::new(|ctx| {\n    ///     CentralPanel::default().show(ctx, |ui| {\n    ///         ui.label(\"Hello, world!\");\n    ///     });\n    /// });\n    /// ```\n    #[track_caller]\n    #[deprecated = \"use `new_ui` instead\"]\n    pub fn new(app: impl FnMut(&egui::Context) + 'a) -> Self {\n        #[expect(deprecated)]\n        Self::builder().build(app)\n    }\n\n    /// Create a new Harness with the given ui closure.\n    /// Use the [`Harness::run`], [`Harness::step`], etc... methods to run the app.\n    ///\n    /// The ui closure will immediately be called once to create the initial ui.\n    ///\n    /// If you need to create Windows / Panels, you can use [`Harness::new`] instead.\n    ///\n    /// If you e.g. want to customize the size of the ui, you can use [`Harness::builder`].\n    ///\n    /// # Example\n    /// ```rust\n    /// # use egui_kittest::Harness;\n    /// let mut harness = Harness::new_ui(|ui| {\n    ///     ui.label(\"Hello, world!\");\n    /// });\n    /// ```\n    #[track_caller]\n    pub fn new_ui(app: impl FnMut(&mut egui::Ui) + 'a) -> Self {\n        Self::builder().build_ui(app)\n    }\n}\n\nimpl<'tree, 'node, State> Queryable<'tree, 'node, Node<'tree>> for Harness<'_, State>\nwhere\n    'node: 'tree,\n{\n    fn queryable_node(&'node self) -> Node<'tree> {\n        self.root()\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/src/node.rs",
    "content": "use egui::accesskit::ActionRequest;\nuse egui::mutex::Mutex;\nuse egui::{Modifiers, PointerButton, Pos2, accesskit};\nuse kittest::{AccessKitNode, NodeT, debug_fmt_node};\nuse std::fmt::{Debug, Formatter};\n\npub(crate) enum EventType {\n    Event(egui::Event),\n    Modifiers(Modifiers),\n}\n\npub(crate) type EventQueue = Mutex<Vec<EventType>>;\n\n#[derive(Clone, Copy)]\npub struct Node<'tree> {\n    pub(crate) accesskit_node: AccessKitNode<'tree>,\n    pub(crate) queue: &'tree EventQueue,\n}\n\nimpl Debug for Node<'_> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        debug_fmt_node(self, f)\n    }\n}\n\nimpl<'tree> NodeT<'tree> for Node<'tree> {\n    fn accesskit_node(&self) -> AccessKitNode<'tree> {\n        self.accesskit_node\n    }\n\n    fn new_related(&self, child_node: AccessKitNode<'tree>) -> Self {\n        Self {\n            queue: self.queue,\n            accesskit_node: child_node,\n        }\n    }\n}\n\nimpl Node<'_> {\n    fn event(&self, event: egui::Event) {\n        self.queue.lock().push(EventType::Event(event));\n    }\n\n    fn modifiers(&self, modifiers: Modifiers) {\n        self.queue.lock().push(EventType::Modifiers(modifiers));\n    }\n\n    pub fn hover(&self) {\n        self.event(egui::Event::PointerMoved(self.rect().center()));\n    }\n\n    /// Click at the node center with the primary button.\n    pub fn click(&self) {\n        self.click_button(PointerButton::Primary);\n    }\n\n    #[deprecated = \"Use `click()` instead.\"]\n    pub fn simulate_click(&self) {\n        self.click();\n    }\n\n    pub fn click_secondary(&self) {\n        self.click_button(PointerButton::Secondary);\n    }\n\n    pub fn click_button(&self, button: PointerButton) {\n        self.hover();\n        for pressed in [true, false] {\n            self.event(egui::Event::PointerButton {\n                pos: self.rect().center(),\n                button,\n                pressed,\n                modifiers: Modifiers::default(),\n            });\n        }\n    }\n\n    pub fn click_modifiers(&self, modifiers: Modifiers) {\n        self.click_button_modifiers(PointerButton::Primary, modifiers);\n    }\n\n    pub fn click_button_modifiers(&self, button: PointerButton, modifiers: Modifiers) {\n        self.hover();\n        self.modifiers(modifiers);\n        for pressed in [true, false] {\n            self.event(egui::Event::PointerButton {\n                pos: self.rect().center(),\n                button,\n                pressed,\n                modifiers,\n            });\n        }\n        self.modifiers(Modifiers::default());\n    }\n\n    /// Click the node via accesskit.\n    ///\n    /// This will trigger a [`accesskit::Action::Click`] action.\n    /// In contrast to `click()`, this can also click widgets that are not currently visible.\n    pub fn click_accesskit(&self) {\n        let (target_node, target_tree) = self.accesskit_node.locate();\n        self.event(egui::Event::AccessKitActionRequest(\n            accesskit::ActionRequest {\n                target_node,\n                target_tree,\n                action: accesskit::Action::Click,\n                data: None,\n            },\n        ));\n    }\n\n    pub fn rect(&self) -> egui::Rect {\n        let rect = self\n            .accesskit_node\n            .bounding_box()\n            .expect(\"Every egui node should have a rect\");\n        egui::Rect {\n            min: Pos2::new(rect.x0 as f32, rect.y0 as f32),\n            max: Pos2::new(rect.x1 as f32, rect.y1 as f32),\n        }\n    }\n\n    pub fn focus(&self) {\n        let (target_node, target_tree) = self.accesskit_node.locate();\n        self.event(egui::Event::AccessKitActionRequest(ActionRequest {\n            action: accesskit::Action::Focus,\n            target_node,\n            target_tree,\n            data: None,\n        }));\n    }\n\n    #[deprecated = \"Use `Harness::key_down` instead.\"]\n    pub fn key_down(&self, key: egui::Key) {\n        self.event(egui::Event::Key {\n            key,\n            pressed: true,\n            modifiers: Modifiers::default(),\n            repeat: false,\n            physical_key: None,\n        });\n    }\n\n    #[deprecated = \"Use `Harness::key_up` instead.\"]\n    pub fn key_up(&self, key: egui::Key) {\n        self.event(egui::Event::Key {\n            key,\n            pressed: false,\n            modifiers: Modifiers::default(),\n            repeat: false,\n            physical_key: None,\n        });\n    }\n\n    pub fn type_text(&self, text: &str) {\n        self.event(egui::Event::Text(text.to_owned()));\n    }\n\n    pub fn value(&self) -> Option<String> {\n        self.accesskit_node.value()\n    }\n\n    pub fn is_focused(&self) -> bool {\n        self.accesskit_node.is_focused()\n    }\n\n    /// Scroll the node into view.\n    pub fn scroll_to_me(&self) {\n        let (target_node, target_tree) = self.accesskit_node.locate();\n        self.event(egui::Event::AccessKitActionRequest(ActionRequest {\n            action: accesskit::Action::ScrollIntoView,\n            target_node,\n            target_tree,\n            data: None,\n        }));\n    }\n\n    /// Scroll the [`egui::ScrollArea`] containing this node down (100px).\n    pub fn scroll_down(&self) {\n        let (target_node, target_tree) = self.accesskit_node.locate();\n        self.event(egui::Event::AccessKitActionRequest(ActionRequest {\n            action: accesskit::Action::ScrollDown,\n            target_node,\n            target_tree,\n            data: None,\n        }));\n    }\n\n    /// Scroll the [`egui::ScrollArea`] containing this node up (100px).\n    pub fn scroll_up(&self) {\n        let (target_node, target_tree) = self.accesskit_node.locate();\n        self.event(egui::Event::AccessKitActionRequest(ActionRequest {\n            action: accesskit::Action::ScrollUp,\n            target_node,\n            target_tree,\n            data: None,\n        }));\n    }\n\n    /// Scroll the [`egui::ScrollArea`] containing this node left (100px).\n    pub fn scroll_left(&self) {\n        let (target_node, target_tree) = self.accesskit_node.locate();\n        self.event(egui::Event::AccessKitActionRequest(ActionRequest {\n            action: accesskit::Action::ScrollLeft,\n            target_node,\n            target_tree,\n            data: None,\n        }));\n    }\n\n    /// Scroll the [`egui::ScrollArea`] containing this node right (100px).\n    pub fn scroll_right(&self) {\n        let (target_node, target_tree) = self.accesskit_node.locate();\n        self.event(egui::Event::AccessKitActionRequest(ActionRequest {\n            action: accesskit::Action::ScrollRight,\n            target_node,\n            target_tree,\n            data: None,\n        }));\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/src/renderer.rs",
    "content": "use egui::TexturesDelta;\n\npub trait TestRenderer {\n    /// We use this to pass the glow / wgpu render state to [`eframe::Frame`].\n    #[cfg(feature = \"eframe\")]\n    fn setup_eframe(&self, _cc: &mut eframe::CreationContext<'_>, _frame: &mut eframe::Frame) {}\n\n    /// Handle a [`TexturesDelta`] by updating the renderer's textures.\n    fn handle_delta(&mut self, delta: &TexturesDelta);\n\n    /// Render the [`crate::Harness`] and return the resulting image.\n    ///\n    /// # Errors\n    /// Returns an error if the rendering fails.\n    #[cfg(any(feature = \"wgpu\", feature = \"snapshot\"))]\n    fn render(\n        &mut self,\n        ctx: &egui::Context,\n        output: &egui::FullOutput,\n    ) -> Result<image::RgbaImage, String>;\n}\n\n/// A lazy renderer that initializes the renderer on the first render call.\n///\n/// By default, this will create a wgpu renderer if the wgpu feature is enabled.\npub enum LazyRenderer {\n    Uninitialized {\n        texture_ops: Vec<egui::TexturesDelta>,\n        builder: Option<Box<dyn FnOnce() -> Box<dyn TestRenderer>>>,\n    },\n    Initialized {\n        renderer: Box<dyn TestRenderer>,\n    },\n}\n\nimpl Default for LazyRenderer {\n    fn default() -> Self {\n        #[cfg(feature = \"wgpu\")]\n        return Self::new(crate::wgpu::WgpuTestRenderer::new);\n        #[cfg(not(feature = \"wgpu\"))]\n        return Self::Uninitialized {\n            texture_ops: Vec::new(),\n            builder: None,\n        };\n    }\n}\n\nimpl LazyRenderer {\n    pub fn new<T: TestRenderer + 'static>(create_renderer: impl FnOnce() -> T + 'static) -> Self {\n        Self::Uninitialized {\n            texture_ops: Vec::new(),\n            builder: Some(Box::new(move || Box::new(create_renderer()))),\n        }\n    }\n}\n\nimpl TestRenderer for LazyRenderer {\n    fn handle_delta(&mut self, delta: &TexturesDelta) {\n        match self {\n            Self::Uninitialized { texture_ops, .. } => texture_ops.push(delta.clone()),\n            Self::Initialized { renderer } => renderer.handle_delta(delta),\n        }\n    }\n\n    #[cfg(any(feature = \"wgpu\", feature = \"snapshot\"))]\n    fn render(\n        &mut self,\n        ctx: &egui::Context,\n        output: &egui::FullOutput,\n    ) -> Result<image::RgbaImage, String> {\n        match self {\n            Self::Uninitialized {\n                texture_ops,\n                builder: build,\n            } => {\n                let mut renderer = build.take().ok_or({\n                    \"No default renderer available. \\\n                    Enable the wgpu feature or set one via HarnessBuilder::renderer\"\n                })?();\n                for delta in texture_ops.drain(..) {\n                    renderer.handle_delta(&delta);\n                }\n                let image = renderer.render(ctx, output)?;\n                *self = Self::Initialized { renderer };\n                Ok(image)\n            }\n            Self::Initialized { renderer } => renderer.render(ctx, output),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/src/snapshot.rs",
    "content": "use std::fmt::Display;\nuse std::io::ErrorKind;\nuse std::path::PathBuf;\n\nuse image::ImageError;\n\nuse crate::{Harness, config::config};\n\npub type SnapshotResult = Result<(), SnapshotError>;\n\n#[non_exhaustive]\n#[derive(Clone, Debug)]\npub struct SnapshotOptions {\n    /// The threshold for the image comparison.\n    ///\n    /// Can be configured via kittest.toml. The fallback is `0.6` (which is enough for most egui\n    /// tests to pass across different wgpu backends).\n    pub threshold: f32,\n\n    /// The number of pixels that can differ before the snapshot is considered a failure.\n    ///\n    /// Preferably, you should use `threshold` to control the sensitivity of the image comparison.\n    /// As a last resort, you can use this to allow a certain number of pixels to differ.\n    /// Can be configured via kittest.toml. The fallback is `0` (meaning no pixels can differ).\n    pub failed_pixel_count_threshold: usize,\n\n    /// The path where the snapshots will be saved.\n    ///\n    /// This is relative to the current working directory (usually the crate root when\n    /// running tests).\n    ///\n    /// Can be configured via kittest.toml. The fallback is `tests/snapshots`.\n    pub output_path: PathBuf,\n}\n\n/// Helper struct to define the number of pixels that can differ before the snapshot is considered a failure.\n///\n/// This is useful if you want to set different thresholds for different operating systems.\n///\n/// [`OsThreshold::default`] gets the default from the config file (`kittest.toml`).\n/// For `usize`, it's the `failed_pixel_count_threshold` value.\n/// For `f32`, it's the `threshold` value.\n///\n/// Example usage:\n/// ```no_run\n///  use egui_kittest::{OsThreshold, SnapshotOptions};\n///  let mut harness = egui_kittest::Harness::new_ui(|ui| {\n///      ui.label(\"Hi!\");\n///  });\n///  harness.snapshot_options(\n///      \"os_threshold_example\",\n///      &SnapshotOptions::new()\n///          .threshold(OsThreshold::new(0.0).windows(10.0))\n///          .failed_pixel_count_threshold(OsThreshold::new(0).windows(10).macos(53)\n///  ))\n/// ```\n#[derive(Debug, Clone, Copy)]\npub struct OsThreshold<T> {\n    pub windows: T,\n    pub macos: T,\n    pub linux: T,\n    pub fallback: T,\n}\n\nimpl Default for OsThreshold<usize> {\n    /// Returns the default `failed_pixel_count_threshold` as configured in `kittest.toml`\n    ///\n    /// The fallback is `0`.\n    fn default() -> Self {\n        config().os_failed_pixel_count_threshold()\n    }\n}\n\nimpl Default for OsThreshold<f32> {\n    /// Returns the default `threshold` as configured in `kittest.toml`\n    ///\n    /// The fallback is `0.6`.\n    fn default() -> Self {\n        config().os_threshold()\n    }\n}\n\nimpl From<usize> for OsThreshold<usize> {\n    fn from(value: usize) -> Self {\n        Self::new(value)\n    }\n}\n\nimpl From<f32> for OsThreshold<f32> {\n    fn from(value: f32) -> Self {\n        Self::new(value)\n    }\n}\n\nimpl<T> OsThreshold<T>\nwhere\n    T: Copy,\n{\n    /// Use the same value for all\n    pub fn new(same: T) -> Self {\n        Self {\n            windows: same,\n            macos: same,\n            linux: same,\n            fallback: same,\n        }\n    }\n\n    /// Set the threshold for Windows.\n    #[inline]\n    pub fn windows(mut self, threshold: T) -> Self {\n        self.windows = threshold;\n        self\n    }\n\n    /// Set the threshold for macOS.\n    #[inline]\n    pub fn macos(mut self, threshold: T) -> Self {\n        self.macos = threshold;\n        self\n    }\n\n    /// Set the threshold for Linux.\n    #[inline]\n    pub fn linux(mut self, threshold: T) -> Self {\n        self.linux = threshold;\n        self\n    }\n\n    /// Get the threshold for the current operating system.\n    pub fn threshold(&self) -> T {\n        if cfg!(target_os = \"windows\") {\n            self.windows\n        } else if cfg!(target_os = \"macos\") {\n            self.macos\n        } else if cfg!(target_os = \"linux\") {\n            self.linux\n        } else {\n            self.fallback\n        }\n    }\n}\n\nimpl From<OsThreshold<Self>> for usize {\n    fn from(threshold: OsThreshold<Self>) -> Self {\n        threshold.threshold()\n    }\n}\n\nimpl From<OsThreshold<Self>> for f32 {\n    fn from(threshold: OsThreshold<Self>) -> Self {\n        threshold.threshold()\n    }\n}\n\nimpl Default for SnapshotOptions {\n    fn default() -> Self {\n        Self {\n            threshold: config().threshold(),\n            output_path: config().output_path(),\n            failed_pixel_count_threshold: config().failed_pixel_count_threshold(),\n        }\n    }\n}\n\nimpl SnapshotOptions {\n    /// Create a new [`SnapshotOptions`] with the default values.\n    pub fn new() -> Self {\n        Default::default()\n    }\n\n    /// Change the threshold for the image comparison.\n    /// The default is `0.6` (which is enough for most egui tests to pass across different\n    /// wgpu backends).\n    #[inline]\n    pub fn threshold(mut self, threshold: impl Into<f32>) -> Self {\n        self.threshold = threshold.into();\n        self\n    }\n\n    /// Change the path where the snapshots will be saved.\n    /// The default is `tests/snapshots`.\n    #[inline]\n    pub fn output_path(mut self, output_path: impl Into<PathBuf>) -> Self {\n        self.output_path = output_path.into();\n        self\n    }\n\n    /// Change the number of pixels that can differ before the snapshot is considered a failure.\n    ///\n    /// Preferably, you should use [`Self::threshold`] to control the sensitivity of the image comparison.\n    /// As a last resort, you can use this to allow a certain number of pixels to differ.\n    #[inline]\n    pub fn failed_pixel_count_threshold(\n        mut self,\n        failed_pixel_count_threshold: impl Into<OsThreshold<usize>>,\n    ) -> Self {\n        let failed_pixel_count_threshold = failed_pixel_count_threshold.into().threshold();\n        self.failed_pixel_count_threshold = failed_pixel_count_threshold;\n        self\n    }\n}\n\n#[derive(Debug)]\npub enum SnapshotError {\n    /// Image did not match snapshot\n    Diff {\n        /// Name of the test\n        name: String,\n\n        /// Count of pixels that were different (above the per-pixel threshold).\n        diff: i32,\n\n        /// Path where the diff image was saved\n        diff_path: PathBuf,\n    },\n\n    /// Error opening the existing snapshot (it probably doesn't exist, check the\n    /// [`ImageError`] for more information)\n    OpenSnapshot {\n        /// Path where the snapshot was expected to be\n        path: PathBuf,\n\n        /// The error that occurred\n        err: ImageError,\n    },\n\n    /// The size of the image did not match the snapshot\n    SizeMismatch {\n        /// Name of the test\n        name: String,\n\n        /// Expected size\n        expected: (u32, u32),\n\n        /// Actual size\n        actual: (u32, u32),\n    },\n\n    /// Error writing the snapshot output\n    WriteSnapshot {\n        /// Path where a file was expected to be written\n        path: PathBuf,\n\n        /// The error that occurred\n        err: ImageError,\n    },\n\n    /// Error rendering the image\n    RenderError {\n        /// The error that occurred\n        err: String,\n    },\n}\n\nconst HOW_TO_UPDATE_SCREENSHOTS: &str =\n    \"Run `UPDATE_SNAPSHOTS=1 cargo test --all-features` to update the snapshots.\";\n\nimpl Display for SnapshotError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Diff {\n                name,\n                diff,\n                diff_path,\n            } => {\n                let diff_path =\n                    std::path::absolute(diff_path).unwrap_or_else(|_| diff_path.clone());\n                write!(\n                    f,\n                    \"'{name}' Image did not match snapshot. Diff: {diff}, {}. {HOW_TO_UPDATE_SCREENSHOTS}\",\n                    diff_path.display()\n                )\n            }\n            Self::OpenSnapshot { path, err } => {\n                let path = std::path::absolute(path).unwrap_or_else(|_| path.clone());\n                match err {\n                    ImageError::IoError(io) => match io.kind() {\n                        ErrorKind::NotFound => {\n                            write!(\n                                f,\n                                \"Missing snapshot: {}. {HOW_TO_UPDATE_SCREENSHOTS}\",\n                                path.display()\n                            )\n                        }\n                        err => {\n                            write!(\n                                f,\n                                \"Error reading snapshot: {err}\\nAt: {}. {HOW_TO_UPDATE_SCREENSHOTS}\",\n                                path.display()\n                            )\n                        }\n                    },\n                    err => {\n                        write!(\n                            f,\n                            \"Error decoding snapshot: {err}\\nAt: {}. Make sure git-lfs is setup correctly. Read the instructions here: https://github.com/emilk/egui/blob/main/CONTRIBUTING.md#making-a-pr\",\n                            path.display()\n                        )\n                    }\n                }\n            }\n            Self::SizeMismatch {\n                name,\n                expected,\n                actual,\n            } => {\n                write!(\n                    f,\n                    \"'{name}' Image size did not match snapshot. Expected: {expected:?}, Actual: {actual:?}. {HOW_TO_UPDATE_SCREENSHOTS}\"\n                )\n            }\n            Self::WriteSnapshot { path, err } => {\n                let path = std::path::absolute(path).unwrap_or_else(|_| path.clone());\n                write!(f, \"Error writing snapshot: {err}\\nAt: {}\", path.display())\n            }\n            Self::RenderError { err } => {\n                write!(f, \"Error rendering image: {err}\")\n            }\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\nenum Mode {\n    Test,\n    UpdateFailing,\n    UpdateAll,\n}\n\nimpl Mode {\n    fn from_env() -> Self {\n        let Ok(value) = std::env::var(\"UPDATE_SNAPSHOTS\") else {\n            return Self::Test;\n        };\n\n        match value.as_str() {\n            \"false\" | \"0\" | \"no\" | \"off\" => Self::Test,\n            \"true\" | \"1\" | \"yes\" | \"on\" => Self::UpdateFailing,\n            \"force\" => Self::UpdateAll,\n            unknown => {\n                panic!(\"Unsupported value for UPDATE_SNAPSHOTS: {unknown:?}\");\n            }\n        }\n    }\n\n    fn is_update(&self) -> bool {\n        match self {\n            Self::Test => false,\n            Self::UpdateFailing | Self::UpdateAll => true,\n        }\n    }\n}\n\n/// Image snapshot test with custom options.\n///\n/// If you want to change the default options for your whole project, it's recommended to create a\n/// new `my_image_snapshot` function in your project that calls this function with the desired options.\n/// You could additionally use the\n/// [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/#disallowed_methods)\n/// lint to disable use of the [`image_snapshot`] to prevent accidentally using the wrong defaults.\n///\n/// The snapshot files will be saved under [`SnapshotOptions::output_path`].\n/// The snapshot will be saved under `{output_path}/{name}.png`.\n/// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`.\n/// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`.\n///\n/// If the env-var `UPDATE_SNAPSHOTS` is set, then the old image will backed up under `{output_path}/{name}.old.png`.\n/// and then new image will be written to `{output_path}/{name}.png`\n///\n/// # Errors\n/// Returns a [`SnapshotError`] if the image does not match the snapshot or if there was an error\n/// reading or writing the snapshot.\npub fn try_image_snapshot_options(\n    new: &image::RgbaImage,\n    name: impl Into<String>,\n    options: &SnapshotOptions,\n) -> SnapshotResult {\n    try_image_snapshot_options_impl(new, name.into(), options)\n}\n\nfn try_image_snapshot_options_impl(\n    new: &image::RgbaImage,\n    name: String,\n    options: &SnapshotOptions,\n) -> SnapshotResult {\n    #![expect(clippy::print_stdout)]\n\n    let mode = Mode::from_env();\n\n    let SnapshotOptions {\n        threshold,\n        output_path,\n        failed_pixel_count_threshold,\n    } = options;\n\n    let parent_path = if let Some(parent) = PathBuf::from(&name).parent() {\n        output_path.join(parent)\n    } else {\n        output_path.clone()\n    };\n    std::fs::create_dir_all(parent_path).ok();\n\n    // The one that is checked in to git\n    let snapshot_path = output_path.join(format!(\"{name}.png\"));\n\n    // These should be in .gitignore:\n    let diff_path = output_path.join(format!(\"{name}.diff.png\"));\n    let old_backup_path = output_path.join(format!(\"{name}.old.png\"));\n    let new_path = output_path.join(format!(\"{name}.new.png\"));\n\n    // Delete old temporary files if they exist:\n    std::fs::remove_file(&diff_path).ok();\n    std::fs::remove_file(&old_backup_path).ok();\n    std::fs::remove_file(&new_path).ok();\n\n    let update_snapshot = || {\n        // Keep the old version so the user can compare it:\n        std::fs::rename(&snapshot_path, &old_backup_path).ok();\n\n        // Write the new file to the checked in path:\n        new.save(&snapshot_path)\n            .map_err(|err| SnapshotError::WriteSnapshot {\n                err,\n                path: snapshot_path.clone(),\n            })?;\n\n        // No need for an explicit `.new` file:\n        std::fs::remove_file(&new_path).ok();\n\n        println!(\"Updated snapshot: {}\", snapshot_path.display());\n\n        Ok(())\n    };\n\n    let write_new_png = || {\n        new.save(&new_path)\n            .map_err(|err| SnapshotError::WriteSnapshot {\n                err,\n                path: new_path.clone(),\n            })?;\n        Ok(())\n    };\n\n    let previous = match image::open(&snapshot_path) {\n        Ok(image) => image.to_rgba8(),\n        Err(err) => {\n            // No previous snapshot - probably a new test.\n            if mode.is_update() {\n                return update_snapshot();\n            } else {\n                write_new_png()?;\n\n                return Err(SnapshotError::OpenSnapshot {\n                    path: snapshot_path.clone(),\n                    err,\n                });\n            }\n        }\n    };\n\n    if previous.dimensions() != new.dimensions() {\n        if mode.is_update() {\n            return update_snapshot();\n        } else {\n            write_new_png()?;\n\n            return Err(SnapshotError::SizeMismatch {\n                name,\n                expected: previous.dimensions(),\n                actual: new.dimensions(),\n            });\n        }\n    }\n\n    // Compare existing image to the new one:\n    let threshold = if mode == Mode::UpdateAll {\n        0.0 // Produce diff for any error, however small\n    } else {\n        *threshold\n    };\n\n    let result =\n        dify::diff::get_results(previous, new.clone(), threshold, true, None, &None, &None);\n\n    let Some((num_wrong_pixels, diff_image)) = result else {\n        return Ok(()); // Difference below threshold\n    };\n\n    let below_threshold = num_wrong_pixels as i64 <= *failed_pixel_count_threshold as i64;\n\n    if !below_threshold {\n        diff_image\n            .save(diff_path.clone())\n            .map_err(|err| SnapshotError::WriteSnapshot {\n                path: diff_path.clone(),\n                err,\n            })?;\n    }\n\n    match mode {\n        Mode::Test => {\n            if below_threshold {\n                Ok(())\n            } else {\n                write_new_png()?;\n\n                Err(SnapshotError::Diff {\n                    name,\n                    diff: num_wrong_pixels,\n                    diff_path,\n                })\n            }\n        }\n        Mode::UpdateFailing => {\n            if below_threshold {\n                Ok(())\n            } else {\n                update_snapshot()\n            }\n        }\n        Mode::UpdateAll => update_snapshot(),\n    }\n}\n\n/// Image snapshot test.\n///\n/// This uses the default [`SnapshotOptions`]. Use [`try_image_snapshot_options`] if you want to\n/// e.g. change the threshold or output path.\n///\n/// The snapshot files will be saved under [`SnapshotOptions::output_path`].\n/// The snapshot will be saved under `{output_path}/{name}.png`.\n/// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`.\n/// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`.\n///\n/// # Errors\n/// Returns a [`SnapshotError`] if the image does not match the snapshot or if there was an error\n/// reading or writing the snapshot.\npub fn try_image_snapshot(current: &image::RgbaImage, name: impl Into<String>) -> SnapshotResult {\n    try_image_snapshot_options(current, name, &SnapshotOptions::default())\n}\n\n/// Image snapshot test with custom options.\n///\n/// If you want to change the default options for your whole project, it's recommended to create a\n/// new `my_image_snapshot` function in your project that calls this function with the desired options.\n/// You could additionally use the\n/// [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/#disallowed_methods)\n/// lint to disable use of the [`image_snapshot`] to prevent accidentally using the wrong defaults.\n///\n/// The snapshot files will be saved under [`SnapshotOptions::output_path`].\n/// The snapshot will be saved under `{output_path}/{name}.png`.\n/// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`.\n/// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`.\n///\n/// # Panics\n/// Panics if the image does not match the snapshot or if there was an error reading or writing the\n/// snapshot.\n#[track_caller]\npub fn image_snapshot_options(\n    current: &image::RgbaImage,\n    name: impl Into<String>,\n    options: &SnapshotOptions,\n) {\n    match try_image_snapshot_options(current, name, options) {\n        Ok(_) => {}\n        Err(err) => {\n            panic!(\"{err}\");\n        }\n    }\n}\n\n/// Image snapshot test.\n///\n/// The snapshot will be saved under `tests/snapshots/{name}.png`.\n/// The new image from the last test run will be saved under `tests/snapshots/{name}.new.png`.\n/// If the new image didn't match the snapshot, a diff image will be saved under `tests/snapshots/{name}.diff.png`.\n///\n/// # Panics\n/// Panics if the image does not match the snapshot or if there was an error reading or writing the\n/// snapshot.\n#[track_caller]\npub fn image_snapshot(current: &image::RgbaImage, name: impl Into<String>) {\n    match try_image_snapshot(current, name) {\n        Ok(_) => {}\n        Err(err) => {\n            panic!(\"{err}\");\n        }\n    }\n}\n\n#[cfg(any(feature = \"wgpu\", feature = \"snapshot\"))]\nimpl<State> Harness<'_, State> {\n    /// The default options used for snapshot tests.\n    /// set by [`crate::HarnessBuilder::with_options`].\n    pub fn options(&self) -> &SnapshotOptions {\n        &self.default_snapshot_options\n    }\n\n    /// Render an image using the setup [`crate::TestRenderer`] and compare it to the snapshot\n    /// with custom options.\n    ///\n    /// These options will override the ones set by [`crate::HarnessBuilder::with_options`].\n    ///\n    /// If you want to change the default options for your whole project, you could create an\n    /// [extension trait](http://xion.io/post/code/rust-extension-traits.html) to create a\n    /// new `my_image_snapshot` function on the Harness that calls this function with the desired options.\n    /// You could additionally use the\n    /// [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/#disallowed_methods)\n    /// lint to disable use of the [`Harness::snapshot`] to prevent accidentally using the wrong defaults.\n    ///\n    /// The snapshot files will be saved under [`SnapshotOptions::output_path`].\n    /// The snapshot will be saved under `{output_path}/{name}.png`.\n    /// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`.\n    /// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`.\n    ///\n    /// # Errors\n    /// Returns a [`SnapshotError`] if the image does not match the snapshot, if there was an\n    /// error reading or writing the snapshot, if the rendering fails or if no default renderer is available.\n    pub fn try_snapshot_options(\n        &mut self,\n        name: impl Into<String>,\n        options: &SnapshotOptions,\n    ) -> SnapshotResult {\n        let image = self\n            .render()\n            .map_err(|err| SnapshotError::RenderError { err })?;\n        try_image_snapshot_options(&image, name.into(), options)\n    }\n\n    /// Render an image using the setup [`crate::TestRenderer`] and compare it to the snapshot.\n    ///\n    /// This is like [`Self::try_snapshot_options`] but will use the options set by [`crate::HarnessBuilder::with_options`].\n    ///\n    /// The snapshot will be saved under `tests/snapshots/{name}.png`.\n    /// The new image from the last test run will be saved under `tests/snapshots/{name}.new.png`.\n    /// If the new image didn't match the snapshot, a diff image will be saved under `tests/snapshots/{name}.diff.png`.\n    ///\n    /// # Errors\n    /// Returns a [`SnapshotError`] if the image does not match the snapshot, if there was an\n    /// error reading or writing the snapshot, if the rendering fails or if no default renderer is available.\n    pub fn try_snapshot(&mut self, name: impl Into<String>) -> SnapshotResult {\n        let image = self\n            .render()\n            .map_err(|err| SnapshotError::RenderError { err })?;\n        try_image_snapshot_options(&image, name.into(), &self.default_snapshot_options)\n    }\n\n    /// Render an image using the setup [`crate::TestRenderer`] and compare it to the snapshot\n    /// with custom options.\n    ///\n    /// These options will override the ones set by [`crate::HarnessBuilder::with_options`].\n    ///\n    /// If you want to change the default options for your whole project, you could create an\n    /// [extension trait](http://xion.io/post/code/rust-extension-traits.html) to create a\n    /// new `my_image_snapshot` function on the Harness that calls this function with the desired options.\n    /// You could additionally use the\n    /// [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/#disallowed_methods)\n    /// lint to disable use of the [`Harness::snapshot`] to prevent accidentally using the wrong defaults.\n    ///\n    /// The snapshot files will be saved under [`SnapshotOptions::output_path`].\n    /// The snapshot will be saved under `{output_path}/{name}.png`.\n    /// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`.\n    /// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`.\n    ///\n    /// # Panics\n    /// The result is added to the [`Harness`]'s internal [`SnapshotResults`].\n    ///\n    /// The harness will panic when dropped if there were any snapshot errors.\n    ///\n    /// Errors happen if the image does not match the snapshot, if there was an error reading or writing the\n    /// snapshot, if the rendering fails or if no default renderer is available.\n    #[track_caller]\n    pub fn snapshot_options(&mut self, name: impl Into<String>, options: &SnapshotOptions) {\n        let result = self.try_snapshot_options(name, options);\n        self.snapshot_results.add(result);\n    }\n\n    /// Render an image using the setup [`crate::TestRenderer`] and compare it to the snapshot.\n    ///\n    /// This is like [`Self::snapshot_options`] but will use the options set by [`crate::HarnessBuilder::with_options`].\n    ///\n    /// The snapshot will be saved under `tests/snapshots/{name}.png`.\n    /// The new image from the last test run will be saved under `tests/snapshots/{name}.new.png`.\n    /// If the new image didn't match the snapshot, a diff image will be saved under `tests/snapshots/{name}.diff.png`.\n    ///\n    /// # Panics\n    /// Panics if the image does not match the snapshot, if there was an error reading or writing the\n    /// snapshot, if the rendering fails or if no default renderer is available.\n    #[track_caller]\n    pub fn snapshot(&mut self, name: impl Into<String>) {\n        let result = self.try_snapshot(name);\n        self.snapshot_results.add(result);\n    }\n\n    /// Render a snapshot, save it to a temp file and open it in the default image viewer.\n    ///\n    /// This method is marked as deprecated to trigger errors in CI (so that it's not accidentally\n    /// committed).\n    #[deprecated = \"Only for debugging, don't commit this.\"]\n    #[cfg(not(target_arch = \"wasm32\"))]\n    pub fn debug_open_snapshot(&mut self) {\n        let image = self\n            .render()\n            .map_err(|err| SnapshotError::RenderError { err })\n            .unwrap();\n        let temp_file = tempfile::Builder::new()\n            .disable_cleanup(true) // we keep the file so it's accessible even after the test ends\n            .prefix(\"kittest-snapshot\")\n            .suffix(\".png\")\n            .tempfile()\n            .expect(\"Failed to create temp file\");\n\n        let path = temp_file.path();\n\n        image\n            .save(temp_file.path())\n            .map_err(|err| SnapshotError::WriteSnapshot {\n                err,\n                path: path.to_path_buf(),\n            })\n            .unwrap();\n\n        // Close temp file so it isn't locked when `open` tries to launch it (on Windows)\n        let path = temp_file.into_temp_path();\n\n        #[expect(clippy::print_stdout)]\n        {\n            println!(\"Wrote debug snapshot to: {}\", path.display());\n        }\n        let result = open::that(&path);\n        if let Err(err) = result {\n            #[expect(clippy::print_stderr)]\n            {\n                eprintln!(\n                    \"Failed to open image {} in default image viewer: {err}\",\n                    path.display()\n                );\n            }\n        }\n    }\n\n    /// This removes the snapshot results from the harness. Useful if you e.g. want to merge it\n    /// with the results from another harness (using [`SnapshotResults::add`]).\n    pub fn take_snapshot_results(&mut self) -> SnapshotResults {\n        std::mem::take(&mut self.snapshot_results)\n    }\n}\n\n/// Utility to collect snapshot errors and display them at the end of the test.\n///\n/// # Example\n/// ```\n/// # let harness = MockHarness;\n/// # struct MockHarness;\n/// # impl MockHarness {\n/// #     fn try_snapshot(&self, _: &str) -> Result<(), egui_kittest::SnapshotError> { Ok(()) }\n/// # }\n///\n/// // [...] Construct a Harness\n///\n/// let mut results = egui_kittest::SnapshotResults::new();\n///\n/// // Call add for each snapshot in your test\n/// results.add(harness.try_snapshot(\"my_test\"));\n///\n/// // If there are any errors, SnapshotResults will panic once dropped.\n/// ```\n///\n/// # Panics\n/// Panics if there are any errors when dropped (this way it is impossible to forget to call `unwrap`).\n/// If you don't want to panic, you can use [`SnapshotResults::into_result`] or [`SnapshotResults::into_inner`].\n/// If you want to panic early, you can use [`SnapshotResults::unwrap`].\n#[derive(Debug)]\npub struct SnapshotResults {\n    errors: Vec<SnapshotError>,\n    handled: bool,\n    location: std::panic::Location<'static>,\n}\n\nimpl Default for SnapshotResults {\n    #[track_caller]\n    fn default() -> Self {\n        Self {\n            errors: Vec::new(),\n            handled: true, // If no snapshots were added, we should consider this handled.\n            location: *std::panic::Location::caller(),\n        }\n    }\n}\n\nimpl Display for SnapshotResults {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        if self.errors.is_empty() {\n            write!(f, \"All snapshots passed\")\n        } else {\n            writeln!(f, \"Snapshot errors:\")?;\n            for error in &self.errors {\n                writeln!(f, \"  {error}\")?;\n            }\n            Ok(())\n        }\n    }\n}\n\nimpl SnapshotResults {\n    #[track_caller]\n    pub fn new() -> Self {\n        Default::default()\n    }\n\n    /// Check if the result is an error and add it to the list of errors.\n    pub fn add(&mut self, result: SnapshotResult) {\n        self.handled = false;\n        if let Err(err) = result {\n            self.errors.push(err);\n        }\n    }\n\n    /// Add all errors from another `SnapshotResults`.\n    pub fn extend(&mut self, other: Self) {\n        self.handled = false;\n        self.errors.extend(other.into_inner());\n    }\n\n    /// Add all errors from a [`Harness`].\n    pub fn extend_harness<T>(&mut self, harness: &mut Harness<'_, T>) {\n        self.extend(harness.take_snapshot_results());\n    }\n\n    /// Check if there are any errors.\n    pub fn has_errors(&self) -> bool {\n        !self.errors.is_empty()\n    }\n\n    /// Convert this into a `Result<(), Self>`.\n    #[expect(clippy::missing_errors_doc)]\n    pub fn into_result(self) -> Result<(), Self> {\n        if self.has_errors() { Err(self) } else { Ok(()) }\n    }\n\n    /// Consume this and return the list of errors.\n    pub fn into_inner(mut self) -> Vec<SnapshotError> {\n        self.handled = true;\n        std::mem::take(&mut self.errors)\n    }\n\n    /// Panics if there are any errors, displaying each.\n    #[expect(clippy::unused_self)]\n    pub fn unwrap(self) {\n        // Panic is handled in drop\n    }\n}\n\nimpl From<SnapshotResults> for Vec<SnapshotError> {\n    fn from(results: SnapshotResults) -> Self {\n        results.into_inner()\n    }\n}\n\nimpl Drop for SnapshotResults {\n    #[track_caller]\n    fn drop(&mut self) {\n        // Don't panic if we are already panicking (the test probably failed for another reason)\n        if std::thread::panicking() {\n            return;\n        }\n        #[expect(clippy::manual_assert)]\n        if self.has_errors() {\n            panic!(\"{}\", self);\n        }\n\n        thread_local! {\n            static UNHANDLED_SNAPSHOT_RESULTS_COUNTER: std::cell::RefCell<usize> = const { std::cell::RefCell::new(0) };\n        }\n\n        if !self.handled {\n            let count = UNHANDLED_SNAPSHOT_RESULTS_COUNTER.with(|counter| {\n                let mut count = counter.borrow_mut();\n                *count += 1;\n                *count\n            });\n\n            #[expect(clippy::manual_assert)]\n            if count >= 2 {\n                panic!(\n                    r#\"\nMultiple SnapshotResults were dropped without being handled.\n\nIn order to allow consistent snapshot updates, all snapshot results within a test should be merged in a single SnapshotResults instance.\nUsually this is handled internally in a harness. If you have multiple harnesses, you can merge the results using `Harness::take_snapshot_results` and `SnapshotResults::extend`.\n\nThe SnapshotResult was constructed at {}\n                    \"#,\n                    self.location\n                );\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/src/texture_to_image.rs",
    "content": "use egui_wgpu::wgpu;\nuse egui_wgpu::wgpu::{Device, Extent3d, Queue, Texture};\nuse image::RgbaImage;\nuse std::iter;\nuse std::mem::size_of;\nuse std::sync::mpsc::channel;\n\nuse crate::wgpu::WAIT_TIMEOUT;\n\npub(crate) fn texture_to_image(device: &Device, queue: &Queue, texture: &Texture) -> RgbaImage {\n    let buffer_dimensions =\n        BufferDimensions::new(texture.width() as usize, texture.height() as usize);\n\n    let output_buffer = device.create_buffer(&wgpu::BufferDescriptor {\n        label: Some(\"Texture to bytes output buffer\"),\n        size: (buffer_dimensions.padded_bytes_per_row * buffer_dimensions.height) as u64,\n        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,\n        mapped_at_creation: false,\n    });\n\n    let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {\n        label: Some(\"Texture to bytes encoder\"),\n    });\n\n    // Copy the data from the texture to the buffer\n    encoder.copy_texture_to_buffer(\n        texture.as_image_copy(),\n        wgpu::TexelCopyBufferInfo {\n            buffer: &output_buffer,\n            layout: wgpu::TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(buffer_dimensions.padded_bytes_per_row as u32),\n                rows_per_image: None,\n            },\n        },\n        Extent3d {\n            width: texture.width(),\n            height: texture.height(),\n            depth_or_array_layers: 1,\n        },\n    );\n\n    let submission_index = queue.submit(iter::once(encoder.finish()));\n\n    // Note that we're not calling `.await` here.\n    let buffer_slice = output_buffer.slice(..);\n    // Sets the buffer up for mapping, sending over the result of the mapping back to us when it is finished.\n    let (sender, receiver) = channel();\n    buffer_slice.map_async(wgpu::MapMode::Read, move |v| drop(sender.send(v)));\n\n    // Poll the device in a blocking manner so that our future resolves.\n    device\n        .poll(wgpu::PollType::Wait {\n            submission_index: Some(submission_index),\n            timeout: Some(WAIT_TIMEOUT),\n        })\n        .expect(\"Failed to poll device\");\n\n    receiver.recv().unwrap().unwrap();\n    let buffer_slice = output_buffer.slice(..);\n    let data = buffer_slice.get_mapped_range();\n    let data = data\n        .chunks_exact(buffer_dimensions.padded_bytes_per_row)\n        .flat_map(|row| row.iter().take(buffer_dimensions.unpadded_bytes_per_row))\n        .copied()\n        .collect::<Vec<_>>();\n\n    RgbaImage::from_raw(texture.width(), texture.height(), data).expect(\"Failed to create image\")\n}\n\nstruct BufferDimensions {\n    height: usize,\n    unpadded_bytes_per_row: usize,\n    padded_bytes_per_row: usize,\n}\n\nimpl BufferDimensions {\n    fn new(width: usize, height: usize) -> Self {\n        let bytes_per_pixel = size_of::<u32>();\n        let unpadded_bytes_per_row = width * bytes_per_pixel;\n        let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;\n        let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;\n        let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;\n        Self {\n            height,\n            unpadded_bytes_per_row,\n            padded_bytes_per_row,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/src/wgpu.rs",
    "content": "use std::sync::Arc;\nuse std::{iter::once, time::Duration};\n\nuse egui::TexturesDelta;\nuse egui_wgpu::{RenderState, ScreenDescriptor, WgpuSetup, wgpu};\nuse image::RgbaImage;\n\nuse crate::texture_to_image::texture_to_image;\n\n/// Timeout for waiting on the GPU to finish rendering.\n///\n/// Windows will reset native drivers after 2 seconds of being stuck (known was TDR - timeout detection & recovery).\n/// However, software rasterizers like lavapipe may not do that and take longer if there's a lot of work in flight.\n/// In the end, what we really want to protect here against is undetected errors that lead to device loss\n/// and therefore infinite waits it happens occasionally on MacOS/Metal as of writing.\npub(crate) const WAIT_TIMEOUT: Duration = Duration::from_secs(10);\n\n/// Default wgpu setup used for the wgpu renderer.\npub fn default_wgpu_setup() -> egui_wgpu::WgpuSetup {\n    let mut setup = egui_wgpu::WgpuSetupCreateNew::default();\n\n    // WebGPU not supported yet since we rely on blocking screenshots.\n    setup\n        .instance_descriptor\n        .backends\n        .remove(wgpu::Backends::BROWSER_WEBGPU);\n\n    // Prefer software rasterizers.\n    setup.native_adapter_selector = Some(Arc::new(|adapters, _surface| {\n        let mut adapters = adapters.iter().collect::<Vec<_>>();\n\n        // Adapters are already sorted by preferred backend by wgpu, but let's be explicit.\n        adapters.sort_by_key(|a| match a.get_info().backend {\n            wgpu::Backend::Metal => 0,\n            wgpu::Backend::Vulkan => 1,\n            wgpu::Backend::Dx12 => 2,\n            wgpu::Backend::Gl => 4,\n            wgpu::Backend::BrowserWebGpu => 6,\n            wgpu::Backend::Noop => 7,\n        });\n\n        // Prefer CPU adapters, otherwise if we can't, prefer discrete GPU over integrated GPU.\n        adapters.sort_by_key(|a| match a.get_info().device_type {\n            wgpu::DeviceType::Cpu => 0, // CPU is the best for our purposes!\n            wgpu::DeviceType::DiscreteGpu => 1,\n            wgpu::DeviceType::Other\n            | wgpu::DeviceType::IntegratedGpu\n            | wgpu::DeviceType::VirtualGpu => 2,\n        });\n\n        adapters\n            .first()\n            .map(|a| (*a).clone())\n            .ok_or_else(|| \"No adapter found\".to_owned())\n    }));\n\n    egui_wgpu::WgpuSetup::CreateNew(setup)\n}\n\npub fn create_render_state(setup: WgpuSetup) -> egui_wgpu::RenderState {\n    let instance = pollster::block_on(setup.new_instance());\n\n    pollster::block_on(egui_wgpu::RenderState::create(\n        &egui_wgpu::WgpuConfiguration {\n            wgpu_setup: setup,\n            ..Default::default()\n        },\n        &instance,\n        None,\n        egui_wgpu::RendererOptions::PREDICTABLE,\n    ))\n    .expect(\"Failed to create render state\")\n}\n\n/// Utility to render snapshots from a [`crate::Harness`] using [`egui_wgpu`].\npub struct WgpuTestRenderer {\n    render_state: RenderState,\n}\n\nimpl Default for WgpuTestRenderer {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl WgpuTestRenderer {\n    /// Create a new [`WgpuTestRenderer`] with the default setup.\n    pub fn new() -> Self {\n        Self {\n            render_state: create_render_state(default_wgpu_setup()),\n        }\n    }\n\n    /// Create a new [`WgpuTestRenderer`] with the given setup.\n    pub fn from_setup(setup: WgpuSetup) -> Self {\n        Self {\n            render_state: create_render_state(setup),\n        }\n    }\n\n    /// Create a new [`WgpuTestRenderer`] from an existing [`RenderState`].\n    ///\n    /// # Panics\n    /// Panics if the [`RenderState`] has been used before.\n    pub fn from_render_state(render_state: RenderState) -> Self {\n        assert!(\n            render_state\n                .renderer\n                .read()\n                .texture(&egui::epaint::TextureId::Managed(0))\n                .is_none(),\n            \"The RenderState passed in has been used before, pass in a fresh RenderState instead.\"\n        );\n        Self { render_state }\n    }\n}\n\nimpl crate::TestRenderer for WgpuTestRenderer {\n    #[cfg(feature = \"eframe\")]\n    fn setup_eframe(&self, cc: &mut eframe::CreationContext<'_>, frame: &mut eframe::Frame) {\n        cc.wgpu_render_state = Some(self.render_state.clone());\n        frame.wgpu_render_state = Some(self.render_state.clone());\n    }\n\n    fn handle_delta(&mut self, delta: &TexturesDelta) {\n        let mut renderer = self.render_state.renderer.write();\n        for (id, image) in &delta.set {\n            renderer.update_texture(\n                &self.render_state.device,\n                &self.render_state.queue,\n                *id,\n                image,\n            );\n        }\n    }\n\n    /// Render the [`crate::Harness`] and return the resulting image.\n    fn render(\n        &mut self,\n        ctx: &egui::Context,\n        output: &egui::FullOutput,\n    ) -> Result<RgbaImage, String> {\n        let mut renderer = self.render_state.renderer.write();\n\n        let mut encoder =\n            self.render_state\n                .device\n                .create_command_encoder(&wgpu::CommandEncoderDescriptor {\n                    label: Some(\"Egui Command Encoder\"),\n                });\n\n        let size = ctx.content_rect().size() * ctx.pixels_per_point();\n        let screen = ScreenDescriptor {\n            pixels_per_point: ctx.pixels_per_point(),\n            size_in_pixels: [size.x.round() as u32, size.y.round() as u32],\n        };\n\n        let tessellated = ctx.tessellate(output.shapes.clone(), ctx.pixels_per_point());\n\n        let user_buffers = renderer.update_buffers(\n            &self.render_state.device,\n            &self.render_state.queue,\n            &mut encoder,\n            &tessellated,\n            &screen,\n        );\n\n        let texture = self\n            .render_state\n            .device\n            .create_texture(&wgpu::TextureDescriptor {\n                label: Some(\"Egui Texture\"),\n                size: wgpu::Extent3d {\n                    width: screen.size_in_pixels[0],\n                    height: screen.size_in_pixels[1],\n                    depth_or_array_layers: 1,\n                },\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: self.render_state.target_format,\n                usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,\n                view_formats: &[],\n            });\n\n        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());\n\n        {\n            let mut pass = encoder\n                .begin_render_pass(&wgpu::RenderPassDescriptor {\n                    label: Some(\"Egui Render Pass\"),\n                    color_attachments: &[Some(wgpu::RenderPassColorAttachment {\n                        view: &texture_view,\n                        resolve_target: None,\n                        ops: wgpu::Operations {\n                            load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),\n                            store: wgpu::StoreOp::Store,\n                        },\n                        depth_slice: None,\n                    })],\n                    ..Default::default()\n                })\n                .forget_lifetime();\n\n            renderer.render(&mut pass, &tessellated, &screen);\n        }\n\n        self.render_state\n            .queue\n            .submit(user_buffers.into_iter().chain(once(encoder.finish())));\n\n        self.render_state\n            .device\n            .poll(wgpu::PollType::Wait {\n                submission_index: None,\n                timeout: Some(WAIT_TIMEOUT),\n            })\n            .map_err(|err| format!(\"PollError: {err}\"))?;\n\n        Ok(texture_to_image(\n            &self.render_state.device,\n            &self.render_state.queue,\n            &texture,\n        ))\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/tests/accesskit.rs",
    "content": "//! Tests the accesskit accessibility output of egui.\n\nuse egui::{\n    CentralPanel, Context, RawInput, Ui, Window,\n    accesskit::{NodeId, Role, TreeUpdate},\n};\n\n/// Baseline test that asserts there are no spurious nodes in the\n/// accesskit output when the ui is empty.\n///\n/// This gives reasonable certainty that any nodes appearing in the other accesskit outputs\n/// are put there because of the widgets rendered.\n#[test]\nfn empty_ui_should_return_tree_with_only_root_window() {\n    let output = accesskit_output_single_egui_frame(|_ui| {\n        // Nothing here beyond the default empty UI\n    });\n\n    assert_eq!(\n        output.nodes.len(),\n        2,\n        \"Expected the root node and the top level Ui; found: {output:#?}\",\n    );\n\n    assert_eq!(\n        output\n            .nodes\n            .iter()\n            .filter(|(_, n)| n.role() == Role::GenericContainer)\n            .count(),\n        1,\n        \"Expected a single Ui as a GenericContainer node.\",\n    );\n\n    let (id, root) = &output.nodes[0];\n\n    assert_eq!(*id, output.tree.unwrap().root);\n    assert_eq!(root.role(), Role::Window);\n}\n\n#[test]\nfn button_node() {\n    let button_text = \"This is a test button!\";\n\n    let output = accesskit_output_single_egui_frame(|ui| {\n        CentralPanel::default().show_inside(ui, |ui| ui.button(button_text));\n    });\n\n    let (_, button) = output\n        .nodes\n        .iter()\n        .find(|(_, node)| node.role() == Role::Button)\n        .expect(\"Button should exist in the accesskit output\");\n\n    assert_eq!(button.label(), Some(button_text));\n    assert!(!button.is_disabled());\n}\n\n#[test]\nfn disabled_button_node() {\n    let button_text = \"This is a test button!\";\n\n    let output = accesskit_output_single_egui_frame(|ui| {\n        CentralPanel::default().show_inside(ui, |ui| {\n            ui.add_enabled(false, egui::Button::new(button_text))\n        });\n    });\n\n    let (_, button) = output\n        .nodes\n        .iter()\n        .find(|(_, node)| node.role() == Role::Button)\n        .expect(\"Button should exist in the accesskit output\");\n\n    assert_eq!(button.label(), Some(button_text));\n    assert!(button.is_disabled());\n}\n\n#[test]\nfn toggle_button_node() {\n    let button_text = \"A toggle button\";\n\n    let mut selected = false;\n    let output = accesskit_output_single_egui_frame(|ui| {\n        CentralPanel::default().show_inside(ui, |ui| ui.toggle_value(&mut selected, button_text));\n    });\n\n    let (_, toggle) = output\n        .nodes\n        .iter()\n        .find(|(_, node)| node.role() == Role::Button)\n        .expect(\"Toggle button should exist in the accesskit output\");\n\n    assert_eq!(toggle.label(), Some(button_text));\n    assert!(!toggle.is_disabled());\n}\n\n#[test]\nfn multiple_disabled_widgets() {\n    let output = accesskit_output_single_egui_frame(|ui| {\n        CentralPanel::default().show_inside(ui, |ui| {\n            ui.add_enabled_ui(false, |ui| {\n                let _ = ui.button(\"Button 1\");\n                let _ = ui.button(\"Button 2\");\n                let _ = ui.button(\"Button 3\");\n            })\n        });\n    });\n\n    assert_eq!(\n        output\n            .nodes\n            .iter()\n            .filter(|(_, node)| node.is_disabled())\n            .count(),\n        3,\n        \"All widgets should be disabled.\"\n    );\n}\n\n#[test]\nfn window_children() {\n    let output = accesskit_output_single_egui_frame(|ui| {\n        let mut open = true;\n        Window::new(\"test window\")\n            .open(&mut open)\n            .resizable(false)\n            .show(ui.ctx(), |ui| {\n                let _ = ui.button(\"A button\");\n            });\n    });\n\n    let root = output.tree.as_ref().map(|tree| tree.root).unwrap();\n\n    let window_id = assert_window_exists(&output, \"test window\", root);\n    assert_button_exists(&output, \"A button\", window_id);\n    assert_button_exists(&output, \"Close window\", window_id);\n    assert_button_exists(&output, \"Hide\", window_id);\n}\n\nfn accesskit_output_single_egui_frame(run_ui: impl FnMut(&mut Ui)) -> TreeUpdate {\n    let ctx = Context::default();\n    // Disable animations, so we do not need to wait for animations to end to see the result.\n    ctx.global_style_mut(|style| style.animation_time = 0.0);\n    ctx.enable_accesskit();\n\n    let output = ctx.run_ui(RawInput::default(), run_ui);\n\n    output\n        .platform_output\n        .accesskit_update\n        .expect(\"Missing accesskit update\")\n}\n\n#[track_caller]\nfn assert_button_exists(tree: &TreeUpdate, label: &str, parent: NodeId) {\n    let (node_id, _) = tree\n        .nodes\n        .iter()\n        .find(|(_, node)| {\n            !node.is_hidden() && node.role() == Role::Button && node.label() == Some(label)\n        })\n        .expect(\"No visible button with that label exists.\");\n\n    assert_parent_child(tree, parent, *node_id);\n}\n\n#[track_caller]\nfn assert_window_exists(tree: &TreeUpdate, title: &str, parent: NodeId) -> NodeId {\n    let (node_id, _) = tree\n        .nodes\n        .iter()\n        .find(|(_, node)| {\n            !node.is_hidden() && node.role() == Role::Window && node.label() == Some(title)\n        })\n        .expect(\"No visible window with that title exists.\");\n\n    assert_parent_child(tree, parent, *node_id);\n\n    *node_id\n}\n\n#[track_caller]\nfn assert_parent_child(tree: &TreeUpdate, parent_id: NodeId, child: NodeId) {\n    assert!(\n        has_child_recursively(tree, parent_id, child),\n        \"Node is not a child of the given parent.\"\n    );\n}\n\nfn has_child_recursively(tree: &TreeUpdate, parent: NodeId, child: NodeId) -> bool {\n    let (_, parent) = tree\n        .nodes\n        .iter()\n        .find(|(id, _)| id == &parent)\n        .expect(\"Parent does not exist.\");\n\n    for &c in parent.children() {\n        if c == child || has_child_recursively(tree, c, child) {\n            return true;\n        }\n    }\n\n    false\n}\n"
  },
  {
    "path": "crates/egui_kittest/tests/menu.rs",
    "content": "use egui::containers::menu::{MenuBar, MenuConfig, SubMenuButton};\nuse egui::{PopupCloseBehavior, Ui, include_image};\nuse egui_kittest::{Harness, SnapshotResults};\nuse kittest::Queryable as _;\n\nstruct TestMenu {\n    config: MenuConfig,\n    checked: bool,\n}\n\nimpl TestMenu {\n    fn new(config: MenuConfig) -> Self {\n        Self {\n            config,\n            checked: false,\n        }\n    }\n\n    fn ui(&mut self, ui: &mut Ui) {\n        ui.vertical(|ui| {\n            MenuBar::new().config(self.config.clone()).ui(ui, |ui| {\n                egui::Sides::new().show(\n                    ui,\n                    |ui| {\n                        ui.menu_button(\"Menu A\", |ui| {\n                            _ = ui.button(\"Button in Menu A\");\n                            ui.menu_button(\"Submenu A\", |ui| {\n                                for i in 0..4 {\n                                    _ = ui.button(format!(\"Button {i} in Submenu A\"));\n                                }\n                            });\n                            ui.menu_image_text_button(\n                                include_image!(\"../../eframe/data/icon.png\"),\n                                \"Submenu B with icon\",\n                                |ui| {\n                                    _ = ui.button(\"Button in Submenu B\");\n                                },\n                            );\n                            SubMenuButton::new(\"Submenu C (CloseOnClickOutside)\")\n                                .config(\n                                    MenuConfig::new()\n                                        .close_behavior(PopupCloseBehavior::CloseOnClickOutside),\n                                )\n                                .ui(ui, |ui| {\n                                    _ = ui.button(\"Button in Submenu C\");\n                                    ui.checkbox(&mut self.checked, \"Checkbox in Submenu C\");\n                                    ui.menu_button(\"Submenu D\", |ui| {\n                                        if ui\n                                            .button(\"Button in Submenu D (close on click)\")\n                                            .clicked()\n                                        {\n                                            ui.close();\n                                        }\n                                    });\n                                });\n                        });\n                        ui.menu_image_text_button(\n                            include_image!(\"../../eframe/data/icon.png\"),\n                            \"Menu B with icon\",\n                            |ui| {\n                                _ = ui.button(\"Button in Menu B\");\n                            },\n                        );\n                        _ = ui.button(\"Menu Button\");\n                        ui.menu_button(\"Menu C\", |ui| {\n                            _ = ui.button(\"Button in Menu C\");\n                        });\n                    },\n                    |ui| {\n                        ui.label(\"Some other label\");\n                    },\n                );\n            });\n        });\n    }\n\n    fn into_harness(self) -> Harness<'static, Self> {\n        Harness::builder()\n            .with_size(egui::Vec2::new(500.0, 300.0))\n            .build_ui_state(\n                |ui, menu| {\n                    egui_extras::install_image_loaders(ui.ctx());\n                    menu.ui(ui);\n                },\n                self,\n            )\n    }\n}\n\n#[test]\nfn menu_close_on_click_outside() {\n    // We're intentionally setting CloseOnClick here so we can test if a submenu can override the\n    // close behavior. (Note how Submenu C has CloseOnClickOutside set)\n    let mut harness =\n        TestMenu::new(MenuConfig::new().close_behavior(PopupCloseBehavior::CloseOnClick))\n            .into_harness();\n\n    harness.get_by_label(\"Menu A\").click();\n    harness.run();\n\n    harness\n        .get_by_label_contains(\"Submenu C (CloseOnClickOutside)\")\n        .hover();\n    harness.run();\n\n    // We should be able to check the checkbox without closing the menu\n    // Click a couple of times, just in case\n    for expect_checked in [true, false, true, false] {\n        harness.get_by_label(\"Checkbox in Submenu C\").click();\n        harness.run();\n        assert_eq!(expect_checked, harness.state().checked);\n    }\n\n    // Hovering outside should not close the menu\n    harness.get_by_label(\"Some other label\").hover();\n    harness.run();\n    assert!(harness.query_by_label(\"Checkbox in Submenu C\").is_some());\n\n    // Clicking outside should close the menu\n    harness.get_by_label(\"Some other label\").click();\n    harness.run();\n    assert!(harness.query_by_label(\"Checkbox in Submenu C\").is_none());\n}\n\n#[test]\nfn menu_close_on_click() {\n    let mut harness =\n        TestMenu::new(MenuConfig::new().close_behavior(PopupCloseBehavior::CloseOnClick))\n            .into_harness();\n\n    harness.get_by_label(\"Menu A\").click();\n    harness.run();\n\n    harness.get_by_label_contains(\"Submenu B with icon\").hover();\n    harness.run();\n\n    // Clicking the button should close the menu (even if ui.close() is not called by the button)\n    harness.get_by_label(\"Button in Submenu B\").click();\n    harness.run();\n    assert!(harness.query_by_label(\"Button in Submenu B\").is_none());\n}\n\n#[test]\nfn clicking_submenu_button_should_never_close_menu() {\n    // We test for this since otherwise the menu wouldn't work on touch devices\n    // The other tests use .hover to open submenus, but this test explicitly uses .click\n    let mut harness =\n        TestMenu::new(MenuConfig::new().close_behavior(PopupCloseBehavior::CloseOnClick))\n            .into_harness();\n\n    harness.get_by_label(\"Menu A\").click();\n    harness.run();\n\n    // Clicking the submenu button should not close the menu\n    harness.get_by_label_contains(\"Submenu B with icon\").click();\n    harness.run();\n\n    harness.get_by_label(\"Button in Submenu B\").click();\n    harness.run();\n    assert!(harness.query_by_label(\"Button in Submenu B\").is_none());\n}\n\n#[test]\nfn menu_snapshots() {\n    let mut harness = TestMenu::new(MenuConfig::new()).into_harness();\n\n    let mut results = SnapshotResults::new();\n\n    harness.get_by_label(\"Menu A\").hover();\n    harness.run();\n    results.add(harness.try_snapshot(\"menu/closed_hovered\"));\n\n    harness.get_by_label(\"Menu A\").click();\n    harness.run();\n    results.add(harness.try_snapshot(\"menu/opened\"));\n\n    harness\n        .get_by_label_contains(\"Submenu C (CloseOnClickOutside)\")\n        .hover();\n    harness.run();\n    results.add(harness.try_snapshot(\"menu/submenu\"));\n\n    harness.get_by_label_contains(\"Submenu D\").hover();\n    harness.run();\n    results.add(harness.try_snapshot(\"menu/subsubmenu\"));\n}\n"
  },
  {
    "path": "crates/egui_kittest/tests/popup.rs",
    "content": "use kittest::Queryable as _;\n\n#[test]\nfn test_interactive_tooltip() {\n    struct State {\n        link_clicked: bool,\n    }\n\n    let mut harness = egui_kittest::Harness::new_ui_state(\n        |ui, state| {\n            ui.label(\"I have a tooltip\").on_hover_ui(|ui| {\n                if ui.link(\"link\").clicked() {\n                    state.link_clicked = true;\n                }\n            });\n        },\n        State {\n            link_clicked: false,\n        },\n    );\n\n    harness.get_by_label_contains(\"tooltip\").hover();\n    harness.run();\n    harness.get_by_label(\"link\").hover();\n    harness.run();\n    harness.get_by_label(\"link\").click();\n\n    harness.run();\n\n    assert!(harness.state().link_clicked);\n}\n"
  },
  {
    "path": "crates/egui_kittest/tests/regression_tests.rs",
    "content": "use egui::accesskit::{self, Role};\nuse egui::{Button, ComboBox, Image, Modifiers, Popup, Vec2, Widget as _};\n#[cfg(all(feature = \"wgpu\", feature = \"snapshot\"))]\nuse egui_kittest::SnapshotResults;\nuse egui_kittest::{Harness, kittest::Queryable as _};\n\n#[test]\npub fn focus_should_skip_over_disabled_buttons() {\n    let mut harness = Harness::new_ui(|ui| {\n        ui.add(Button::new(\"Button 1\"));\n        ui.add_enabled(false, Button::new(\"Button Disabled\"));\n        ui.add(Button::new(\"Button 3\"));\n    });\n\n    harness.key_press(egui::Key::Tab);\n    harness.run();\n\n    let button_1 = harness.get_by_label(\"Button 1\");\n    assert!(button_1.is_focused());\n\n    harness.key_press(egui::Key::Tab);\n    harness.run();\n\n    let button_3 = harness.get_by_label(\"Button 3\");\n    assert!(button_3.is_focused());\n\n    harness.key_press(egui::Key::Tab);\n    harness.run();\n\n    let button_1 = harness.get_by_label(\"Button 1\");\n    assert!(button_1.is_focused());\n}\n\n#[test]\npub fn focus_should_skip_over_disabled_drag_values() {\n    let mut value_1: u16 = 1;\n    let mut value_2: u16 = 2;\n    let mut value_3: u16 = 3;\n\n    let mut harness = Harness::new_ui(|ui| {\n        ui.add(egui::DragValue::new(&mut value_1));\n        ui.add_enabled(false, egui::DragValue::new(&mut value_2));\n        ui.add(egui::DragValue::new(&mut value_3));\n    });\n\n    harness.key_press(egui::Key::Tab);\n    harness.run();\n\n    let drag_value_1 = harness.get_by(|node| node.numeric_value() == Some(1.0));\n    assert!(drag_value_1.is_focused());\n\n    harness.key_press(egui::Key::Tab);\n    harness.run();\n\n    let drag_value_3 = harness.get_by(|node| node.numeric_value() == Some(3.0));\n    assert!(drag_value_3.is_focused());\n}\n\n#[test]\nfn image_failed() {\n    let mut harness = Harness::new_ui(|ui| {\n        Image::new(\"file://invalid/path\")\n            .alt_text(\"I have an alt text\")\n            .max_size(Vec2::new(100.0, 100.0))\n            .ui(ui);\n    });\n\n    harness.run();\n    harness.fit_contents();\n\n    #[cfg(all(feature = \"wgpu\", feature = \"snapshot\"))]\n    harness.snapshot(\"image_snapshots\");\n}\n\n#[test]\nfn test_combobox() {\n    let items = [\"Item 1\", \"Item 2\", \"Item 3\"];\n    let mut harness = Harness::builder()\n        .with_size(Vec2::new(300.0, 200.0))\n        .build_ui_state(\n            |ui, selected| {\n                ComboBox::new(\"combobox\", \"Select Something\").show_index(\n                    ui,\n                    selected,\n                    items.len(),\n                    |idx| *items.get(idx).expect(\"Invalid index\"),\n                );\n            },\n            0,\n        );\n\n    harness.run();\n\n    #[cfg(all(feature = \"wgpu\", feature = \"snapshot\"))]\n    let mut results = SnapshotResults::new();\n\n    #[cfg(all(feature = \"wgpu\", feature = \"snapshot\"))]\n    results.add(harness.try_snapshot(\"combobox_closed\"));\n\n    let combobox = harness.get_by_role_and_label(Role::ComboBox, \"Select Something\");\n    combobox.click();\n\n    harness.run();\n\n    #[cfg(all(feature = \"wgpu\", feature = \"snapshot\"))]\n    results.add(harness.try_snapshot(\"combobox_opened\"));\n\n    let item_2 = harness.get_by_role_and_label(Role::Button, \"Item 2\");\n    item_2.click();\n\n    harness.run();\n\n    assert_eq!(harness.state(), &1);\n\n    // Popup should be closed now\n    assert!(harness.query_by_label(\"Item 2\").is_none());\n}\n\n/// `https://github.com/emilk/egui/issues/7065`\n#[test]\npub fn slider_should_move_with_fixed_decimals() {\n    let mut value: f32 = 1.0;\n\n    let mut harness = Harness::new_ui(|ui| {\n        // Movement on arrow-key is relative to slider width; make the slider wide so the movement becomes small.\n        ui.spacing_mut().slider_width = 2000.0;\n        ui.add(egui::Slider::new(&mut value, 0.1..=10.0).fixed_decimals(2));\n    });\n\n    harness.key_press(egui::Key::Tab);\n    harness.run();\n\n    let actual_slider = harness.get_by_role(accesskit::Role::SpinButton);\n    assert_eq!(actual_slider.value(), Some(\"1.00\".to_owned()));\n\n    harness.key_press(egui::Key::ArrowRight);\n    harness.run();\n\n    let actual_slider = harness.get_by_role(accesskit::Role::SpinButton);\n    assert_eq!(actual_slider.value(), Some(\"1.01\".to_owned()));\n\n    harness.key_press(egui::Key::ArrowRight);\n    harness.run();\n\n    let actual_slider = harness.get_by_role(accesskit::Role::SpinButton);\n    assert_eq!(actual_slider.value(), Some(\"1.02\".to_owned()));\n\n    harness.key_press(egui::Key::ArrowLeft);\n    harness.run();\n\n    let actual_slider = harness.get_by_role(accesskit::Role::SpinButton);\n    assert_eq!(actual_slider.value(), Some(\"1.01\".to_owned()));\n\n    harness.key_press(egui::Key::ArrowLeft);\n    harness.run();\n\n    let actual_slider = harness.get_by_role(accesskit::Role::SpinButton);\n    assert_eq!(actual_slider.value(), Some(\"1.00\".to_owned()));\n}\n\n#[test]\npub fn override_text_color_affects_interactive_widgets() {\n    use egui::{Color32, RichText};\n\n    let mut harness = Harness::new_ui(|ui| {\n        _ = ui.button(\"normal\");\n        _ = ui.checkbox(&mut true, \"normal\");\n        _ = ui.radio(true, \"normal\");\n        ui.visuals_mut().widgets.inactive.fg_stroke.color = Color32::RED;\n        _ = ui.button(\"red\");\n        _ = ui.checkbox(&mut true, \"red\");\n        _ = ui.radio(true, \"red\");\n        // override_text_color takes precedence over `WidgetVisuals`, as it docstring claims\n        ui.visuals_mut().override_text_color = Some(Color32::GREEN);\n        _ = ui.button(\"green\");\n        _ = ui.checkbox(&mut true, \"green\");\n        _ = ui.radio(true, \"green\");\n        // Setting the color explicitly with `RichText` overrides style\n        _ = ui.button(RichText::new(\"blue\").color(Color32::BLUE));\n        _ = ui.checkbox(&mut true, RichText::new(\"blue\").color(Color32::BLUE));\n        _ = ui.radio(true, RichText::new(\"blue\").color(Color32::BLUE));\n    });\n\n    #[cfg(all(feature = \"wgpu\", feature = \"snapshot\"))]\n    let mut results = SnapshotResults::new();\n\n    #[cfg(all(feature = \"wgpu\", feature = \"snapshot\"))]\n    results.add(harness.try_snapshot(\"override_text_color_interactive\"));\n}\n\n/// <https://github.com/rerun-io/rerun/issues/11301>\n#[test]\npub fn menus_should_close_even_if_submenu_disappears() {\n    const OTHER_BUTTON: &str = \"Other button\";\n    const MENU_BUTTON: &str = \"Menu\";\n    const SUB_MENU_BUTTON: &str = \"Always here\";\n    const TOGGLEABLE_SUB_MENU_BUTTON: &str = \"Maybe here\";\n    const INSIDE_SUB_MENU_BUTTON: &str = \"Inside submenu\";\n\n    for frame_delay in (0..3).rev() {\n        let mut harness = Harness::builder().build_ui_state(\n            |ui, state| {\n                let _ = ui.button(OTHER_BUTTON).clicked();\n                let response = ui.button(MENU_BUTTON);\n\n                Popup::menu(&response).show(|ui| {\n                    let _ = ui.button(SUB_MENU_BUTTON);\n                    if *state {\n                        ui.menu_button(TOGGLEABLE_SUB_MENU_BUTTON, |ui| {\n                            let _ = ui.button(INSIDE_SUB_MENU_BUTTON);\n                        });\n                    }\n                });\n            },\n            true,\n        );\n\n        // Open the main menu\n        harness.get_by_label(MENU_BUTTON).click();\n        harness.run();\n\n        // Open the sub menu\n        harness\n            .get_by_label_contains(TOGGLEABLE_SUB_MENU_BUTTON)\n            .hover();\n        harness.run();\n\n        // Have we opened the submenu successfully?\n        harness.get_by_label(INSIDE_SUB_MENU_BUTTON).hover();\n        harness.run();\n\n        // We click manually, since we want to precisely time that the sub menu disappears when the\n        // button is released\n        let center = harness.get_by_label(OTHER_BUTTON).rect().center();\n        harness.input_mut().events.push(egui::Event::PointerButton {\n            pos: center,\n            button: egui::PointerButton::Primary,\n            pressed: true,\n            modifiers: Modifiers::default(),\n        });\n        harness.step();\n\n        // Yank the sub menu from under the pointer\n        *harness.state_mut() = false;\n\n        // See if we handle it with or without a frame delay\n        harness.run_steps(frame_delay);\n\n        // Actually close the menu by clicking somewhere outside\n        harness.input_mut().events.push(egui::Event::PointerButton {\n            pos: center,\n            button: egui::PointerButton::Primary,\n            pressed: false,\n            modifiers: Modifiers::default(),\n        });\n\n        harness.run();\n\n        assert!(\n            harness.query_by_label_contains(SUB_MENU_BUTTON).is_none(),\n            \"Menu failed to close. frame_delay = {frame_delay}\"\n        );\n    }\n}\n"
  },
  {
    "path": "crates/egui_kittest/tests/tests.rs",
    "content": "use egui::{Modifiers, ScrollArea, Vec2, include_image};\nuse egui_kittest::{Harness, SnapshotResults};\nuse kittest::Queryable as _;\n\n#[test]\nfn test_shrink() {\n    let mut harness = Harness::new_ui(|ui| {\n        ui.label(\"Hello, world!\");\n        ui.separator();\n        ui.label(\"This is a test\");\n    });\n\n    harness.fit_contents();\n\n    #[cfg(all(feature = \"snapshot\", feature = \"wgpu\"))]\n    harness.snapshot(\"test_shrink\");\n}\n\n#[test]\nfn test_tooltip() {\n    let mut harness = Harness::new_ui(|ui| {\n        ui.label(\"Hello, world!\");\n        ui.separator();\n        ui.label(\"This is a test\")\n            .on_hover_text(\"This\\nis\\na\\nvery\\ntall\\ntooltip!\");\n    });\n\n    harness.fit_contents();\n\n    #[cfg(all(feature = \"snapshot\", feature = \"wgpu\"))]\n    harness.snapshot(\"test_tooltip_hidden\");\n\n    harness.get_by_label(\"This is a test\").hover();\n    harness.run_ok();\n    harness.fit_contents();\n\n    #[cfg(all(feature = \"snapshot\", feature = \"wgpu\"))]\n    harness.snapshot(\"test_tooltip_shown\");\n}\n\n#[test]\nfn test_modifiers() {\n    #[derive(Default)]\n    struct State {\n        cmd_clicked: bool,\n        cmd_z_pressed: bool,\n        cmd_y_pressed: bool,\n    }\n    let mut harness = Harness::new_ui_state(\n        |ui, state| {\n            if ui.button(\"Click me\").clicked() && ui.input(|i| i.modifiers.command) {\n                state.cmd_clicked = true;\n            }\n            if ui.input(|i| i.modifiers.command && i.key_pressed(egui::Key::Z)) {\n                state.cmd_z_pressed = true;\n            }\n            if ui.input(|i| i.modifiers.command && i.key_pressed(egui::Key::Y)) {\n                state.cmd_y_pressed = true;\n            }\n        },\n        State::default(),\n    );\n\n    harness\n        .get_by_label(\"Click me\")\n        .click_modifiers(Modifiers::COMMAND);\n    harness.run();\n\n    harness.key_press_modifiers(Modifiers::COMMAND, egui::Key::Z);\n    harness.run();\n\n    harness.key_combination_modifiers(Modifiers::COMMAND, &[egui::Key::Y]);\n    harness.run();\n\n    let state = harness.state();\n    assert!(state.cmd_clicked, \"The button wasn't command-clicked\");\n    assert!(state.cmd_z_pressed, \"Cmd+Z wasn't pressed\");\n    assert!(state.cmd_y_pressed, \"Cmd+Y wasn't pressed\");\n}\n\n#[test]\nfn should_wait_for_images() {\n    let mut harness = Harness::builder()\n        .with_size(Vec2::new(60.0, 120.0))\n        .build_ui(|ui| {\n            egui_extras::install_image_loaders(ui.ctx());\n            let size = Vec2::splat(30.0);\n            ui.label(\"Url:\");\n            ui.add_sized(\n                size,\n                egui::Image::new(\n                    \"https://raw.githubusercontent.com\\\n                    /emilk/egui/refs/heads/main/crates/eframe/data/icon.png\",\n                ),\n            );\n\n            ui.label(\"Include:\");\n            ui.add_sized(\n                size,\n                egui::Image::new(include_image!(\"../../eframe/data/icon.png\")),\n            );\n        });\n\n    harness.snapshot(\"should_wait_for_images\");\n}\n\nfn test_scroll_harness() -> Harness<'static, bool> {\n    Harness::builder()\n        .with_size(Vec2::new(100.0, 200.0))\n        .build_ui_state(\n            |ui, state| {\n                ScrollArea::vertical().show(ui, |ui| {\n                    for i in 0..20 {\n                        ui.label(format!(\"Item {i}\"));\n                    }\n                    if ui.button(\"Hidden Button\").clicked() {\n                        *state = true;\n                    }\n                });\n            },\n            false,\n        )\n}\n\n#[test]\nfn test_scroll_to_me() {\n    let mut harness = test_scroll_harness();\n    let mut results = SnapshotResults::new();\n\n    results.add(harness.try_snapshot(\"test_scroll_initial\"));\n\n    harness.get_by_label(\"Hidden Button\").scroll_to_me();\n\n    harness.run();\n    results.add(harness.try_snapshot(\"test_scroll_scrolled\"));\n\n    harness.get_by_label(\"Hidden Button\").click();\n    harness.run();\n\n    assert!(\n        harness.state(),\n        \"The button was not clicked after scrolling.\"\n    );\n}\n\n#[test]\nfn test_scroll_down() {\n    let mut harness = test_scroll_harness();\n\n    let button = harness.get_by_label(\"Hidden Button\");\n    button.scroll_down();\n    button.scroll_down();\n    harness.run();\n\n    harness.get_by_label(\"Hidden Button\").click();\n    harness.run();\n\n    assert!(\n        harness.state(),\n        \"The button was not clicked after scrolling down. (Probably not scrolled enough / at all)\"\n    );\n}\n\n#[test]\nfn test_masking() {\n    let mut harness = Harness::new_ui(|ui| {\n        let timestamp = std::time::SystemTime::now()\n            .duration_since(std::time::UNIX_EPOCH)\n            .unwrap()\n            .as_millis();\n\n        ui.label(\"I should not be masked.\");\n        ui.label(format!(\"Timestamp: {timestamp}\"));\n        ui.label(\"I should also not be masked.\");\n    });\n\n    harness.fit_contents();\n\n    let to_be_masked = harness.get_by_label_contains(\"Timestamp: \");\n    harness.mask(to_be_masked.rect());\n\n    harness.snapshot(\"test_masking\");\n}\n\n#[test]\nfn test_remove_cursor() {\n    let hovered = false;\n    let mut harness = Harness::new_ui_state(\n        |ui, state| {\n            let response = ui.button(\"Click me\");\n            *state = response.hovered();\n        },\n        hovered,\n    );\n\n    harness.fit_contents();\n\n    harness.get_by_label(\"Click me\").click();\n    harness.run();\n\n    assert!(harness.state(), \"The button should be hovered\");\n    let hovered_button_snapshot = harness.render().expect(\"Failed to render\");\n\n    harness.remove_cursor();\n    harness.run();\n    assert!(\n        !harness.state(),\n        \"The button should not be hovered after removing cursor\"\n    );\n\n    let non_hovered_button_snapshot = harness.render().expect(\"Failed to render\");\n    assert_ne!(\n        hovered_button_snapshot, non_hovered_button_snapshot,\n        \"The button appearance should change\"\n    );\n}\n"
  },
  {
    "path": "crates/egui_plot/README.md",
    "content": "`egui_plot` has been moved to <https://github.com/emilk/egui_plot>\n"
  },
  {
    "path": "crates/egui_web/CHANGELOG.md",
    "content": "# Legacy changelog for egui_web\nBetween versions 0.17 and 0.18, `egui_web` was absorbed into `eframe`. Most of this changelog was then merged into [the `eframe` changelog](../eframe/CHANGELOG.md). This changelog is now only kept for historical reasons.\n\n\n\n## 0.17.0 - 2022-02-22\n* The default painter is now glow instead of WebGL ([#1020](https://github.com/emilk/egui/pull/1020)).\n* Made the WebGL painter opt-in ([#1020](https://github.com/emilk/egui/pull/1020)).\n* Fixed glow failure on Chromium ([#1092](https://github.com/emilk/egui/pull/1092)).\n* Shift-scroll will now result in horizontal scrolling ([#1136](https://github.com/emilk/egui/pull/1136)).\n* Updated `epi::IntegrationInfo::web_location_hash` on `hashchange` event ([#1140](https://github.com/emilk/egui/pull/1140)).\n* Parse and percent-decode the web location query string ([#1258](https://github.com/emilk/egui/pull/1258)).\n\n\n## 0.16.0 - 2021-12-29\n* Fixed [dark rendering in WebKitGTK](https://github.com/emilk/egui/issues/794) ([#888](https://github.com/emilk/egui/pull/888/)).\n* Added feature `glow` to switch to a [`glow`](https://github.com/grovesNL/glow) based painter ([#868](https://github.com/emilk/egui/pull/868)).\n\n\n## 0.15.0 - 2021-10-24\n### Added\n* Remove \"http\" feature (use https://github.com/emilk/ehttp instead!).\n* Implement `epi::NativeTexture` trait for the WebGL painter.\n* Deprecate `Painter::register_webgl_texture.\n\n### Fixed 🐛\n* Fixed multiline paste.\n* Fixed painting with non-opaque backgrounds.\n* Improve text input on mobile and for IME.\n\n\n## 0.14.1 - 2021-08-28\n### Fixed 🐛\n* Fixed alpha blending for WebGL2 and WebGL1 with sRGB support backends, now having identical results as egui_glium.\n* Fixed use of egui on devices with both touch and mouse.\n\n\n## 0.14.0 - 2021-08-24\n### Added ⭐\n* Added support for dragging and dropping files into the browser window.\n\n### Fixed 🐛\n* Made text thicker and less pixelated.\n\n\n## 0.13.0 - 2021-06-24\n### Changed 🔧\n* Default to light visuals unless the system reports a preference for dark mode.\n\n### Fixed 🐛\n* Improve alpha blending, making fonts look much better (especially in light mode)\n* Fixed double-paste bug\n\n\n## 0.12.0 - 2021-05-10\n### Fixed 🐛\n* Scroll faster when scrolling with mouse wheel.\n\n\n## 0.11.0 - 2021-04-05\n### Added ⭐\n* [Fix mobile and IME text input](https://github.com/emilk/egui/pull/253)\n* Hold down a modifier key when clicking a link to open it in a new tab.\n\nContributors: [n2](https://github.com/n2)\n\n\n## 0.10.0 - 2021-02-28\n### Added ⭐\n* You can control the maximum egui canvas size with `App::max_size_points`.\n\n\n## 0.9.0 - 2021-02-07\n### Added ⭐\n* Right-clicks will no longer open browser context menu.\n\n### Fixed 🐛\n* Fixed a bug where one couldn't select items in a combo box on a touch screen.\n\n\n## 0.8.0 - 2021-01-17\n### Added ⭐\n* WebGL2 is now supported, with improved texture sampler. WebGL1 will be used as a fallback.\n\n### Changed 🔧\n* Slightly improved alpha-blending (work-around for non-existing linear-space blending).\n\n### Fixed 🐛\n* Call prevent_default for arrow keys when entering text\n\n\n## 0.7.0 - 2021-01-04\n### Changed 🔧\n* `http` and `persistence` are now optional (and opt-in) features.\n\n### Fixed 🐛\n* egui_web now compiled without `RUSTFLAGS=--cfg=web_sys_unstable_apis`, but copy/paste won't work.\n\n\n## 0.6.0 - 2020-12-26\n### Added ⭐\n* Auto-save of app state to local storage\n\n### Changed 🔧\n* Set a maximum canvas size to alleviate performance issues on some machines\n* Simplify `egui_web::start` arguments\n\n\n## 0.4.0 - 2020-11-28\n### Added ⭐\n* A simple HTTP fetch API (wraps `web_sys`).\n* Added ability to request a repaint\n* Copy/cut/paste support\n\n### Changed 🔧\n* Automatic repaint every second\n\n### Fixed 🐛\n* Web browser zooming should now work as expected\n* A bunch of bug fixes related to keyboard events\n"
  },
  {
    "path": "crates/egui_web/README.md",
    "content": "`egui_web` used to be a standalone crate, but has now been moved into [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe).\n"
  },
  {
    "path": "crates/emath/CHANGELOG.md",
    "content": "# Changelog for emath\nAll notable changes to the `emath` crate will be noted in this file.\n\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n## 0.33.3 - 2025-12-11\nNothing new\n\n\n## 0.33.2 - 2025-11-13\n* Fix edge cases in \"smart aiming\" in sliders [#7680](https://github.com/emilk/egui/pull/7680) by [@emilk](https://github.com/emilk)\n\n\n## 0.33.0 - 2025-10-09\n* Add `emath::fast_midpoint` [#7435](https://github.com/emilk/egui/pull/7435) by [@emilk](https://github.com/emilk)\n* Generate changelogs for emath [#7513](https://github.com/emilk/egui/pull/7513) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Improve `OrderedFloat` hash performance [#7512](https://github.com/emilk/egui/pull/7512) by [@valadaptive](https://github.com/valadaptive)\n* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)\n\n"
  },
  {
    "path": "crates/emath/Cargo.toml",
    "content": "[package]\nname = \"emath\"\nversion.workspace = true\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\ndescription = \"Minimal 2D math library for GUI work\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui/tree/main/crates/emath\"\nlicense.workspace = true\nreadme = \"README.md\"\nrepository = \"https://github.com/emilk/egui/tree/main/crates/emath\"\ncategories = [\"mathematics\", \"gui\"]\nkeywords = [\"math\", \"gui\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"Cargo.toml\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[lib]\n\n\n[features]\ndefault = []\n\n\n[dependencies]\n#! ### Optional dependencies\n\n## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast `emath` types to `&[u8]`.\nbytemuck = { workspace = true, optional = true, features = [\"derive\"] }\n\n## Enable this when generating docs.\ndocument-features = { workspace = true, optional = true }\n\n## [`mint`](https://docs.rs/mint) enables interoperability with other math libraries such as [`glam`](https://docs.rs/glam) and [`nalgebra`](https://docs.rs/nalgebra).\nmint = { workspace = true, optional = true }\n\n## Allow serialization using [`serde`](https://docs.rs/serde).\nserde = { workspace = true, optional = true }\n"
  },
  {
    "path": "crates/emath/README.md",
    "content": "# emath - egui math library\n\n[![Latest version](https://img.shields.io/crates/v/emath.svg)](https://crates.io/crates/emath)\n[![Documentation](https://docs.rs/emath/badge.svg)](https://docs.rs/emath)\n[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\nA bare-bones 2D math library with types and functions useful for GUI building.\n\nMade for [`egui`](https://github.com/emilk/egui/).\n"
  },
  {
    "path": "crates/emath/src/align.rs",
    "content": "//! One- and two-dimensional alignment ([`Align::Center`], [`Align2::LEFT_TOP`] etc).\n\nuse crate::{Pos2, Rangef, Rect, Vec2, pos2, vec2};\n\n/// left/center/right or top/center/bottom alignment for e.g. anchors and layouts.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum Align {\n    /// Left or top.\n    #[default]\n    Min,\n\n    /// Horizontal or vertical center.\n    Center,\n\n    /// Right or bottom.\n    Max,\n}\n\nimpl Align {\n    /// Convenience for [`Self::Min`]\n    pub const LEFT: Self = Self::Min;\n\n    /// Convenience for [`Self::Max`]\n    pub const RIGHT: Self = Self::Max;\n\n    /// Convenience for [`Self::Min`]\n    pub const TOP: Self = Self::Min;\n\n    /// Convenience for [`Self::Max`]\n    pub const BOTTOM: Self = Self::Max;\n\n    /// Convert `Min => 0.0`, `Center => 0.5` or `Max => 1.0`.\n    #[inline(always)]\n    pub fn to_factor(self) -> f32 {\n        match self {\n            Self::Min => 0.0,\n            Self::Center => 0.5,\n            Self::Max => 1.0,\n        }\n    }\n\n    /// Convert `Min => -1.0`, `Center => 0.0` or `Max => 1.0`.\n    #[inline(always)]\n    pub fn to_sign(self) -> f32 {\n        match self {\n            Self::Min => -1.0,\n            Self::Center => 0.0,\n            Self::Max => 1.0,\n        }\n    }\n\n    /// Returns the inverse alignment.\n    /// `Min` becomes `Max`, `Center` stays the same, `Max` becomes `Min`.\n    pub fn flip(self) -> Self {\n        match self {\n            Self::Min => Self::Max,\n            Self::Center => Self::Center,\n            Self::Max => Self::Min,\n        }\n    }\n\n    /// Returns a range of given size within a specified range.\n    ///\n    /// If the requested `size` is bigger than the size of `range`, then the returned\n    /// range will not fit into the available `range`. The extra space will be allocated\n    /// from:\n    ///\n    /// |Align |Side        |\n    /// |------|------------|\n    /// |Min   |right (end) |\n    /// |Center|both        |\n    /// |Max   |left (start)|\n    ///\n    /// # Examples\n    /// ```\n    /// use std::f32::{INFINITY, NEG_INFINITY};\n    /// use emath::Align::*;\n    ///\n    /// // The size is smaller than a range\n    /// assert_eq!(Min   .align_size_within_range(2.0, 10.0..=20.0), 10.0..=12.0);\n    /// assert_eq!(Center.align_size_within_range(2.0, 10.0..=20.0), 14.0..=16.0);\n    /// assert_eq!(Max   .align_size_within_range(2.0, 10.0..=20.0), 18.0..=20.0);\n    ///\n    /// // The size is bigger than a range\n    /// assert_eq!(Min   .align_size_within_range(20.0, 10.0..=20.0), 10.0..=30.0);\n    /// assert_eq!(Center.align_size_within_range(20.0, 10.0..=20.0),  5.0..=25.0);\n    /// assert_eq!(Max   .align_size_within_range(20.0, 10.0..=20.0),  0.0..=20.0);\n    ///\n    /// // The size is infinity, but range is finite - a special case of a previous example\n    /// assert_eq!(Min   .align_size_within_range(INFINITY, 10.0..=20.0),         10.0..=INFINITY);\n    /// assert_eq!(Center.align_size_within_range(INFINITY, 10.0..=20.0), NEG_INFINITY..=INFINITY);\n    /// assert_eq!(Max   .align_size_within_range(INFINITY, 10.0..=20.0), NEG_INFINITY..=20.0);\n    /// ```\n    ///\n    /// The infinity-sized ranges can produce a surprising results, if the size is also infinity,\n    /// use such ranges with carefully!\n    ///\n    /// ```\n    /// use std::f32::{INFINITY, NEG_INFINITY};\n    /// use emath::Align::*;\n    ///\n    /// // Allocating a size aligned for infinity bound will lead to empty ranges!\n    /// assert_eq!(Min   .align_size_within_range(2.0, 10.0..=INFINITY),     10.0..=12.0);\n    /// assert_eq!(Center.align_size_within_range(2.0, 10.0..=INFINITY), INFINITY..=INFINITY);// (!)\n    /// assert_eq!(Max   .align_size_within_range(2.0, 10.0..=INFINITY), INFINITY..=INFINITY);// (!)\n    ///\n    /// assert_eq!(Min   .align_size_within_range(2.0, NEG_INFINITY..=20.0), NEG_INFINITY..=NEG_INFINITY);// (!)\n    /// assert_eq!(Center.align_size_within_range(2.0, NEG_INFINITY..=20.0), NEG_INFINITY..=NEG_INFINITY);// (!)\n    /// assert_eq!(Max   .align_size_within_range(2.0, NEG_INFINITY..=20.0),         18.0..=20.0);\n    ///\n    ///\n    /// // The infinity size will always return the given range if it has at least one infinity bound\n    /// assert_eq!(Min   .align_size_within_range(INFINITY, 10.0..=INFINITY), 10.0..=INFINITY);\n    /// assert_eq!(Center.align_size_within_range(INFINITY, 10.0..=INFINITY), 10.0..=INFINITY);\n    /// assert_eq!(Max   .align_size_within_range(INFINITY, 10.0..=INFINITY), 10.0..=INFINITY);\n    ///\n    /// assert_eq!(Min   .align_size_within_range(INFINITY, NEG_INFINITY..=20.0), NEG_INFINITY..=20.0);\n    /// assert_eq!(Center.align_size_within_range(INFINITY, NEG_INFINITY..=20.0), NEG_INFINITY..=20.0);\n    /// assert_eq!(Max   .align_size_within_range(INFINITY, NEG_INFINITY..=20.0), NEG_INFINITY..=20.0);\n    /// ```\n    #[inline]\n    pub fn align_size_within_range(self, size: f32, range: impl Into<Rangef>) -> Rangef {\n        let range = range.into();\n        let Rangef { min, max } = range;\n\n        if max - min == f32::INFINITY && size == f32::INFINITY {\n            return range;\n        }\n\n        match self {\n            Self::Min => Rangef::new(min, min + size),\n            Self::Center => {\n                if size == f32::INFINITY {\n                    Rangef::new(f32::NEG_INFINITY, f32::INFINITY)\n                } else {\n                    let left = crate::fast_midpoint(min, max) - size / 2.0;\n                    Rangef::new(left, left + size)\n                }\n            }\n            Self::Max => Rangef::new(max - size, max),\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Two-dimension alignment, e.g. [`Align2::LEFT_TOP`].\n#[derive(Clone, Copy, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Align2(pub [Align; 2]);\n\nimpl Align2 {\n    pub const LEFT_BOTTOM: Self = Self([Align::Min, Align::Max]);\n    pub const LEFT_CENTER: Self = Self([Align::Min, Align::Center]);\n    pub const LEFT_TOP: Self = Self([Align::Min, Align::Min]);\n    pub const CENTER_BOTTOM: Self = Self([Align::Center, Align::Max]);\n    pub const CENTER_CENTER: Self = Self([Align::Center, Align::Center]);\n    pub const CENTER_TOP: Self = Self([Align::Center, Align::Min]);\n    pub const RIGHT_BOTTOM: Self = Self([Align::Max, Align::Max]);\n    pub const RIGHT_CENTER: Self = Self([Align::Max, Align::Center]);\n    pub const RIGHT_TOP: Self = Self([Align::Max, Align::Min]);\n}\n\nimpl Align2 {\n    /// Returns an alignment by the X (horizontal) axis\n    #[inline(always)]\n    pub fn x(self) -> Align {\n        self.0[0]\n    }\n\n    /// Returns an alignment by the Y (vertical) axis\n    #[inline(always)]\n    pub fn y(self) -> Align {\n        self.0[1]\n    }\n\n    /// -1, 0, or +1 for each axis\n    pub fn to_sign(self) -> Vec2 {\n        vec2(self.x().to_sign(), self.y().to_sign())\n    }\n\n    /// Flip on the x-axis\n    /// e.g. `TOP_LEFT` -> `TOP_RIGHT`\n    pub fn flip_x(self) -> Self {\n        Self([self.x().flip(), self.y()])\n    }\n\n    /// Flip on the y-axis\n    /// e.g. `TOP_LEFT` -> `BOTTOM_LEFT`\n    pub fn flip_y(self) -> Self {\n        Self([self.x(), self.y().flip()])\n    }\n\n    /// Flip on both axes\n    /// e.g. `TOP_LEFT` -> `BOTTOM_RIGHT`\n    pub fn flip(self) -> Self {\n        Self([self.x().flip(), self.y().flip()])\n    }\n\n    /// Used e.g. to anchor a piece of text to a part of the rectangle.\n    /// Give a position within the rect, specified by the aligns\n    pub fn anchor_rect(self, rect: Rect) -> Rect {\n        let x = match self.x() {\n            Align::Min => rect.left(),\n            Align::Center => rect.left() - 0.5 * rect.width(),\n            Align::Max => rect.left() - rect.width(),\n        };\n        let y = match self.y() {\n            Align::Min => rect.top(),\n            Align::Center => rect.top() - 0.5 * rect.height(),\n            Align::Max => rect.top() - rect.height(),\n        };\n        Rect::from_min_size(pos2(x, y), rect.size())\n    }\n\n    /// Use this anchor to position something around `pos`,\n    /// e.g. [`Self::RIGHT_TOP`] means the right-top of the rect\n    /// will end up at `pos`.\n    pub fn anchor_size(self, pos: Pos2, size: Vec2) -> Rect {\n        let x = match self.x() {\n            Align::Min => pos.x,\n            Align::Center => pos.x - 0.5 * size.x,\n            Align::Max => pos.x - size.x,\n        };\n        let y = match self.y() {\n            Align::Min => pos.y,\n            Align::Center => pos.y - 0.5 * size.y,\n            Align::Max => pos.y - size.y,\n        };\n        Rect::from_min_size(pos2(x, y), size)\n    }\n\n    /// e.g. center a size within a given frame\n    pub fn align_size_within_rect(self, size: Vec2, frame: Rect) -> Rect {\n        let x_range = self.x().align_size_within_range(size.x, frame.x_range());\n        let y_range = self.y().align_size_within_range(size.y, frame.y_range());\n        Rect::from_x_y_ranges(x_range, y_range)\n    }\n\n    /// Returns the point on the rect's frame or in the center of a rect according\n    /// to the alignments of this object.\n    ///\n    /// ```text\n    /// (*)-----------+------(*)------+-----------(*)--> X\n    ///  |            |               |            |\n    ///  |  Min, Min  |  Center, Min  |  Max, Min  |\n    ///  |            |               |            |\n    ///  +------------+---------------+------------+\n    ///  |            |               |            |\n    /// (*)Min, Center|Center(*)Center|Max, Center(*)\n    ///  |            |               |            |\n    ///  +------------+---------------+------------+\n    ///  |            |               |            |\n    ///  |  Min, Max  | Center, Max   |  Max, Max  |\n    ///  |            |               |            |\n    /// (*)-----------+------(*)------+-----------(*)\n    ///  |\n    ///  Y\n    /// ```\n    pub fn pos_in_rect(self, frame: &Rect) -> Pos2 {\n        let x = match self.x() {\n            Align::Min => frame.left(),\n            Align::Center => frame.center().x,\n            Align::Max => frame.right(),\n        };\n        let y = match self.y() {\n            Align::Min => frame.top(),\n            Align::Center => frame.center().y,\n            Align::Max => frame.bottom(),\n        };\n\n        pos2(x, y)\n    }\n}\n\nimpl std::ops::Index<usize> for Align2 {\n    type Output = Align;\n\n    #[inline(always)]\n    fn index(&self, index: usize) -> &Align {\n        &self.0[index]\n    }\n}\n\nimpl std::ops::IndexMut<usize> for Align2 {\n    #[inline(always)]\n    fn index_mut(&mut self, index: usize) -> &mut Align {\n        &mut self.0[index]\n    }\n}\n\n/// Allocates a rectangle of the specified `size` inside the `frame` rectangle\n/// around of its center.\n///\n/// If `size` is bigger than the `frame`s size the returned rect will bounce out\n/// of the `frame`.\npub fn center_size_in_rect(size: Vec2, frame: Rect) -> Rect {\n    Align2::CENTER_CENTER.align_size_within_rect(size, frame)\n}\n\nimpl std::fmt::Debug for Align2 {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"Align2({:?}, {:?})\", self.x(), self.y())\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/easing.rs",
    "content": "//! Easing functions for animations.\n//!\n//! Contains most easing functions from <https://easings.net/>.\n//!\n//! All functions take a value in `[0, 1]` and return a value in `[0, 1]`.\n//!\n//! Derived from <https://github.com/warrenm/AHEasing/blob/master/AHEasing/easing.c>.\nuse std::f32::consts::PI;\n\n#[inline]\nfn powf(base: f32, exp: f32) -> f32 {\n    base.powf(exp)\n}\n\n/// No easing, just `y = x`\n#[inline]\npub fn linear(t: f32) -> f32 {\n    t\n}\n\n/// <https://easings.net/#easeInQuad>\n///\n/// Modeled after the parabola `y = x^2`\n#[inline]\npub fn quadratic_in(t: f32) -> f32 {\n    t * t\n}\n\n/// <https://easings.net/#easeOutQuad>\n///\n/// Same as `1.0 - quadratic_in(1.0 - t)`.\n#[inline]\npub fn quadratic_out(t: f32) -> f32 {\n    -(t * (t - 2.))\n}\n\n/// <https://easings.net/#easeInOutQuad>\n#[inline]\npub fn quadratic_in_out(t: f32) -> f32 {\n    if t < 0.5 {\n        2. * t * t\n    } else {\n        (-2. * t * t) + (4. * t) - 1.\n    }\n}\n\n/// <https://easings.net/#easeInCubic>\n///\n/// Modeled after the parabola `y = x^3`\n#[inline]\npub fn cubic_in(t: f32) -> f32 {\n    t * t * t\n}\n\n/// <https://easings.net/#easeOutCubic>\n#[inline]\npub fn cubic_out(t: f32) -> f32 {\n    let f = t - 1.;\n    f * f * f + 1.\n}\n\n/// <https://easings.net/#easeInOutCubic>\n#[inline]\npub fn cubic_in_out(t: f32) -> f32 {\n    if t < 0.5 {\n        4. * t * t * t\n    } else {\n        let f = (2. * t) - 2.;\n        0.5 * f * f * f + 1.\n    }\n}\n\n/// <https://easings.net/#easeInSine>\n///\n/// Modeled after quarter-cycle of sine wave\n#[inline]\npub fn sin_in(t: f32) -> f32 {\n    ((t - 1.) * 2. * PI).sin() + 1.\n}\n\n/// <https://easings.net/#easeOuSine>\n///\n/// Modeled after quarter-cycle of sine wave (different phase)\n#[inline]\npub fn sin_out(t: f32) -> f32 {\n    (t * 2. * PI).sin()\n}\n\n/// <https://easings.net/#easeInOutSine>\n///\n/// Modeled after half sine wave\n#[inline]\npub fn sin_in_out(t: f32) -> f32 {\n    0.5 * (1. - (t * PI).cos())\n}\n\n/// <https://easings.net/#easeInCirc>\n///\n/// Modeled after shifted quadrant IV of unit circle\n#[inline]\npub fn circular_in(t: f32) -> f32 {\n    1. - (1. - t * t).sqrt()\n}\n\n/// <https://easings.net/#easeOutCirc>\n///\n/// Modeled after shifted quadrant II of unit circle\n#[inline]\npub fn circular_out(t: f32) -> f32 {\n    (2. - t).sqrt() * t\n}\n\n/// <https://easings.net/#easeInOutCirc>\n#[inline]\npub fn circular_in_out(t: f32) -> f32 {\n    if t < 0.5 {\n        0.5 * (1. - (1. - 4. * t * t).sqrt())\n    } else {\n        0.5 * ((-(2. * t - 3.) * (2. * t - 1.)).sqrt() + 1.)\n    }\n}\n\n/// <https://easings.net/#easeInExpo>\n///\n/// There is a small discontinuity at 0.\n#[inline]\npub fn exponential_in(t: f32) -> f32 {\n    if t == 0. {\n        t\n    } else {\n        powf(2.0, 10. * (t - 1.))\n    }\n}\n\n/// <https://easings.net/#easeOutExpo>\n///\n/// There is a small discontinuity at 1.\n#[inline]\npub fn exponential_out(t: f32) -> f32 {\n    if t == 1. { t } else { 1. - powf(2.0, -10. * t) }\n}\n\n/// <https://easings.net/#easeInOutExpo>\n///\n/// There is a small discontinuity at 0 and 1.\n#[inline]\npub fn exponential_in_out(t: f32) -> f32 {\n    if t == 0. || t == 1. {\n        t\n    } else if t < 0.5 {\n        0.5 * powf(2.0, 20. * t - 10.)\n    } else {\n        0.5 * powf(2.0, -20. * t + 10.) + 1.\n    }\n}\n\n/// <https://easings.net/#easeInBack>\n#[inline]\npub fn back_in(t: f32) -> f32 {\n    t * t * t - t * (t * PI).sin()\n}\n\n/// <https://easings.net/#easeOutBack>\n#[inline]\npub fn back_out(t: f32) -> f32 {\n    let f = 1. - t;\n    1. - (f * f * f - f * (f * PI).sin())\n}\n\n/// <https://easings.net/#easeInOutBack>\n#[inline]\npub fn back_in_out(t: f32) -> f32 {\n    if t < 0.5 {\n        let f = 2. * t;\n        0.5 * (f * f * f - f * (f * PI).sin())\n    } else {\n        let f = 1. - (2. * t - 1.);\n        0.5 * (1. - (f * f * f - f * (f * PI).sin())) + 0.5\n    }\n}\n\n/// <https://easings.net/#easeInBounce>\n///\n/// Each bounce is modelled as a parabola.\n#[inline]\npub fn bounce_in(t: f32) -> f32 {\n    1. - bounce_out(1. - t)\n}\n\n/// <https://easings.net/#easeOutBounce>\n///\n/// Each bounce is modelled as a parabola.\n#[inline]\npub fn bounce_out(t: f32) -> f32 {\n    if t < 4. / 11. {\n        const T2: f32 = 121. / 16.;\n        T2 * t * t\n    } else if t < 8. / 11. {\n        const T2: f32 = 363. / 40.;\n        const T1: f32 = -99. / 10.;\n        const T0: f32 = 17. / 5.;\n        T2 * t * t + T1 * t + T0\n    } else if t < 9. / 10. {\n        const T2: f32 = 4356. / 361.;\n        const T1: f32 = -35442. / 1805.;\n        const T0: f32 = 16061. / 1805.;\n        T2 * t * t + T1 * t + T0\n    } else {\n        const T2: f32 = 54. / 5.;\n        const T1: f32 = -513. / 25.;\n        const T0: f32 = 268. / 25.;\n        T2 * t * t + T1 * t + T0\n    }\n}\n\n/// <https://easings.net/#easeInOutBounce>\n///\n/// Each bounce is modelled as a parabola.\n#[inline]\npub fn bounce_in_out(t: f32) -> f32 {\n    if t < 0.5 {\n        0.5 * bounce_in(t * 2.)\n    } else {\n        0.5 * bounce_out(t * 2. - 1.) + 0.5\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/gui_rounding.rs",
    "content": "/// We (sometimes) round sizes and coordinates to an even multiple of this value.\n///\n/// This is only used for rounding _logical UI points_, used for widget coordinates and sizes.\n/// When rendering, you may want to round to an integer multiple of the physical _pixels_ instead,\n/// using [`GuiRounding::round_to_pixels`].\n///\n/// See [`GuiRounding::round_ui`] for more information.\n///\n/// This constant has to be a (negative) power of two so that it can be represented exactly\n/// by a floating point number.\n///\n/// If we pick too large a value (e.g. 1 or 1/2), then we get judder during scrolling and animations.\n/// If we pick too small a value (e.g. 1/4096), we run the risk of rounding errors again.\n///\n/// `f32` has 23 bits of mantissa, so if we use e.g. 1/8 as the rounding factor,\n/// we can represent all numbers up to 2^20 exactly, which is plenty\n/// (to my knowledge there are no displays that are a million pixels wide).\npub const GUI_ROUNDING: f32 = 1.0 / 32.0;\n\n/// Trait for rounding coordinates and sizes to align with either .\n///\n/// See [`GuiRounding::round_ui`] for more information.\npub trait GuiRounding {\n    /// Rounds floating point numbers to an even multiple of the GUI rounding factor, [`crate::GUI_ROUNDING`].\n    ///\n    /// Use this for widget coordinates and sizes.\n    ///\n    /// Rounding sizes and positions prevent rounding errors when doing sizing calculations.\n    /// We don't round to integers, because that would be too coarse (causing visible juddering when scrolling, for instance).\n    /// Instead we round to an even multiple of [`GUI_ROUNDING`].\n    fn round_ui(self) -> Self;\n\n    /// Like [`Self::round_ui`], but always rounds towards negative infinity.\n    fn floor_ui(self) -> Self;\n\n    /// Round a size or position to an even multiple of the physical pixel size.\n    ///\n    /// This can be useful for crisp rendering.\n    ///\n    /// The `self` should be in coordinates of _logical UI points_.\n    /// The argument `pixels_per_point` is the number of _physical pixels_ per logical UI point.\n    /// For instance, on a high-DPI screen, `pixels_per_point` could be `2.0`.\n    fn round_to_pixels(self, pixels_per_point: f32) -> Self;\n\n    /// Will round the position to be in the center of a pixel.\n    ///\n    /// The pixel size is `1.0 / pixels_per_point`.\n    ///\n    /// So if `pixels_per_point = 2` (i.e. `pixel size = 0.5`),\n    /// then the position will be rounded to the closest of `…, 0.25, 0.75, 1.25, …`.\n    ///\n    /// This is useful, for instance, when picking the center of a line that is one pixel wide.\n    fn round_to_pixel_center(self, pixels_per_point: f32) -> Self;\n}\n\nimpl GuiRounding for f32 {\n    #[inline]\n    fn round_ui(self) -> Self {\n        (self / GUI_ROUNDING).round() * GUI_ROUNDING\n    }\n\n    #[inline]\n    fn floor_ui(self) -> Self {\n        (self / GUI_ROUNDING).floor() * GUI_ROUNDING\n    }\n\n    #[inline]\n    fn round_to_pixels(self, pixels_per_point: f32) -> Self {\n        (self * pixels_per_point).round() / pixels_per_point\n    }\n\n    #[inline]\n    fn round_to_pixel_center(self, pixels_per_point: f32) -> Self {\n        ((self * pixels_per_point - 0.5).round() + 0.5) / pixels_per_point\n    }\n}\n\nimpl GuiRounding for f64 {\n    #[inline]\n    fn round_ui(self) -> Self {\n        (self / GUI_ROUNDING as Self).round() * GUI_ROUNDING as Self\n    }\n\n    #[inline]\n    fn floor_ui(self) -> Self {\n        (self / GUI_ROUNDING as Self).floor() * GUI_ROUNDING as Self\n    }\n\n    #[inline]\n    fn round_to_pixels(self, pixels_per_point: f32) -> Self {\n        (self * pixels_per_point as Self).round() / pixels_per_point as Self\n    }\n\n    #[inline]\n    fn round_to_pixel_center(self, pixels_per_point: f32) -> Self {\n        ((self * pixels_per_point as Self - 0.5).round() + 0.5) / pixels_per_point as Self\n    }\n}\n\nimpl GuiRounding for crate::Vec2 {\n    #[inline]\n    fn round_ui(self) -> Self {\n        Self::new(self.x.round_ui(), self.y.round_ui())\n    }\n\n    #[inline]\n    fn floor_ui(self) -> Self {\n        Self::new(self.x.floor_ui(), self.y.floor_ui())\n    }\n\n    #[inline]\n    fn round_to_pixels(self, pixels_per_point: f32) -> Self {\n        Self::new(\n            self.x.round_to_pixels(pixels_per_point),\n            self.y.round_to_pixels(pixels_per_point),\n        )\n    }\n\n    // This doesn't really make sense for a Vec2, but 🤷‍♂️\n    #[inline]\n    fn round_to_pixel_center(self, pixels_per_point: f32) -> Self {\n        Self::new(\n            self.x.round_to_pixel_center(pixels_per_point),\n            self.y.round_to_pixel_center(pixels_per_point),\n        )\n    }\n}\n\nimpl GuiRounding for crate::Pos2 {\n    #[inline]\n    fn round_ui(self) -> Self {\n        Self::new(self.x.round_ui(), self.y.round_ui())\n    }\n\n    #[inline]\n    fn floor_ui(self) -> Self {\n        Self::new(self.x.floor_ui(), self.y.floor_ui())\n    }\n\n    #[inline]\n    fn round_to_pixels(self, pixels_per_point: f32) -> Self {\n        Self::new(\n            self.x.round_to_pixels(pixels_per_point),\n            self.y.round_to_pixels(pixels_per_point),\n        )\n    }\n\n    #[inline]\n    fn round_to_pixel_center(self, pixels_per_point: f32) -> Self {\n        Self::new(\n            self.x.round_to_pixel_center(pixels_per_point),\n            self.y.round_to_pixel_center(pixels_per_point),\n        )\n    }\n}\n\nimpl GuiRounding for crate::Rect {\n    /// Rounded so that two adjacent rects that tile perfectly\n    /// will continue to tile perfectly.\n    #[inline]\n    fn round_ui(self) -> Self {\n        Self::from_min_max(self.min.round_ui(), self.max.round_ui())\n    }\n\n    /// Rounded so that two adjacent rects that tile perfectly\n    /// will continue to tile perfectly.\n    #[inline]\n    fn floor_ui(self) -> Self {\n        Self::from_min_max(self.min.floor_ui(), self.max.floor_ui())\n    }\n\n    /// Rounded so that two adjacent rects that tile perfectly\n    /// will continue to tile perfectly.\n    #[inline]\n    fn round_to_pixels(self, pixels_per_point: f32) -> Self {\n        Self::from_min_max(\n            self.min.round_to_pixels(pixels_per_point),\n            self.max.round_to_pixels(pixels_per_point),\n        )\n    }\n\n    /// Rounded so that two adjacent rects that tile perfectly\n    /// will continue to tile perfectly.\n    #[inline]\n    fn round_to_pixel_center(self, pixels_per_point: f32) -> Self {\n        Self::from_min_max(\n            self.min.round_to_pixel_center(pixels_per_point),\n            self.max.round_to_pixel_center(pixels_per_point),\n        )\n    }\n}\n\n#[test]\nfn test_gui_rounding() {\n    assert_eq!(0.0_f32.round_ui(), 0.0);\n    assert_eq!((GUI_ROUNDING * 1.11).round_ui(), GUI_ROUNDING);\n    assert_eq!((-GUI_ROUNDING * 1.11).round_ui(), -GUI_ROUNDING);\n    assert_eq!(f32::NEG_INFINITY.round_ui(), f32::NEG_INFINITY);\n    assert_eq!(f32::INFINITY.round_ui(), f32::INFINITY);\n\n    assert_eq!(0.17_f32.round_to_pixel_center(2.0), 0.25);\n}\n"
  },
  {
    "path": "crates/emath/src/history.rs",
    "content": "use std::collections::VecDeque;\n\n/// This struct tracks recent values of some time series.\n///\n/// It can be used as a smoothing filter for e.g. latency, fps etc,\n/// or to show a log or graph of recent events.\n///\n/// It has a minimum and maximum length, as well as a maximum storage time.\n/// * The minimum length is to ensure you have enough data for an estimate.\n/// * The maximum length is to make sure the history doesn't take up too much space.\n/// * The maximum age is to make sure the estimate isn't outdated.\n///\n/// Time difference between values can be zero, but never negative.\n///\n/// This can be used for things like smoothed averages (for e.g. FPS)\n/// or for smoothed velocity (e.g. mouse pointer speed).\n/// All times are in seconds.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct History<T> {\n    /// In elements, i.e. of `values.len()`.\n    /// The length is initially zero, but once past `min_len` will not shrink below it.\n    min_len: usize,\n\n    /// In elements, i.e. of `values.len()`.\n    max_len: usize,\n\n    /// In seconds.\n    max_age: f32,\n\n    /// Total number of elements seen ever\n    total_count: u64,\n\n    /// (time, value) pairs, oldest front, newest back.\n    /// Time difference between values can be zero, but never negative.\n    values: VecDeque<(f64, T)>,\n}\n\nimpl<T> History<T>\nwhere\n    T: Copy,\n{\n    /// Example:\n    /// ```\n    /// # use emath::History;\n    /// # fn now() -> f64 { 0.0 }\n    /// // Drop events that are older than one second,\n    /// // as long we keep at least two events. Never keep more than a hundred events.\n    /// let mut history = History::new(2..100, 1.0);\n    /// assert_eq!(history.average(), None);\n    /// history.add(now(), 40.0_f32);\n    /// history.add(now(), 44.0_f32);\n    /// assert_eq!(history.average(), Some(42.0));\n    /// ```\n    pub fn new(length_range: std::ops::Range<usize>, max_age: f32) -> Self {\n        Self {\n            min_len: length_range.start,\n            max_len: length_range.end,\n            max_age,\n            total_count: 0,\n            values: Default::default(),\n        }\n    }\n\n    #[inline]\n    pub fn max_len(&self) -> usize {\n        self.max_len\n    }\n\n    #[inline]\n    pub fn max_age(&self) -> f32 {\n        self.max_age\n    }\n\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.values.is_empty()\n    }\n\n    /// Current number of values kept in history\n    #[inline]\n    pub fn len(&self) -> usize {\n        self.values.len()\n    }\n\n    /// Total number of values seen.\n    /// Includes those that have been discarded due to `max_len` or `max_age`.\n    #[inline]\n    pub fn total_count(&self) -> u64 {\n        self.total_count\n    }\n\n    pub fn latest(&self) -> Option<T> {\n        self.values.back().map(|(_, value)| *value)\n    }\n\n    pub fn latest_mut(&mut self) -> Option<&mut T> {\n        self.values.back_mut().map(|(_, value)| value)\n    }\n\n    /// Amount of time contained from start to end in this [`History`].\n    pub fn duration(&self) -> f32 {\n        if let (Some(front), Some(back)) = (self.values.front(), self.values.back()) {\n            (back.0 - front.0) as f32\n        } else {\n            0.0\n        }\n    }\n\n    /// `(time, value)` pairs\n    /// Time difference between values can be zero, but never negative.\n    // TODO(emilk): impl IntoIter\n    pub fn iter(&self) -> impl ExactSizeIterator<Item = (f64, T)> + '_ {\n        self.values.iter().map(|(time, value)| (*time, *value))\n    }\n\n    pub fn values(&self) -> impl ExactSizeIterator<Item = T> + '_ {\n        self.values.iter().map(|(_time, value)| *value)\n    }\n\n    #[inline]\n    pub fn clear(&mut self) {\n        self.values.clear();\n    }\n\n    /// Values must be added with a monotonically increasing time, or at least not decreasing.\n    pub fn add(&mut self, now: f64, value: T) {\n        if let Some((last_time, _)) = self.values.back() {\n            debug_assert!(*last_time <= now, \"Time shouldn't move backwards\");\n        }\n        self.total_count += 1;\n        self.values.push_back((now, value));\n        self.flush(now);\n    }\n\n    /// Mean time difference between values in this [`History`].\n    pub fn mean_time_interval(&self) -> Option<f32> {\n        if let (Some(first), Some(last)) = (self.values.front(), self.values.back()) {\n            let n = self.len();\n            if n >= 2 {\n                Some((last.0 - first.0) as f32 / ((n - 1) as f32))\n            } else {\n                None\n            }\n        } else {\n            None\n        }\n    }\n\n    // Mean number of events per second.\n    pub fn rate(&self) -> Option<f32> {\n        self.mean_time_interval().map(|time| 1.0 / time)\n    }\n\n    /// Remove samples that are too old.\n    pub fn flush(&mut self, now: f64) {\n        while self.values.len() > self.max_len {\n            self.values.pop_front();\n        }\n        while self.values.len() > self.min_len {\n            if let Some((front_time, _)) = self.values.front() {\n                if *front_time < now - (self.max_age as f64) {\n                    self.values.pop_front();\n                } else {\n                    break;\n                }\n            } else {\n                break;\n            }\n        }\n    }\n}\n\nimpl<T> History<T>\nwhere\n    T: Copy,\n    T: std::iter::Sum,\n    T: std::ops::Div<f32, Output = T>,\n{\n    #[inline]\n    pub fn sum(&self) -> T {\n        self.values().sum()\n    }\n\n    pub fn average(&self) -> Option<T> {\n        let num = self.len();\n        if num > 0 {\n            Some(self.sum() / (num as f32))\n        } else {\n            None\n        }\n    }\n}\n\nimpl<T> History<T>\nwhere\n    T: Copy,\n    T: std::iter::Sum,\n    T: std::ops::Div<f32, Output = T>,\n    T: std::ops::Mul<f32, Output = T>,\n{\n    /// Average times rate.\n    /// If you are keeping track of individual sizes of things (e.g. bytes),\n    /// this will estimate the bandwidth (bytes per second).\n    pub fn bandwidth(&self) -> Option<T> {\n        Some(self.average()? * self.rate()?)\n    }\n}\n\nimpl<T, Vel> History<T>\nwhere\n    T: Copy,\n    T: std::ops::Sub<Output = Vel>,\n    Vel: std::ops::Div<f32, Output = Vel>,\n{\n    /// Calculate a smooth velocity (per second) over the entire time span.\n    /// Calculated as the last value minus the first value over the elapsed time between them.\n    pub fn velocity(&self) -> Option<Vel> {\n        if let (Some(first), Some(last)) = (self.values.front(), self.values.back()) {\n            let dt = (last.0 - first.0) as f32;\n            if dt > 0.0 {\n                Some((last.1 - first.1) / dt)\n            } else {\n                None\n            }\n        } else {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/lib.rs",
    "content": "//! Opinionated 2D math library for building GUIs.\n//!\n//! Includes vectors, positions, rectangles etc.\n//!\n//! Conventions (unless otherwise specified):\n//!\n//! * All angles are in radians\n//! * X+ is right and Y+ is down.\n//! * (0,0) is left top.\n//! * Dimension order is always `x y`\n//!\n//! ## Integrating with other math libraries.\n//! `emath` does not strive to become a general purpose or all-powerful math library.\n//!\n//! For that, use something else ([`glam`](https://docs.rs/glam), [`nalgebra`](https://docs.rs/nalgebra), …)\n//! and enable the `mint` feature flag in `emath` to enable implicit conversion to/from `emath`.\n//!\n//! ## Feature flags\n#![cfg_attr(feature = \"document-features\", doc = document_features::document_features!())]\n//!\n\n#![expect(clippy::float_cmp)]\n\nuse std::ops::{Add, Div, Mul, RangeInclusive, Sub};\n\n// ----------------------------------------------------------------------------\n\npub mod align;\npub mod easing;\nmod gui_rounding;\nmod history;\nmod numeric;\nmod ordered_float;\nmod pos2;\nmod range;\nmod rect;\nmod rect_align;\nmod rect_transform;\nmod rot2;\npub mod smart_aim;\nmod ts_transform;\nmod vec2;\nmod vec2b;\n\npub use self::{\n    align::{Align, Align2},\n    gui_rounding::{GUI_ROUNDING, GuiRounding},\n    history::History,\n    numeric::*,\n    ordered_float::*,\n    pos2::*,\n    range::Rangef,\n    rect::*,\n    rect_align::RectAlign,\n    rect_transform::*,\n    rot2::*,\n    ts_transform::*,\n    vec2::*,\n    vec2b::*,\n};\n\n// ----------------------------------------------------------------------------\n\n/// Helper trait to implement [`lerp`] and [`remap`].\npub trait One {\n    const ONE: Self;\n}\n\nimpl One for f32 {\n    const ONE: Self = 1.0;\n}\n\nimpl One for f64 {\n    const ONE: Self = 1.0;\n}\n\n/// Helper trait to implement [`lerp`] and [`remap`].\npub trait Real:\n    Copy\n    + PartialEq\n    + PartialOrd\n    + One\n    + Add<Self, Output = Self>\n    + Sub<Self, Output = Self>\n    + Mul<Self, Output = Self>\n    + Div<Self, Output = Self>\n{\n}\n\nimpl Real for f32 {}\n\nimpl Real for f64 {}\n\n// ----------------------------------------------------------------------------\n\n/// Linear interpolation.\n///\n/// ```\n/// # use emath::lerp;\n/// assert_eq!(lerp(1.0..=5.0, 0.0), 1.0);\n/// assert_eq!(lerp(1.0..=5.0, 0.5), 3.0);\n/// assert_eq!(lerp(1.0..=5.0, 1.0), 5.0);\n/// assert_eq!(lerp(1.0..=5.0, 2.0), 9.0);\n/// ```\n#[inline(always)]\npub fn lerp<R, T>(range: impl Into<RangeInclusive<R>>, t: T) -> R\nwhere\n    T: Real + Mul<R, Output = R>,\n    R: Copy + Add<R, Output = R>,\n{\n    let range = range.into();\n    (T::ONE - t) * *range.start() + t * *range.end()\n}\n\n/// This is a faster version of [`f32::midpoint`] which doesn't handle overflow.\n///\n/// ```\n/// # use emath::fast_midpoint;\n/// assert_eq!(fast_midpoint(1.0, 5.0), 3.0);\n/// ```\n#[inline(always)]\npub fn fast_midpoint<R>(a: R, b: R) -> R\nwhere\n    R: Copy + Add<R, Output = R> + Div<R, Output = R> + One,\n{\n    let two = R::ONE + R::ONE;\n    (a + b) / two\n}\n\n/// Where in the range is this value? Returns 0-1 if within the range.\n///\n/// Returns <0 if before and >1 if after.\n///\n/// Returns `None` if the input range is zero-width.\n///\n/// ```\n/// # use emath::inverse_lerp;\n/// assert_eq!(inverse_lerp(1.0..=5.0, 1.0), Some(0.0));\n/// assert_eq!(inverse_lerp(1.0..=5.0, 3.0), Some(0.5));\n/// assert_eq!(inverse_lerp(1.0..=5.0, 5.0), Some(1.0));\n/// assert_eq!(inverse_lerp(1.0..=5.0, 9.0), Some(2.0));\n/// assert_eq!(inverse_lerp(1.0..=1.0, 3.0), None);\n/// ```\n#[inline]\npub fn inverse_lerp<R>(range: RangeInclusive<R>, value: R) -> Option<R>\nwhere\n    R: Copy + PartialEq + Sub<R, Output = R> + Div<R, Output = R>,\n{\n    let min = *range.start();\n    let max = *range.end();\n    if min == max {\n        None\n    } else {\n        Some((value - min) / (max - min))\n    }\n}\n\n/// Linearly remap a value from one range to another,\n/// so that when `x == from.start()` returns `to.start()`\n/// and when `x == from.end()` returns `to.end()`.\npub fn remap<T>(x: T, from: impl Into<RangeInclusive<T>>, to: impl Into<RangeInclusive<T>>) -> T\nwhere\n    T: Real,\n{\n    let from = from.into();\n    let to = to.into();\n    debug_assert!(\n        from.start() != from.end(),\n        \"from.start() and from.end() should not be equal\"\n    );\n    let t = (x - *from.start()) / (*from.end() - *from.start());\n    lerp(to, t)\n}\n\n/// Like [`remap`], but also clamps the value so that the returned value is always in the `to` range.\npub fn remap_clamp<T>(\n    x: T,\n    from: impl Into<RangeInclusive<T>>,\n    to: impl Into<RangeInclusive<T>>,\n) -> T\nwhere\n    T: Real,\n{\n    let from = from.into();\n    let to = to.into();\n    if from.end() < from.start() {\n        return remap_clamp(x, *from.end()..=*from.start(), *to.end()..=*to.start());\n    }\n    if x <= *from.start() {\n        *to.start()\n    } else if *from.end() <= x {\n        *to.end()\n    } else {\n        debug_assert!(\n            from.start() != from.end(),\n            \"from.start() and from.end() should not be equal\"\n        );\n        let t = (x - *from.start()) / (*from.end() - *from.start());\n        // Ensure no numerical inaccuracies sneak in:\n        if T::ONE <= t { *to.end() } else { lerp(to, t) }\n    }\n}\n\n/// Round a value to the given number of decimal places.\npub fn round_to_decimals(value: f64, decimal_places: usize) -> f64 {\n    // This is a stupid way of doing this, but stupid works.\n    format!(\"{value:.decimal_places$}\").parse().unwrap_or(value)\n}\n\npub fn format_with_minimum_decimals(value: f64, decimals: usize) -> String {\n    format_with_decimals_in_range(value, decimals..=6)\n}\n\n/// Use as few decimals as possible to show the value accurately, but within the given range.\n///\n/// Decimals are counted after the decimal point.\npub fn format_with_decimals_in_range(value: f64, decimal_range: RangeInclusive<usize>) -> String {\n    let min_decimals = *decimal_range.start();\n    let max_decimals = *decimal_range.end();\n    debug_assert!(\n        min_decimals <= max_decimals,\n        \"min_decimals should be <= max_decimals, but got min_decimals: {min_decimals}, max_decimals: {max_decimals}\"\n    );\n    debug_assert!(\n        max_decimals < 100,\n        \"max_decimals should be < 100, but got {max_decimals}\"\n    );\n    let max_decimals = max_decimals.min(16);\n    let min_decimals = min_decimals.min(max_decimals);\n\n    if min_decimals < max_decimals {\n        // Ugly/slow way of doing this. TODO(emilk): clean up precision.\n        for decimals in min_decimals..max_decimals {\n            let text = format!(\"{value:.decimals$}\");\n            let epsilon = 16.0 * f32::EPSILON; // margin large enough to handle most peoples round-tripping needs\n            if let Ok(parsed_value) = text.parse::<f32>()\n                && almost_equal(parsed_value, value as f32, epsilon)\n            {\n                // Enough precision to show the value accurately - good!\n                return text;\n            }\n        }\n        // The value has more precision than we expected.\n        // Probably the value was set not by the slider, but from outside.\n        // In any case: show the full value\n    }\n    format!(\"{value:.max_decimals$}\")\n}\n\n/// Return true when arguments are the same within some rounding error.\n///\n/// For instance `almost_equal(x, x.to_degrees().to_radians(), f32::EPSILON)` should hold true for all x.\n/// The `epsilon`  can be `f32::EPSILON` to handle simple transforms (like degrees -> radians)\n/// but should be higher to handle more complex transformations.\npub fn almost_equal(a: f32, b: f32, epsilon: f32) -> bool {\n    if a == b {\n        true // handle infinites\n    } else {\n        let abs_max = a.abs().max(b.abs());\n        abs_max <= epsilon || ((a - b).abs() / abs_max) <= epsilon\n    }\n}\n\n#[expect(clippy::approx_constant)]\n#[test]\nfn test_format() {\n    assert_eq!(format_with_minimum_decimals(1_234_567.0, 0), \"1234567\");\n    assert_eq!(format_with_minimum_decimals(1_234_567.0, 1), \"1234567.0\");\n    assert_eq!(format_with_minimum_decimals(3.14, 2), \"3.14\");\n    assert_eq!(format_with_minimum_decimals(3.14, 3), \"3.140\");\n    assert_eq!(\n        format_with_minimum_decimals(std::f64::consts::PI, 2),\n        \"3.14159\"\n    );\n}\n\n#[test]\nfn test_almost_equal() {\n    for &x in &[\n        0.0_f32,\n        f32::MIN_POSITIVE,\n        1e-20,\n        1e-10,\n        f32::EPSILON,\n        0.1,\n        0.99,\n        1.0,\n        1.001,\n        1e10,\n        f32::MAX / 100.0,\n        // f32::MAX, // overflows in rad<->deg test\n        f32::INFINITY,\n    ] {\n        for &x in &[-x, x] {\n            for roundtrip in &[\n                |x: f32| x.to_degrees().to_radians(),\n                |x: f32| x.to_radians().to_degrees(),\n            ] {\n                let epsilon = f32::EPSILON;\n                assert!(\n                    almost_equal(x, roundtrip(x), epsilon),\n                    \"{} vs {}\",\n                    x,\n                    roundtrip(x)\n                );\n            }\n        }\n    }\n}\n\n#[test]\nfn test_remap() {\n    assert_eq!(remap_clamp(1.0, 0.0..=1.0, 0.0..=16.0), 16.0);\n    assert_eq!(remap_clamp(1.0, 1.0..=0.0, 16.0..=0.0), 16.0);\n    assert_eq!(remap_clamp(0.5, 1.0..=0.0, 16.0..=0.0), 8.0);\n}\n\n// ----------------------------------------------------------------------------\n\n/// Extends `f32`, [`Vec2`] etc with `at_least` and `at_most` as aliases for `max` and `min`.\npub trait NumExt {\n    /// More readable version of `self.max(lower_limit)`\n    #[must_use]\n    fn at_least(self, lower_limit: Self) -> Self;\n\n    /// More readable version of `self.min(upper_limit)`\n    #[must_use]\n    fn at_most(self, upper_limit: Self) -> Self;\n}\n\nmacro_rules! impl_num_ext {\n    ($t: ty) => {\n        impl NumExt for $t {\n            #[inline(always)]\n            fn at_least(self, lower_limit: Self) -> Self {\n                self.max(lower_limit)\n            }\n\n            #[inline(always)]\n            fn at_most(self, upper_limit: Self) -> Self {\n                self.min(upper_limit)\n            }\n        }\n    };\n}\n\nimpl_num_ext!(u8);\nimpl_num_ext!(u16);\nimpl_num_ext!(u32);\nimpl_num_ext!(u64);\nimpl_num_ext!(u128);\nimpl_num_ext!(usize);\nimpl_num_ext!(i8);\nimpl_num_ext!(i16);\nimpl_num_ext!(i32);\nimpl_num_ext!(i64);\nimpl_num_ext!(i128);\nimpl_num_ext!(isize);\nimpl_num_ext!(f32);\nimpl_num_ext!(f64);\nimpl_num_ext!(Vec2);\nimpl_num_ext!(Pos2);\n\n// ----------------------------------------------------------------------------\n\n/// Wrap angle to `[-PI, PI]` range.\npub fn normalized_angle(mut angle: f32) -> f32 {\n    use std::f32::consts::{PI, TAU};\n    angle %= TAU;\n    if angle > PI {\n        angle -= TAU;\n    } else if angle < -PI {\n        angle += TAU;\n    }\n    angle\n}\n\n#[test]\nfn test_normalized_angle() {\n    macro_rules! almost_eq {\n        ($left: expr, $right: expr) => {\n            let left = $left;\n            let right = $right;\n            assert!((left - right).abs() < 1e-6, \"{} != {}\", left, right);\n        };\n    }\n\n    use std::f32::consts::TAU;\n    almost_eq!(normalized_angle(-3.0 * TAU), 0.0);\n    almost_eq!(normalized_angle(-2.3 * TAU), -0.3 * TAU);\n    almost_eq!(normalized_angle(-TAU), 0.0);\n    almost_eq!(normalized_angle(0.0), 0.0);\n    almost_eq!(normalized_angle(TAU), 0.0);\n    almost_eq!(normalized_angle(2.7 * TAU), -0.3 * TAU);\n}\n\n// ----------------------------------------------------------------------------\n\n/// Calculate a lerp-factor for exponential smoothing using a time step.\n///\n/// * `exponential_smooth_factor(0.90, 1.0, dt)`: reach 90% in 1.0 seconds\n/// * `exponential_smooth_factor(0.50, 0.2, dt)`: reach 50% in 0.2 seconds\n///\n/// Example:\n/// ```\n/// # use emath::{lerp, exponential_smooth_factor};\n/// # let (mut smoothed_value, target_value, dt) = (0.0_f32, 1.0_f32, 0.01_f32);\n/// let t = exponential_smooth_factor(0.90, 0.2, dt); // reach 90% in 0.2 seconds\n/// smoothed_value = lerp(smoothed_value..=target_value, t);\n/// ```\npub fn exponential_smooth_factor(\n    reach_this_fraction: f32,\n    in_this_many_seconds: f32,\n    dt: f32,\n) -> f32 {\n    1.0 - (1.0 - reach_this_fraction).powf(dt / in_this_many_seconds)\n}\n\n/// If you have a value animating over time,\n/// how much towards its target do you need to move it this frame?\n///\n/// You only need to store the start time and target value in order to animate using this function.\n///\n/// ``` rs\n/// struct Animation {\n///     current_value: f32,\n///\n///     animation_time_span: (f64, f64),\n///     target_value: f32,\n/// }\n///\n/// impl Animation {\n///     fn update(&mut self, now: f64, dt: f32) {\n///         let t = interpolation_factor(self.animation_time_span, now, dt, ease_in_ease_out);\n///         self.current_value = emath::lerp(self.current_value..=self.target_value, t);\n///     }\n/// }\n/// ```\npub fn interpolation_factor(\n    (start_time, end_time): (f64, f64),\n    current_time: f64,\n    dt: f32,\n    easing: impl Fn(f32) -> f32,\n) -> f32 {\n    let animation_duration = (end_time - start_time) as f32;\n    let prev_time = current_time - dt as f64;\n    let prev_t = easing((prev_time - start_time) as f32 / animation_duration);\n    let end_t = easing((current_time - start_time) as f32 / animation_duration);\n    if end_t < 1.0 {\n        (end_t - prev_t) / (1.0 - prev_t)\n    } else {\n        1.0\n    }\n}\n\n/// Ease in, ease out.\n///\n/// `f(0) = 0, f'(0) = 0, f(1) = 1, f'(1) = 0`.\n#[inline]\npub fn ease_in_ease_out(t: f32) -> f32 {\n    let t = t.clamp(0.0, 1.0);\n    (3.0 * t * t - 2.0 * t * t * t).clamp(0.0, 1.0)\n}\n"
  },
  {
    "path": "crates/emath/src/numeric.rs",
    "content": "/// Implemented for all builtin numeric types\npub trait Numeric: Clone + Copy + PartialEq + PartialOrd + 'static {\n    /// Is this an integer type?\n    const INTEGRAL: bool;\n\n    /// Smallest finite value\n    const MIN: Self;\n\n    /// Largest finite value\n    const MAX: Self;\n\n    fn to_f64(self) -> f64;\n\n    fn from_f64(num: f64) -> Self;\n}\n\nmacro_rules! impl_numeric_float {\n    ($t: ident) => {\n        impl Numeric for $t {\n            const INTEGRAL: bool = false;\n            const MIN: Self = $t::MIN;\n            const MAX: Self = $t::MAX;\n\n            #[inline(always)]\n            fn to_f64(self) -> f64 {\n                #[allow(clippy::allow_attributes, trivial_numeric_casts)]\n                {\n                    self as f64\n                }\n            }\n\n            #[inline(always)]\n            fn from_f64(num: f64) -> Self {\n                #[allow(clippy::allow_attributes, trivial_numeric_casts)]\n                {\n                    num as Self\n                }\n            }\n        }\n    };\n}\n\nmacro_rules! impl_numeric_integer {\n    ($t: ident) => {\n        impl Numeric for $t {\n            const INTEGRAL: bool = true;\n            const MIN: Self = $t::MIN;\n            const MAX: Self = $t::MAX;\n\n            #[inline(always)]\n            fn to_f64(self) -> f64 {\n                self as f64\n            }\n\n            #[inline(always)]\n            fn from_f64(num: f64) -> Self {\n                num as Self\n            }\n        }\n    };\n}\n\nmacro_rules! impl_numeric_non_zero_unsigned {\n    ($t: path) => {\n        impl Numeric for $t {\n            const INTEGRAL: bool = true;\n            const MIN: Self = Self::MIN;\n            const MAX: Self = Self::MAX;\n\n            #[inline(always)]\n            fn to_f64(self) -> f64 {\n                self.get() as f64\n            }\n\n            #[inline(always)]\n            fn from_f64(num: f64) -> Self {\n                Self::new(num.round().max(1.0) as _).unwrap_or(Self::MIN)\n            }\n        }\n    };\n}\n\nimpl_numeric_float!(f32);\nimpl_numeric_float!(f64);\nimpl_numeric_integer!(i8);\nimpl_numeric_integer!(u8);\nimpl_numeric_integer!(i16);\nimpl_numeric_integer!(u16);\nimpl_numeric_integer!(i32);\nimpl_numeric_integer!(u32);\nimpl_numeric_integer!(i64);\nimpl_numeric_integer!(u64);\nimpl_numeric_integer!(isize);\nimpl_numeric_integer!(usize);\nimpl_numeric_non_zero_unsigned!(std::num::NonZeroU8);\nimpl_numeric_non_zero_unsigned!(std::num::NonZeroU16);\nimpl_numeric_non_zero_unsigned!(std::num::NonZeroU32);\nimpl_numeric_non_zero_unsigned!(std::num::NonZeroU64);\nimpl_numeric_non_zero_unsigned!(std::num::NonZeroU128);\nimpl_numeric_non_zero_unsigned!(std::num::NonZeroUsize);\n"
  },
  {
    "path": "crates/emath/src/ordered_float.rs",
    "content": "//! Total order on floating point types.\n//! Can be used for sorting, min/max computation, and other collection algorithms.\n\nuse std::cmp::Ordering;\nuse std::hash::{Hash, Hasher};\n\n/// Wraps a floating-point value to add total order and hash.\n/// Possible types for `T` are `f32` and `f64`.\n///\n/// All NaNs are considered equal to each other.\n/// The size of zero is ignored.\n///\n/// See also [`Float`].\n#[derive(Clone, Copy)]\npub struct OrderedFloat<T>(pub T);\n\nimpl<T: Float + Copy> OrderedFloat<T> {\n    #[inline]\n    pub fn into_inner(self) -> T {\n        self.0\n    }\n}\n\nimpl<T: std::fmt::Debug> std::fmt::Debug for OrderedFloat<T> {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\nimpl<T: Float> Eq for OrderedFloat<T> {}\n\nimpl<T: Float> PartialEq<Self> for OrderedFloat<T> {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        // NaNs are considered equal (equivalent) when it comes to ordering\n        if self.0.is_nan() {\n            other.0.is_nan()\n        } else {\n            self.0 == other.0\n        }\n    }\n}\n\nimpl<T: Float> PartialOrd<Self> for OrderedFloat<T> {\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl<T: Float> Ord for OrderedFloat<T> {\n    #[inline]\n    fn cmp(&self, other: &Self) -> Ordering {\n        match self.0.partial_cmp(&other.0) {\n            Some(ord) => ord,\n            None => self.0.is_nan().cmp(&other.0.is_nan()),\n        }\n    }\n}\n\nimpl<T: Float> Hash for OrderedFloat<T> {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        self.0.hash(state);\n    }\n}\n\nimpl<T> From<T> for OrderedFloat<T> {\n    #[inline]\n    fn from(val: T) -> Self {\n        Self(val)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Extension trait to provide `ord()` method.\n///\n/// Example with `f64`:\n/// ```\n/// use emath::Float as _;\n///\n/// let array = [1.0, 2.5, 2.0];\n/// let max = array.iter().max_by_key(|val| val.ord());\n///\n/// assert_eq!(max, Some(&2.5));\n/// ```\npub trait Float: PartialOrd + PartialEq + private::FloatImpl {\n    /// Type to provide total order, useful as key in sorted contexts.\n    fn ord(self) -> OrderedFloat<Self>\n    where\n        Self: Sized;\n}\n\nimpl Float for f32 {\n    #[inline]\n    fn ord(self) -> OrderedFloat<Self> {\n        OrderedFloat(self)\n    }\n}\n\nimpl Float for f64 {\n    #[inline]\n    fn ord(self) -> OrderedFloat<Self> {\n        OrderedFloat(self)\n    }\n}\n\n// Keep this trait in private module, to avoid exposing its methods as extensions in user code\nmod private {\n    use super::{Hash as _, Hasher};\n\n    pub trait FloatImpl {\n        fn is_nan(&self) -> bool;\n\n        fn hash<H: Hasher>(&self, state: &mut H);\n    }\n\n    impl FloatImpl for f32 {\n        #[inline]\n        fn is_nan(&self) -> bool {\n            Self::is_nan(*self)\n        }\n\n        #[inline]\n        fn hash<H: Hasher>(&self, state: &mut H) {\n            let bits = if self.is_nan() {\n                // \"Canonical\" NaN.\n                0x7fc00000\n            } else {\n                // A trick taken from the `ordered-float` crate: -0.0 + 0.0 == +0.0.\n                // https://github.com/reem/rust-ordered-float/blob/1841f0541ea0e56779cbac03de2705149e020675/src/lib.rs#L2178-L2181\n                (self + 0.0).to_bits()\n            };\n            bits.hash(state);\n        }\n    }\n\n    impl FloatImpl for f64 {\n        #[inline]\n        fn is_nan(&self) -> bool {\n            Self::is_nan(*self)\n        }\n\n        #[inline]\n        fn hash<H: Hasher>(&self, state: &mut H) {\n            let bits = if self.is_nan() {\n                // \"Canonical\" NaN.\n                0x7ff8000000000000\n            } else {\n                (self + 0.0).to_bits()\n            };\n            bits.hash(state);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/pos2.rs",
    "content": "use std::{\n    fmt,\n    ops::{Add, AddAssign, MulAssign, Sub, SubAssign},\n};\n\nuse crate::{Div, Mul, Vec2, lerp};\n\n/// A position on screen.\n///\n/// Normally given in points (logical pixels).\n///\n/// Mathematically this is known as a \"point\", but the term position was chosen so not to\n/// conflict with the unit (one point = X physical pixels).\n#[repr(C)]\n#[derive(Clone, Copy, Default, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct Pos2 {\n    /// How far to the right.\n    pub x: f32,\n\n    /// How far down.\n    pub y: f32,\n    // implicit w = 1\n}\n\n/// `pos2(x, y) == Pos2::new(x, y)`\n#[inline(always)]\npub const fn pos2(x: f32, y: f32) -> Pos2 {\n    Pos2 { x, y }\n}\n\n// ----------------------------------------------------------------------------\n// Compatibility and convenience conversions to and from [f32; 2]:\n\nimpl From<[f32; 2]> for Pos2 {\n    #[inline(always)]\n    fn from(v: [f32; 2]) -> Self {\n        Self { x: v[0], y: v[1] }\n    }\n}\n\nimpl From<&[f32; 2]> for Pos2 {\n    #[inline(always)]\n    fn from(v: &[f32; 2]) -> Self {\n        Self { x: v[0], y: v[1] }\n    }\n}\n\nimpl From<Pos2> for [f32; 2] {\n    #[inline(always)]\n    fn from(v: Pos2) -> Self {\n        [v.x, v.y]\n    }\n}\n\nimpl From<&Pos2> for [f32; 2] {\n    #[inline(always)]\n    fn from(v: &Pos2) -> Self {\n        [v.x, v.y]\n    }\n}\n\n// ----------------------------------------------------------------------------\n// Compatibility and convenience conversions to and from (f32, f32):\n\nimpl From<(f32, f32)> for Pos2 {\n    #[inline(always)]\n    fn from(v: (f32, f32)) -> Self {\n        Self { x: v.0, y: v.1 }\n    }\n}\n\nimpl From<&(f32, f32)> for Pos2 {\n    #[inline(always)]\n    fn from(v: &(f32, f32)) -> Self {\n        Self { x: v.0, y: v.1 }\n    }\n}\n\nimpl From<Pos2> for (f32, f32) {\n    #[inline(always)]\n    fn from(v: Pos2) -> Self {\n        (v.x, v.y)\n    }\n}\n\nimpl From<&Pos2> for (f32, f32) {\n    #[inline(always)]\n    fn from(v: &Pos2) -> Self {\n        (v.x, v.y)\n    }\n}\n\n// ----------------------------------------------------------------------------\n// Mint compatibility and convenience conversions\n\n#[cfg(feature = \"mint\")]\nimpl From<mint::Point2<f32>> for Pos2 {\n    #[inline(always)]\n    fn from(v: mint::Point2<f32>) -> Self {\n        Self::new(v.x, v.y)\n    }\n}\n\n#[cfg(feature = \"mint\")]\nimpl From<Pos2> for mint::Point2<f32> {\n    #[inline(always)]\n    fn from(v: Pos2) -> Self {\n        Self { x: v.x, y: v.y }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nimpl Pos2 {\n    /// The zero position, the origin.\n    /// The top left corner in a GUI.\n    /// Same as `Pos2::default()`.\n    pub const ZERO: Self = Self { x: 0.0, y: 0.0 };\n\n    #[inline(always)]\n    pub const fn new(x: f32, y: f32) -> Self {\n        Self { x, y }\n    }\n\n    /// The vector from origin to this position.\n    /// `p.to_vec2()` is equivalent to `p - Pos2::default()`.\n    #[inline(always)]\n    pub fn to_vec2(self) -> Vec2 {\n        Vec2 {\n            x: self.x,\n            y: self.y,\n        }\n    }\n\n    #[inline]\n    pub fn distance(self, other: Self) -> f32 {\n        (self - other).length()\n    }\n\n    #[inline]\n    pub fn distance_sq(self, other: Self) -> f32 {\n        (self - other).length_sq()\n    }\n\n    #[inline(always)]\n    pub fn floor(self) -> Self {\n        pos2(self.x.floor(), self.y.floor())\n    }\n\n    #[inline(always)]\n    pub fn round(self) -> Self {\n        pos2(self.x.round(), self.y.round())\n    }\n\n    #[inline(always)]\n    pub fn ceil(self) -> Self {\n        pos2(self.x.ceil(), self.y.ceil())\n    }\n\n    /// True if all members are also finite.\n    #[inline(always)]\n    pub fn is_finite(self) -> bool {\n        self.x.is_finite() && self.y.is_finite()\n    }\n\n    /// True if any member is NaN.\n    #[inline(always)]\n    pub fn any_nan(self) -> bool {\n        self.x.is_nan() || self.y.is_nan()\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn min(self, other: Self) -> Self {\n        pos2(self.x.min(other.x), self.y.min(other.y))\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn max(self, other: Self) -> Self {\n        pos2(self.x.max(other.x), self.y.max(other.y))\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn clamp(self, min: Self, max: Self) -> Self {\n        Self {\n            x: self.x.clamp(min.x, max.x),\n            y: self.y.clamp(min.y, max.y),\n        }\n    }\n\n    /// Linearly interpolate towards another point, so that `0.0 => self, 1.0 => other`.\n    pub fn lerp(&self, other: Self, t: f32) -> Self {\n        Self {\n            x: lerp(self.x..=other.x, t),\n            y: lerp(self.y..=other.y, t),\n        }\n    }\n}\n\nimpl std::ops::Index<usize> for Pos2 {\n    type Output = f32;\n\n    #[inline(always)]\n    fn index(&self, index: usize) -> &f32 {\n        match index {\n            0 => &self.x,\n            1 => &self.y,\n            _ => panic!(\"Pos2 index out of bounds: {index}\"),\n        }\n    }\n}\n\nimpl std::ops::IndexMut<usize> for Pos2 {\n    #[inline(always)]\n    fn index_mut(&mut self, index: usize) -> &mut f32 {\n        match index {\n            0 => &mut self.x,\n            1 => &mut self.y,\n            _ => panic!(\"Pos2 index out of bounds: {index}\"),\n        }\n    }\n}\n\nimpl Eq for Pos2 {}\n\nimpl AddAssign<Vec2> for Pos2 {\n    #[inline(always)]\n    fn add_assign(&mut self, rhs: Vec2) {\n        *self = Self {\n            x: self.x + rhs.x,\n            y: self.y + rhs.y,\n        };\n    }\n}\n\nimpl SubAssign<Vec2> for Pos2 {\n    #[inline(always)]\n    fn sub_assign(&mut self, rhs: Vec2) {\n        *self = Self {\n            x: self.x - rhs.x,\n            y: self.y - rhs.y,\n        };\n    }\n}\n\nimpl Add<Vec2> for Pos2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn add(self, rhs: Vec2) -> Self {\n        Self {\n            x: self.x + rhs.x,\n            y: self.y + rhs.y,\n        }\n    }\n}\n\nimpl Sub for Pos2 {\n    type Output = Vec2;\n\n    #[inline(always)]\n    fn sub(self, rhs: Self) -> Vec2 {\n        Vec2 {\n            x: self.x - rhs.x,\n            y: self.y - rhs.y,\n        }\n    }\n}\n\nimpl Sub<Vec2> for Pos2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn sub(self, rhs: Vec2) -> Self {\n        Self {\n            x: self.x - rhs.x,\n            y: self.y - rhs.y,\n        }\n    }\n}\n\nimpl Mul<f32> for Pos2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn mul(self, factor: f32) -> Self {\n        Self {\n            x: self.x * factor,\n            y: self.y * factor,\n        }\n    }\n}\n\nimpl Mul<Pos2> for f32 {\n    type Output = Pos2;\n\n    #[inline(always)]\n    fn mul(self, vec: Pos2) -> Pos2 {\n        Pos2 {\n            x: self * vec.x,\n            y: self * vec.y,\n        }\n    }\n}\n\nimpl MulAssign<f32> for Pos2 {\n    #[inline(always)]\n    fn mul_assign(&mut self, rhs: f32) {\n        self.x *= rhs;\n        self.y *= rhs;\n    }\n}\n\nimpl Div<f32> for Pos2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn div(self, factor: f32) -> Self {\n        Self {\n            x: self.x / factor,\n            y: self.y / factor,\n        }\n    }\n}\n\nimpl fmt::Debug for Pos2 {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        if let Some(precision) = f.precision() {\n            write!(f, \"[{1:.0$} {2:.0$}]\", precision, self.x, self.y)\n        } else {\n            write!(f, \"[{:.1} {:.1}]\", self.x, self.y)\n        }\n    }\n}\n\nimpl fmt::Display for Pos2 {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"[\")?;\n        self.x.fmt(f)?;\n        f.write_str(\" \")?;\n        self.y.fmt(f)?;\n        f.write_str(\"]\")?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/range.rs",
    "content": "use std::ops::{RangeFrom, RangeFull, RangeInclusive, RangeToInclusive};\n\n/// Inclusive range of floats, i.e. `min..=max`, but more ergonomic than [`RangeInclusive`].\n#[repr(C)]\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct Rangef {\n    pub min: f32,\n    pub max: f32,\n}\n\nimpl Rangef {\n    /// Infinite range that contains everything, from -∞ to +∞, inclusive.\n    pub const EVERYTHING: Self = Self {\n        min: f32::NEG_INFINITY,\n        max: f32::INFINITY,\n    };\n\n    /// The inverse of [`Self::EVERYTHING`]: stretches from positive infinity to negative infinity.\n    /// Contains nothing.\n    pub const NOTHING: Self = Self {\n        min: f32::INFINITY,\n        max: f32::NEG_INFINITY,\n    };\n\n    /// An invalid [`Rangef`] filled with [`f32::NAN`].\n    pub const NAN: Self = Self {\n        min: f32::NAN,\n        max: f32::NAN,\n    };\n\n    #[inline]\n    pub fn new(min: f32, max: f32) -> Self {\n        Self { min, max }\n    }\n\n    #[inline]\n    pub fn point(min_and_max: f32) -> Self {\n        Self {\n            min: min_and_max,\n            max: min_and_max,\n        }\n    }\n\n    /// The length of the range, i.e. `max - min`.\n    #[inline]\n    pub fn span(self) -> f32 {\n        self.max - self.min\n    }\n\n    /// The center of the range\n    #[inline]\n    pub fn center(self) -> f32 {\n        0.5 * (self.min + self.max)\n    }\n\n    #[inline]\n    #[must_use]\n    pub fn contains(self, x: f32) -> bool {\n        self.min <= x && x <= self.max\n    }\n\n    /// Equivalent to `x.clamp(min, max)`\n    #[inline]\n    #[must_use]\n    pub fn clamp(self, x: f32) -> f32 {\n        x.clamp(self.min, self.max)\n    }\n\n    /// Flip `min` and `max` if needed, so that `min <= max` after.\n    #[inline]\n    pub fn as_positive(self) -> Self {\n        Self {\n            min: self.min.min(self.max),\n            max: self.min.max(self.max),\n        }\n    }\n\n    /// Shrink by this much on each side, keeping the center\n    #[inline]\n    #[must_use]\n    pub fn shrink(self, amnt: f32) -> Self {\n        Self {\n            min: self.min + amnt,\n            max: self.max - amnt,\n        }\n    }\n\n    /// Expand by this much on each side, keeping the center\n    #[inline]\n    #[must_use]\n    pub fn expand(self, amnt: f32) -> Self {\n        Self {\n            min: self.min - amnt,\n            max: self.max + amnt,\n        }\n    }\n\n    /// Flip the min and the max\n    #[inline]\n    #[must_use]\n    pub fn flip(self) -> Self {\n        Self {\n            min: self.max,\n            max: self.min,\n        }\n    }\n\n    /// The overlap of two ranges, i.e. the range that is contained by both.\n    ///\n    /// If the ranges do not overlap, returns a range with `span() < 0.0`.\n    ///\n    /// ```\n    /// # use emath::Rangef;\n    /// assert_eq!(Rangef::new(0.0, 10.0).intersection(Rangef::new(5.0, 15.0)), Rangef::new(5.0, 10.0));\n    /// assert_eq!(Rangef::new(0.0, 10.0).intersection(Rangef::new(10.0, 20.0)), Rangef::new(10.0, 10.0));\n    /// assert!(Rangef::new(0.0, 10.0).intersection(Rangef::new(20.0, 30.0)).span() < 0.0);\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn intersection(self, other: Self) -> Self {\n        Self {\n            min: self.min.max(other.min),\n            max: self.max.min(other.max),\n        }\n    }\n\n    /// Do the two ranges intersect?\n    ///\n    /// ```\n    /// # use emath::Rangef;\n    /// assert!(Rangef::new(0.0, 10.0).intersects(Rangef::new(5.0, 15.0)));\n    /// assert!(Rangef::new(0.0, 10.0).intersects(Rangef::new(5.0, 6.0)));\n    /// assert!(Rangef::new(0.0, 10.0).intersects(Rangef::new(10.0, 20.0)));\n    /// assert!(!Rangef::new(0.0, 10.0).intersects(Rangef::new(20.0, 30.0)));\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn intersects(self, other: Self) -> bool {\n        other.min <= self.max && self.min <= other.max\n    }\n}\n\nimpl From<Rangef> for RangeInclusive<f32> {\n    #[inline]\n    fn from(Rangef { min, max }: Rangef) -> Self {\n        min..=max\n    }\n}\n\nimpl From<&Rangef> for RangeInclusive<f32> {\n    #[inline]\n    fn from(&Rangef { min, max }: &Rangef) -> Self {\n        min..=max\n    }\n}\n\nimpl From<RangeInclusive<f32>> for Rangef {\n    #[inline]\n    fn from(range: RangeInclusive<f32>) -> Self {\n        Self::new(*range.start(), *range.end())\n    }\n}\n\nimpl From<&RangeInclusive<f32>> for Rangef {\n    #[inline]\n    fn from(range: &RangeInclusive<f32>) -> Self {\n        Self::new(*range.start(), *range.end())\n    }\n}\n\nimpl From<RangeFrom<f32>> for Rangef {\n    #[inline]\n    fn from(range: RangeFrom<f32>) -> Self {\n        Self::new(range.start, f32::INFINITY)\n    }\n}\n\nimpl From<&RangeFrom<f32>> for Rangef {\n    #[inline]\n    fn from(range: &RangeFrom<f32>) -> Self {\n        Self::new(range.start, f32::INFINITY)\n    }\n}\n\nimpl From<RangeFull> for Rangef {\n    #[inline]\n    fn from(_: RangeFull) -> Self {\n        Self::new(f32::NEG_INFINITY, f32::INFINITY)\n    }\n}\n\nimpl From<&RangeFull> for Rangef {\n    #[inline]\n    fn from(_: &RangeFull) -> Self {\n        Self::new(f32::NEG_INFINITY, f32::INFINITY)\n    }\n}\n\nimpl From<RangeToInclusive<f32>> for Rangef {\n    #[inline]\n    fn from(range: RangeToInclusive<f32>) -> Self {\n        Self::new(f32::NEG_INFINITY, range.end)\n    }\n}\n\nimpl PartialEq<RangeInclusive<f32>> for Rangef {\n    #[inline]\n    fn eq(&self, other: &RangeInclusive<f32>) -> bool {\n        self.min == *other.start() && self.max == *other.end()\n    }\n}\n\nimpl PartialEq<Rangef> for RangeInclusive<f32> {\n    #[inline]\n    fn eq(&self, other: &Rangef) -> bool {\n        *self.start() == other.min && *self.end() == other.max\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/rect.rs",
    "content": "use std::fmt;\n\nuse crate::{Div, Mul, NumExt as _, Pos2, Rangef, Rot2, Vec2, fast_midpoint, lerp, pos2, vec2};\nuse std::ops::{BitOr, BitOrAssign};\n\n/// A rectangular region of space.\n///\n/// Usually a [`Rect`] has a positive (or zero) size,\n/// and then [`Self::min`] `<=` [`Self::max`].\n/// In these cases [`Self::min`] is the left-top corner\n/// and [`Self::max`] is the right-bottom corner.\n///\n/// A rectangle is allowed to have a negative size, which happens when the order\n/// of `min` and `max` are swapped. These are usually a sign of an error.\n///\n/// Normally the unit is points (logical pixels) in screen space coordinates.\n///\n/// `Rect` does NOT implement `Default`, because there is no obvious default value.\n/// [`Rect::ZERO`] may seem reasonable, but when used as a bounding box, [`Rect::NOTHING`]\n/// is a better default - so be explicit instead!\n#[repr(C)]\n#[derive(Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct Rect {\n    /// One of the corners of the rectangle, usually the left top one.\n    pub min: Pos2,\n\n    /// The other corner, opposing [`Self::min`]. Usually the right bottom one.\n    pub max: Pos2,\n}\n\nimpl Rect {\n    /// Infinite rectangle that contains every point.\n    pub const EVERYTHING: Self = Self {\n        min: pos2(-f32::INFINITY, -f32::INFINITY),\n        max: pos2(f32::INFINITY, f32::INFINITY),\n    };\n\n    /// The inverse of [`Self::EVERYTHING`]: stretches from positive infinity to negative infinity.\n    /// Contains no points.\n    ///\n    /// This is useful as the seed for bounding boxes.\n    ///\n    /// # Example:\n    /// ```\n    /// # use emath::*;\n    /// let mut rect = Rect::NOTHING;\n    /// assert!(rect.size() == Vec2::splat(-f32::INFINITY));\n    /// assert!(rect.contains(pos2(0.0, 0.0)) == false);\n    /// rect.extend_with(pos2(2.0, 1.0));\n    /// rect.extend_with(pos2(0.0, 3.0));\n    /// assert_eq!(rect, Rect::from_min_max(pos2(0.0, 1.0), pos2(2.0, 3.0)))\n    /// ```\n    pub const NOTHING: Self = Self {\n        min: pos2(f32::INFINITY, f32::INFINITY),\n        max: pos2(-f32::INFINITY, -f32::INFINITY),\n    };\n\n    /// An invalid [`Rect`] filled with [`f32::NAN`].\n    pub const NAN: Self = Self {\n        min: pos2(f32::NAN, f32::NAN),\n        max: pos2(f32::NAN, f32::NAN),\n    };\n\n    /// A [`Rect`] filled with zeroes.\n    pub const ZERO: Self = Self {\n        min: Pos2::ZERO,\n        max: Pos2::ZERO,\n    };\n\n    #[inline(always)]\n    pub const fn from_min_max(min: Pos2, max: Pos2) -> Self {\n        Self { min, max }\n    }\n\n    /// left-top corner plus a size (stretching right-down).\n    #[inline(always)]\n    pub fn from_min_size(min: Pos2, size: Vec2) -> Self {\n        Self {\n            min,\n            max: min + size,\n        }\n    }\n\n    #[inline(always)]\n    pub fn from_center_size(center: Pos2, size: Vec2) -> Self {\n        Self {\n            min: center - size * 0.5,\n            max: center + size * 0.5,\n        }\n    }\n\n    #[inline(always)]\n    pub fn from_x_y_ranges(x_range: impl Into<Rangef>, y_range: impl Into<Rangef>) -> Self {\n        let x_range = x_range.into();\n        let y_range = y_range.into();\n        Self {\n            min: pos2(x_range.min, y_range.min),\n            max: pos2(x_range.max, y_range.max),\n        }\n    }\n\n    /// Returns the bounding rectangle of the two points.\n    #[inline]\n    pub fn from_two_pos(a: Pos2, b: Pos2) -> Self {\n        Self {\n            min: pos2(a.x.min(b.x), a.y.min(b.y)),\n            max: pos2(a.x.max(b.x), a.y.max(b.y)),\n        }\n    }\n\n    /// A zero-sized rect at a specific point.\n    #[inline]\n    pub fn from_pos(point: Pos2) -> Self {\n        Self {\n            min: point,\n            max: point,\n        }\n    }\n\n    /// Bounding-box around the points.\n    pub fn from_points(points: &[Pos2]) -> Self {\n        let mut rect = Self::NOTHING;\n        for &p in points {\n            rect.extend_with(p);\n        }\n        rect\n    }\n\n    /// A [`Rect`] that contains every point to the right of the given X coordinate.\n    #[inline]\n    pub fn everything_right_of(left_x: f32) -> Self {\n        let mut rect = Self::EVERYTHING;\n        rect.set_left(left_x);\n        rect\n    }\n\n    /// A [`Rect`] that contains every point to the left of the given X coordinate.\n    #[inline]\n    pub fn everything_left_of(right_x: f32) -> Self {\n        let mut rect = Self::EVERYTHING;\n        rect.set_right(right_x);\n        rect\n    }\n\n    /// A [`Rect`] that contains every point below a certain y coordinate\n    #[inline]\n    pub fn everything_below(top_y: f32) -> Self {\n        let mut rect = Self::EVERYTHING;\n        rect.set_top(top_y);\n        rect\n    }\n\n    /// A [`Rect`] that contains every point above a certain y coordinate\n    #[inline]\n    pub fn everything_above(bottom_y: f32) -> Self {\n        let mut rect = Self::EVERYTHING;\n        rect.set_bottom(bottom_y);\n        rect\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn with_min_x(mut self, min_x: f32) -> Self {\n        self.min.x = min_x;\n        self\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn with_min_y(mut self, min_y: f32) -> Self {\n        self.min.y = min_y;\n        self\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn with_max_x(mut self, max_x: f32) -> Self {\n        self.max.x = max_x;\n        self\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn with_max_y(mut self, max_y: f32) -> Self {\n        self.max.y = max_y;\n        self\n    }\n\n    /// Expand by this much in each direction, keeping the center\n    #[must_use]\n    pub fn expand(self, amnt: f32) -> Self {\n        self.expand2(Vec2::splat(amnt))\n    }\n\n    /// Expand by this much in each direction, keeping the center\n    #[must_use]\n    pub fn expand2(self, amnt: Vec2) -> Self {\n        Self::from_min_max(self.min - amnt, self.max + amnt)\n    }\n\n    /// Scale up by this factor in each direction, keeping the center\n    #[must_use]\n    pub fn scale_from_center(self, scale_factor: f32) -> Self {\n        self.scale_from_center2(Vec2::splat(scale_factor))\n    }\n\n    /// Scale up by this factor in each direction, keeping the center\n    #[must_use]\n    pub fn scale_from_center2(self, scale_factor: Vec2) -> Self {\n        Self::from_center_size(self.center(), self.size() * scale_factor)\n    }\n\n    /// Shrink by this much in each direction, keeping the center\n    #[must_use]\n    pub fn shrink(self, amnt: f32) -> Self {\n        self.shrink2(Vec2::splat(amnt))\n    }\n\n    /// Shrink by this much in each direction, keeping the center\n    #[must_use]\n    pub fn shrink2(self, amnt: Vec2) -> Self {\n        Self::from_min_max(self.min + amnt, self.max - amnt)\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn translate(self, amnt: Vec2) -> Self {\n        Self::from_min_size(self.min + amnt, self.size())\n    }\n\n    /// Rotate the bounds (will expand the [`Rect`])\n    #[must_use]\n    #[inline]\n    pub fn rotate_bb(self, rot: Rot2) -> Self {\n        let a = rot * self.left_top().to_vec2();\n        let b = rot * self.right_top().to_vec2();\n        let c = rot * self.left_bottom().to_vec2();\n        let d = rot * self.right_bottom().to_vec2();\n\n        Self::from_min_max(\n            a.min(b).min(c).min(d).to_pos2(),\n            a.max(b).max(c).max(d).to_pos2(),\n        )\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn intersects(self, other: Self) -> bool {\n        self.min.x <= other.max.x\n            && other.min.x <= self.max.x\n            && self.min.y <= other.max.y\n            && other.min.y <= self.max.y\n    }\n\n    /// keep min\n    pub fn set_width(&mut self, w: f32) {\n        self.max.x = self.min.x + w;\n    }\n\n    /// keep min\n    pub fn set_height(&mut self, h: f32) {\n        self.max.y = self.min.y + h;\n    }\n\n    /// Keep size\n    pub fn set_center(&mut self, center: Pos2) {\n        *self = self.translate(center - self.center());\n    }\n\n    #[must_use]\n    #[inline(always)]\n    pub fn contains(&self, p: Pos2) -> bool {\n        self.min.x <= p.x && p.x <= self.max.x && self.min.y <= p.y && p.y <= self.max.y\n    }\n\n    #[must_use]\n    pub fn contains_rect(&self, other: Self) -> bool {\n        self.contains(other.min) && self.contains(other.max)\n    }\n\n    /// Return the given points clamped to be inside the rectangle\n    /// Panics if [`Self::is_negative`].\n    #[must_use]\n    pub fn clamp(&self, p: Pos2) -> Pos2 {\n        p.clamp(self.min, self.max)\n    }\n\n    #[inline(always)]\n    pub fn extend_with(&mut self, p: Pos2) {\n        self.min = self.min.min(p);\n        self.max = self.max.max(p);\n    }\n\n    #[inline(always)]\n    /// Expand to include the given x coordinate\n    pub fn extend_with_x(&mut self, x: f32) {\n        self.min.x = self.min.x.min(x);\n        self.max.x = self.max.x.max(x);\n    }\n\n    #[inline(always)]\n    /// Expand to include the given y coordinate\n    pub fn extend_with_y(&mut self, y: f32) {\n        self.min.y = self.min.y.min(y);\n        self.max.y = self.max.y.max(y);\n    }\n\n    /// The union of two bounding rectangle, i.e. the minimum [`Rect`]\n    /// that contains both input rectangles.\n    #[inline(always)]\n    #[must_use]\n    pub fn union(self, other: Self) -> Self {\n        Self {\n            min: self.min.min(other.min),\n            max: self.max.max(other.max),\n        }\n    }\n\n    /// The intersection of two [`Rect`], i.e. the area covered by both.\n    #[inline]\n    #[must_use]\n    pub fn intersect(self, other: Self) -> Self {\n        Self {\n            min: self.min.max(other.min),\n            max: self.max.min(other.max),\n        }\n    }\n\n    #[inline(always)]\n    pub fn center(&self) -> Pos2 {\n        Pos2 {\n            x: fast_midpoint(self.min.x, self.max.x),\n            y: fast_midpoint(self.min.y, self.max.y),\n        }\n    }\n\n    /// `rect.size() == Vec2 { x: rect.width(), y: rect.height() }`\n    #[inline(always)]\n    pub fn size(&self) -> Vec2 {\n        self.max - self.min\n    }\n\n    /// Note: this can be negative.\n    #[inline(always)]\n    pub fn width(&self) -> f32 {\n        self.max.x - self.min.x\n    }\n\n    /// Note: this can be negative.\n    #[inline(always)]\n    pub fn height(&self) -> f32 {\n        self.max.y - self.min.y\n    }\n\n    /// Width / height\n    ///\n    /// * `aspect_ratio < 1`: portrait / high\n    /// * `aspect_ratio = 1`: square\n    /// * `aspect_ratio > 1`: landscape / wide\n    pub fn aspect_ratio(&self) -> f32 {\n        self.width() / self.height()\n    }\n\n    /// `[2, 1]` for wide screen, and `[1, 2]` for portrait, etc.\n    /// At least one dimension = 1, the other >= 1\n    /// Returns the proportions required to letter-box a square view area.\n    pub fn square_proportions(&self) -> Vec2 {\n        let w = self.width();\n        let h = self.height();\n        if w > h {\n            vec2(w / h, 1.0)\n        } else {\n            vec2(1.0, h / w)\n        }\n    }\n\n    /// This is never negative, and instead returns zero for negative rectangles.\n    #[inline(always)]\n    pub fn area(&self) -> f32 {\n        self.width().at_least(0.0) * self.height().at_least(0.0)\n    }\n\n    /// The distance from the rect to the position.\n    ///\n    /// The distance is zero when the position is in the interior of the rectangle.\n    ///\n    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].\n    #[inline]\n    pub fn distance_to_pos(&self, pos: Pos2) -> f32 {\n        self.distance_sq_to_pos(pos).sqrt()\n    }\n\n    /// The distance from the rect to the position, squared.\n    ///\n    /// The distance is zero when the position is in the interior of the rectangle.\n    ///\n    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].\n    #[inline]\n    pub fn distance_sq_to_pos(&self, pos: Pos2) -> f32 {\n        if self.is_negative() {\n            return f32::INFINITY;\n        }\n\n        let dx = if self.min.x > pos.x {\n            self.min.x - pos.x\n        } else if pos.x > self.max.x {\n            pos.x - self.max.x\n        } else {\n            0.0\n        };\n\n        let dy = if self.min.y > pos.y {\n            self.min.y - pos.y\n        } else if pos.y > self.max.y {\n            pos.y - self.max.y\n        } else {\n            0.0\n        };\n\n        dx * dx + dy * dy\n    }\n\n    /// Signed distance to the edge of the box.\n    ///\n    /// Negative inside the box.\n    ///\n    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].\n    ///\n    /// ```\n    /// # use emath::{pos2, Rect};\n    /// let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));\n    /// assert_eq!(rect.signed_distance_to_pos(pos2(0.50, 0.50)), -0.50);\n    /// assert_eq!(rect.signed_distance_to_pos(pos2(0.75, 0.50)), -0.25);\n    /// assert_eq!(rect.signed_distance_to_pos(pos2(1.50, 0.50)), 0.50);\n    /// ```\n    pub fn signed_distance_to_pos(&self, pos: Pos2) -> f32 {\n        if self.is_negative() {\n            return f32::INFINITY;\n        }\n\n        let edge_distances = (pos - self.center()).abs() - self.size() * 0.5;\n        let inside_dist = edge_distances.max_elem().min(0.0);\n        let outside_dist = edge_distances.max(Vec2::ZERO).length();\n        inside_dist + outside_dist\n    }\n\n    /// Linearly interpolate so that `[0, 0]` is [`Self::min`] and\n    /// `[1, 1]` is [`Self::max`].\n    #[inline]\n    pub fn lerp_inside(&self, t: impl Into<Vec2>) -> Pos2 {\n        let t = t.into();\n        Pos2 {\n            x: lerp(self.min.x..=self.max.x, t.x),\n            y: lerp(self.min.y..=self.max.y, t.y),\n        }\n    }\n\n    /// Linearly self towards other rect.\n    #[inline]\n    pub fn lerp_towards(&self, other: &Self, t: f32) -> Self {\n        Self {\n            min: self.min.lerp(other.min, t),\n            max: self.max.lerp(other.max, t),\n        }\n    }\n\n    #[inline(always)]\n    pub fn x_range(&self) -> Rangef {\n        Rangef::new(self.min.x, self.max.x)\n    }\n\n    #[inline(always)]\n    pub fn y_range(&self) -> Rangef {\n        Rangef::new(self.min.y, self.max.y)\n    }\n\n    #[inline(always)]\n    pub fn bottom_up_range(&self) -> Rangef {\n        Rangef::new(self.max.y, self.min.y)\n    }\n\n    /// `width < 0 || height < 0`\n    #[inline(always)]\n    pub fn is_negative(&self) -> bool {\n        self.max.x < self.min.x || self.max.y < self.min.y\n    }\n\n    /// `width > 0 && height > 0`\n    #[inline(always)]\n    pub fn is_positive(&self) -> bool {\n        self.min.x < self.max.x && self.min.y < self.max.y\n    }\n\n    /// True if all members are also finite.\n    #[inline(always)]\n    pub fn is_finite(&self) -> bool {\n        self.min.is_finite() && self.max.is_finite()\n    }\n\n    /// True if any member is NaN.\n    #[inline(always)]\n    pub fn any_nan(self) -> bool {\n        self.min.any_nan() || self.max.any_nan()\n    }\n}\n\n/// ## Convenience functions (assumes origin is towards left top):\nimpl Rect {\n    /// `min.x`\n    #[inline(always)]\n    pub fn left(&self) -> f32 {\n        self.min.x\n    }\n\n    /// `min.x`\n    #[inline(always)]\n    pub fn left_mut(&mut self) -> &mut f32 {\n        &mut self.min.x\n    }\n\n    /// `min.x`\n    #[inline(always)]\n    pub fn set_left(&mut self, x: f32) {\n        self.min.x = x;\n    }\n\n    /// `max.x`\n    #[inline(always)]\n    pub fn right(&self) -> f32 {\n        self.max.x\n    }\n\n    /// `max.x`\n    #[inline(always)]\n    pub fn right_mut(&mut self) -> &mut f32 {\n        &mut self.max.x\n    }\n\n    /// `max.x`\n    #[inline(always)]\n    pub fn set_right(&mut self, x: f32) {\n        self.max.x = x;\n    }\n\n    /// `min.y`\n    #[inline(always)]\n    pub fn top(&self) -> f32 {\n        self.min.y\n    }\n\n    /// `min.y`\n    #[inline(always)]\n    pub fn top_mut(&mut self) -> &mut f32 {\n        &mut self.min.y\n    }\n\n    /// `min.y`\n    #[inline(always)]\n    pub fn set_top(&mut self, y: f32) {\n        self.min.y = y;\n    }\n\n    /// `max.y`\n    #[inline(always)]\n    pub fn bottom(&self) -> f32 {\n        self.max.y\n    }\n\n    /// `max.y`\n    #[inline(always)]\n    pub fn bottom_mut(&mut self) -> &mut f32 {\n        &mut self.max.y\n    }\n\n    /// `max.y`\n    #[inline(always)]\n    pub fn set_bottom(&mut self, y: f32) {\n        self.max.y = y;\n    }\n\n    #[inline(always)]\n    #[doc(alias = \"top_left\")]\n    pub fn left_top(&self) -> Pos2 {\n        pos2(self.left(), self.top())\n    }\n\n    #[inline(always)]\n    pub fn center_top(&self) -> Pos2 {\n        pos2(self.center().x, self.top())\n    }\n\n    #[inline(always)]\n    #[doc(alias = \"top_right\")]\n    pub fn right_top(&self) -> Pos2 {\n        pos2(self.right(), self.top())\n    }\n\n    #[inline(always)]\n    pub fn left_center(&self) -> Pos2 {\n        pos2(self.left(), self.center().y)\n    }\n\n    #[inline(always)]\n    pub fn right_center(&self) -> Pos2 {\n        pos2(self.right(), self.center().y)\n    }\n\n    #[inline(always)]\n    #[doc(alias = \"bottom_left\")]\n    pub fn left_bottom(&self) -> Pos2 {\n        pos2(self.left(), self.bottom())\n    }\n\n    #[inline(always)]\n    pub fn center_bottom(&self) -> Pos2 {\n        pos2(self.center().x, self.bottom())\n    }\n\n    #[inline(always)]\n    #[doc(alias = \"bottom_right\")]\n    pub fn right_bottom(&self) -> Pos2 {\n        pos2(self.right(), self.bottom())\n    }\n\n    /// Split rectangle in left and right halves. `t` is expected to be in the (0,1) range.\n    pub fn split_left_right_at_fraction(&self, t: f32) -> (Self, Self) {\n        self.split_left_right_at_x(lerp(self.min.x..=self.max.x, t))\n    }\n\n    /// Split rectangle in left and right halves at the given `x` coordinate.\n    pub fn split_left_right_at_x(&self, split_x: f32) -> (Self, Self) {\n        let left = Self::from_min_max(self.min, Pos2::new(split_x, self.max.y));\n        let right = Self::from_min_max(Pos2::new(split_x, self.min.y), self.max);\n        (left, right)\n    }\n\n    /// Split rectangle in top and bottom halves. `t` is expected to be in the (0,1) range.\n    pub fn split_top_bottom_at_fraction(&self, t: f32) -> (Self, Self) {\n        self.split_top_bottom_at_y(lerp(self.min.y..=self.max.y, t))\n    }\n\n    /// Split rectangle in top and bottom halves at the given `y` coordinate.\n    pub fn split_top_bottom_at_y(&self, split_y: f32) -> (Self, Self) {\n        let top = Self::from_min_max(self.min, Pos2::new(self.max.x, split_y));\n        let bottom = Self::from_min_max(Pos2::new(self.min.x, split_y), self.max);\n        (top, bottom)\n    }\n}\n\nimpl Rect {\n    /// Does this Rect intersect the given ray (where `d` is normalized)?\n    ///\n    /// A ray that starts inside the rect will return `true`.\n    pub fn intersects_ray(&self, o: Pos2, d: Vec2) -> bool {\n        debug_assert!(\n            d.is_normalized(),\n            \"Debug assert: expected normalized direction, but `d` has length {}\",\n            d.length()\n        );\n\n        let mut tmin = -f32::INFINITY;\n        let mut tmax = f32::INFINITY;\n\n        if d.x != 0.0 {\n            let tx1 = (self.min.x - o.x) / d.x;\n            let tx2 = (self.max.x - o.x) / d.x;\n\n            tmin = tmin.max(tx1.min(tx2));\n            tmax = tmax.min(tx1.max(tx2));\n        }\n\n        if d.y != 0.0 {\n            let ty1 = (self.min.y - o.y) / d.y;\n            let ty2 = (self.max.y - o.y) / d.y;\n\n            tmin = tmin.max(ty1.min(ty2));\n            tmax = tmax.min(ty1.max(ty2));\n        }\n\n        0.0 <= tmax && tmin <= tmax\n    }\n\n    /// Where does a ray from the center intersect the rectangle?\n    ///\n    /// `d` is the direction of the ray and assumed to be normalized.\n    pub fn intersects_ray_from_center(&self, d: Vec2) -> Pos2 {\n        debug_assert!(\n            d.is_normalized(),\n            \"expected normalized direction, but `d` has length {}\",\n            d.length()\n        );\n\n        let mut tmin = f32::NEG_INFINITY;\n        let mut tmax = f32::INFINITY;\n\n        for i in 0..2 {\n            let inv_d = 1.0 / -d[i];\n            let mut t0 = (self.min[i] - self.center()[i]) * inv_d;\n            let mut t1 = (self.max[i] - self.center()[i]) * inv_d;\n\n            if inv_d < 0.0 {\n                std::mem::swap(&mut t0, &mut t1);\n            }\n\n            tmin = tmin.max(t0);\n            tmax = tmax.min(t1);\n        }\n\n        let t = tmax.min(tmin);\n        self.center() + t * -d\n    }\n}\n\nimpl fmt::Debug for Rect {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        if let Some(precision) = f.precision() {\n            write!(f, \"[{1:.0$?} - {2:.0$?}]\", precision, self.min, self.max)\n        } else {\n            write!(f, \"[{:?} - {:?}]\", self.min, self.max)\n        }\n    }\n}\n\nimpl fmt::Display for Rect {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"[\")?;\n        self.min.fmt(f)?;\n        f.write_str(\" - \")?;\n        self.max.fmt(f)?;\n        f.write_str(\"]\")?;\n        Ok(())\n    }\n}\n\n/// from (min, max) or (left top, right bottom)\nimpl From<[Pos2; 2]> for Rect {\n    #[inline]\n    fn from([min, max]: [Pos2; 2]) -> Self {\n        Self { min, max }\n    }\n}\n\nimpl Mul<f32> for Rect {\n    type Output = Self;\n\n    #[inline]\n    fn mul(self, factor: f32) -> Self {\n        Self {\n            min: self.min * factor,\n            max: self.max * factor,\n        }\n    }\n}\n\nimpl Mul<Rect> for f32 {\n    type Output = Rect;\n\n    #[inline]\n    fn mul(self, vec: Rect) -> Rect {\n        Rect {\n            min: self * vec.min,\n            max: self * vec.max,\n        }\n    }\n}\n\nimpl Div<f32> for Rect {\n    type Output = Self;\n\n    #[inline]\n    fn div(self, factor: f32) -> Self {\n        Self {\n            min: self.min / factor,\n            max: self.max / factor,\n        }\n    }\n}\n\nimpl BitOr for Rect {\n    type Output = Self;\n\n    #[inline]\n    fn bitor(self, other: Self) -> Self {\n        self.union(other)\n    }\n}\n\nimpl BitOrAssign for Rect {\n    #[inline]\n    fn bitor_assign(&mut self, other: Self) {\n        *self = self.union(other);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_rect() {\n        let r = Rect::from_min_max(pos2(10.0, 10.0), pos2(20.0, 20.0));\n        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 15.0)), 0.0);\n        assert_eq!(r.distance_sq_to_pos(pos2(10.0, 15.0)), 0.0);\n        assert_eq!(r.distance_sq_to_pos(pos2(10.0, 10.0)), 0.0);\n\n        assert_eq!(r.distance_sq_to_pos(pos2(5.0, 15.0)), 25.0); // left of\n        assert_eq!(r.distance_sq_to_pos(pos2(25.0, 15.0)), 25.0); // right of\n        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 5.0)), 25.0); // above\n        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 25.0)), 25.0); // below\n        assert_eq!(r.distance_sq_to_pos(pos2(25.0, 5.0)), 50.0); // right and above\n    }\n\n    #[test]\n    fn scale_rect() {\n        let c = pos2(100.0, 50.0);\n        let r = Rect::from_center_size(c, vec2(30.0, 60.0));\n\n        assert_eq!(\n            r.scale_from_center(2.0),\n            Rect::from_center_size(c, vec2(60.0, 120.0))\n        );\n        assert_eq!(\n            r.scale_from_center(0.5),\n            Rect::from_center_size(c, vec2(15.0, 30.0))\n        );\n        assert_eq!(\n            r.scale_from_center2(vec2(2.0, 3.0)),\n            Rect::from_center_size(c, vec2(60.0, 180.0))\n        );\n    }\n\n    #[expect(clippy::print_stdout)]\n    #[test]\n    fn test_ray_intersection() {\n        let rect = Rect::from_min_max(pos2(1.0, 1.0), pos2(3.0, 3.0));\n\n        println!(\"Righward ray from left:\");\n        assert!(rect.intersects_ray(pos2(0.0, 2.0), Vec2::RIGHT));\n\n        println!(\"Righward ray from center:\");\n        assert!(rect.intersects_ray(pos2(2.0, 2.0), Vec2::RIGHT));\n\n        println!(\"Righward ray from right:\");\n        assert!(!rect.intersects_ray(pos2(4.0, 2.0), Vec2::RIGHT));\n\n        println!(\"Leftward ray from left:\");\n        assert!(!rect.intersects_ray(pos2(0.0, 2.0), Vec2::LEFT));\n\n        println!(\"Leftward ray from center:\");\n        assert!(rect.intersects_ray(pos2(2.0, 2.0), Vec2::LEFT));\n\n        println!(\"Leftward ray from right:\");\n        assert!(rect.intersects_ray(pos2(4.0, 2.0), Vec2::LEFT));\n    }\n\n    #[test]\n    fn test_ray_from_center_intersection() {\n        let rect = Rect::from_min_max(pos2(1.0, 1.0), pos2(3.0, 3.0));\n\n        assert_eq!(\n            rect.intersects_ray_from_center(Vec2::RIGHT),\n            pos2(3.0, 2.0),\n            \"rightward ray\"\n        );\n\n        assert_eq!(\n            rect.intersects_ray_from_center(Vec2::UP),\n            pos2(2.0, 1.0),\n            \"upward ray\"\n        );\n\n        assert_eq!(\n            rect.intersects_ray_from_center(Vec2::LEFT),\n            pos2(1.0, 2.0),\n            \"leftward ray\"\n        );\n\n        assert_eq!(\n            rect.intersects_ray_from_center(Vec2::DOWN),\n            pos2(2.0, 3.0),\n            \"downward ray\"\n        );\n\n        assert_eq!(\n            rect.intersects_ray_from_center((Vec2::LEFT + Vec2::DOWN).normalized()),\n            pos2(1.0, 3.0),\n            \"bottom-left corner ray\"\n        );\n\n        assert_eq!(\n            rect.intersects_ray_from_center((Vec2::LEFT + Vec2::UP).normalized()),\n            pos2(1.0, 1.0),\n            \"top-left corner ray\"\n        );\n\n        assert_eq!(\n            rect.intersects_ray_from_center((Vec2::RIGHT + Vec2::DOWN).normalized()),\n            pos2(3.0, 3.0),\n            \"bottom-right corner ray\"\n        );\n\n        assert_eq!(\n            rect.intersects_ray_from_center((Vec2::RIGHT + Vec2::UP).normalized()),\n            pos2(3.0, 1.0),\n            \"top-right corner ray\"\n        );\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/rect_align.rs",
    "content": "use crate::{Align2, Pos2, Rect, Vec2};\n\n/// Position a child [`Rect`] relative to a parent [`Rect`].\n///\n/// The corner from [`RectAlign::child`] on the new rect will be aligned to\n/// the corner from [`RectAlign::parent`] on the original rect.\n///\n/// There are helper constants for the 12 common menu positions:\n/// ```text\n///              ┌───────────┐  ┌────────┐  ┌─────────┐\n///              │ TOP_START │  │  TOP   │  │ TOP_END │\n///              └───────────┘  └────────┘  └─────────┘\n/// ┌──────────┐ ┌────────────────────────────────────┐ ┌───────────┐\n/// │LEFT_START│ │                                    │ │RIGHT_START│\n/// └──────────┘ │                                    │ └───────────┘\n/// ┌──────────┐ │                                    │ ┌───────────┐\n/// │   LEFT   │ │             some_rect              │ │   RIGHT   │\n/// └──────────┘ │                                    │ └───────────┘\n/// ┌──────────┐ │                                    │ ┌───────────┐\n/// │ LEFT_END │ │                                    │ │ RIGHT_END │\n/// └──────────┘ └────────────────────────────────────┘ └───────────┘\n///              ┌────────────┐  ┌──────┐  ┌──────────┐\n///              │BOTTOM_START│  │BOTTOM│  │BOTTOM_END│\n///              └────────────┘  └──────┘  └──────────┘\n/// ```\n// There is no `new` function on purpose, since writing out `parent` and `child` is more\n// reasonable.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct RectAlign {\n    /// The alignment in the parent (original) rect.\n    pub parent: Align2,\n\n    /// The alignment in the child (new) rect.\n    pub child: Align2,\n}\n\nimpl Default for RectAlign {\n    fn default() -> Self {\n        Self::BOTTOM_START\n    }\n}\n\nimpl RectAlign {\n    /// Along the top edge, leftmost.\n    pub const TOP_START: Self = Self {\n        parent: Align2::LEFT_TOP,\n        child: Align2::LEFT_BOTTOM,\n    };\n\n    /// Along the top edge, centered.\n    pub const TOP: Self = Self {\n        parent: Align2::CENTER_TOP,\n        child: Align2::CENTER_BOTTOM,\n    };\n\n    /// Along the top edge, rightmost.\n    pub const TOP_END: Self = Self {\n        parent: Align2::RIGHT_TOP,\n        child: Align2::RIGHT_BOTTOM,\n    };\n\n    /// Along the right edge, topmost.\n    pub const RIGHT_START: Self = Self {\n        parent: Align2::RIGHT_TOP,\n        child: Align2::LEFT_TOP,\n    };\n\n    /// Along the right edge, centered.\n    pub const RIGHT: Self = Self {\n        parent: Align2::RIGHT_CENTER,\n        child: Align2::LEFT_CENTER,\n    };\n\n    /// Along the right edge, bottommost.\n    pub const RIGHT_END: Self = Self {\n        parent: Align2::RIGHT_BOTTOM,\n        child: Align2::LEFT_BOTTOM,\n    };\n\n    /// Along the bottom edge, rightmost.\n    pub const BOTTOM_END: Self = Self {\n        parent: Align2::RIGHT_BOTTOM,\n        child: Align2::RIGHT_TOP,\n    };\n\n    /// Along the bottom edge, centered.\n    pub const BOTTOM: Self = Self {\n        parent: Align2::CENTER_BOTTOM,\n        child: Align2::CENTER_TOP,\n    };\n\n    /// Along the bottom edge, leftmost.\n    pub const BOTTOM_START: Self = Self {\n        parent: Align2::LEFT_BOTTOM,\n        child: Align2::LEFT_TOP,\n    };\n\n    /// Along the left edge, bottommost.\n    pub const LEFT_END: Self = Self {\n        parent: Align2::LEFT_BOTTOM,\n        child: Align2::RIGHT_BOTTOM,\n    };\n\n    /// Along the left edge, centered.\n    pub const LEFT: Self = Self {\n        parent: Align2::LEFT_CENTER,\n        child: Align2::RIGHT_CENTER,\n    };\n\n    /// Along the left edge, topmost.\n    pub const LEFT_START: Self = Self {\n        parent: Align2::LEFT_TOP,\n        child: Align2::RIGHT_TOP,\n    };\n\n    /// The 12 most common menu positions as an array, for use with [`RectAlign::find_best_align`].\n    pub const MENU_ALIGNS: [Self; 12] = [\n        Self::BOTTOM_START,\n        Self::BOTTOM_END,\n        Self::TOP_START,\n        Self::TOP_END,\n        Self::RIGHT_END,\n        Self::RIGHT_START,\n        Self::LEFT_END,\n        Self::LEFT_START,\n        // These come last on purpose, we prefer the corner ones\n        Self::TOP,\n        Self::RIGHT,\n        Self::BOTTOM,\n        Self::LEFT,\n    ];\n\n    /// Align in the parent rect.\n    pub fn parent(&self) -> Align2 {\n        self.parent\n    }\n\n    /// Align in the child rect.\n    pub fn child(&self) -> Align2 {\n        self.child\n    }\n\n    /// Convert an [`Align2`] to an [`RectAlign`], positioning the child rect inside the parent.\n    pub fn from_align2(align: Align2) -> Self {\n        Self {\n            parent: align,\n            child: align,\n        }\n    }\n\n    /// The center of the child rect will be aligned to a corner of the parent rect.\n    pub fn over_corner(align: Align2) -> Self {\n        Self {\n            parent: align,\n            child: Align2::CENTER_CENTER,\n        }\n    }\n\n    /// Position the child rect outside the parent rect.\n    pub fn outside(align: Align2) -> Self {\n        Self {\n            parent: align,\n            child: align.flip(),\n        }\n    }\n\n    /// Calculate the child rect based on a size and some optional gap.\n    pub fn align_rect(&self, parent_rect: &Rect, size: Vec2, gap: f32) -> Rect {\n        let (pivot, anchor) = self.pivot_pos(parent_rect, gap);\n        pivot.anchor_size(anchor, size)\n    }\n\n    /// Returns a [`Align2`] and a [`Pos2`] that you can e.g. use with `Area::fixed_pos`\n    /// and `Area::pivot` to align an `Area` to some rect.\n    pub fn pivot_pos(&self, parent_rect: &Rect, gap: f32) -> (Align2, Pos2) {\n        (self.child(), self.anchor(parent_rect, gap))\n    }\n\n    /// Returns a sign vector (-1, 0 or 1 in each direction) that can be used as an offset to the\n    /// child rect, creating a gap between the rects while keeping the edges aligned.\n    pub fn gap_vector(&self) -> Vec2 {\n        let mut gap = -self.child.to_sign();\n\n        // Align the edges in these cases\n        match *self {\n            Self::TOP_START | Self::TOP_END | Self::BOTTOM_START | Self::BOTTOM_END => {\n                gap.x = 0.0;\n            }\n            Self::LEFT_START | Self::LEFT_END | Self::RIGHT_START | Self::RIGHT_END => {\n                gap.y = 0.0;\n            }\n            _ => {}\n        }\n\n        gap\n    }\n\n    /// Calculator the anchor point for the child rect, based on the parent rect and an optional gap.\n    pub fn anchor(&self, parent_rect: &Rect, gap: f32) -> Pos2 {\n        let pos = self.parent.pos_in_rect(parent_rect);\n\n        let offset = self.gap_vector() * gap;\n\n        pos + offset\n    }\n\n    /// Flip the alignment on the x-axis.\n    pub fn flip_x(self) -> Self {\n        Self {\n            parent: self.parent.flip_x(),\n            child: self.child.flip_x(),\n        }\n    }\n\n    /// Flip the alignment on the y-axis.\n    pub fn flip_y(self) -> Self {\n        Self {\n            parent: self.parent.flip_y(),\n            child: self.child.flip_y(),\n        }\n    }\n\n    /// Flip the alignment on both axes.\n    pub fn flip(self) -> Self {\n        Self {\n            parent: self.parent.flip(),\n            child: self.child.flip(),\n        }\n    }\n\n    /// Returns the 3 alternative [`RectAlign`]s that are flipped in various ways, for use\n    /// with [`RectAlign::find_best_align`].\n    pub fn symmetries(self) -> [Self; 3] {\n        [self.flip_x(), self.flip_y(), self.flip()]\n    }\n\n    /// Look for the first alternative [`RectAlign`] that allows the child rect to fit\n    /// inside the `content_rect`.\n    ///\n    /// If no alternative fits, the first is returned.\n    /// If no alternatives are given, `None` is returned.\n    ///\n    /// See also:\n    /// - [`RectAlign::symmetries`] to calculate alternatives\n    /// - [`RectAlign::MENU_ALIGNS`] for the 12 common menu positions\n    pub fn find_best_align(\n        values_to_try: impl Iterator<Item = Self>,\n        content_rect: Rect,\n        parent_rect: Rect,\n        gap: f32,\n        expected_size: Vec2,\n    ) -> Option<Self> {\n        let mut first_choice = None;\n\n        for align in values_to_try {\n            first_choice = first_choice.or(Some(align)); // Remember the first alternative\n\n            let suggested_popup_rect = align.align_rect(&parent_rect, expected_size, gap);\n\n            if content_rect.contains_rect(suggested_popup_rect) {\n                return Some(align);\n            }\n        }\n\n        first_choice\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/rect_transform.rs",
    "content": "use crate::{Pos2, Rect, Vec2, pos2, remap, remap_clamp};\n\n/// Linearly transforms positions from one [`Rect`] to another.\n///\n/// [`RectTransform`] stores the rectangles, and therefore supports clamping and culling.\n#[repr(C)]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct RectTransform {\n    from: Rect,\n    to: Rect,\n}\n\nimpl RectTransform {\n    pub fn identity(from_and_to: Rect) -> Self {\n        Self::from_to(from_and_to, from_and_to)\n    }\n\n    pub fn from_to(from: Rect, to: Rect) -> Self {\n        Self { from, to }\n    }\n\n    pub fn from(&self) -> &Rect {\n        &self.from\n    }\n\n    pub fn to(&self) -> &Rect {\n        &self.to\n    }\n\n    /// The scale factors.\n    pub fn scale(&self) -> Vec2 {\n        self.to.size() / self.from.size()\n    }\n\n    pub fn inverse(&self) -> Self {\n        Self::from_to(self.to, self.from)\n    }\n\n    /// Transforms the given coordinate in the `from` space to the `to` space.\n    pub fn transform_pos(&self, pos: Pos2) -> Pos2 {\n        pos2(\n            remap(pos.x, self.from.x_range(), self.to.x_range()),\n            remap(pos.y, self.from.y_range(), self.to.y_range()),\n        )\n    }\n\n    /// Transforms the given rectangle in the `in`-space to a rectangle in the `out`-space.\n    pub fn transform_rect(&self, rect: Rect) -> Rect {\n        Rect {\n            min: self.transform_pos(rect.min),\n            max: self.transform_pos(rect.max),\n        }\n    }\n\n    /// Transforms the given coordinate in the `from` space to the `to` space,\n    /// clamping if necessary.\n    pub fn transform_pos_clamped(&self, pos: Pos2) -> Pos2 {\n        pos2(\n            remap_clamp(pos.x, self.from.x_range(), self.to.x_range()),\n            remap_clamp(pos.y, self.from.y_range(), self.to.y_range()),\n        )\n    }\n}\n\n/// Transforms the position.\nimpl std::ops::Mul<Pos2> for RectTransform {\n    type Output = Pos2;\n\n    fn mul(self, pos: Pos2) -> Pos2 {\n        self.transform_pos(pos)\n    }\n}\n\n/// Transforms the position.\nimpl std::ops::Mul<Pos2> for &RectTransform {\n    type Output = Pos2;\n\n    fn mul(self, pos: Pos2) -> Pos2 {\n        self.transform_pos(pos)\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/rot2.rs",
    "content": "use super::Vec2;\n\n// {s,c} represents the rotation matrix:\n//\n// | c -s |\n// | s  c |\n//\n// `vec2(c,s)` represents where the X axis will end up after rotation.\n//\n/// Represents a rotation in the 2D plane.\n///\n/// A rotation of 𝞃/4 = 90° rotates the X axis to the Y axis.\n///\n/// Normally a [`Rot2`] is normalized (unit-length).\n/// If not, it will also scale vectors.\n#[repr(C)]\n#[derive(Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct Rot2 {\n    /// `angle.sin()`\n    s: f32,\n\n    /// `angle.cos()`\n    c: f32,\n}\n\n/// Identity rotation\nimpl Default for Rot2 {\n    /// Identity rotation\n    #[inline]\n    fn default() -> Self {\n        Self { s: 0.0, c: 1.0 }\n    }\n}\n\nimpl Rot2 {\n    /// The identity rotation: nothing rotates\n    pub const IDENTITY: Self = Self { s: 0.0, c: 1.0 };\n\n    /// Angle is clockwise in radians.\n    /// A 𝞃/4 = 90° rotation means rotating the X axis to the Y axis.\n    #[inline]\n    pub fn from_angle(angle: f32) -> Self {\n        let (s, c) = angle.sin_cos();\n        Self { s, c }\n    }\n\n    #[inline]\n    pub fn angle(self) -> f32 {\n        self.s.atan2(self.c)\n    }\n\n    /// The factor by which vectors will be scaled.\n    #[inline]\n    pub fn length(self) -> f32 {\n        self.c.hypot(self.s)\n    }\n\n    #[inline]\n    pub fn length_squared(self) -> f32 {\n        self.c.powi(2) + self.s.powi(2)\n    }\n\n    #[inline]\n    pub fn is_finite(self) -> bool {\n        self.c.is_finite() && self.s.is_finite()\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn inverse(self) -> Self {\n        Self {\n            s: -self.s,\n            c: self.c,\n        } / self.length_squared()\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn normalized(self) -> Self {\n        let l = self.length();\n        let ret = Self {\n            c: self.c / l,\n            s: self.s / l,\n        };\n        debug_assert!(\n            ret.is_finite(),\n            \"Rot2::normalized produced a non-finite result\"\n        );\n        ret\n    }\n}\n\nimpl std::fmt::Debug for Rot2 {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        if let Some(precision) = f.precision() {\n            write!(\n                f,\n                \"Rot2 {{ angle: {:.2$}°, length: {} }}\",\n                self.angle().to_degrees(),\n                self.length(),\n                precision\n            )\n        } else {\n            write!(\n                f,\n                \"Rot2 {{ angle: {:.1}°, length: {} }}\",\n                self.angle().to_degrees(),\n                self.length(),\n            )\n        }\n    }\n}\n\nimpl std::ops::Mul<Self> for Rot2 {\n    type Output = Self;\n\n    #[inline]\n    fn mul(self, r: Self) -> Self {\n        /*\n        |lc -ls| * |rc -rs|\n        |ls  lc|   |rs  rc|\n        */\n        Self {\n            c: self.c * r.c - self.s * r.s,\n            s: self.s * r.c + self.c * r.s,\n        }\n    }\n}\n\n/// Rotates (and maybe scales) the vector.\nimpl std::ops::Mul<Vec2> for Rot2 {\n    type Output = Vec2;\n\n    #[inline]\n    fn mul(self, v: Vec2) -> Vec2 {\n        Vec2 {\n            x: self.c * v.x - self.s * v.y,\n            y: self.s * v.x + self.c * v.y,\n        }\n    }\n}\n\n/// Scales the rotor.\nimpl std::ops::Mul<Rot2> for f32 {\n    type Output = Rot2;\n\n    #[inline]\n    fn mul(self, r: Rot2) -> Rot2 {\n        Rot2 {\n            c: self * r.c,\n            s: self * r.s,\n        }\n    }\n}\n\n/// Scales the rotor.\nimpl std::ops::Mul<f32> for Rot2 {\n    type Output = Self;\n\n    #[inline]\n    fn mul(self, r: f32) -> Self {\n        Self {\n            c: self.c * r,\n            s: self.s * r,\n        }\n    }\n}\n\n/// Scales the rotor.\nimpl std::ops::Div<f32> for Rot2 {\n    type Output = Self;\n\n    #[inline]\n    fn div(self, r: f32) -> Self {\n        Self {\n            c: self.c / r,\n            s: self.s / r,\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::Rot2;\n    use crate::vec2;\n\n    #[test]\n    fn test_rotation2() {\n        {\n            let angle = std::f32::consts::TAU / 6.0;\n            let rot = Rot2::from_angle(angle);\n            assert!((rot.angle() - angle).abs() < 1e-5);\n            assert!((rot * rot.inverse()).angle().abs() < 1e-5);\n            assert!((rot.inverse() * rot).angle().abs() < 1e-5);\n        }\n\n        {\n            let angle = std::f32::consts::TAU / 4.0;\n            let rot = Rot2::from_angle(angle);\n            assert!(((rot * vec2(1.0, 0.0)) - vec2(0.0, 1.0)).length() < 1e-5);\n        }\n\n        {\n            // Test rotation and scaling\n            let angle = std::f32::consts::TAU / 4.0;\n            let rot = 3.0 * Rot2::from_angle(angle);\n            let rotated = rot * vec2(1.0, 0.0);\n            let expected = vec2(0.0, 3.0);\n            assert!(\n                (rotated - expected).length() < 1e-5,\n                \"Expected {rotated:?} to equal {expected:?}. rot: {rot:?}\",\n            );\n\n            let undone = rot.inverse() * rot;\n            assert!(undone.angle().abs() < 1e-5);\n            assert!((undone.length() - 1.0).abs() < 1e-5,);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/smart_aim.rs",
    "content": "//! Find \"simple\" numbers is some range. Used by sliders.\n\nuse crate::fast_midpoint;\n\nconst NUM_DECIMALS: usize = 16;\n\n/// Find the \"simplest\" number in a closed range [min, max], i.e. the one with the fewest decimal digits.\n///\n/// So in the range `[0.83, 1.354]` you will get `1.0`, and for `[0.37, 0.48]` you will get `0.4`.\n/// This is used when dragging sliders etc to get the values that users are most likely to desire.\n/// This assumes a decimal centric user.\npub fn best_in_range_f64(min: f64, max: f64) -> f64 {\n    // Avoid NaN if we can:\n    if min.is_nan() {\n        return max;\n    }\n    if max.is_nan() {\n        return min;\n    }\n\n    if max < min {\n        return best_in_range_f64(max, min);\n    }\n    if min == max {\n        return min;\n    }\n    if min <= 0.0 && 0.0 <= max {\n        return 0.0; // always prefer zero\n    }\n    if min < 0.0 {\n        return -best_in_range_f64(-max, -min);\n    }\n\n    debug_assert!(0.0 < min && min < max, \"Logic bug\");\n\n    // Prefer finite numbers:\n    if !max.is_finite() {\n        return min;\n    }\n    debug_assert!(\n        min.is_finite() && max.is_finite(),\n        \"min: {min:?}, max: {max:?}\"\n    );\n\n    let min_exponent = min.log10();\n    let max_exponent = max.log10();\n\n    if min_exponent.floor() != max_exponent.floor() {\n        // Different orders of magnitude.\n        // Pick the geometric center of the two:\n        let exponent = fast_midpoint(min_exponent, max_exponent);\n        return 10.0_f64.powi(exponent.round() as i32);\n    }\n\n    if is_integer(min_exponent) {\n        return 10.0_f64.powf(min_exponent);\n    }\n    if is_integer(max_exponent) {\n        return 10.0_f64.powf(max_exponent);\n    }\n\n    // Find the proper scale, and then convert to integers:\n\n    let scale = NUM_DECIMALS as i32 - max_exponent.floor() as i32 - 1;\n    let scale_factor = 10.0_f64.powi(scale);\n\n    let min_str = to_decimal_string((min * scale_factor).round() as u64);\n    let max_str = to_decimal_string((max * scale_factor).round() as u64);\n\n    // We now have two positive integers of the same length.\n    // We want to find the first non-matching digit,\n    // which we will call the \"deciding digit\".\n    // Everything before it will be the same,\n    // everything after will be zero,\n    // and the deciding digit itself will be picked as a \"smart average\"\n    // min:    12345\n    // max:    12780\n    // output: 12500\n\n    let mut ret_str = [0; NUM_DECIMALS];\n\n    for i in 0..NUM_DECIMALS {\n        if min_str[i] == max_str[i] {\n            ret_str[i] = min_str[i];\n        } else {\n            // Found the deciding digit at index `i`\n            let mut deciding_digit_min = min_str[i];\n            let deciding_digit_max = max_str[i];\n\n            debug_assert!(\n                deciding_digit_min < deciding_digit_max,\n                \"Bug in smart aim code\"\n            );\n\n            let rest_of_min_is_zeroes = min_str[i + 1..].iter().all(|&c| c == 0);\n\n            if !rest_of_min_is_zeroes {\n                // There are more digits coming after `deciding_digit_min`, so we cannot pick it.\n                // So the true min of what we can pick is one greater:\n                deciding_digit_min += 1;\n            }\n\n            let deciding_digit = if deciding_digit_min == 0 {\n                0\n            } else if deciding_digit_min <= 5 && 5 <= deciding_digit_max {\n                5 // 5 is the roundest number in the range\n            } else {\n                deciding_digit_min.midpoint(deciding_digit_max)\n            };\n\n            ret_str[i] = deciding_digit;\n\n            return from_decimal_string(ret_str) as f64 / scale_factor;\n        }\n    }\n\n    min // All digits are the same. Already handled earlier, but better safe than sorry\n}\n\nfn is_integer(f: f64) -> bool {\n    f.round() == f\n}\n\nfn to_decimal_string(v: u64) -> [u8; NUM_DECIMALS] {\n    let mut ret = [0; NUM_DECIMALS];\n    let mut value = v;\n    for i in (0..NUM_DECIMALS).rev() {\n        ret[i] = (value % 10) as u8;\n        value /= 10;\n    }\n    ret\n}\n\nfn from_decimal_string(s: [u8; NUM_DECIMALS]) -> u64 {\n    let mut value = 0;\n    for &c in &s {\n        debug_assert!(c <= 9, \"Bad number\");\n        value = value * 10 + c as u64;\n    }\n    value\n}\n\n#[expect(clippy::approx_constant)]\n#[test]\nfn test_aim() {\n    assert_eq!(\n        best_in_range_f64(0.0799999999999996, 0.09999999999999995),\n        0.08,\n    );\n    assert_eq!(best_in_range_f64(-0.2, 0.0), 0.0, \"Prefer zero\");\n    assert_eq!(best_in_range_f64(-10_004.23, 3.14), 0.0, \"Prefer zero\");\n    assert_eq!(best_in_range_f64(-0.2, 100.0), 0.0, \"Prefer zero\");\n    assert_eq!(best_in_range_f64(0.2, 0.0), 0.0, \"Prefer zero\");\n    assert_eq!(best_in_range_f64(7.8, 17.8), 10.0);\n    assert_eq!(best_in_range_f64(99.0, 300.0), 100.0);\n    assert_eq!(best_in_range_f64(-99.0, -300.0), -100.0);\n    assert_eq!(best_in_range_f64(0.4, 0.9), 0.5, \"Prefer ending on 5\");\n    assert_eq!(best_in_range_f64(14.1, 19.99), 15.0, \"Prefer ending on 5\");\n    assert_eq!(best_in_range_f64(12.3, 65.9), 50.0, \"Prefer leading 5\");\n    assert_eq!(best_in_range_f64(493.0, 879.0), 500.0, \"Prefer leading 5\");\n    assert_eq!(best_in_range_f64(0.37, 0.48), 0.40);\n    // assert_eq!(best_in_range_f64(123.71, 123.76), 123.75); // TODO(emilk): we get 123.74999999999999 here\n    // assert_eq!(best_in_range_f32(123.71, 123.76), 123.75);\n    assert_eq!(best_in_range_f64(7.5, 16.3), 10.0);\n    assert_eq!(best_in_range_f64(7.5, 76.3), 10.0);\n    assert_eq!(best_in_range_f64(7.5, 763.3), 100.0);\n    assert_eq!(best_in_range_f64(7.5, 1_345.0), 100.0);\n    assert_eq!(best_in_range_f64(7.5, 123_456.0), 1000.0, \"Geometric mean\");\n    assert_eq!(best_in_range_f64(9.9999, 99.999), 10.0);\n    assert_eq!(best_in_range_f64(10.000, 99.999), 10.0);\n    assert_eq!(best_in_range_f64(10.001, 99.999), 50.0);\n    assert_eq!(best_in_range_f64(10.001, 100.000), 100.0);\n    assert_eq!(best_in_range_f64(99.999, 100.000), 100.0);\n    assert_eq!(best_in_range_f64(10.001, 100.001), 100.0);\n\n    const NAN: f64 = f64::NAN;\n    const INFINITY: f64 = f64::INFINITY;\n    const NEG_INFINITY: f64 = f64::NEG_INFINITY;\n    assert!(best_in_range_f64(NAN, NAN).is_nan());\n    assert_eq!(best_in_range_f64(NAN, 1.2), 1.2);\n    assert_eq!(best_in_range_f64(NAN, INFINITY), INFINITY);\n    assert_eq!(best_in_range_f64(1.2, NAN), 1.2);\n    assert_eq!(best_in_range_f64(1.2, INFINITY), 1.2);\n    assert_eq!(best_in_range_f64(INFINITY, 1.2), 1.2);\n    assert_eq!(best_in_range_f64(NEG_INFINITY, 1.2), 0.0);\n    assert_eq!(best_in_range_f64(NEG_INFINITY, -2.7), -2.7);\n    assert_eq!(best_in_range_f64(INFINITY, INFINITY), INFINITY);\n    assert_eq!(best_in_range_f64(NEG_INFINITY, NEG_INFINITY), NEG_INFINITY);\n    assert_eq!(best_in_range_f64(NEG_INFINITY, INFINITY), 0.0);\n    assert_eq!(best_in_range_f64(INFINITY, NEG_INFINITY), 0.0);\n\n    #[track_caller]\n    fn test_f64((min, max): (f64, f64), expected: f64) {\n        let aimed = best_in_range_f64(min, max);\n        assert!(\n            aimed == expected,\n            \"smart_aim({min} – {max}) => {aimed}, but expected {expected}\"\n        );\n    }\n    #[track_caller]\n    fn test_i64((min, max): (i64, i64), expected: i64) {\n        let aimed = best_in_range_f64(min as _, max as _);\n        assert!(\n            aimed == expected as f64,\n            \"smart_aim({min} – {max}) => {aimed}, but expected {expected}\"\n        );\n    }\n\n    test_i64((99, 300), 100);\n    test_i64((300, 99), 100);\n    test_i64((-99, -300), -100);\n    test_i64((-99, 123), 0); // Prefer zero\n    test_i64((4, 9), 5); // Prefer ending on 5\n    test_i64((14, 19), 15); // Prefer ending on 5\n    test_i64((12, 65), 50); // Prefer leading 5\n    test_i64((493, 879), 500); // Prefer leading 5\n    test_i64((37, 48), 40);\n    test_i64((100, 123), 100);\n    test_i64((101, 1000), 1000);\n    test_i64((999, 1000), 1000);\n    test_i64((123, 500), 500);\n    test_i64((500, 777), 500);\n    test_i64((500, 999), 500);\n    test_i64((12345, 12780), 12500);\n    test_i64((12371, 12376), 12375);\n    test_i64((12371, 12376), 12375);\n\n    test_f64((7.5, 16.3), 10.0);\n    test_f64((7.5, 76.3), 10.0);\n    test_f64((7.5, 763.3), 100.0);\n    test_f64((7.5, 1_345.0), 100.0); // Geometric mean\n    test_f64((7.5, 123_456.0), 1_000.0); // Geometric mean\n    test_f64((-0.2, 0.0), 0.0); // Prefer zero\n    test_f64((-10_004.23, 4.14), 0.0); // Prefer zero\n    test_f64((-0.2, 100.0), 0.0); // Prefer zero\n    test_f64((0.2, 0.0), 0.0); // Prefer zero\n    test_f64((7.8, 17.8), 10.0);\n    test_f64((14.1, 19.1), 15.0); // Prefer ending on 5\n    test_f64((12.3, 65.9), 50.0); // Prefer leading 5\n}\n"
  },
  {
    "path": "crates/emath/src/ts_transform.rs",
    "content": "use crate::{Pos2, Rect, Vec2};\n\n/// Linearly transforms positions via a translation, then a scaling.\n///\n/// [`TSTransform`] first scales points with the scaling origin at `0, 0`\n/// (the top left corner), then translates them.\n#[repr(C)]\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct TSTransform {\n    /// Scaling applied first, scaled around (0, 0).\n    pub scaling: f32,\n\n    /// Translation amount, applied after scaling.\n    pub translation: Vec2,\n}\n\nimpl Eq for TSTransform {}\n\nimpl Default for TSTransform {\n    #[inline]\n    fn default() -> Self {\n        Self::IDENTITY\n    }\n}\n\nimpl TSTransform {\n    pub const IDENTITY: Self = Self {\n        translation: Vec2::ZERO,\n        scaling: 1.0,\n    };\n\n    #[inline]\n    /// Creates a new translation that first scales points around\n    /// `(0, 0)`, then translates them.\n    pub fn new(translation: Vec2, scaling: f32) -> Self {\n        Self {\n            scaling,\n            translation,\n        }\n    }\n\n    #[inline]\n    pub fn from_translation(translation: Vec2) -> Self {\n        Self::new(translation, 1.0)\n    }\n\n    #[inline]\n    pub fn from_scaling(scaling: f32) -> Self {\n        Self::new(Vec2::ZERO, scaling)\n    }\n\n    /// Is this a valid, invertible transform?\n    pub fn is_valid(&self) -> bool {\n        self.scaling.is_finite() && self.translation.x.is_finite() && self.scaling != 0.0\n    }\n\n    /// Inverts the transform.\n    ///\n    /// ```\n    /// # use emath::{pos2, vec2, TSTransform};\n    /// let p1 = pos2(2.0, 3.0);\n    /// let p2 = pos2(12.0, 5.0);\n    /// let ts = TSTransform::new(vec2(2.0, 3.0), 2.0);\n    /// let inv = ts.inverse();\n    /// assert_eq!(inv.mul_pos(p1), pos2(0.0, 0.0));\n    /// assert_eq!(inv.mul_pos(p2), pos2(5.0, 1.0));\n    ///\n    /// assert_eq!(ts.inverse().inverse(), ts);\n    /// ```\n    #[inline]\n    pub fn inverse(&self) -> Self {\n        Self::new(-self.translation / self.scaling, 1.0 / self.scaling)\n    }\n\n    /// Transforms the given coordinate.\n    ///\n    /// ```\n    /// # use emath::{pos2, vec2, TSTransform};\n    /// let p1 = pos2(0.0, 0.0);\n    /// let p2 = pos2(5.0, 1.0);\n    /// let ts = TSTransform::new(vec2(2.0, 3.0), 2.0);\n    /// assert_eq!(ts.mul_pos(p1), pos2(2.0, 3.0));\n    /// assert_eq!(ts.mul_pos(p2), pos2(12.0, 5.0));\n    /// ```\n    #[inline]\n    pub fn mul_pos(&self, pos: Pos2) -> Pos2 {\n        self.scaling * pos + self.translation\n    }\n\n    /// Transforms the given rectangle.\n    ///\n    /// ```\n    /// # use emath::{pos2, vec2, Rect, TSTransform};\n    /// let rect = Rect::from_min_max(pos2(5.0, 5.0), pos2(15.0, 10.0));\n    /// let ts = TSTransform::new(vec2(1.0, 0.0), 3.0);\n    /// let transformed = ts.mul_rect(rect);\n    /// assert_eq!(transformed.min, pos2(16.0, 15.0));\n    /// assert_eq!(transformed.max, pos2(46.0, 30.0));\n    /// ```\n    #[inline]\n    pub fn mul_rect(&self, rect: Rect) -> Rect {\n        Rect {\n            min: self.mul_pos(rect.min),\n            max: self.mul_pos(rect.max),\n        }\n    }\n}\n\n/// Transforms the position.\nimpl std::ops::Mul<Pos2> for TSTransform {\n    type Output = Pos2;\n\n    #[inline]\n    fn mul(self, pos: Pos2) -> Pos2 {\n        self.mul_pos(pos)\n    }\n}\n\n/// Transforms the rectangle.\nimpl std::ops::Mul<Rect> for TSTransform {\n    type Output = Rect;\n\n    #[inline]\n    fn mul(self, rect: Rect) -> Rect {\n        self.mul_rect(rect)\n    }\n}\n\nimpl std::ops::Mul<Self> for TSTransform {\n    type Output = Self;\n\n    #[inline]\n    /// Applies the right hand side transform, then the left hand side.\n    ///\n    /// ```\n    /// # use emath::{TSTransform, vec2};\n    /// let ts1 = TSTransform::new(vec2(1.0, 0.0), 2.0);\n    /// let ts2 = TSTransform::new(vec2(-1.0, -1.0), 3.0);\n    /// let ts_combined = TSTransform::new(vec2(2.0, -1.0), 6.0);\n    /// assert_eq!(ts_combined, ts2 * ts1);\n    /// ```\n    fn mul(self, rhs: Self) -> Self::Output {\n        // Apply rhs first.\n        Self {\n            scaling: self.scaling * rhs.scaling,\n            translation: self.translation + self.scaling * rhs.translation,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/vec2.rs",
    "content": "use std::fmt;\nuse std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};\n\nuse crate::Vec2b;\n\n/// A vector has a direction and length.\n/// A [`Vec2`] is often used to represent a size.\n///\n/// emath represents positions using [`crate::Pos2`].\n///\n/// Normally the units are points (logical pixels).\n#[repr(C)]\n#[derive(Clone, Copy, Default, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct Vec2 {\n    /// Rightwards. Width.\n    pub x: f32,\n\n    /// Downwards. Height.\n    pub y: f32,\n}\n\n/// `vec2(x, y) == Vec2::new(x, y)`\n#[inline(always)]\npub const fn vec2(x: f32, y: f32) -> Vec2 {\n    Vec2 { x, y }\n}\n\n// ----------------------------------------------------------------------------\n// Compatibility and convenience conversions to and from [f32; 2]:\n\nimpl From<[f32; 2]> for Vec2 {\n    #[inline(always)]\n    fn from(v: [f32; 2]) -> Self {\n        Self { x: v[0], y: v[1] }\n    }\n}\n\nimpl From<&[f32; 2]> for Vec2 {\n    #[inline(always)]\n    fn from(v: &[f32; 2]) -> Self {\n        Self { x: v[0], y: v[1] }\n    }\n}\n\nimpl From<Vec2> for [f32; 2] {\n    #[inline(always)]\n    fn from(v: Vec2) -> Self {\n        [v.x, v.y]\n    }\n}\n\nimpl From<&Vec2> for [f32; 2] {\n    #[inline(always)]\n    fn from(v: &Vec2) -> Self {\n        [v.x, v.y]\n    }\n}\n\n// ----------------------------------------------------------------------------\n// Compatibility and convenience conversions to and from (f32, f32):\n\nimpl From<(f32, f32)> for Vec2 {\n    #[inline(always)]\n    fn from(v: (f32, f32)) -> Self {\n        Self { x: v.0, y: v.1 }\n    }\n}\n\nimpl From<&(f32, f32)> for Vec2 {\n    #[inline(always)]\n    fn from(v: &(f32, f32)) -> Self {\n        Self { x: v.0, y: v.1 }\n    }\n}\n\nimpl From<Vec2> for (f32, f32) {\n    #[inline(always)]\n    fn from(v: Vec2) -> Self {\n        (v.x, v.y)\n    }\n}\n\nimpl From<&Vec2> for (f32, f32) {\n    #[inline(always)]\n    fn from(v: &Vec2) -> Self {\n        (v.x, v.y)\n    }\n}\n\nimpl From<Vec2b> for Vec2 {\n    #[inline(always)]\n    fn from(v: Vec2b) -> Self {\n        Self {\n            x: v.x as i32 as f32,\n            y: v.y as i32 as f32,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n// Mint compatibility and convenience conversions\n\n#[cfg(feature = \"mint\")]\nimpl From<mint::Vector2<f32>> for Vec2 {\n    #[inline]\n    fn from(v: mint::Vector2<f32>) -> Self {\n        Self::new(v.x, v.y)\n    }\n}\n\n#[cfg(feature = \"mint\")]\nimpl From<Vec2> for mint::Vector2<f32> {\n    #[inline]\n    fn from(v: Vec2) -> Self {\n        Self { x: v.x, y: v.y }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nimpl Vec2 {\n    /// Right\n    pub const X: Self = Self { x: 1.0, y: 0.0 };\n\n    /// Down\n    pub const Y: Self = Self { x: 0.0, y: 1.0 };\n\n    /// +X\n    pub const RIGHT: Self = Self { x: 1.0, y: 0.0 };\n\n    /// -X\n    pub const LEFT: Self = Self { x: -1.0, y: 0.0 };\n\n    /// -Y\n    pub const UP: Self = Self { x: 0.0, y: -1.0 };\n\n    /// +Y\n    pub const DOWN: Self = Self { x: 0.0, y: 1.0 };\n\n    pub const ZERO: Self = Self { x: 0.0, y: 0.0 };\n    pub const ONE: Self = Self { x: 1.0, y: 1.0 };\n    pub const INFINITY: Self = Self::splat(f32::INFINITY);\n    pub const NAN: Self = Self::splat(f32::NAN);\n\n    #[inline(always)]\n    pub const fn new(x: f32, y: f32) -> Self {\n        Self { x, y }\n    }\n\n    /// Set both `x` and `y` to the same value.\n    #[inline(always)]\n    pub const fn splat(v: f32) -> Self {\n        Self { x: v, y: v }\n    }\n\n    /// Treat this vector as a position.\n    /// `v.to_pos2()` is equivalent to `Pos2::default() + v`.\n    #[inline(always)]\n    pub fn to_pos2(self) -> crate::Pos2 {\n        crate::Pos2 {\n            x: self.x,\n            y: self.y,\n        }\n    }\n\n    /// Safe normalize: returns zero if input is zero.\n    #[must_use]\n    #[inline(always)]\n    pub fn normalized(self) -> Self {\n        let len = self.length();\n        if len <= 0.0 { self } else { self / len }\n    }\n\n    /// Checks if `self` has length `1.0` up to a precision of `1e-6`.\n    #[inline(always)]\n    pub fn is_normalized(self) -> bool {\n        (self.length_sq() - 1.0).abs() < 2e-6\n    }\n\n    /// Rotates the vector by 90°, i.e positive X to positive Y\n    /// (clockwise in egui coordinates).\n    #[inline(always)]\n    pub fn rot90(self) -> Self {\n        vec2(self.y, -self.x)\n    }\n\n    #[inline(always)]\n    pub fn length(self) -> f32 {\n        self.x.hypot(self.y)\n    }\n\n    #[inline(always)]\n    pub fn length_sq(self) -> f32 {\n        self.x * self.x + self.y * self.y\n    }\n\n    /// Measures the angle of the vector.\n    ///\n    /// ```\n    /// # use emath::Vec2;\n    /// use std::f32::consts::TAU;\n    ///\n    /// assert_eq!(Vec2::ZERO.angle(), 0.0);\n    /// assert_eq!(Vec2::angled(0.0).angle(), 0.0);\n    /// assert_eq!(Vec2::angled(1.0).angle(), 1.0);\n    /// assert_eq!(Vec2::X.angle(), 0.0);\n    /// assert_eq!(Vec2::Y.angle(), 0.25 * TAU);\n    ///\n    /// assert_eq!(Vec2::RIGHT.angle(), 0.0);\n    /// assert_eq!(Vec2::DOWN.angle(), 0.25 * TAU);\n    /// assert_eq!(Vec2::UP.angle(), -0.25 * TAU);\n    /// ```\n    #[inline(always)]\n    pub fn angle(self) -> f32 {\n        self.y.atan2(self.x)\n    }\n\n    /// Create a unit vector with the given CW angle (in radians).\n    /// * An angle of zero gives the unit X axis.\n    /// * An angle of 𝞃/4 = 90° gives the unit Y axis.\n    ///\n    /// ```\n    /// # use emath::Vec2;\n    /// use std::f32::consts::TAU;\n    ///\n    /// assert_eq!(Vec2::angled(0.0), Vec2::X);\n    /// assert!((Vec2::angled(0.25 * TAU) - Vec2::Y).length() < 1e-5);\n    /// ```\n    #[inline(always)]\n    pub fn angled(angle: f32) -> Self {\n        let (sin, cos) = angle.sin_cos();\n        vec2(cos, sin)\n    }\n\n    #[must_use]\n    #[inline(always)]\n    pub fn floor(self) -> Self {\n        vec2(self.x.floor(), self.y.floor())\n    }\n\n    #[must_use]\n    #[inline(always)]\n    pub fn round(self) -> Self {\n        vec2(self.x.round(), self.y.round())\n    }\n\n    #[must_use]\n    #[inline(always)]\n    pub fn ceil(self) -> Self {\n        vec2(self.x.ceil(), self.y.ceil())\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn abs(self) -> Self {\n        vec2(self.x.abs(), self.y.abs())\n    }\n\n    /// True if all members are also finite.\n    #[inline(always)]\n    pub fn is_finite(self) -> bool {\n        self.x.is_finite() && self.y.is_finite()\n    }\n\n    /// True if any member is NaN.\n    #[inline(always)]\n    pub fn any_nan(self) -> bool {\n        self.x.is_nan() || self.y.is_nan()\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn min(self, other: Self) -> Self {\n        vec2(self.x.min(other.x), self.y.min(other.y))\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn max(self, other: Self) -> Self {\n        vec2(self.x.max(other.x), self.y.max(other.y))\n    }\n\n    /// The dot-product of two vectors.\n    #[inline]\n    pub fn dot(self, other: Self) -> f32 {\n        self.x * other.x + self.y * other.y\n    }\n\n    /// Returns the minimum of `self.x` and `self.y`.\n    #[must_use]\n    #[inline(always)]\n    pub fn min_elem(self) -> f32 {\n        self.x.min(self.y)\n    }\n\n    /// Returns the maximum of `self.x` and `self.y`.\n    #[inline(always)]\n    #[must_use]\n    pub fn max_elem(self) -> f32 {\n        self.x.max(self.y)\n    }\n\n    /// Swizzle the axes.\n    #[inline]\n    #[must_use]\n    pub fn yx(self) -> Self {\n        Self {\n            x: self.y,\n            y: self.x,\n        }\n    }\n\n    #[must_use]\n    #[inline]\n    pub fn clamp(self, min: Self, max: Self) -> Self {\n        Self {\n            x: self.x.clamp(min.x, max.x),\n            y: self.y.clamp(min.y, max.y),\n        }\n    }\n}\n\nimpl std::ops::Index<usize> for Vec2 {\n    type Output = f32;\n\n    #[inline(always)]\n    fn index(&self, index: usize) -> &f32 {\n        match index {\n            0 => &self.x,\n            1 => &self.y,\n            _ => panic!(\"Vec2 index out of bounds: {index}\"),\n        }\n    }\n}\n\nimpl std::ops::IndexMut<usize> for Vec2 {\n    #[inline(always)]\n    fn index_mut(&mut self, index: usize) -> &mut f32 {\n        match index {\n            0 => &mut self.x,\n            1 => &mut self.y,\n            _ => panic!(\"Vec2 index out of bounds: {index}\"),\n        }\n    }\n}\n\nimpl Eq for Vec2 {}\n\nimpl Neg for Vec2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn neg(self) -> Self {\n        vec2(-self.x, -self.y)\n    }\n}\n\nimpl AddAssign for Vec2 {\n    #[inline(always)]\n    fn add_assign(&mut self, rhs: Self) {\n        *self = Self {\n            x: self.x + rhs.x,\n            y: self.y + rhs.y,\n        };\n    }\n}\n\nimpl SubAssign for Vec2 {\n    #[inline(always)]\n    fn sub_assign(&mut self, rhs: Self) {\n        *self = Self {\n            x: self.x - rhs.x,\n            y: self.y - rhs.y,\n        };\n    }\n}\n\nimpl Add for Vec2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn add(self, rhs: Self) -> Self {\n        Self {\n            x: self.x + rhs.x,\n            y: self.y + rhs.y,\n        }\n    }\n}\n\nimpl Sub for Vec2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn sub(self, rhs: Self) -> Self {\n        Self {\n            x: self.x - rhs.x,\n            y: self.y - rhs.y,\n        }\n    }\n}\n\n/// Element-wise multiplication\nimpl Mul<Self> for Vec2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn mul(self, vec: Self) -> Self {\n        Self {\n            x: self.x * vec.x,\n            y: self.y * vec.y,\n        }\n    }\n}\n\n/// Element-wise division\nimpl Div<Self> for Vec2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn div(self, rhs: Self) -> Self {\n        Self {\n            x: self.x / rhs.x,\n            y: self.y / rhs.y,\n        }\n    }\n}\n\nimpl MulAssign<f32> for Vec2 {\n    #[inline(always)]\n    fn mul_assign(&mut self, rhs: f32) {\n        self.x *= rhs;\n        self.y *= rhs;\n    }\n}\n\nimpl DivAssign<f32> for Vec2 {\n    #[inline(always)]\n    fn div_assign(&mut self, rhs: f32) {\n        self.x /= rhs;\n        self.y /= rhs;\n    }\n}\n\nimpl Mul<f32> for Vec2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn mul(self, factor: f32) -> Self {\n        Self {\n            x: self.x * factor,\n            y: self.y * factor,\n        }\n    }\n}\n\nimpl Mul<Vec2> for f32 {\n    type Output = Vec2;\n\n    #[inline(always)]\n    fn mul(self, vec: Vec2) -> Vec2 {\n        Vec2 {\n            x: self * vec.x,\n            y: self * vec.y,\n        }\n    }\n}\n\nimpl Div<f32> for Vec2 {\n    type Output = Self;\n\n    #[inline(always)]\n    fn div(self, factor: f32) -> Self {\n        Self {\n            x: self.x / factor,\n            y: self.y / factor,\n        }\n    }\n}\n\nimpl fmt::Debug for Vec2 {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        if let Some(precision) = f.precision() {\n            write!(f, \"[{1:.0$} {2:.0$}]\", precision, self.x, self.y)\n        } else {\n            write!(f, \"[{:.1} {:.1}]\", self.x, self.y)\n        }\n    }\n}\n\nimpl fmt::Display for Vec2 {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"[\")?;\n        self.x.fmt(f)?;\n        f.write_str(\" \")?;\n        self.y.fmt(f)?;\n        f.write_str(\"]\")?;\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    macro_rules! almost_eq {\n        ($left: expr, $right: expr) => {\n            let left = $left;\n            let right = $right;\n            assert!((left - right).abs() < 1e-6, \"{} != {}\", left, right);\n        };\n    }\n\n    #[test]\n    fn test_vec2() {\n        use std::f32::consts::TAU;\n\n        assert_eq!(Vec2::ZERO.angle(), 0.0);\n        assert_eq!(Vec2::angled(0.0).angle(), 0.0);\n        assert_eq!(Vec2::angled(1.0).angle(), 1.0);\n        assert_eq!(Vec2::X.angle(), 0.0);\n        assert_eq!(Vec2::Y.angle(), 0.25 * TAU);\n\n        assert_eq!(Vec2::RIGHT.angle(), 0.0);\n        assert_eq!(Vec2::DOWN.angle(), 0.25 * TAU);\n        almost_eq!(Vec2::LEFT.angle(), 0.50 * TAU);\n        assert_eq!(Vec2::UP.angle(), -0.25 * TAU);\n\n        let mut assignment = vec2(1.0, 2.0);\n        assignment += vec2(3.0, 4.0);\n        assert_eq!(assignment, vec2(4.0, 6.0));\n\n        let mut assignment = vec2(4.0, 6.0);\n        assignment -= vec2(1.0, 2.0);\n        assert_eq!(assignment, vec2(3.0, 4.0));\n\n        let mut assignment = vec2(1.0, 2.0);\n        assignment *= 2.0;\n        assert_eq!(assignment, vec2(2.0, 4.0));\n\n        let mut assignment = vec2(2.0, 4.0);\n        assignment /= 2.0;\n        assert_eq!(assignment, vec2(1.0, 2.0));\n    }\n\n    #[test]\n    fn test_vec2_normalized() {\n        fn generate_spiral(n: usize, start: Vec2, end: Vec2) -> impl Iterator<Item = Vec2> {\n            let angle_step = 2.0 * std::f32::consts::PI / n as f32;\n            let radius_step = (end.length() - start.length()) / n as f32;\n\n            (0..n).map(move |i| {\n                let angle = i as f32 * angle_step;\n                let radius = start.length() + i as f32 * radius_step;\n                let x = radius * angle.cos();\n                let y = radius * angle.sin();\n                vec2(x, y)\n            })\n        }\n\n        for v in generate_spiral(40, Vec2::splat(0.1), Vec2::splat(2.0)) {\n            let vn = v.normalized();\n            almost_eq!(vn.length(), 1.0);\n            assert!(vn.is_normalized());\n        }\n    }\n}\n"
  },
  {
    "path": "crates/emath/src/vec2b.rs",
    "content": "use crate::Vec2;\n\n/// Two bools, one for each axis (X and Y).\n#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Vec2b {\n    pub x: bool,\n    pub y: bool,\n}\n\nimpl Vec2b {\n    pub const FALSE: Self = Self { x: false, y: false };\n    pub const TRUE: Self = Self { x: true, y: true };\n\n    #[inline]\n    pub fn new(x: bool, y: bool) -> Self {\n        Self { x, y }\n    }\n\n    #[inline]\n    pub fn any(&self) -> bool {\n        self.x || self.y\n    }\n\n    /// Are both `x` and `y` true?\n    #[inline]\n    pub fn all(&self) -> bool {\n        self.x && self.y\n    }\n\n    #[inline]\n    pub fn and(&self, other: impl Into<Self>) -> Self {\n        let other = other.into();\n        Self {\n            x: self.x && other.x,\n            y: self.y && other.y,\n        }\n    }\n\n    #[inline]\n    pub fn or(&self, other: impl Into<Self>) -> Self {\n        let other = other.into();\n        Self {\n            x: self.x || other.x,\n            y: self.y || other.y,\n        }\n    }\n\n    /// Convert to a float `Vec2` where the components are 1.0 for `true` and 0.0 for `false`.\n    #[inline]\n    pub fn to_vec2(self) -> Vec2 {\n        Vec2::new(self.x.into(), self.y.into())\n    }\n}\n\nimpl From<bool> for Vec2b {\n    #[inline]\n    fn from(val: bool) -> Self {\n        Self { x: val, y: val }\n    }\n}\n\nimpl From<[bool; 2]> for Vec2b {\n    #[inline]\n    fn from([x, y]: [bool; 2]) -> Self {\n        Self { x, y }\n    }\n}\n\nimpl std::ops::Index<usize> for Vec2b {\n    type Output = bool;\n\n    #[inline(always)]\n    fn index(&self, index: usize) -> &bool {\n        match index {\n            0 => &self.x,\n            1 => &self.y,\n            _ => panic!(\"Vec2b index out of bounds: {index}\"),\n        }\n    }\n}\n\nimpl std::ops::IndexMut<usize> for Vec2b {\n    #[inline(always)]\n    fn index_mut(&mut self, index: usize) -> &mut bool {\n        match index {\n            0 => &mut self.x,\n            1 => &mut self.y,\n            _ => panic!(\"Vec2b index out of bounds: {index}\"),\n        }\n    }\n}\n\nimpl std::ops::Not for Vec2b {\n    type Output = Self;\n\n    #[inline]\n    fn not(self) -> Self::Output {\n        Self {\n            x: !self.x,\n            y: !self.y,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/epaint/CHANGELOG.md",
    "content": "# epaint changelog\nAll notable changes to the epaint crate will be documented in this file.\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n## 0.33.3 - 2025-12-11\nNothing new\n\n\n## 0.33.2 - 2025-11-13\nNothing new\n\n\n## 0.33.0 - 2025-10-09\n* Remove the `deadlock_detection` feature [#7497](https://github.com/emilk/egui/pull/7497) by [@lucasmerlin](https://github.com/lucasmerlin)\n* More even text kerning [#7431](https://github.com/emilk/egui/pull/7431) by [@valadaptive](https://github.com/valadaptive)\n* Return `0.0` if font not found in `glyph_width` instead of panic [#7559](https://github.com/emilk/egui/pull/7559) by [@lucasmerlin](https://github.com/lucasmerlin)\n* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)\n* Remove `log` feature [#7583](https://github.com/emilk/egui/pull/7583) by [@emilk](https://github.com/emilk)\n\n\n## 0.32.3 - 2025-09-12\n* Optimize `Mesh::add_rect_with_uv` [#7511](https://github.com/emilk/egui/pull/7511) by [@valadaptive](https://github.com/valadaptive)\n\n\n## 0.32.2 - 2025-09-04\n* Panic mutexes that can't lock for 30 seconds, in debug builds [#7468](https://github.com/emilk/egui/pull/7468) by [@emilk](https://github.com/emilk)\n* Skip zero-length layout job sections [#7430](https://github.com/emilk/egui/pull/7430) by [@HactarCE](https://github.com/HactarCE)\n\n\n## 0.32.1 - 2025-08-15\n* Fix multi-line `TextShape` rotation [#7404](https://github.com/emilk/egui/pull/7404) by [@afishhh](https://github.com/afishhh)\n* Fix glyph rendering: clamp coverage to [0, 1] [#7415](https://github.com/emilk/egui/pull/7415) by [@emilk](https://github.com/emilk)\n\n\n## 0.32.0 - 2025-07-10\n### ⭐ Added\n* Impl AsRef<[u8]> for FontData [#5757](https://github.com/emilk/egui/pull/5757) by [@StratusFearMe21](https://github.com/StratusFearMe21)\n* Add `expand_bg` to customize size of text background [#5365](https://github.com/emilk/egui/pull/5365) by [@MeGaGiGaGon](https://github.com/MeGaGiGaGon)\n* Add anchored text rotation method, and clarify related docs [#7130](https://github.com/emilk/egui/pull/7130) by [@pmarks](https://github.com/pmarks)\n* Add `Galley::intrinsic_size` [#7146](https://github.com/emilk/egui/pull/7146) by [@lucasmerlin](https://github.com/lucasmerlin)\n\n### 🔧 Changed\n* Fix semi-transparent colors appearing too bright [#5824](https://github.com/emilk/egui/pull/5824) by [@emilk](https://github.com/emilk)\n* Improve text sharpness [#5838](https://github.com/emilk/egui/pull/5838) by [@emilk](https://github.com/emilk)\n* Improve text rendering in light mode [#7290](https://github.com/emilk/egui/pull/7290) by [@emilk](https://github.com/emilk)\n* Make text underline and strikethrough pixel perfect crisp [#5857](https://github.com/emilk/egui/pull/5857) by [@emilk](https://github.com/emilk)\n* Update `emoji-icon-font` with fix for fullwidth latin characters [#7067](https://github.com/emilk/egui/pull/7067) by [@emilk](https://github.com/emilk)\n* Add assert messages and print bad argument values in asserts [#5216](https://github.com/emilk/egui/pull/5216) by [@bircni](https://github.com/bircni)\n\n### 🔥 Removed\n* Remove things that have been deprecated for over a year [#7099](https://github.com/emilk/egui/pull/7099) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Fix: transform `TextShape` underline width [#5865](https://github.com/emilk/egui/pull/5865) by [@emilk](https://github.com/emilk)\n* Fix `visual_bounding_rect` for rotated text [#7050](https://github.com/emilk/egui/pull/7050) by [@pmarks](https://github.com/pmarks)\n\n### 🚀 Performance\n* Optimize editing long text by caching each paragraph [#5411](https://github.com/emilk/egui/pull/5411) by [@afishhh](https://github.com/afishhh)\n\n\n## 0.31.1 - 2025-03-05\n* Fix panic when rendering thin textured rectangles [#5692](https://github.com/emilk/egui/pull/5692) by [@PPakalns](https://github.com/PPakalns)\n\n\n## 0.31.0 - 2025-02-04\n### ⭐ Added\n* Improve tessellation quality [#5669](https://github.com/emilk/egui/pull/5669) by [@emilk](https://github.com/emilk)\n* Add `epaint::Brush` for controlling `RectShape` texturing [#5565](https://github.com/emilk/egui/pull/5565) by [@emilk](https://github.com/emilk)\n* Add `RectShape::stroke_kind ` to control if stroke is inside/outside/centered [#5647](https://github.com/emilk/egui/pull/5647) by [@emilk](https://github.com/emilk)\n\n### 🔧 Changed\n* Rename `Rounding` to `CornerRadius` [#5673](https://github.com/emilk/egui/pull/5673) by [@emilk](https://github.com/emilk)\n* Make all lines and rectangles crisp [#5518](https://github.com/emilk/egui/pull/5518) by [@emilk](https://github.com/emilk)\n* Better rounding of rectangles with thin outlines [#5571](https://github.com/emilk/egui/pull/5571) by [@emilk](https://github.com/emilk)\n* Require a `StrokeKind` when painting rectangles with strokes [#5648](https://github.com/emilk/egui/pull/5648) by [@emilk](https://github.com/emilk)\n\n### 🔥 Removed\n* Remove `StrokeKind::default` [#5658](https://github.com/emilk/egui/pull/5658) by [@emilk](https://github.com/emilk)\n\n### 🚀 Performance\n* Use `u8` in `Rounding`, and introduce `Roundingf` [#5563](https://github.com/emilk/egui/pull/5563) by [@emilk](https://github.com/emilk)\n* Store `Margin` using `i8` to reduce its size [#5567](https://github.com/emilk/egui/pull/5567) by [@emilk](https://github.com/emilk)\n* Shrink size of `Shadow` by using `i8/u8` instead of `f32` [#5568](https://github.com/emilk/egui/pull/5568) by [@emilk](https://github.com/emilk)\n\n\n## 0.30.0 - 2024-12-16\n* Expand max font atlas size from 8k to 16k [#5257](https://github.com/emilk/egui/pull/5257) by [@rustbasic](https://github.com/rustbasic)\n* Put font data into `Arc` to reduce memory consumption [#5276](https://github.com/emilk/egui/pull/5276) by [@StarStarJ](https://github.com/StarStarJ)\n* Reduce aliasing when painting thin box outlines [#5484](https://github.com/emilk/egui/pull/5484) by [@emilk](https://github.com/emilk)\n* Fix zero-width strokes still affecting the feathering color of boxes [#5485](https://github.com/emilk/egui/pull/5485) by [@emilk](https://github.com/emilk)\n\n\n## 0.29.1 - 2024-10-01\nNothing new\n\n\n## 0.29.0 - 2024-09-26\n### 🚀 Performance\n* Optimize `Color32::from_rgba_unmultiplied` with LUT [#5088](https://github.com/emilk/egui/pull/5088) by [@YgorSouza](https://github.com/YgorSouza)\n\n### 🔧 Changed\n* Fix blurry lines by aligning to pixel grid [#4943](https://github.com/emilk/egui/pull/4943) by [@juancampa](https://github.com/juancampa)\n* Better vertical text alignment [#5117](https://github.com/emilk/egui/pull/5117) by [@emilk](https://github.com/emilk)\n* Deprecate `ahash` re-exports [#4979](https://github.com/emilk/egui/pull/4979) by [@oscargus](https://github.com/oscargus)\n\n### 🐛 Fixed\n* Fix bug in size calculation of truncated text [#5076](https://github.com/emilk/egui/pull/5076) by [@emilk](https://github.com/emilk)\n* Fix text sometime line-breaking or truncating too early [#5077](https://github.com/emilk/egui/pull/5077) by [@emilk](https://github.com/emilk)\n* Prevent text shrinking in tooltips; round wrap-width to integer [#5161](https://github.com/emilk/egui/pull/5161) by [@emilk](https://github.com/emilk)\n\n\n## 0.28.1 - 2024-07-05\nNothing new\n\n\n## 0.28.0 - 2024-07-03\n### ⭐ Added\n* Add `RectShape::blur_width` to implement shadows [#4267](https://github.com/emilk/egui/pull/4267) by [@emilk](https://github.com/emilk)\n* Overload operators for `Rect + Margin`, `Rect - Margin` etc [#4277](https://github.com/emilk/egui/pull/4277) by [@emilk](https://github.com/emilk)\n* Added ability to define colors at UV coordinates along a path [#4353](https://github.com/emilk/egui/pull/4353) by [@murl-digital](https://github.com/murl-digital)\n* Add a `Display` impl for `Vec2`, `Pos2`, and `Rect` [#4428](https://github.com/emilk/egui/pull/4428) by [@tgross35](https://github.com/tgross35)\n\n### 🔧 Changed\n* Move `epaint::util::OrderedFloat` to `emath::OrderedFloat` [#4389](https://github.com/emilk/egui/pull/4389) by [@emilk](https://github.com/emilk)\n* Remove `extra_asserts` and `extra_debug_asserts` feature flags [#4478](https://github.com/emilk/egui/pull/4478) by [@emilk](https://github.com/emilk)\n* Make `epaint::mutex::RwLock` allow `?Sized` types [#4485](https://github.com/emilk/egui/pull/4485) by [@crumblingstatue](https://github.com/crumblingstatue)\n* Round text galley sizes to nearest UI point size [#4578](https://github.com/emilk/egui/pull/4578) by [@emilk](https://github.com/emilk)\n\n### 🐛 Fixed\n* Fix incorrect line breaks [#4377](https://github.com/emilk/egui/pull/4377) by [@juancampa](https://github.com/juancampa)\n* Fix `hex_color!` macro by re-exporting `color_hex` crate from `ecolor` [#4372](https://github.com/emilk/egui/pull/4372) by [@dataphract](https://github.com/dataphract)\n* Don't panic when replacement glyph is not found [#4542](https://github.com/emilk/egui/pull/4542) by [@RyanBluth](https://github.com/RyanBluth)\n\n\n## 0.27.2 - 2024-04-02\n* Nothing new\n\n\n## 0.27.1 - 2024-03-29\n* Fix visual glitch on the right side of highly rounded rectangles [#4244](https://github.com/emilk/egui/pull/4244)\n* Prevent visual glitch when shadow blur width is very high [#4245](https://github.com/emilk/egui/pull/4245)\n\n\n## 0.27.0 - 2024-03-26\n* Add `ColorImage::from_gray_iter` [#3536](https://github.com/emilk/egui/pull/3536) (thanks [@wangxiaochuTHU](https://github.com/wangxiaochuTHU)!)\n* Convenience const fn for `Margin`, `Rounding` and `Shadow` [#4080](https://github.com/emilk/egui/pull/4080) (thanks [@0Qwel](https://github.com/0Qwel)!)\n* Added `Shape::{scale,translate}` wrappers [#4090](https://github.com/emilk/egui/pull/4090) (thanks [@varphone](https://github.com/varphone)!)\n* Add `EllipseShape` [#4122](https://github.com/emilk/egui/pull/4122) (thanks [@TheTacBanana](https://github.com/TheTacBanana)!)\n* Add `Margin` to `epaint` [#4231](https://github.com/emilk/egui/pull/4231)\n* CSS-like `Shadow` with offset, spread, and blur [#4232](https://github.com/emilk/egui/pull/4232)\n\n\n## 0.26.2 - 2024-02-14\n* Nothing new\n\n\n## 0.26.1 - 2024-02-11\n* Nothing new\n\n\n## 0.26.0 - 2024-02-05\n* Add `Align2::anchor_size` [#3863](https://github.com/emilk/egui/pull/3863)\n* Add opacity factor to `TextShape` [#3916](https://github.com/emilk/egui/pull/3916) (thanks [@StratusFearMe21](https://github.com/StratusFearMe21)!)\n* Parallel tessellation with opt-in `rayon` feature [#3934](https://github.com/emilk/egui/pull/3934)\n\n\n## 0.25.0 - 2024-01-08\n* Replace a special `Color32::PLACEHOLDER` with widget fallback color [#3727](https://github.com/emilk/egui/pull/3727)\n* Add support for dashed lines with offset [#3720](https://github.com/emilk/egui/pull/3720) (thanks [@oscargus](https://github.com/oscargus)!)\n* Impl `Clone` for `Fonts` [#3737](https://github.com/emilk/egui/pull/3737)\n* Fix: allow using the full Private Use Area for custom fonts [#3509](https://github.com/emilk/egui/pull/3509) (thanks [@varphone](https://github.com/varphone)!)\n* Add `Color32::from_hex` and `Color32::to_hex` [#3570](https://github.com/emilk/egui/pull/3570) [#3777](https://github.com/emilk/egui/pull/3777) (thanks [@YgorSouza](https://github.com/YgorSouza)!)\n\n\n## 0.24.1 - 2023-11-30\n* Optimize `FontImage::srgba_pixels` and reduce the initial font atlas texture size from 8MiB -> 1MiB [#3666](https://github.com/emilk/egui/pull/3666)\n\n\n## 0.24.0 - 2023-11-23\n* Use `impl Into<Stroke>` as argument in a few more places [#3420](https://github.com/emilk/egui/pull/3420) (thanks [@Phen-Ro](https://github.com/Phen-Ro)!)\n* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)\n* Make `ViewportInPixels` use integers, and clamp to bounds [#3604](https://github.com/emilk/egui/pull/3604) (thanks [@Wumpf](https://github.com/Wumpf)!)\n\n\n## 0.23.0 - 2023-09-27\n* Update MSRV to Rust 1.70.0 [#3310](https://github.com/emilk/egui/pull/3310)\n* Add option to truncate text at wrap width [#3244](https://github.com/emilk/egui/pull/3244) [#3366](https://github.com/emilk/egui/pull/3366)\n* Add control of line height and letter spacing [#3302](https://github.com/emilk/egui/pull/3302)\n* Support images with rounded corners [#3257](https://github.com/emilk/egui/pull/3257)\n* Add `ColorImage::from_gray` [#3166](https://github.com/emilk/egui/pull/3166) (thanks [@thomaseliot](https://github.com/thomaseliot)!)\n* Provide `into_inner()` for `egui::mutex::{Mutex, RwLock}` [#3110](https://github.com/emilk/egui/pull/3110) (thanks [@KmolYuan](https://github.com/KmolYuan)!)\n* Fix problems with tabs in text [#3355](https://github.com/emilk/egui/pull/3355)\n* Refactor: change `ClippedShape` from struct-enum to a normal struct [#3225](https://github.com/emilk/egui/pull/3225)\n* Document when `Galley`s get invalidated [#3024](https://github.com/emilk/egui/pull/3024) (thanks [@e00E](https://github.com/e00E)!)\n\n\n## 0.22.0 - 2023-05-23\n* Fix compiling `epaint` without `bytemuck` dependency [#2913](https://github.com/emilk/egui/pull/2913) (thanks [@lunixbochs](https://github.com/lunixbochs)!)\n* Fix documentation for `TextureId::Managed(0)` [#2998](https://github.com/emilk/egui/pull/2998) (thanks [@andersk](https://github.com/andersk)!)\n\n\n## 0.21.0 - 2023-02-08\n* Improve the look of thin white lines ([#2437](https://github.com/emilk/egui/pull/2437)).\n* Don't render `\\r` (Carriage Return) ([#2452](https://github.com/emilk/egui/pull/2452)).\n* Fix bug in `Mesh::split_to_u16` ([#2459](https://github.com/emilk/egui/pull/2459)).\n* Improve rendering of very thin rectangles.\n\n\n## 0.20.0 - 2022-12-08\n* ⚠️ BREAKING: Fix text being too small ([#2069](https://github.com/emilk/egui/pull/2069)).\n* ⚠️ BREAKING: epaint now expects integrations to do all color blending in gamma space ([#2071](https://github.com/emilk/egui/pull/2071)).\n* Improve mixed CJK/Latin line-breaking ([#1986](https://github.com/emilk/egui/pull/1986)).\n* Added `Fonts::has_glyph(s)` for querying if a glyph is supported ([#2202](https://github.com/emilk/egui/pull/2202)).\n* Added support for [thin space](https://en.wikipedia.org/wiki/Thin_space).\n* Split out color into its own crate, `ecolor` ([#2399](https://github.com/emilk/egui/pull/2399)).\n\n\n## 0.19.0 - 2022-08-20\n* MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)).\n* Added `epaint::hex_color!` to create `Color32`'s from hex strings under the `color-hex` feature ([#1596](https://github.com/emilk/egui/pull/1596)).\n* Optimize tessellation of filled circles by 10x or more ([#1616](https://github.com/emilk/egui/pull/1616)).\n* Added opt-in feature `deadlock_detection` to detect double-lock of mutexes on the same thread ([#1619](https://github.com/emilk/egui/pull/1619)).\n* Texture loading now takes a `TextureOptions` with minification and magnification filters ([#2224](https://github.com/emilk/egui/pull/2224)).\n\n\n## 0.18.1 - 2022-05-01\n* Change `Shape::Callback` from `&dyn Any` to `&mut dyn Any` to support more backends.\n\n\n## 0.18.0 - 2022-04-30\n* MSRV (Minimum Supported Rust Version) is now `1.60.0` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Added `Shape::Callback` for backend-specific painting ([#1351](https://github.com/emilk/egui/pull/1351)).\n* Added more text wrapping options ([#1291](https://github.com/emilk/egui/pull/1291)):\n  * Added `TextWrapping` struct containing all wrapping options.\n  * Added `LayoutJob::wrap` field containing these options.\n  * Moved `LayoutJob::wrap_width` to `TextWrapping::max_width`.\n  * Added `TextWrapping::max_rows` to limit amount of rows the text should have.\n  * Added `TextWrapping::break_anywhere` to control should the text break at appropriate places or not.\n  * Added `TextWrapping::overflow_character` to specify what character should be used to represent clipped text.\n* Removed the `single_threaded/multi_threaded` flags - epaint is now always thread-safe ([#1390](https://github.com/emilk/egui/pull/1390)).\n* `Tessellator::from_options` is now `Tessellator::new` ([#1408](https://github.com/emilk/egui/pull/1408)).\n* Renamed `TessellationOptions::anti_alias` to `feathering` ([#1408](https://github.com/emilk/egui/pull/1408)).\n* Renamed `AlphaImage` to `FontImage` to discourage any other use for it ([#1412](https://github.com/emilk/egui/pull/1412)).\n* Dark text is darker and more readable on bright backgrounds ([#1412](https://github.com/emilk/egui/pull/1412)).\n* Fixed panic when tessellating a `Shape::Vec` containing meshes with differing `TextureId`s ([#1445](https://github.com/emilk/egui/pull/1445)).\n* Added `Shape::galley_with_color` which adds the functionality of `Painter::galley_with_color` into the Shape enum ([#1461](https://github.com/emilk/egui/pull/1461)).\n* Renamed the feature `convert_bytemuck` to `bytemuck` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Renamed the feature `serialize` to `serde` ([#1467](https://github.com/emilk/egui/pull/1467)).\n* Added line breaking rules for Japanese text ([#1498](https://github.com/emilk/egui/pull/1498)).\n* Optimize tessellation of circles and boxes with rounded corners ([#1547](https://github.com/emilk/egui/pull/1547)).\n\n\n## 0.17.0 - 2022-02-22\n* Much improved font selection ([#1154](https://github.com/emilk/egui/pull/1154)):\n  * Replaced `TextStyle` with `FontId` which lets you pick any font size and font family.\n  * Replaced `Fonts::font_image` with `font_image_delta` for partial font atlas updates.\n* Made the v-align and scale of user fonts tweakable ([#1241](https://github.com/emilk/egui/pull/1027)).\n* Added `ImageData` and `TextureManager` for loading images into textures ([#1110](https://github.com/emilk/egui/pull/1110)).\n* Added `Shape::dashed_line_many` ([#1027](https://github.com/emilk/egui/pull/1027)).\n* Replaced `corner_radius: f32` with `rounding: Rounding`, allowing per-corner rounding settings ([#1206](https://github.com/emilk/egui/pull/1206)).\n* Fixed anti-aliasing of filled paths with counter-clockwise winding order.\n* Improve the anti-aliasing of filled paths with sharp corners, at the cost of these corners sometimes becoming badly extruded instead (see https://github.com/emilk/egui/issues/1226).\n\n\n## 0.16.0 - 2021-12-29\n* Anti-alias path ends ([#893](https://github.com/emilk/egui/pull/893)).\n* `Rgba` now implements `Hash` ([#886](https://github.com/emilk/egui/pull/886)).\n* Renamed `Texture` to `FontImage`.\n\n\n## 0.15.0 - 2021-10-24\n* `Fonts::layout_job`: New text layout engine allowing mixing fonts, colors and styles, with underlining and strikethrough.\n* New `CircleShape`, `PathShape`, `RectShape` and `TextShape` used in `enum Shape`.\n* Added support for rotated text (see `TextShape`).\n* Added `\"convert_bytemuck\"` feature.\n"
  },
  {
    "path": "crates/epaint/Cargo.toml",
    "content": "[package]\nname = \"epaint\"\nversion.workspace = true\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\ndescription = \"Minimal 2D graphics library for GUI work\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui/tree/main/crates/epaint\"\nlicense = \"MIT OR Apache-2.0\"\nreadme = \"README.md\"\nrepository = \"https://github.com/emilk/egui/tree/main/crates/epaint\"\ncategories = [\"graphics\", \"gui\"]\nkeywords = [\"graphics\", \"gui\", \"egui\"]\ninclude = [\"../LICENSE-APACHE\", \"../LICENSE-MIT\", \"**/*.rs\", \"Cargo.toml\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[lib]\n\n\n[features]\ndefault = [\"default_fonts\"]\n\n## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`Vertex`] to `&[u8]`.\nbytemuck = [\"dep:bytemuck\", \"emath/bytemuck\", \"ecolor/bytemuck\"]\n\n## [`cint`](https://docs.rs/cint) enables interoperability with other color libraries.\ncint = [\"ecolor/cint\"]\n\n## Enable the [`hex_color`] macro.\ncolor-hex = [\"ecolor/color-hex\"]\n\n## If set, epaint will use `include_bytes!` to bundle some fonts.\n## If you plan on specifying your own fonts you may disable this feature.\ndefault_fonts = [\"epaint_default_fonts\"]\n\n## [`mint`](https://docs.rs/mint) enables interoperability with other math libraries such as [`glam`](https://docs.rs/glam) and [`nalgebra`](https://docs.rs/nalgebra).\nmint = [\"emath/mint\"]\n\n## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon).\n##\n## This can help performance for graphics-intense applications.\nrayon = [\"dep:rayon\"]\n\n## Allow serialization using [`serde`](https://docs.rs/serde).\nserde = [\"dep:serde\", \"ahash/serde\", \"emath/serde\", \"ecolor/serde\", \"font-types/serde\", \"smallvec/serde\"]\n\n## Change Vertex layout to be compatible with unity\nunity = []\n\n## Override and disable the unity feature\n## This exists, so that when testing with --all-features, snapshots render correctly.\n_override_unity = []\n\n[dependencies]\nemath.workspace = true\necolor.workspace = true\n\nahash.workspace = true\nfont-types.workspace = true\nlog.workspace = true\nnohash-hasher.workspace = true\nparking_lot.workspace = true # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios.\nprofiling.workspace = true\nself_cell.workspace = true\nskrifa.workspace = true\nsmallvec.workspace = true\nvello_cpu.workspace = true\n\n#! ### Optional dependencies\nbytemuck = { workspace = true, optional = true, features = [\"derive\"] }\n\n## Enable this when generating docs.\ndocument-features = { workspace = true, optional = true }\n\nrayon = { workspace = true, optional = true }\n\n## Allow serialization using [`serde`](https://docs.rs/serde) .\nserde = { workspace = true, optional = true, features = [\"derive\", \"rc\"] }\n\nepaint_default_fonts = { workspace = true, optional = true }\n\n[dev-dependencies]\ncriterion.workspace = true\nmimalloc.workspace = true\nsimilar-asserts.workspace = true\n\n\n[[bench]]\nname = \"benchmark\"\nharness = false\n"
  },
  {
    "path": "crates/epaint/README.md",
    "content": "# epaint - egui paint library\n\n[![Latest version](https://img.shields.io/crates/v/epaint.svg)](https://crates.io/crates/epaint)\n[![Documentation](https://docs.rs/epaint/badge.svg)](https://docs.rs/epaint)\n[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\nA bare-bones 2D graphics library for turning simple 2D shapes and text into textured triangles.\n\nMade for [`egui`](https://github.com/emilk/egui/).\n"
  },
  {
    "path": "crates/epaint/benches/benchmark.rs",
    "content": "use criterion::{Criterion, criterion_group, criterion_main};\n\nuse epaint::{\n    ClippedShape, Color32, Mesh, PathStroke, Pos2, Rect, Shape, Stroke, TessellationOptions,\n    Tessellator, TextureAtlas, Vec2, pos2, tessellator::Path,\n};\n\nuse std::hint::black_box;\n\n#[global_allocator]\nstatic GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; // Much faster allocator\n\nfn single_dashed_lines(c: &mut Criterion) {\n    c.bench_function(\"single_dashed_lines\", move |b| {\n        b.iter(|| {\n            let mut v = Vec::new();\n\n            let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];\n\n            for _ in 0..100 {\n                v.extend(Shape::dashed_line(\n                    &line,\n                    Stroke::new(1.5, Color32::RED),\n                    10.0,\n                    2.5,\n                ));\n            }\n\n            black_box(v);\n        });\n    });\n}\n\nfn many_dashed_lines(c: &mut Criterion) {\n    c.bench_function(\"many_dashed_lines\", move |b| {\n        b.iter(|| {\n            let mut v = Vec::new();\n\n            let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];\n\n            for _ in 0..100 {\n                Shape::dashed_line_many(&line, Stroke::new(1.5, Color32::RED), 10.0, 2.5, &mut v);\n            }\n\n            black_box(v);\n        });\n    });\n}\n\nfn tessellate_circles(c: &mut Criterion) {\n    c.bench_function(\"tessellate_circles_100k\", move |b| {\n        let radii: [f32; 10] = [1.0, 2.0, 3.6, 4.0, 5.7, 8.0, 10.0, 13.0, 15.0, 17.0];\n        let mut clipped_shapes = vec![];\n        for r in radii {\n            for _ in 0..10_000 {\n                let clip_rect = Rect::from_min_size(Pos2::ZERO, Vec2::splat(1024.0));\n                let shape = Shape::circle_filled(Pos2::new(10.0, 10.0), r, Color32::WHITE);\n                clipped_shapes.push(ClippedShape { clip_rect, shape });\n            }\n        }\n        assert_eq!(\n            clipped_shapes.len(),\n            100_000,\n            \"length of clipped shapes should be 100k, but was {}\",\n            clipped_shapes.len()\n        );\n\n        let pixels_per_point = 2.0;\n        let options = TessellationOptions::default();\n\n        let atlas = TextureAtlas::new([4096, 256], Default::default());\n        let font_tex_size = atlas.size();\n        let prepared_discs = atlas.prepared_discs();\n\n        b.iter(|| {\n            let mut tessellator = Tessellator::new(\n                pixels_per_point,\n                options,\n                font_tex_size,\n                prepared_discs.clone(),\n            );\n            let clipped_primitives = tessellator.tessellate_shapes(clipped_shapes.clone());\n            black_box(clipped_primitives);\n        });\n    });\n}\n\nfn thick_line_solid(c: &mut Criterion) {\n    c.bench_function(\"thick_solid_line\", move |b| {\n        let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];\n        let mut path = Path::default();\n        path.add_open_points(&line);\n\n        b.iter(|| {\n            let mut mesh = Mesh::default();\n            path.stroke_closed(1.5, &Stroke::new(2.0, Color32::RED).into(), &mut mesh);\n\n            black_box(mesh);\n        });\n    });\n}\n\nfn thick_large_line_solid(c: &mut Criterion) {\n    c.bench_function(\"thick_large_solid_line\", move |b| {\n        let line = (0..1000).map(|i| pos2(i as f32, 10.0)).collect::<Vec<_>>();\n        let mut path = Path::default();\n        path.add_open_points(&line);\n\n        b.iter(|| {\n            let mut mesh = Mesh::default();\n            path.stroke_closed(1.5, &Stroke::new(2.0, Color32::RED).into(), &mut mesh);\n\n            black_box(mesh);\n        });\n    });\n}\n\nfn thin_line_solid(c: &mut Criterion) {\n    c.bench_function(\"thin_solid_line\", move |b| {\n        let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];\n        let mut path = Path::default();\n        path.add_open_points(&line);\n\n        b.iter(|| {\n            let mut mesh = Mesh::default();\n            path.stroke_closed(1.5, &Stroke::new(0.5, Color32::RED).into(), &mut mesh);\n\n            black_box(mesh);\n        });\n    });\n}\n\nfn thin_large_line_solid(c: &mut Criterion) {\n    c.bench_function(\"thin_large_solid_line\", move |b| {\n        let line = (0..1000).map(|i| pos2(i as f32, 10.0)).collect::<Vec<_>>();\n        let mut path = Path::default();\n        path.add_open_points(&line);\n\n        b.iter(|| {\n            let mut mesh = Mesh::default();\n            path.stroke_closed(1.5, &Stroke::new(0.5, Color32::RED).into(), &mut mesh);\n\n            black_box(mesh);\n        });\n    });\n}\n\nfn thick_line_uv(c: &mut Criterion) {\n    c.bench_function(\"thick_uv_line\", move |b| {\n        let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];\n        let mut path = Path::default();\n        path.add_open_points(&line);\n\n        b.iter(|| {\n            let mut mesh = Mesh::default();\n            path.stroke_closed(\n                1.5,\n                &PathStroke::new_uv(2.0, |_, p| {\n                    black_box(p * 2.0);\n                    Color32::RED\n                }),\n                &mut mesh,\n            );\n\n            black_box(mesh);\n        });\n    });\n}\n\nfn thick_large_line_uv(c: &mut Criterion) {\n    c.bench_function(\"thick_large_uv_line\", move |b| {\n        let line = (0..1000).map(|i| pos2(i as f32, 10.0)).collect::<Vec<_>>();\n        let mut path = Path::default();\n        path.add_open_points(&line);\n\n        b.iter(|| {\n            let mut mesh = Mesh::default();\n            path.stroke_closed(\n                1.5,\n                &PathStroke::new_uv(2.0, |_, p| {\n                    black_box(p * 2.0);\n                    Color32::RED\n                }),\n                &mut mesh,\n            );\n\n            black_box(mesh);\n        });\n    });\n}\n\nfn thin_line_uv(c: &mut Criterion) {\n    c.bench_function(\"thin_uv_line\", move |b| {\n        let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];\n        let mut path = Path::default();\n        path.add_open_points(&line);\n\n        b.iter(|| {\n            let mut mesh = Mesh::default();\n            path.stroke_closed(\n                1.5,\n                &PathStroke::new_uv(2.0, |_, p| {\n                    black_box(p * 2.0);\n                    Color32::RED\n                }),\n                &mut mesh,\n            );\n\n            black_box(mesh);\n        });\n    });\n}\n\nfn thin_large_line_uv(c: &mut Criterion) {\n    c.bench_function(\"thin_large_uv_line\", move |b| {\n        let line = (0..1000).map(|i| pos2(i as f32, 10.0)).collect::<Vec<_>>();\n        let mut path = Path::default();\n        path.add_open_points(&line);\n\n        b.iter(|| {\n            let mut mesh = Mesh::default();\n            path.stroke_closed(\n                1.5,\n                &PathStroke::new_uv(2.0, |_, p| {\n                    black_box(p * 2.0);\n                    Color32::RED\n                }),\n                &mut mesh,\n            );\n\n            black_box(mesh);\n        });\n    });\n}\n\nfn rgba_values() -> [[u8; 4]; 1000] {\n    core::array::from_fn(|i| [5, 7, 11, 13].map(|m| (i * m) as u8))\n}\n\nfn from_rgba_unmultiplied_0(c: &mut Criterion) {\n    c.bench_function(\"from_rgba_unmultiplied_0\", move |b| {\n        let values = black_box(rgba_values().map(|[r, g, b, _]| [r, g, b, 0]));\n        b.iter(|| {\n            for [r, g, b, a] in values {\n                let color = ecolor::Color32::from_rgba_unmultiplied(r, g, b, a);\n                black_box(color);\n            }\n        });\n    });\n}\n\nfn from_rgba_unmultiplied_other(c: &mut Criterion) {\n    c.bench_function(\"from_rgba_unmultiplied_other\", move |b| {\n        let values = black_box(rgba_values().map(|[r, g, b, a]| [r, g, b, a.clamp(1, 254)]));\n        b.iter(|| {\n            for [r, g, b, a] in values {\n                let color = ecolor::Color32::from_rgba_unmultiplied(r, g, b, a);\n                black_box(color);\n            }\n        });\n    });\n}\n\nfn from_rgba_unmultiplied_255(c: &mut Criterion) {\n    c.bench_function(\"from_rgba_unmultiplied_255\", move |b| {\n        let values = black_box(rgba_values().map(|[r, g, b, _]| [r, g, b, 255]));\n        b.iter(|| {\n            for [r, g, b, a] in values {\n                let color = ecolor::Color32::from_rgba_unmultiplied(r, g, b, a);\n                black_box(color);\n            }\n        });\n    });\n}\n\ncriterion_group!(\n    benches,\n    single_dashed_lines,\n    many_dashed_lines,\n    tessellate_circles,\n    thick_line_solid,\n    thick_large_line_solid,\n    thin_line_solid,\n    thin_large_line_solid,\n    thick_line_uv,\n    thick_large_line_uv,\n    thin_line_uv,\n    thin_large_line_uv,\n    from_rgba_unmultiplied_0,\n    from_rgba_unmultiplied_other,\n    from_rgba_unmultiplied_255,\n);\ncriterion_main!(benches);\n"
  },
  {
    "path": "crates/epaint/src/brush.rs",
    "content": "use crate::{Rect, TextureId};\n\n/// Controls texturing of a [`crate::RectShape`].\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Brush {\n    /// If the rect should be filled with a texture, which one?\n    ///\n    /// The texture is multiplied with [`crate::RectShape::fill`].\n    pub fill_texture_id: TextureId,\n\n    /// What UV coordinates to use for the texture?\n    ///\n    /// To display a texture, set [`Self::fill_texture_id`],\n    /// and set this to `Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0))`.\n    ///\n    /// Use [`Rect::ZERO`] to turn off texturing.\n    pub uv: Rect,\n}\n"
  },
  {
    "path": "crates/epaint/src/color.rs",
    "content": "use std::{fmt::Debug, sync::Arc};\n\nuse ecolor::Color32;\nuse emath::{Pos2, Rect};\n\n/// How paths will be colored.\n#[derive(Clone)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum ColorMode {\n    /// The entire path is one solid color, this is the default.\n    Solid(Color32),\n\n    /// Provide a callback which takes in the path's bounding box and a position and converts it to a color.\n    /// When used with a path, the bounding box will have a margin of [`TessellationOptions::feathering_size_in_pixels`](`crate::tessellator::TessellationOptions::feathering_size_in_pixels`)\n    ///\n    /// **This cannot be serialized**\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    UV(Arc<dyn Fn(Rect, Pos2) -> Color32 + Send + Sync>),\n}\n\nimpl Default for ColorMode {\n    fn default() -> Self {\n        Self::Solid(Color32::TRANSPARENT)\n    }\n}\n\nimpl Debug for ColorMode {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Solid(arg0) => f.debug_tuple(\"Solid\").field(arg0).finish(),\n            Self::UV(_arg0) => f.debug_tuple(\"UV\").field(&\"<closure>\").finish(),\n        }\n    }\n}\n\nimpl PartialEq for ColorMode {\n    fn eq(&self, other: &Self) -> bool {\n        match (self, other) {\n            (Self::Solid(l0), Self::Solid(r0)) => l0 == r0,\n            (Self::UV(_l0), Self::UV(_r0)) => false,\n            _ => false,\n        }\n    }\n}\n\nimpl ColorMode {\n    pub const TRANSPARENT: Self = Self::Solid(Color32::TRANSPARENT);\n}\n"
  },
  {
    "path": "crates/epaint/src/corner_radius.rs",
    "content": "/// How rounded the corners of things should be.\n///\n/// This specific the _corner radius_ of the underlying geometric shape (e.g. rectangle).\n/// If there is a stroke, then the stroke will have an inner and outer corner radius\n/// which will depends on its width and [`crate::StrokeKind`].\n///\n/// The rounding uses `u8` to save space,\n/// so the amount of rounding is limited to integers in the range `[0, 255]`.\n///\n/// For calculations, you may want to use [`crate::CornerRadiusF32`] instead, which uses `f32`.\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct CornerRadius {\n    /// Radius of the rounding of the North-West (left top) corner.\n    pub nw: u8,\n\n    /// Radius of the rounding of the North-East (right top) corner.\n    pub ne: u8,\n\n    /// Radius of the rounding of the South-West (left bottom) corner.\n    pub sw: u8,\n\n    /// Radius of the rounding of the South-East (right bottom) corner.\n    pub se: u8,\n}\n\nimpl Default for CornerRadius {\n    #[inline]\n    fn default() -> Self {\n        Self::ZERO\n    }\n}\n\nimpl From<u8> for CornerRadius {\n    #[inline]\n    fn from(radius: u8) -> Self {\n        Self::same(radius)\n    }\n}\n\nimpl From<f32> for CornerRadius {\n    #[inline]\n    fn from(radius: f32) -> Self {\n        Self::same(radius.round() as u8)\n    }\n}\n\nimpl CornerRadius {\n    /// No rounding on any corner.\n    pub const ZERO: Self = Self {\n        nw: 0,\n        ne: 0,\n        sw: 0,\n        se: 0,\n    };\n\n    /// Same rounding on all four corners.\n    #[inline]\n    pub const fn same(radius: u8) -> Self {\n        Self {\n            nw: radius,\n            ne: radius,\n            sw: radius,\n            se: radius,\n        }\n    }\n\n    /// Do all corners have the same rounding?\n    #[inline]\n    pub fn is_same(self) -> bool {\n        self.nw == self.ne && self.nw == self.sw && self.nw == self.se\n    }\n\n    /// Make sure each corner has a rounding of at least this.\n    #[inline]\n    pub fn at_least(self, min: u8) -> Self {\n        Self {\n            nw: self.nw.max(min),\n            ne: self.ne.max(min),\n            sw: self.sw.max(min),\n            se: self.se.max(min),\n        }\n    }\n\n    /// Make sure each corner has a rounding of at most this.\n    #[inline]\n    pub fn at_most(self, max: u8) -> Self {\n        Self {\n            nw: self.nw.min(max),\n            ne: self.ne.min(max),\n            sw: self.sw.min(max),\n            se: self.se.min(max),\n        }\n    }\n\n    /// Average rounding of the corners.\n    pub fn average(&self) -> f32 {\n        (self.nw as f32 + self.ne as f32 + self.sw as f32 + self.se as f32) / 4.0\n    }\n}\n\nimpl std::ops::Add for CornerRadius {\n    type Output = Self;\n    #[inline]\n    fn add(self, rhs: Self) -> Self {\n        Self {\n            nw: self.nw.saturating_add(rhs.nw),\n            ne: self.ne.saturating_add(rhs.ne),\n            sw: self.sw.saturating_add(rhs.sw),\n            se: self.se.saturating_add(rhs.se),\n        }\n    }\n}\n\nimpl std::ops::Add<u8> for CornerRadius {\n    type Output = Self;\n    #[inline]\n    fn add(self, rhs: u8) -> Self {\n        Self {\n            nw: self.nw.saturating_add(rhs),\n            ne: self.ne.saturating_add(rhs),\n            sw: self.sw.saturating_add(rhs),\n            se: self.se.saturating_add(rhs),\n        }\n    }\n}\n\nimpl std::ops::AddAssign for CornerRadius {\n    #[inline]\n    fn add_assign(&mut self, rhs: Self) {\n        *self = Self {\n            nw: self.nw.saturating_add(rhs.nw),\n            ne: self.ne.saturating_add(rhs.ne),\n            sw: self.sw.saturating_add(rhs.sw),\n            se: self.se.saturating_add(rhs.se),\n        };\n    }\n}\n\nimpl std::ops::AddAssign<u8> for CornerRadius {\n    #[inline]\n    fn add_assign(&mut self, rhs: u8) {\n        *self = Self {\n            nw: self.nw.saturating_add(rhs),\n            ne: self.ne.saturating_add(rhs),\n            sw: self.sw.saturating_add(rhs),\n            se: self.se.saturating_add(rhs),\n        };\n    }\n}\n\nimpl std::ops::Sub for CornerRadius {\n    type Output = Self;\n    #[inline]\n    fn sub(self, rhs: Self) -> Self {\n        Self {\n            nw: self.nw.saturating_sub(rhs.nw),\n            ne: self.ne.saturating_sub(rhs.ne),\n            sw: self.sw.saturating_sub(rhs.sw),\n            se: self.se.saturating_sub(rhs.se),\n        }\n    }\n}\n\nimpl std::ops::Sub<u8> for CornerRadius {\n    type Output = Self;\n    #[inline]\n    fn sub(self, rhs: u8) -> Self {\n        Self {\n            nw: self.nw.saturating_sub(rhs),\n            ne: self.ne.saturating_sub(rhs),\n            sw: self.sw.saturating_sub(rhs),\n            se: self.se.saturating_sub(rhs),\n        }\n    }\n}\n\nimpl std::ops::SubAssign for CornerRadius {\n    #[inline]\n    fn sub_assign(&mut self, rhs: Self) {\n        *self = Self {\n            nw: self.nw.saturating_sub(rhs.nw),\n            ne: self.ne.saturating_sub(rhs.ne),\n            sw: self.sw.saturating_sub(rhs.sw),\n            se: self.se.saturating_sub(rhs.se),\n        };\n    }\n}\n\nimpl std::ops::SubAssign<u8> for CornerRadius {\n    #[inline]\n    fn sub_assign(&mut self, rhs: u8) {\n        *self = Self {\n            nw: self.nw.saturating_sub(rhs),\n            ne: self.ne.saturating_sub(rhs),\n            sw: self.sw.saturating_sub(rhs),\n            se: self.se.saturating_sub(rhs),\n        };\n    }\n}\n\nimpl std::ops::Div<f32> for CornerRadius {\n    type Output = Self;\n    #[inline]\n    fn div(self, rhs: f32) -> Self {\n        Self {\n            nw: (self.nw as f32 / rhs) as u8,\n            ne: (self.ne as f32 / rhs) as u8,\n            sw: (self.sw as f32 / rhs) as u8,\n            se: (self.se as f32 / rhs) as u8,\n        }\n    }\n}\n\nimpl std::ops::DivAssign<f32> for CornerRadius {\n    #[inline]\n    fn div_assign(&mut self, rhs: f32) {\n        *self = Self {\n            nw: (self.nw as f32 / rhs) as u8,\n            ne: (self.ne as f32 / rhs) as u8,\n            sw: (self.sw as f32 / rhs) as u8,\n            se: (self.se as f32 / rhs) as u8,\n        };\n    }\n}\n\nimpl std::ops::Mul<f32> for CornerRadius {\n    type Output = Self;\n    #[inline]\n    fn mul(self, rhs: f32) -> Self {\n        Self {\n            nw: (self.nw as f32 * rhs) as u8,\n            ne: (self.ne as f32 * rhs) as u8,\n            sw: (self.sw as f32 * rhs) as u8,\n            se: (self.se as f32 * rhs) as u8,\n        }\n    }\n}\n\nimpl std::ops::MulAssign<f32> for CornerRadius {\n    #[inline]\n    fn mul_assign(&mut self, rhs: f32) {\n        *self = Self {\n            nw: (self.nw as f32 * rhs) as u8,\n            ne: (self.ne as f32 * rhs) as u8,\n            sw: (self.sw as f32 * rhs) as u8,\n            se: (self.se as f32 * rhs) as u8,\n        };\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/corner_radius_f32.rs",
    "content": "use crate::CornerRadius;\n\n/// How rounded the corners of things should be, in `f32`.\n///\n/// This is used for calculations, but storage is usually done with the more compact [`CornerRadius`].\n#[derive(Copy, Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct CornerRadiusF32 {\n    /// Radius of the rounding of the North-West (left top) corner.\n    pub nw: f32,\n\n    /// Radius of the rounding of the North-East (right top) corner.\n    pub ne: f32,\n\n    /// Radius of the rounding of the South-West (left bottom) corner.\n    pub sw: f32,\n\n    /// Radius of the rounding of the South-East (right bottom) corner.\n    pub se: f32,\n}\n\nimpl From<CornerRadius> for CornerRadiusF32 {\n    #[inline]\n    fn from(cr: CornerRadius) -> Self {\n        Self {\n            nw: cr.nw as f32,\n            ne: cr.ne as f32,\n            sw: cr.sw as f32,\n            se: cr.se as f32,\n        }\n    }\n}\n\nimpl From<CornerRadiusF32> for CornerRadius {\n    #[inline]\n    fn from(cr: CornerRadiusF32) -> Self {\n        Self {\n            nw: cr.nw.round() as u8,\n            ne: cr.ne.round() as u8,\n            sw: cr.sw.round() as u8,\n            se: cr.se.round() as u8,\n        }\n    }\n}\n\nimpl Default for CornerRadiusF32 {\n    #[inline]\n    fn default() -> Self {\n        Self::ZERO\n    }\n}\n\nimpl From<f32> for CornerRadiusF32 {\n    #[inline]\n    fn from(radius: f32) -> Self {\n        Self {\n            nw: radius,\n            ne: radius,\n            sw: radius,\n            se: radius,\n        }\n    }\n}\n\nimpl CornerRadiusF32 {\n    /// No rounding on any corner.\n    pub const ZERO: Self = Self {\n        nw: 0.0,\n        ne: 0.0,\n        sw: 0.0,\n        se: 0.0,\n    };\n\n    /// Same rounding on all four corners.\n    #[inline]\n    pub const fn same(radius: f32) -> Self {\n        Self {\n            nw: radius,\n            ne: radius,\n            sw: radius,\n            se: radius,\n        }\n    }\n\n    /// Do all corners have the same rounding?\n    #[inline]\n    pub fn is_same(&self) -> bool {\n        self.nw == self.ne && self.nw == self.sw && self.nw == self.se\n    }\n\n    /// Make sure each corner has a rounding of at least this.\n    #[inline]\n    pub fn at_least(&self, min: f32) -> Self {\n        Self {\n            nw: self.nw.max(min),\n            ne: self.ne.max(min),\n            sw: self.sw.max(min),\n            se: self.se.max(min),\n        }\n    }\n\n    /// Make sure each corner has a rounding of at most this.\n    #[inline]\n    pub fn at_most(&self, max: f32) -> Self {\n        Self {\n            nw: self.nw.min(max),\n            ne: self.ne.min(max),\n            sw: self.sw.min(max),\n            se: self.se.min(max),\n        }\n    }\n}\n\nimpl std::ops::Add for CornerRadiusF32 {\n    type Output = Self;\n    #[inline]\n    fn add(self, rhs: Self) -> Self {\n        Self {\n            nw: self.nw + rhs.nw,\n            ne: self.ne + rhs.ne,\n            sw: self.sw + rhs.sw,\n            se: self.se + rhs.se,\n        }\n    }\n}\n\nimpl std::ops::AddAssign for CornerRadiusF32 {\n    #[inline]\n    fn add_assign(&mut self, rhs: Self) {\n        *self = Self {\n            nw: self.nw + rhs.nw,\n            ne: self.ne + rhs.ne,\n            sw: self.sw + rhs.sw,\n            se: self.se + rhs.se,\n        };\n    }\n}\n\nimpl std::ops::AddAssign<f32> for CornerRadiusF32 {\n    #[inline]\n    fn add_assign(&mut self, rhs: f32) {\n        *self = Self {\n            nw: self.nw + rhs,\n            ne: self.ne + rhs,\n            sw: self.sw + rhs,\n            se: self.se + rhs,\n        };\n    }\n}\n\nimpl std::ops::Sub for CornerRadiusF32 {\n    type Output = Self;\n    #[inline]\n    fn sub(self, rhs: Self) -> Self {\n        Self {\n            nw: self.nw - rhs.nw,\n            ne: self.ne - rhs.ne,\n            sw: self.sw - rhs.sw,\n            se: self.se - rhs.se,\n        }\n    }\n}\n\nimpl std::ops::SubAssign for CornerRadiusF32 {\n    #[inline]\n    fn sub_assign(&mut self, rhs: Self) {\n        *self = Self {\n            nw: self.nw - rhs.nw,\n            ne: self.ne - rhs.ne,\n            sw: self.sw - rhs.sw,\n            se: self.se - rhs.se,\n        };\n    }\n}\n\nimpl std::ops::SubAssign<f32> for CornerRadiusF32 {\n    #[inline]\n    fn sub_assign(&mut self, rhs: f32) {\n        *self = Self {\n            nw: self.nw - rhs,\n            ne: self.ne - rhs,\n            sw: self.sw - rhs,\n            se: self.se - rhs,\n        };\n    }\n}\n\nimpl std::ops::Div<f32> for CornerRadiusF32 {\n    type Output = Self;\n    #[inline]\n    fn div(self, rhs: f32) -> Self {\n        Self {\n            nw: self.nw / rhs,\n            ne: self.ne / rhs,\n            sw: self.sw / rhs,\n            se: self.se / rhs,\n        }\n    }\n}\n\nimpl std::ops::DivAssign<f32> for CornerRadiusF32 {\n    #[inline]\n    fn div_assign(&mut self, rhs: f32) {\n        *self = Self {\n            nw: self.nw / rhs,\n            ne: self.ne / rhs,\n            sw: self.sw / rhs,\n            se: self.se / rhs,\n        };\n    }\n}\n\nimpl std::ops::Mul<f32> for CornerRadiusF32 {\n    type Output = Self;\n    #[inline]\n    fn mul(self, rhs: f32) -> Self {\n        Self {\n            nw: self.nw * rhs,\n            ne: self.ne * rhs,\n            sw: self.sw * rhs,\n            se: self.se * rhs,\n        }\n    }\n}\n\nimpl std::ops::MulAssign<f32> for CornerRadiusF32 {\n    #[inline]\n    fn mul_assign(&mut self, rhs: f32) {\n        *self = Self {\n            nw: self.nw * rhs,\n            ne: self.ne * rhs,\n            sw: self.sw * rhs,\n            se: self.se * rhs,\n        };\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/image.rs",
    "content": "use emath::Vec2;\n\nuse crate::{Color32, textures::TextureOptions};\nuse std::sync::Arc;\n\n/// An image stored in RAM.\n///\n/// To load an image file, see [`ColorImage::from_rgba_unmultiplied`].\n///\n/// This is currently an enum with only one variant, but more image types may be added in the future.\n///\n/// See also: [`ColorImage`].\n#[derive(Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum ImageData {\n    /// RGBA image.\n    Color(Arc<ColorImage>),\n}\n\nimpl ImageData {\n    pub fn size(&self) -> [usize; 2] {\n        match self {\n            Self::Color(image) => image.size,\n        }\n    }\n\n    pub fn width(&self) -> usize {\n        self.size()[0]\n    }\n\n    pub fn height(&self) -> usize {\n        self.size()[1]\n    }\n\n    pub fn bytes_per_pixel(&self) -> usize {\n        match self {\n            Self::Color(_) => 4,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A 2D RGBA color image in RAM.\n#[derive(Clone, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct ColorImage {\n    /// width, height in texels.\n    pub size: [usize; 2],\n\n    /// Size of the original SVG image (if any), or just the texel size of the image.\n    pub source_size: Vec2,\n\n    /// The pixels, row by row, from top to bottom.\n    pub pixels: Vec<Color32>,\n}\n\nimpl ColorImage {\n    /// Create an image filled with the given color.\n    pub fn new(size: [usize; 2], pixels: Vec<Color32>) -> Self {\n        debug_assert!(\n            size[0] * size[1] == pixels.len(),\n            \"size: {size:?}, pixels.len(): {}\",\n            pixels.len()\n        );\n        Self {\n            size,\n            source_size: Vec2::new(size[0] as f32, size[1] as f32),\n            pixels,\n        }\n    }\n\n    /// Create an image filled with the given color.\n    pub fn filled(size: [usize; 2], color: Color32) -> Self {\n        Self {\n            size,\n            source_size: Vec2::new(size[0] as f32, size[1] as f32),\n            pixels: vec![color; size[0] * size[1]],\n        }\n    }\n\n    /// Create a [`ColorImage`] from flat un-multiplied RGBA data.\n    ///\n    /// This is usually what you want to use after having loaded an image file.\n    ///\n    /// Panics if `size[0] * size[1] * 4 != rgba.len()`.\n    ///\n    /// ## Example using the [`image`](crates.io/crates/image) crate:\n    /// ``` ignore\n    /// fn load_image_from_path(path: &std::path::Path) -> Result<egui::ColorImage, image::ImageError> {\n    ///     let image = image::io::Reader::open(path)?.decode()?;\n    ///     let size = [image.width() as _, image.height() as _];\n    ///     let image_buffer = image.to_rgba8();\n    ///     let pixels = image_buffer.as_flat_samples();\n    ///     Ok(egui::ColorImage::from_rgba_unmultiplied(\n    ///         size,\n    ///         pixels.as_slice(),\n    ///     ))\n    /// }\n    ///\n    /// fn load_image_from_memory(image_data: &[u8]) -> Result<ColorImage, image::ImageError> {\n    ///     let image = image::load_from_memory(image_data)?;\n    ///     let size = [image.width() as _, image.height() as _];\n    ///     let image_buffer = image.to_rgba8();\n    ///     let pixels = image_buffer.as_flat_samples();\n    ///     Ok(ColorImage::from_rgba_unmultiplied(\n    ///         size,\n    ///         pixels.as_slice(),\n    ///     ))\n    /// }\n    /// ```\n    pub fn from_rgba_unmultiplied(size: [usize; 2], rgba: &[u8]) -> Self {\n        assert_eq!(\n            size[0] * size[1] * 4,\n            rgba.len(),\n            \"size: {:?}, rgba.len(): {}\",\n            size,\n            rgba.len()\n        );\n        let pixels = rgba\n            .chunks_exact(4)\n            .map(|p| Color32::from_rgba_unmultiplied(p[0], p[1], p[2], p[3]))\n            .collect();\n        Self::new(size, pixels)\n    }\n\n    pub fn from_rgba_premultiplied(size: [usize; 2], rgba: &[u8]) -> Self {\n        assert_eq!(\n            size[0] * size[1] * 4,\n            rgba.len(),\n            \"size: {:?}, rgba.len(): {}\",\n            size,\n            rgba.len()\n        );\n        let pixels = rgba\n            .chunks_exact(4)\n            .map(|p| Color32::from_rgba_premultiplied(p[0], p[1], p[2], p[3]))\n            .collect();\n        Self::new(size, pixels)\n    }\n\n    /// Create a [`ColorImage`] from flat opaque gray data.\n    ///\n    /// Panics if `size[0] * size[1] != gray.len()`.\n    pub fn from_gray(size: [usize; 2], gray: &[u8]) -> Self {\n        assert_eq!(\n            size[0] * size[1],\n            gray.len(),\n            \"size: {:?}, gray.len(): {}\",\n            size,\n            gray.len()\n        );\n        let pixels = gray.iter().map(|p| Color32::from_gray(*p)).collect();\n        Self::new(size, pixels)\n    }\n\n    /// Alternative method to `from_gray`.\n    /// Create a [`ColorImage`] from iterator over flat opaque gray data.\n    ///\n    /// Panics if `size[0] * size[1] != gray_iter.len()`.\n    #[doc(alias = \"from_grey_iter\")]\n    pub fn from_gray_iter(size: [usize; 2], gray_iter: impl Iterator<Item = u8>) -> Self {\n        let pixels: Vec<_> = gray_iter.map(Color32::from_gray).collect();\n        assert_eq!(\n            size[0] * size[1],\n            pixels.len(),\n            \"size: {:?}, pixels.len(): {}\",\n            size,\n            pixels.len()\n        );\n        Self::new(size, pixels)\n    }\n\n    /// A view of the underlying data as `&[u8]`\n    #[cfg(feature = \"bytemuck\")]\n    pub fn as_raw(&self) -> &[u8] {\n        bytemuck::cast_slice(&self.pixels)\n    }\n\n    /// A view of the underlying data as `&mut [u8]`\n    #[cfg(feature = \"bytemuck\")]\n    pub fn as_raw_mut(&mut self) -> &mut [u8] {\n        bytemuck::cast_slice_mut(&mut self.pixels)\n    }\n\n    /// Create a [`ColorImage`] from flat RGB data.\n    ///\n    /// This is what you want to use after having loaded an image file (and if\n    /// you are ignoring the alpha channel - considering it to always be 0xff)\n    ///\n    /// Panics if `size[0] * size[1] * 3 != rgb.len()`.\n    pub fn from_rgb(size: [usize; 2], rgb: &[u8]) -> Self {\n        assert_eq!(\n            size[0] * size[1] * 3,\n            rgb.len(),\n            \"size: {:?}, rgb.len(): {}\",\n            size,\n            rgb.len()\n        );\n        let pixels = rgb\n            .chunks_exact(3)\n            .map(|p| Color32::from_rgb(p[0], p[1], p[2]))\n            .collect();\n        Self::new(size, pixels)\n    }\n\n    /// An example color image, useful for tests.\n    pub fn example() -> Self {\n        let width = 128;\n        let height = 64;\n        let mut img = Self::filled([width, height], Color32::TRANSPARENT);\n        for y in 0..height {\n            for x in 0..width {\n                let h = x as f32 / width as f32;\n                let s = 1.0;\n                let v = 1.0;\n                let a = y as f32 / height as f32;\n                img[(x, y)] = crate::Hsva { h, s, v, a }.into();\n            }\n        }\n        img\n    }\n\n    /// Set the source size of e.g. the original SVG image.\n    #[inline]\n    pub fn with_source_size(mut self, source_size: Vec2) -> Self {\n        self.source_size = source_size;\n        self\n    }\n\n    #[inline]\n    pub fn width(&self) -> usize {\n        self.size[0]\n    }\n\n    #[inline]\n    pub fn height(&self) -> usize {\n        self.size[1]\n    }\n\n    /// Create a new image from a patch of the current image.\n    ///\n    /// This method is especially convenient for screenshotting a part of the app\n    /// since `region` can be interpreted as screen coordinates of the entire screenshot if `pixels_per_point` is provided for the native application.\n    /// The floats of [`emath::Rect`] are cast to usize, rounding them down in order to interpret them as indices to the image data.\n    ///\n    /// Panics if `region.min.x > region.max.x || region.min.y > region.max.y`, or if a region larger than the image is passed.\n    pub fn region(&self, region: &emath::Rect, pixels_per_point: Option<f32>) -> Self {\n        let pixels_per_point = pixels_per_point.unwrap_or(1.0);\n        let min_x = (region.min.x * pixels_per_point) as usize;\n        let max_x = (region.max.x * pixels_per_point) as usize;\n        let min_y = (region.min.y * pixels_per_point) as usize;\n        let max_y = (region.max.y * pixels_per_point) as usize;\n        assert!(\n            min_x <= max_x && min_y <= max_y,\n            \"Screenshot region is invalid: {region:?}\"\n        );\n        let width = max_x - min_x;\n        let height = max_y - min_y;\n        let mut output = Vec::with_capacity(width * height);\n        let row_stride = self.size[0];\n\n        for row in min_y..max_y {\n            output.extend_from_slice(\n                &self.pixels[row * row_stride + min_x..row * row_stride + max_x],\n            );\n        }\n        Self::new([width, height], output)\n    }\n\n    /// Clone a sub-region as a new image.\n    pub fn region_by_pixels(&self, [x, y]: [usize; 2], [w, h]: [usize; 2]) -> Self {\n        assert!(\n            x + w <= self.width(),\n            \"x + w should be <= self.width(), but x: {}, w: {}, width: {}\",\n            x,\n            w,\n            self.width()\n        );\n        assert!(\n            y + h <= self.height(),\n            \"y + h should be <= self.height(), but y: {}, h: {}, height: {}\",\n            y,\n            h,\n            self.height()\n        );\n\n        let mut pixels = Vec::with_capacity(w * h);\n        for y in y..y + h {\n            let offset = y * self.width() + x;\n            pixels.extend(&self.pixels[offset..(offset + w)]);\n        }\n        assert_eq!(\n            pixels.len(),\n            w * h,\n            \"pixels.len should be w * h, but got {}\",\n            pixels.len()\n        );\n        Self::new([w, h], pixels)\n    }\n}\n\nimpl std::ops::Index<(usize, usize)> for ColorImage {\n    type Output = Color32;\n\n    #[inline]\n    fn index(&self, (x, y): (usize, usize)) -> &Color32 {\n        let [w, h] = self.size;\n        assert!(x < w && y < h, \"x: {x}, y: {y}, w: {w}, h: {h}\");\n        &self.pixels[y * w + x]\n    }\n}\n\nimpl std::ops::IndexMut<(usize, usize)> for ColorImage {\n    #[inline]\n    fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Color32 {\n        let [w, h] = self.size;\n        assert!(x < w && y < h, \"x: {x}, y: {y}, w: {w}, h: {h}\");\n        &mut self.pixels[y * w + x]\n    }\n}\n\nimpl From<ColorImage> for ImageData {\n    #[inline(always)]\n    fn from(image: ColorImage) -> Self {\n        Self::Color(Arc::new(image))\n    }\n}\n\nimpl From<Arc<ColorImage>> for ImageData {\n    #[inline]\n    fn from(image: Arc<ColorImage>) -> Self {\n        Self::Color(image)\n    }\n}\n\nimpl std::fmt::Debug for ColorImage {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"ColorImage\")\n            .field(\"size\", &self.size)\n            .field(\"pixel-count\", &self.pixels.len())\n            .finish_non_exhaustive()\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// How to convert font coverage values into alpha and color values.\n//\n// This whole thing is less than rigorous.\n// Ideally we should do this in a shader instead, and use different computations\n// for different text colors.\n// See https://hikogui.org/2022/10/24/the-trouble-with-anti-aliasing.html for an in-depth analysis.\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum AlphaFromCoverage {\n    /// `alpha = coverage`.\n    ///\n    /// Looks good for black-on-white text, i.e. light mode.\n    ///\n    /// Same as [`Self::Gamma`]`(1.0)`, but more efficient.\n    Linear,\n\n    /// `alpha = coverage^gamma`.\n    Gamma(f32),\n\n    /// `alpha = 2 * coverage - coverage^2`\n    ///\n    /// This looks good for white-on-black text, i.e. dark mode.\n    ///\n    /// Very similar to a gamma of 0.5, but produces sharper text.\n    /// See <https://www.desmos.com/calculator/w0ndf5blmn> for a comparison to gamma=0.5.\n    #[default]\n    TwoCoverageMinusCoverageSq,\n}\n\nimpl AlphaFromCoverage {\n    /// A good-looking default for light mode (black-on-white text).\n    pub const LIGHT_MODE_DEFAULT: Self = Self::Linear;\n\n    /// A good-looking default for dark mode (white-on-black text).\n    pub const DARK_MODE_DEFAULT: Self = Self::TwoCoverageMinusCoverageSq;\n\n    /// Convert coverage to alpha.\n    #[inline(always)]\n    pub fn alpha_from_coverage(&self, coverage: f32) -> f32 {\n        let coverage = coverage.clamp(0.0, 1.0);\n        match self {\n            Self::Linear => coverage,\n            Self::Gamma(gamma) => coverage.powf(*gamma),\n            Self::TwoCoverageMinusCoverageSq => 2.0 * coverage - coverage * coverage,\n        }\n    }\n\n    #[inline(always)]\n    pub fn color_from_coverage(&self, coverage: f32) -> Color32 {\n        let alpha = self.alpha_from_coverage(coverage);\n        Color32::from_white_alpha(ecolor::linear_u8_from_linear_f32(alpha))\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A change to an image.\n///\n/// Either a whole new image, or an update to a rectangular region of it.\n#[derive(Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[must_use = \"The painter must take care of this\"]\npub struct ImageDelta {\n    /// What to set the texture to.\n    ///\n    /// If [`Self::pos`] is `None`, this describes the whole texture.\n    ///\n    /// If [`Self::pos`] is `Some`, this describes a patch of the whole image starting at [`Self::pos`].\n    pub image: ImageData,\n\n    pub options: TextureOptions,\n\n    /// If `None`, set the whole texture to [`Self::image`].\n    ///\n    /// If `Some(pos)`, update a sub-region of an already allocated texture with the patch in [`Self::image`].\n    pub pos: Option<[usize; 2]>,\n}\n\nimpl ImageDelta {\n    /// Update the whole texture.\n    pub fn full(image: impl Into<ImageData>, options: TextureOptions) -> Self {\n        Self {\n            image: image.into(),\n            options,\n            pos: None,\n        }\n    }\n\n    /// Update a sub-region of an existing texture.\n    pub fn partial(pos: [usize; 2], image: impl Into<ImageData>, options: TextureOptions) -> Self {\n        Self {\n            image: image.into(),\n            options,\n            pos: Some(pos),\n        }\n    }\n\n    /// Is this affecting the whole texture?\n    /// If `false`, this is a partial (sub-region) update.\n    pub fn is_whole(&self) -> bool {\n        self.pos.is_none()\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/lib.rs",
    "content": "//! A simple 2D graphics library for turning simple 2D shapes and text into textured triangles.\n//!\n//! Made for [`egui`](https://github.com/emilk/egui/).\n//!\n//! Create some [`Shape`]:s and pass them to [`Tessellator::tessellate_shapes`] to generate [`Mesh`]:es\n//! that you can then paint using some graphics API of your choice (e.g. OpenGL).\n//!\n//! ## Coordinate system\n//! The left-top corner of the screen is `(0.0, 0.0)`,\n//! with X increasing to the right and Y increasing downwards.\n//!\n//! `epaint` uses logical _points_ as its coordinate system.\n//! Those related to physical _pixels_ by the `pixels_per_point` scale factor.\n//! For example, a high-dpi screen can have `pixels_per_point = 2.0`,\n//! meaning there are two physical screen pixels for each logical point.\n//!\n//! Angles are in radians, and are measured clockwise from the X-axis, which has angle=0.\n//!\n//! ## Feature flags\n#![cfg_attr(feature = \"document-features\", doc = document_features::document_features!())]\n//!\n\n#![expect(clippy::float_cmp)]\n#![expect(clippy::manual_range_contains)]\n\nmod brush;\npub mod color;\nmod corner_radius;\nmod corner_radius_f32;\npub mod image;\nmod margin;\nmod margin_f32;\nmod mesh;\npub mod mutex;\nmod shadow;\npub mod shape_transform;\nmod shapes;\npub mod stats;\nmod stroke;\npub mod tessellator;\npub mod text;\nmod texture_atlas;\nmod texture_handle;\npub mod textures;\npub mod util;\nmod viewport;\n\npub use self::{\n    brush::Brush,\n    color::ColorMode,\n    corner_radius::CornerRadius,\n    corner_radius_f32::CornerRadiusF32,\n    image::{AlphaFromCoverage, ColorImage, ImageData, ImageDelta},\n    margin::Margin,\n    margin_f32::*,\n    mesh::{Mesh, Mesh16, Vertex},\n    shadow::Shadow,\n    shapes::{\n        CircleShape, CubicBezierShape, EllipseShape, PaintCallback, PaintCallbackInfo, PathShape,\n        QuadraticBezierShape, RectShape, Shape, TextShape,\n    },\n    stats::PaintStats,\n    stroke::{PathStroke, Stroke, StrokeKind},\n    tessellator::{TessellationOptions, Tessellator},\n    text::{FontFamily, FontId, Fonts, FontsView, Galley, TextOptions},\n    texture_atlas::TextureAtlas,\n    texture_handle::TextureHandle,\n    textures::TextureManager,\n    viewport::ViewportInPixels,\n};\n\n#[deprecated = \"Renamed to CornerRadius\"]\npub type Rounding = CornerRadius;\n\npub use ecolor::{Color32, Hsva, HsvaGamma, Rgba};\npub use emath::{Pos2, Rect, Vec2, pos2, vec2};\n\n#[deprecated = \"Use the ahash crate directly.\"]\npub use ahash;\n\npub use ecolor;\npub use emath;\n\n#[cfg(feature = \"color-hex\")]\npub use ecolor::hex_color;\n\n/// The UV coordinate of a white region of the texture mesh.\n///\n/// The default egui texture has the top-left corner pixel fully white.\n/// You need need use a clamping texture sampler for this to work\n/// (so it doesn't do bilinear blending with bottom right corner).\npub const WHITE_UV: emath::Pos2 = emath::pos2(0.0, 0.0);\n\n/// What texture to use in a [`Mesh`] mesh.\n///\n/// If you don't want to use a texture, use `TextureId::Managed(0)` and the [`WHITE_UV`] for uv-coord.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum TextureId {\n    /// Textures allocated using [`TextureManager`].\n    ///\n    /// The first texture (`TextureId::Managed(0)`) is used for the font data.\n    Managed(u64),\n\n    /// Your own texture, defined in any which way you want.\n    /// The backend renderer will presumably use this to look up what texture to use.\n    User(u64),\n}\n\nimpl Default for TextureId {\n    /// The epaint font texture.\n    fn default() -> Self {\n        Self::Managed(0)\n    }\n}\n\n/// A [`Shape`] within a clip rectangle.\n///\n/// Everything is using logical points.\n#[derive(Clone, Debug, PartialEq)]\npub struct ClippedShape {\n    /// Clip / scissor rectangle.\n    /// Only show the part of the [`Shape`] that falls within this.\n    pub clip_rect: emath::Rect,\n\n    /// The shape\n    pub shape: Shape,\n}\n\nimpl ClippedShape {\n    /// Transform (move/scale) the shape in-place.\n    ///\n    /// If using a [`PaintCallback`], note that only the rect is scaled as opposed\n    /// to other shapes where the stroke is also scaled.\n    pub fn transform(&mut self, transform: emath::TSTransform) {\n        let Self { clip_rect, shape } = self;\n        *clip_rect = transform * *clip_rect;\n        shape.transform(transform);\n    }\n}\n\n/// A [`Mesh`] or [`PaintCallback`] within a clip rectangle.\n///\n/// Everything is using logical points.\n#[derive(Clone, Debug)]\npub struct ClippedPrimitive {\n    /// Clip / scissor rectangle.\n    /// Only show the part of the [`Mesh`] that falls within this.\n    pub clip_rect: emath::Rect,\n\n    /// What to paint - either a [`Mesh`] or a [`PaintCallback`].\n    pub primitive: Primitive,\n}\n\n/// A rendering primitive - either a [`Mesh`] or a [`PaintCallback`].\n#[derive(Clone, Debug)]\npub enum Primitive {\n    Mesh(Mesh),\n    Callback(PaintCallback),\n}\n\n// ---------------------------------------------------------------------------\n\n/// Was epaint compiled with the `rayon` feature?\npub const HAS_RAYON: bool = cfg!(feature = \"rayon\");\n"
  },
  {
    "path": "crates/epaint/src/margin.rs",
    "content": "use emath::{Rect, Vec2, vec2};\n\n/// A value for all four sides of a rectangle,\n/// often used to express padding or spacing.\n///\n/// Can be added and subtracted to/from [`Rect`]s.\n///\n/// Negative margins are possible, but may produce weird behavior.\n/// Use with care.\n///\n/// All values are stored as [`i8`] to keep the size of [`Margin`] small.\n/// If you want floats, use [`crate::MarginF32`] instead.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Margin {\n    pub left: i8,\n    pub right: i8,\n    pub top: i8,\n    pub bottom: i8,\n}\n\nimpl Margin {\n    pub const ZERO: Self = Self {\n        left: 0,\n        right: 0,\n        top: 0,\n        bottom: 0,\n    };\n\n    /// The same margin on every side.\n    #[doc(alias = \"symmetric\")]\n    #[inline]\n    pub const fn same(margin: i8) -> Self {\n        Self {\n            left: margin,\n            right: margin,\n            top: margin,\n            bottom: margin,\n        }\n    }\n\n    /// Margins with the same size on opposing sides\n    #[inline]\n    pub const fn symmetric(x: i8, y: i8) -> Self {\n        Self {\n            left: x,\n            right: x,\n            top: y,\n            bottom: y,\n        }\n    }\n\n    /// Left margin, as `f32`\n    #[inline]\n    pub const fn leftf(self) -> f32 {\n        self.left as _\n    }\n\n    /// Right margin, as `f32`\n    #[inline]\n    pub const fn rightf(self) -> f32 {\n        self.right as _\n    }\n\n    /// Top margin, as `f32`\n    #[inline]\n    pub const fn topf(self) -> f32 {\n        self.top as _\n    }\n\n    /// Bottom margin, as `f32`\n    #[inline]\n    pub const fn bottomf(self) -> f32 {\n        self.bottom as _\n    }\n\n    /// Total margins on both sides\n    #[inline]\n    pub fn sum(self) -> Vec2 {\n        vec2(self.leftf() + self.rightf(), self.topf() + self.bottomf())\n    }\n\n    #[inline]\n    pub const fn left_top(self) -> Vec2 {\n        vec2(self.leftf(), self.topf())\n    }\n\n    #[inline]\n    pub const fn right_bottom(self) -> Vec2 {\n        vec2(self.rightf(), self.bottomf())\n    }\n\n    /// Are the margin on every side the same?\n    #[doc(alias = \"symmetric\")]\n    #[inline]\n    pub const fn is_same(self) -> bool {\n        self.left == self.right && self.left == self.top && self.left == self.bottom\n    }\n}\n\nimpl From<i8> for Margin {\n    #[inline]\n    fn from(v: i8) -> Self {\n        Self::same(v)\n    }\n}\n\nimpl From<f32> for Margin {\n    #[inline]\n    fn from(v: f32) -> Self {\n        Self::same(v.round() as _)\n    }\n}\n\nimpl From<Vec2> for Margin {\n    #[inline]\n    fn from(v: Vec2) -> Self {\n        Self::symmetric(v.x.round() as _, v.y.round() as _)\n    }\n}\n\n/// `Margin + Margin`\nimpl std::ops::Add for Margin {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, other: Self) -> Self {\n        Self {\n            left: self.left.saturating_add(other.left),\n            right: self.right.saturating_add(other.right),\n            top: self.top.saturating_add(other.top),\n            bottom: self.bottom.saturating_add(other.bottom),\n        }\n    }\n}\n\n/// `Margin + i8`\nimpl std::ops::Add<i8> for Margin {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, v: i8) -> Self {\n        Self {\n            left: self.left.saturating_add(v),\n            right: self.right.saturating_add(v),\n            top: self.top.saturating_add(v),\n            bottom: self.bottom.saturating_add(v),\n        }\n    }\n}\n\n/// `Margin += i8`\nimpl std::ops::AddAssign<i8> for Margin {\n    #[inline]\n    fn add_assign(&mut self, v: i8) {\n        *self = *self + v;\n    }\n}\n\n/// `Margin * f32`\nimpl std::ops::Mul<f32> for Margin {\n    type Output = Self;\n\n    #[inline]\n    fn mul(self, v: f32) -> Self {\n        Self {\n            left: (self.leftf() * v).round() as _,\n            right: (self.rightf() * v).round() as _,\n            top: (self.topf() * v).round() as _,\n            bottom: (self.bottomf() * v).round() as _,\n        }\n    }\n}\n\n/// `Margin *= f32`\nimpl std::ops::MulAssign<f32> for Margin {\n    #[inline]\n    fn mul_assign(&mut self, v: f32) {\n        *self = *self * v;\n    }\n}\n\n/// `Margin / f32`\nimpl std::ops::Div<f32> for Margin {\n    type Output = Self;\n\n    #[inline]\n    fn div(self, v: f32) -> Self {\n        #![expect(clippy::suspicious_arithmetic_impl)]\n        self * v.recip()\n    }\n}\n\n/// `Margin /= f32`\nimpl std::ops::DivAssign<f32> for Margin {\n    #[inline]\n    fn div_assign(&mut self, v: f32) {\n        *self = *self / v;\n    }\n}\n\n/// `Margin - Margin`\nimpl std::ops::Sub for Margin {\n    type Output = Self;\n\n    #[inline]\n    fn sub(self, other: Self) -> Self {\n        Self {\n            left: self.left.saturating_sub(other.left),\n            right: self.right.saturating_sub(other.right),\n            top: self.top.saturating_sub(other.top),\n            bottom: self.bottom.saturating_sub(other.bottom),\n        }\n    }\n}\n\n/// `Margin - i8`\nimpl std::ops::Sub<i8> for Margin {\n    type Output = Self;\n\n    #[inline]\n    fn sub(self, v: i8) -> Self {\n        Self {\n            left: self.left.saturating_sub(v),\n            right: self.right.saturating_sub(v),\n            top: self.top.saturating_sub(v),\n            bottom: self.bottom.saturating_sub(v),\n        }\n    }\n}\n\n/// `Margin -= i8`\nimpl std::ops::SubAssign<i8> for Margin {\n    #[inline]\n    fn sub_assign(&mut self, v: i8) {\n        *self = *self - v;\n    }\n}\n\n/// `Rect + Margin`\nimpl std::ops::Add<Margin> for Rect {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, margin: Margin) -> Self {\n        Self::from_min_max(\n            self.min - margin.left_top(),\n            self.max + margin.right_bottom(),\n        )\n    }\n}\n\n/// `Rect += Margin`\nimpl std::ops::AddAssign<Margin> for Rect {\n    #[inline]\n    fn add_assign(&mut self, margin: Margin) {\n        *self = *self + margin;\n    }\n}\n\n/// `Rect - Margin`\nimpl std::ops::Sub<Margin> for Rect {\n    type Output = Self;\n\n    #[inline]\n    fn sub(self, margin: Margin) -> Self {\n        Self::from_min_max(\n            self.min + margin.left_top(),\n            self.max - margin.right_bottom(),\n        )\n    }\n}\n\n/// `Rect -= Margin`\nimpl std::ops::SubAssign<Margin> for Rect {\n    #[inline]\n    fn sub_assign(&mut self, margin: Margin) {\n        *self = *self - margin;\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/margin_f32.rs",
    "content": "use emath::{Rect, Vec2, vec2};\n\nuse crate::Margin;\n\n/// A value for all four sides of a rectangle,\n/// often used to express padding or spacing.\n///\n/// Can be added and subtracted to/from [`Rect`]s.\n///\n/// For storage, use [`crate::Margin`] instead.\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct MarginF32 {\n    pub left: f32,\n    pub right: f32,\n    pub top: f32,\n    pub bottom: f32,\n}\n\n#[deprecated = \"Renamed to MarginF32\"]\npub type Marginf = MarginF32;\n\nimpl From<Margin> for MarginF32 {\n    #[inline]\n    fn from(margin: Margin) -> Self {\n        Self {\n            left: margin.left as _,\n            right: margin.right as _,\n            top: margin.top as _,\n            bottom: margin.bottom as _,\n        }\n    }\n}\n\nimpl From<MarginF32> for Margin {\n    #[inline]\n    fn from(marginf: MarginF32) -> Self {\n        Self {\n            left: marginf.left as _,\n            right: marginf.right as _,\n            top: marginf.top as _,\n            bottom: marginf.bottom as _,\n        }\n    }\n}\n\nimpl MarginF32 {\n    pub const ZERO: Self = Self {\n        left: 0.0,\n        right: 0.0,\n        top: 0.0,\n        bottom: 0.0,\n    };\n\n    /// The same margin on every side.\n    #[doc(alias = \"symmetric\")]\n    #[inline]\n    pub const fn same(margin: f32) -> Self {\n        Self {\n            left: margin,\n            right: margin,\n            top: margin,\n            bottom: margin,\n        }\n    }\n\n    /// Margins with the same size on opposing sides\n    #[inline]\n    pub const fn symmetric(x: f32, y: f32) -> Self {\n        Self {\n            left: x,\n            right: x,\n            top: y,\n            bottom: y,\n        }\n    }\n\n    /// Total margins on both sides\n    #[inline]\n    pub fn sum(&self) -> Vec2 {\n        vec2(self.left + self.right, self.top + self.bottom)\n    }\n\n    #[inline]\n    pub const fn left_top(&self) -> Vec2 {\n        vec2(self.left, self.top)\n    }\n\n    #[inline]\n    pub const fn right_bottom(&self) -> Vec2 {\n        vec2(self.right, self.bottom)\n    }\n\n    /// Are the margin on every side the same?\n    #[doc(alias = \"symmetric\")]\n    #[inline]\n    pub fn is_same(&self) -> bool {\n        self.left == self.right && self.left == self.top && self.left == self.bottom\n    }\n\n    #[deprecated = \"Use `rect + margin` instead\"]\n    #[inline]\n    pub fn expand_rect(&self, rect: Rect) -> Rect {\n        Rect::from_min_max(rect.min - self.left_top(), rect.max + self.right_bottom())\n    }\n\n    #[deprecated = \"Use `rect - margin` instead\"]\n    #[inline]\n    pub fn shrink_rect(&self, rect: Rect) -> Rect {\n        Rect::from_min_max(rect.min + self.left_top(), rect.max - self.right_bottom())\n    }\n}\n\nimpl From<f32> for MarginF32 {\n    #[inline]\n    fn from(v: f32) -> Self {\n        Self::same(v)\n    }\n}\n\nimpl From<Vec2> for MarginF32 {\n    #[inline]\n    fn from(v: Vec2) -> Self {\n        Self::symmetric(v.x, v.y)\n    }\n}\n\n/// `MarginF32 + MarginF32`\nimpl std::ops::Add for MarginF32 {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, other: Self) -> Self {\n        Self {\n            left: self.left + other.left,\n            right: self.right + other.right,\n            top: self.top + other.top,\n            bottom: self.bottom + other.bottom,\n        }\n    }\n}\n\n/// `MarginF32 + f32`\nimpl std::ops::Add<f32> for MarginF32 {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, v: f32) -> Self {\n        Self {\n            left: self.left + v,\n            right: self.right + v,\n            top: self.top + v,\n            bottom: self.bottom + v,\n        }\n    }\n}\n\n/// `Margind += f32`\nimpl std::ops::AddAssign<f32> for MarginF32 {\n    #[inline]\n    fn add_assign(&mut self, v: f32) {\n        self.left += v;\n        self.right += v;\n        self.top += v;\n        self.bottom += v;\n    }\n}\n\n/// `MarginF32 * f32`\nimpl std::ops::Mul<f32> for MarginF32 {\n    type Output = Self;\n\n    #[inline]\n    fn mul(self, v: f32) -> Self {\n        Self {\n            left: self.left * v,\n            right: self.right * v,\n            top: self.top * v,\n            bottom: self.bottom * v,\n        }\n    }\n}\n\n/// `MarginF32 *= f32`\nimpl std::ops::MulAssign<f32> for MarginF32 {\n    #[inline]\n    fn mul_assign(&mut self, v: f32) {\n        self.left *= v;\n        self.right *= v;\n        self.top *= v;\n        self.bottom *= v;\n    }\n}\n\n/// `MarginF32 / f32`\nimpl std::ops::Div<f32> for MarginF32 {\n    type Output = Self;\n\n    #[inline]\n    fn div(self, v: f32) -> Self {\n        Self {\n            left: self.left / v,\n            right: self.right / v,\n            top: self.top / v,\n            bottom: self.bottom / v,\n        }\n    }\n}\n\n/// `MarginF32 /= f32`\nimpl std::ops::DivAssign<f32> for MarginF32 {\n    #[inline]\n    fn div_assign(&mut self, v: f32) {\n        self.left /= v;\n        self.right /= v;\n        self.top /= v;\n        self.bottom /= v;\n    }\n}\n\n/// `MarginF32 - MarginF32`\nimpl std::ops::Sub for MarginF32 {\n    type Output = Self;\n\n    #[inline]\n    fn sub(self, other: Self) -> Self {\n        Self {\n            left: self.left - other.left,\n            right: self.right - other.right,\n            top: self.top - other.top,\n            bottom: self.bottom - other.bottom,\n        }\n    }\n}\n\n/// `MarginF32 - f32`\nimpl std::ops::Sub<f32> for MarginF32 {\n    type Output = Self;\n\n    #[inline]\n    fn sub(self, v: f32) -> Self {\n        Self {\n            left: self.left - v,\n            right: self.right - v,\n            top: self.top - v,\n            bottom: self.bottom - v,\n        }\n    }\n}\n\n/// `MarginF32 -= f32`\nimpl std::ops::SubAssign<f32> for MarginF32 {\n    #[inline]\n    fn sub_assign(&mut self, v: f32) {\n        self.left -= v;\n        self.right -= v;\n        self.top -= v;\n        self.bottom -= v;\n    }\n}\n\n/// `Rect + MarginF32`\nimpl std::ops::Add<MarginF32> for Rect {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, margin: MarginF32) -> Self {\n        Self::from_min_max(\n            self.min - margin.left_top(),\n            self.max + margin.right_bottom(),\n        )\n    }\n}\n\n/// `Rect += MarginF32`\nimpl std::ops::AddAssign<MarginF32> for Rect {\n    #[inline]\n    fn add_assign(&mut self, margin: MarginF32) {\n        *self = *self + margin;\n    }\n}\n\n/// `Rect - MarginF32`\nimpl std::ops::Sub<MarginF32> for Rect {\n    type Output = Self;\n\n    #[inline]\n    fn sub(self, margin: MarginF32) -> Self {\n        Self::from_min_max(\n            self.min + margin.left_top(),\n            self.max - margin.right_bottom(),\n        )\n    }\n}\n\n/// `Rect -= MarginF32`\nimpl std::ops::SubAssign<MarginF32> for Rect {\n    #[inline]\n    fn sub_assign(&mut self, margin: MarginF32) {\n        *self = *self - margin;\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/mesh.rs",
    "content": "use crate::{Color32, TextureId, WHITE_UV, emath};\nuse emath::{Pos2, Rect, Rot2, TSTransform, Vec2};\n\n/// The 2D vertex type.\n///\n/// Should be friendly to send to GPU as is.\n#[repr(C)]\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg(any(not(feature = \"unity\"), feature = \"_override_unity\"))]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct Vertex {\n    /// Logical pixel coordinates (points).\n    /// (0,0) is the top left corner of the screen.\n    pub pos: Pos2, // 64 bit\n\n    /// Normalized texture coordinates.\n    /// (0, 0) is the top left corner of the texture.\n    /// (1, 1) is the bottom right corner of the texture.\n    pub uv: Pos2, // 64 bit\n\n    /// sRGBA with premultiplied alpha\n    pub color: Color32, // 32 bit\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg(all(feature = \"unity\", not(feature = \"_override_unity\")))]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"bytemuck\", derive(bytemuck::Pod, bytemuck::Zeroable))]\npub struct Vertex {\n    /// Logical pixel coordinates (points).\n    /// (0,0) is the top left corner of the screen.\n    pub pos: Pos2, // 64 bit\n\n    /// sRGBA with premultiplied alpha\n    pub color: Color32, // 32 bit\n\n    /// Normalized texture coordinates.\n    /// (0, 0) is the top left corner of the texture.\n    /// (1, 1) is the bottom right corner of the texture.\n    pub uv: Pos2, // 64 bit\n}\n\n/// Textured triangles in two dimensions.\n#[derive(Clone, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Mesh {\n    /// Draw as triangles (i.e. the length is always multiple of three).\n    ///\n    /// If you only support 16-bit indices you can use [`Mesh::split_to_u16`].\n    ///\n    /// egui is NOT consistent with what winding order it uses, so turn off backface culling.\n    pub indices: Vec<u32>,\n\n    /// The vertex data indexed by `indices`.\n    pub vertices: Vec<Vertex>,\n\n    /// The texture to use when drawing these triangles.\n    pub texture_id: TextureId,\n    // TODO(emilk): bounding rectangle\n}\n\nimpl Mesh {\n    pub fn with_texture(texture_id: TextureId) -> Self {\n        Self {\n            texture_id,\n            ..Default::default()\n        }\n    }\n\n    /// Restore to default state, but without freeing memory.\n    pub fn clear(&mut self) {\n        self.indices.clear();\n        self.vertices.clear();\n        self.vertices = Default::default();\n    }\n\n    /// Returns the amount of memory used by the vertices and indices.\n    pub fn bytes_used(&self) -> usize {\n        std::mem::size_of::<Self>()\n            + self.vertices.len() * std::mem::size_of::<Vertex>()\n            + self.indices.len() * std::mem::size_of::<u32>()\n    }\n\n    /// Are all indices within the bounds of the contained vertices?\n    pub fn is_valid(&self) -> bool {\n        profiling::function_scope!();\n\n        if let Ok(n) = u32::try_from(self.vertices.len()) {\n            self.indices.iter().all(|&i| i < n)\n        } else {\n            false\n        }\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.indices.is_empty() && self.vertices.is_empty()\n    }\n\n    /// Iterate over the triangles of this mesh, returning vertex indices.\n    pub fn triangles(&self) -> impl Iterator<Item = [u32; 3]> + '_ {\n        self.indices\n            .chunks_exact(3)\n            .map(|chunk| [chunk[0], chunk[1], chunk[2]])\n    }\n\n    /// Calculate a bounding rectangle.\n    pub fn calc_bounds(&self) -> Rect {\n        let mut bounds = Rect::NOTHING;\n        for v in &self.vertices {\n            bounds.extend_with(v.pos);\n        }\n        bounds\n    }\n\n    /// Append all the indices and vertices of `other` to `self`.\n    ///\n    /// Panics when `other` mesh has a different texture.\n    pub fn append(&mut self, other: Self) {\n        profiling::function_scope!();\n        debug_assert!(other.is_valid(), \"Other mesh is invalid\");\n\n        if self.is_empty() {\n            *self = other;\n        } else {\n            self.append_ref(&other);\n        }\n    }\n\n    /// Append all the indices and vertices of `other` to `self` without\n    /// taking ownership.\n    ///\n    /// Panics when `other` mesh has a different texture.\n    pub fn append_ref(&mut self, other: &Self) {\n        debug_assert!(other.is_valid(), \"Other mesh is invalid\");\n\n        if self.is_empty() {\n            self.texture_id = other.texture_id;\n        } else {\n            assert_eq!(\n                self.texture_id, other.texture_id,\n                \"Can't merge Mesh using different textures\"\n            );\n        }\n\n        let index_offset = self.vertices.len() as u32;\n        self.indices\n            .extend(other.indices.iter().map(|index| index + index_offset));\n        self.vertices.extend(other.vertices.iter());\n    }\n\n    /// Add a colored vertex.\n    ///\n    /// Panics when the mesh has assigned a texture.\n    #[inline(always)]\n    pub fn colored_vertex(&mut self, pos: Pos2, color: Color32) {\n        debug_assert!(\n            self.texture_id == TextureId::default(),\n            \"Mesh has an assigned texture\"\n        );\n        self.vertices.push(Vertex {\n            pos,\n            uv: WHITE_UV,\n            color,\n        });\n    }\n\n    /// Add a triangle.\n    #[inline(always)]\n    pub fn add_triangle(&mut self, a: u32, b: u32, c: u32) {\n        self.indices.extend_from_slice(&[a, b, c]);\n    }\n\n    /// Make room for this many additional triangles (will reserve 3x as many indices).\n    /// See also `reserve_vertices`.\n    #[inline(always)]\n    pub fn reserve_triangles(&mut self, additional_triangles: usize) {\n        self.indices.reserve(3 * additional_triangles);\n    }\n\n    /// Make room for this many additional vertices.\n    /// See also `reserve_triangles`.\n    #[inline(always)]\n    pub fn reserve_vertices(&mut self, additional: usize) {\n        self.vertices.reserve(additional);\n    }\n\n    /// Rectangle with a texture and color.\n    #[inline(always)]\n    pub fn add_rect_with_uv(&mut self, rect: Rect, uv: Rect, color: Color32) {\n        #![expect(clippy::identity_op)]\n        let idx = self.vertices.len() as u32;\n        self.indices\n            .extend_from_slice(&[idx + 0, idx + 1, idx + 2, idx + 2, idx + 1, idx + 3]);\n\n        self.vertices.extend_from_slice(&[\n            Vertex {\n                pos: rect.left_top(),\n                uv: uv.left_top(),\n                color,\n            },\n            Vertex {\n                pos: rect.right_top(),\n                uv: uv.right_top(),\n                color,\n            },\n            Vertex {\n                pos: rect.left_bottom(),\n                uv: uv.left_bottom(),\n                color,\n            },\n            Vertex {\n                pos: rect.right_bottom(),\n                uv: uv.right_bottom(),\n                color,\n            },\n        ]);\n    }\n\n    /// Uniformly colored rectangle.\n    #[inline(always)]\n    pub fn add_colored_rect(&mut self, rect: Rect, color: Color32) {\n        debug_assert!(\n            self.texture_id == TextureId::default(),\n            \"Mesh has an assigned texture\"\n        );\n        self.add_rect_with_uv(rect, [WHITE_UV, WHITE_UV].into(), color);\n    }\n\n    /// This is for platforms that only support 16-bit index buffers.\n    ///\n    /// Splits this mesh into many smaller meshes (if needed)\n    /// where the smaller meshes have 16-bit indices.\n    pub fn split_to_u16(self) -> Vec<Mesh16> {\n        debug_assert!(self.is_valid(), \"Mesh is invalid\");\n\n        const MAX_SIZE: u32 = u16::MAX as u32;\n\n        if self.vertices.len() <= MAX_SIZE as usize {\n            // Common-case optimization:\n            return vec![Mesh16 {\n                indices: self.indices.iter().map(|&i| i as u16).collect(),\n                vertices: self.vertices,\n                texture_id: self.texture_id,\n            }];\n        }\n\n        let mut output = vec![];\n        let mut index_cursor = 0;\n\n        while index_cursor < self.indices.len() {\n            let span_start = index_cursor;\n            let mut min_vindex = self.indices[index_cursor];\n            let mut max_vindex = self.indices[index_cursor];\n\n            while index_cursor < self.indices.len() {\n                let (mut new_min, mut new_max) = (min_vindex, max_vindex);\n                for i in 0..3 {\n                    let idx = self.indices[index_cursor + i];\n                    new_min = new_min.min(idx);\n                    new_max = new_max.max(idx);\n                }\n\n                let new_span_size = new_max - new_min + 1; // plus one, because it is an inclusive range\n                if new_span_size <= MAX_SIZE {\n                    // Triangle fits\n                    min_vindex = new_min;\n                    max_vindex = new_max;\n                    index_cursor += 3;\n                } else {\n                    break;\n                }\n            }\n\n            assert!(\n                index_cursor > span_start,\n                \"One triangle spanned more than {MAX_SIZE} vertices\"\n            );\n\n            let mesh = Mesh16 {\n                indices: self.indices[span_start..index_cursor]\n                    .iter()\n                    .map(|vi| {\n                        #[expect(clippy::unwrap_used)]\n                        {\n                            u16::try_from(vi - min_vindex).unwrap()\n                        }\n                    })\n                    .collect(),\n                vertices: self.vertices[(min_vindex as usize)..=(max_vindex as usize)].to_vec(),\n                texture_id: self.texture_id,\n            };\n            debug_assert!(mesh.is_valid(), \"Mesh is invalid\");\n            output.push(mesh);\n        }\n        output\n    }\n\n    /// Translate location by this much, in-place\n    pub fn translate(&mut self, delta: Vec2) {\n        for v in &mut self.vertices {\n            v.pos += delta;\n        }\n    }\n\n    /// Transform the mesh in-place with the given transform.\n    pub fn transform(&mut self, transform: TSTransform) {\n        for v in &mut self.vertices {\n            v.pos = transform * v.pos;\n        }\n    }\n\n    /// Rotate by some angle about an origin, in-place.\n    ///\n    /// Origin is a position in screen space.\n    pub fn rotate(&mut self, rot: Rot2, origin: Pos2) {\n        for v in &mut self.vertices {\n            v.pos = origin + rot * (v.pos - origin);\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A version of [`Mesh`] that uses 16-bit indices.\n///\n/// This is produced by [`Mesh::split_to_u16`] and is meant to be used for legacy render backends.\npub struct Mesh16 {\n    /// Draw as triangles (i.e. the length is always multiple of three).\n    ///\n    /// egui is NOT consistent with what winding order it uses, so turn off backface culling.\n    pub indices: Vec<u16>,\n\n    /// The vertex data indexed by `indices`.\n    pub vertices: Vec<Vertex>,\n\n    /// The texture to use when drawing these triangles.\n    pub texture_id: TextureId,\n}\n\nimpl Mesh16 {\n    /// Are all indices within the bounds of the contained vertices?\n    pub fn is_valid(&self) -> bool {\n        if let Ok(n) = u16::try_from(self.vertices.len()) {\n            self.indices.iter().all(|&i| i < n)\n        } else {\n            false\n        }\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/mutex.rs",
    "content": "//! Wrappers around `parking_lot` locks, with a simple deadlock detection mechanism.\n\n// ----------------------------------------------------------------------------\n\nconst DEADLOCK_DURATION: std::time::Duration = std::time::Duration::from_secs(10);\n\n/// Provides interior mutability.\n///\n/// It's tailored for internal use in egui should only be used for short locks (as a guideline,\n/// locks should never be held longer than a single frame). In debug builds, when a lock can't\n/// be acquired within 10 seconds, we assume a deadlock and will panic.\n///\n/// This is a thin wrapper around [`parking_lot::Mutex`].\n#[derive(Default)]\npub struct Mutex<T>(parking_lot::Mutex<T>);\n\n/// The lock you get from [`Mutex`].\npub use parking_lot::MutexGuard;\n\nimpl<T> Mutex<T> {\n    #[inline(always)]\n    pub fn new(val: T) -> Self {\n        Self(parking_lot::Mutex::new(val))\n    }\n\n    /// Try to acquire the lock.\n    ///\n    /// ## Panics\n    /// Will panic in debug builds if the lock can't be acquired within 10 seconds.\n    #[inline(always)]\n    #[cfg_attr(debug_assertions, track_caller)]\n    pub fn lock(&self) -> MutexGuard<'_, T> {\n        if cfg!(debug_assertions) {\n            self.0.try_lock_for(DEADLOCK_DURATION).unwrap_or_else(|| {\n                panic!(\n                    \"DEBUG PANIC: Failed to acquire Mutex after {}s. Deadlock?\",\n                    DEADLOCK_DURATION.as_secs()\n                )\n            })\n        } else {\n            self.0.lock()\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// The lock you get from [`RwLock::read`].\npub use parking_lot::MappedRwLockReadGuard as RwLockReadGuard;\n\n/// The lock you get from [`RwLock::write`].\npub use parking_lot::MappedRwLockWriteGuard as RwLockWriteGuard;\n\n/// Provides interior mutability.\n///\n/// It's tailored for internal use in egui should only be used for short locks (as a guideline,\n/// locks should never be held longer than a single frame). In debug builds, when a lock can't\n/// be acquired within 10 seconds, we assume a deadlock and will panic.\n///\n/// This is a thin wrapper around [`parking_lot::RwLock`].\n#[derive(Default)]\npub struct RwLock<T: ?Sized>(parking_lot::RwLock<T>);\n\nimpl<T> RwLock<T> {\n    #[inline(always)]\n    pub fn new(val: T) -> Self {\n        Self(parking_lot::RwLock::new(val))\n    }\n}\n\nimpl<T: ?Sized> RwLock<T> {\n    /// Try to acquire read-access to the lock.\n    ///\n    /// ## Panics\n    /// Will panic in debug builds if the lock can't be acquired within 10 seconds.\n    #[inline(always)]\n    #[cfg_attr(debug_assertions, track_caller)]\n    pub fn read(&self) -> RwLockReadGuard<'_, T> {\n        let guard = if cfg!(debug_assertions) {\n            self.0.try_read_for(DEADLOCK_DURATION).unwrap_or_else(|| {\n                panic!(\n                    \"DEBUG PANIC: Failed to acquire RwLock read after {}s. Deadlock?\",\n                    DEADLOCK_DURATION.as_secs()\n                )\n            })\n        } else {\n            self.0.read()\n        };\n        parking_lot::RwLockReadGuard::map(guard, |v| v)\n    }\n\n    /// Try to acquire write-access to the lock.\n    ///\n    /// ## Panics\n    /// Will panic in debug builds if the lock can't be acquired within 10 seconds.\n    #[inline(always)]\n    #[cfg_attr(debug_assertions, track_caller)]\n    pub fn write(&self) -> RwLockWriteGuard<'_, T> {\n        let guard = if cfg!(debug_assertions) {\n            self.0.try_write_for(DEADLOCK_DURATION).unwrap_or_else(|| {\n                panic!(\n                    \"DEBUG PANIC: Failed to acquire RwLock write after {}s. Deadlock?\",\n                    DEADLOCK_DURATION.as_secs()\n                )\n            })\n        } else {\n            self.0.write()\n        };\n        parking_lot::RwLockWriteGuard::map(guard, |v| v)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nimpl<T> Clone for Mutex<T>\nwhere\n    T: Clone,\n{\n    fn clone(&self) -> Self {\n        Self::new(self.lock().clone())\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg(test)]\nmod tests {\n    #![expect(clippy::disallowed_methods)] // Ok for tests\n\n    use crate::mutex::Mutex;\n    use std::time::Duration;\n\n    #[test]\n    fn lock_two_different_mutexes_single_thread() {\n        let one = Mutex::new(());\n        let two = Mutex::new(());\n        let _a = one.lock();\n        let _b = two.lock();\n    }\n\n    #[test]\n    fn lock_multiple_threads() {\n        use std::sync::Arc;\n        let one = Arc::new(Mutex::new(()));\n        let our_lock = one.lock();\n        let other_thread = {\n            let one = Arc::clone(&one);\n            std::thread::spawn(move || {\n                let _lock = one.lock();\n            })\n        };\n        std::thread::sleep(Duration::from_millis(200));\n        drop(our_lock);\n        other_thread.join().unwrap();\n    }\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(test)]\nmod tests_rwlock {\n    #![expect(clippy::disallowed_methods)] // Ok for tests\n\n    use crate::mutex::RwLock;\n    use std::time::Duration;\n\n    #[test]\n    fn lock_two_different_rwlocks_single_thread() {\n        let one = RwLock::new(());\n        let two = RwLock::new(());\n        let _a = one.write();\n        let _b = two.write();\n    }\n\n    #[test]\n    fn rwlock_multiple_threads() {\n        use std::sync::Arc;\n        let one = Arc::new(RwLock::new(()));\n        let our_lock = one.write();\n        let other_thread1 = {\n            let one = Arc::clone(&one);\n            std::thread::spawn(move || {\n                let _ = one.write();\n            })\n        };\n        let other_thread2 = {\n            let one = Arc::clone(&one);\n            std::thread::spawn(move || {\n                let _ = one.read();\n            })\n        };\n        std::thread::sleep(Duration::from_millis(200));\n        drop(our_lock);\n        other_thread1.join().unwrap();\n        other_thread2.join().unwrap();\n    }\n\n    #[test]\n    #[should_panic]\n    fn rwlock_write_write_reentrancy() {\n        let one = RwLock::new(());\n        let _a1 = one.write();\n        let _a2 = one.write(); // panics\n    }\n\n    #[test]\n    #[should_panic]\n    fn rwlock_write_read_reentrancy() {\n        let one = RwLock::new(());\n        let _a1 = one.write();\n        let _a2 = one.read(); // panics\n    }\n\n    #[test]\n    #[should_panic]\n    fn rwlock_read_write_reentrancy() {\n        let one = RwLock::new(());\n        let _a1 = one.read();\n        let _a2 = one.write(); // panics\n    }\n\n    #[test]\n    fn rwlock_read_read_reentrancy() {\n        let one = RwLock::new(());\n        let _a1 = one.read();\n        // This is legal: this test suite specifically targets native, which relies\n        // on parking_lot's rw-locks, which are reentrant.\n        let _a2 = one.read();\n    }\n\n    #[test]\n    fn rwlock_short_read_foreign_read_write_reentrancy() {\n        use std::sync::Arc;\n\n        let lock = Arc::new(RwLock::new(()));\n\n        // Thread #0 grabs a read lock\n        let t0r0 = lock.read();\n\n        // Thread #1 grabs the same read lock\n        let other_thread = {\n            let lock = Arc::clone(&lock);\n            std::thread::spawn(move || {\n                let _t1r0 = lock.read();\n            })\n        };\n        other_thread.join().unwrap();\n\n        // Thread #0 releases its read lock\n        drop(t0r0);\n\n        // Thread #0 now grabs a write lock, which is legal\n        let _t0w0 = lock.write();\n    }\n\n    #[test]\n    #[should_panic]\n    fn rwlock_read_foreign_read_write_reentrancy() {\n        use std::sync::Arc;\n\n        let lock = Arc::new(RwLock::new(()));\n\n        // Thread #0 grabs a read lock\n        let _t0r0 = lock.read();\n\n        // Thread #1 grabs the same read lock\n        let other_thread = {\n            let lock = Arc::clone(&lock);\n            std::thread::spawn(move || {\n                let _t1r0 = lock.read();\n            })\n        };\n        other_thread.join().unwrap();\n\n        // Thread #0 now grabs a write lock, which should panic (read-write)\n        let _t0w0 = lock.write(); // panics\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/shadow.rs",
    "content": "use crate::{Color32, CornerRadius, MarginF32, Rect, RectShape, Vec2};\n\n/// The color and fuzziness of a fuzzy shape.\n///\n/// Can be used for a rectangular shadow with a soft penumbra.\n///\n/// Very similar to a box-shadow in CSS.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Shadow {\n    /// Move the shadow by this much.\n    ///\n    /// For instance, a value of `[1.0, 2.0]` will move the shadow 1 point to the right and 2 points down,\n    /// causing a drop-shadow effect.\n    pub offset: [i8; 2],\n\n    /// The width of the blur, i.e. the width of the fuzzy penumbra.\n    ///\n    /// A value of 0 means a sharp shadow.\n    pub blur: u8,\n\n    /// Expand the shadow in all directions by this much.\n    pub spread: u8,\n\n    /// Color of the opaque center of the shadow.\n    pub color: Color32,\n}\n\n#[test]\nfn shadow_size() {\n    assert_eq!(\n        std::mem::size_of::<Shadow>(),\n        8,\n        \"Shadow changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it.\"\n    );\n}\n\nimpl Shadow {\n    /// No shadow at all.\n    pub const NONE: Self = Self {\n        offset: [0, 0],\n        blur: 0,\n        spread: 0,\n        color: Color32::TRANSPARENT,\n    };\n\n    /// The argument is the rectangle of the shadow caster.\n    pub fn as_shape(&self, rect: Rect, corner_radius: impl Into<CornerRadius>) -> RectShape {\n        // tessellator.clip_rect = clip_rect; // TODO(emilk): culling\n\n        let Self {\n            offset,\n            blur,\n            spread,\n            color,\n        } = *self;\n        let [offset_x, offset_y] = offset;\n\n        let rect = rect\n            .translate(Vec2::new(offset_x as _, offset_y as _))\n            .expand(spread as _);\n        let corner_radius = corner_radius.into() + CornerRadius::from(spread);\n\n        RectShape::filled(rect, corner_radius, color).with_blur_width(blur as _)\n    }\n\n    /// How much larger than the parent rect are we in each direction?\n    pub fn margin(&self) -> MarginF32 {\n        let Self {\n            offset,\n            blur,\n            spread,\n            color: _,\n        } = *self;\n        let spread = spread as f32;\n        let blur = blur as f32;\n        let [offset_x, offset_y] = offset;\n        MarginF32 {\n            left: spread + 0.5 * blur - offset_x as f32,\n            right: spread + 0.5 * blur + offset_x as f32,\n            top: spread + 0.5 * blur - offset_y as f32,\n            bottom: spread + 0.5 * blur + offset_y as f32,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/shape_transform.rs",
    "content": "use std::sync::Arc;\n\nuse crate::{\n    CircleShape, Color32, ColorMode, CubicBezierShape, EllipseShape, Mesh, PathShape,\n    QuadraticBezierShape, RectShape, Shape, TextShape, color,\n};\n\n/// Remember to handle [`Color32::PLACEHOLDER`] specially!\npub fn adjust_colors(\n    shape: &mut Shape,\n    adjust_color: impl Fn(&mut Color32) + Send + Sync + Copy + 'static,\n) {\n    #![expect(clippy::match_same_arms)]\n    match shape {\n        Shape::Noop => {}\n\n        Shape::Vec(shapes) => {\n            for shape in shapes {\n                adjust_colors(shape, adjust_color);\n            }\n        }\n\n        Shape::LineSegment { stroke, points: _ } => {\n            adjust_color(&mut stroke.color);\n        }\n\n        Shape::Path(PathShape {\n            points: _,\n            closed: _,\n            fill,\n            stroke,\n        })\n        | Shape::QuadraticBezier(QuadraticBezierShape {\n            points: _,\n            closed: _,\n            fill,\n            stroke,\n        })\n        | Shape::CubicBezier(CubicBezierShape {\n            points: _,\n            closed: _,\n            fill,\n            stroke,\n        }) => {\n            adjust_color(fill);\n            adjust_color_mode(&mut stroke.color, adjust_color);\n        }\n\n        Shape::Circle(CircleShape {\n            center: _,\n            radius: _,\n            fill,\n            stroke,\n        })\n        | Shape::Ellipse(EllipseShape {\n            center: _,\n            radius: _,\n            fill,\n            stroke,\n        })\n        | Shape::Rect(RectShape {\n            rect: _,\n            corner_radius: _,\n            fill,\n            stroke,\n            stroke_kind: _,\n            round_to_pixels: _,\n            blur_width: _,\n            brush: _,\n        }) => {\n            adjust_color(fill);\n            adjust_color(&mut stroke.color);\n        }\n\n        Shape::Text(TextShape {\n            pos: _,\n            galley,\n            underline,\n            fallback_color,\n            override_text_color,\n            opacity_factor: _,\n            angle: _,\n        }) => {\n            adjust_color(&mut underline.color);\n            adjust_color(fallback_color);\n            if let Some(override_text_color) = override_text_color {\n                adjust_color(override_text_color);\n            }\n\n            if !galley.is_empty() {\n                let galley = Arc::make_mut(galley);\n                for placed_row in &mut galley.rows {\n                    let row = Arc::make_mut(&mut placed_row.row);\n                    for vertex in &mut row.visuals.mesh.vertices {\n                        adjust_color(&mut vertex.color);\n                    }\n                }\n            }\n        }\n\n        Shape::Mesh(mesh) => {\n            let Mesh {\n                indices: _,\n                vertices,\n                texture_id: _,\n            } = Arc::make_mut(mesh);\n\n            for v in vertices {\n                adjust_color(&mut v.color);\n            }\n        }\n\n        Shape::Callback(_) => {\n            // Can't tint user callback code\n        }\n    }\n}\n\nfn adjust_color_mode(\n    color_mode: &mut ColorMode,\n    adjust_color: impl Fn(&mut Color32) + Send + Sync + Copy + 'static,\n) {\n    match color_mode {\n        color::ColorMode::Solid(color) => adjust_color(color),\n        color::ColorMode::UV(callback) => {\n            let callback = Arc::clone(callback);\n            *color_mode = color::ColorMode::UV(Arc::new(Box::new(move |rect, pos| {\n                let mut color = callback(rect, pos);\n                adjust_color(&mut color);\n                color\n            })));\n        }\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/shapes/bezier_shape.rs",
    "content": "#![expect(clippy::many_single_char_names)]\n\nuse std::ops::Range;\n\nuse crate::{Color32, PathShape, PathStroke, Shape};\nuse emath::{Pos2, Rect, RectTransform};\n\n// ----------------------------------------------------------------------------\n\n/// A cubic [Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).\n///\n/// See also [`QuadraticBezierShape`].\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct CubicBezierShape {\n    /// The first point is the starting point and the last one is the ending point of the curve.\n    /// The middle points are the control points.\n    pub points: [Pos2; 4],\n    pub closed: bool,\n\n    pub fill: Color32,\n    pub stroke: PathStroke,\n}\n\nimpl CubicBezierShape {\n    /// Creates a cubic Bézier curve based on 4 points and stroke.\n    ///\n    /// The first point is the starting point and the last one is the ending point of the curve.\n    /// The middle points are the control points.\n    pub fn from_points_stroke(\n        points: [Pos2; 4],\n        closed: bool,\n        fill: Color32,\n        stroke: impl Into<PathStroke>,\n    ) -> Self {\n        Self {\n            points,\n            closed,\n            fill,\n            stroke: stroke.into(),\n        }\n    }\n\n    /// Transform the curve with the given transform.\n    pub fn transform(&self, transform: &RectTransform) -> Self {\n        let mut points = [Pos2::default(); 4];\n        for (i, origin_point) in self.points.iter().enumerate() {\n            points[i] = transform * *origin_point;\n        }\n        Self {\n            points,\n            closed: self.closed,\n            fill: self.fill,\n            stroke: self.stroke.clone(),\n        }\n    }\n\n    /// Convert the cubic Bézier curve to one or two [`PathShape`]'s.\n    /// When the curve is closed and it has to intersect with the base line, it will be converted into two shapes.\n    /// Otherwise, it will be converted into one shape.\n    /// The `tolerance` will be used to control the max distance between the curve and the base line.\n    /// The `epsilon` is used when comparing two floats.\n    pub fn to_path_shapes(&self, tolerance: Option<f32>, epsilon: Option<f32>) -> Vec<PathShape> {\n        let mut pathshapes = Vec::new();\n        let mut points_vec = self.flatten_closed(tolerance, epsilon);\n        for points in points_vec.drain(..) {\n            let pathshape = PathShape {\n                points,\n                closed: self.closed,\n                fill: self.fill,\n                stroke: self.stroke.clone(),\n            };\n            pathshapes.push(pathshape);\n        }\n        pathshapes\n    }\n\n    /// The visual bounding rectangle (includes stroke width)\n    pub fn visual_bounding_rect(&self) -> Rect {\n        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {\n            Rect::NOTHING\n        } else {\n            self.logical_bounding_rect().expand(self.stroke.width / 2.0)\n        }\n    }\n\n    /// Logical bounding rectangle (ignoring stroke width)\n    pub fn logical_bounding_rect(&self) -> Rect {\n        //temporary solution\n        let (mut min_x, mut max_x) = if self.points[0].x < self.points[3].x {\n            (self.points[0].x, self.points[3].x)\n        } else {\n            (self.points[3].x, self.points[0].x)\n        };\n        let (mut min_y, mut max_y) = if self.points[0].y < self.points[3].y {\n            (self.points[0].y, self.points[3].y)\n        } else {\n            (self.points[3].y, self.points[0].y)\n        };\n\n        // find the inflection points and get the x value\n        cubic_for_each_local_extremum(\n            self.points[0].x,\n            self.points[1].x,\n            self.points[2].x,\n            self.points[3].x,\n            &mut |t| {\n                let x = self.sample(t).x;\n                if x < min_x {\n                    min_x = x;\n                }\n                if x > max_x {\n                    max_x = x;\n                }\n            },\n        );\n\n        // find the inflection points and get the y value\n        cubic_for_each_local_extremum(\n            self.points[0].y,\n            self.points[1].y,\n            self.points[2].y,\n            self.points[3].y,\n            &mut |t| {\n                let y = self.sample(t).y;\n                if y < min_y {\n                    min_y = y;\n                }\n                if y > max_y {\n                    max_y = y;\n                }\n            },\n        );\n\n        Rect {\n            min: Pos2 { x: min_x, y: min_y },\n            max: Pos2 { x: max_x, y: max_y },\n        }\n    }\n\n    /// split the original cubic curve into a new one within a range.\n    pub fn split_range(&self, t_range: Range<f32>) -> Self {\n        debug_assert!(\n            0.0 <= t_range.start && t_range.end <= 1.0 && t_range.start <= t_range.end,\n            \"range should be in [0.0,1.0]\"\n        );\n\n        let from = self.sample(t_range.start);\n        let to = self.sample(t_range.end);\n\n        let d_from = self.points[1] - self.points[0].to_vec2();\n        let d_ctrl = self.points[2] - self.points[1].to_vec2();\n        let d_to = self.points[3] - self.points[2].to_vec2();\n        let q = QuadraticBezierShape {\n            points: [d_from, d_ctrl, d_to],\n            closed: self.closed,\n            fill: self.fill,\n            stroke: self.stroke.clone(),\n        };\n        let delta_t = t_range.end - t_range.start;\n        let q_start = q.sample(t_range.start);\n        let q_end = q.sample(t_range.end);\n        let ctrl1 = from + q_start.to_vec2() * delta_t;\n        let ctrl2 = to - q_end.to_vec2() * delta_t;\n\n        Self {\n            points: [from, ctrl1, ctrl2, to],\n            closed: self.closed,\n            fill: self.fill,\n            stroke: self.stroke.clone(),\n        }\n    }\n\n    // copied from <https://docs.rs/lyon_geom/latest/src/lyon_geom/cubic_bezier.rs.html#384-396>\n    // Computes the number of quadratic bézier segments to approximate a cubic one.\n    // Derived by Raph Levien from section 10.6 of Sedeberg's CAGD notes\n    // https://scholarsarchive.byu.edu/cgi/viewcontent.cgi?article=1000&context=facpub#section.10.6\n    // and the error metric from the caffein owl blog post http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html\n    pub fn num_quadratics(&self, tolerance: f32) -> u32 {\n        debug_assert!(tolerance > 0.0, \"the tolerance should be positive\");\n\n        let x =\n            self.points[0].x - 3.0 * self.points[1].x + 3.0 * self.points[2].x - self.points[3].x;\n        let y =\n            self.points[0].y - 3.0 * self.points[1].y + 3.0 * self.points[2].y - self.points[3].y;\n        let err = x * x + y * y;\n\n        (err / (432.0 * tolerance * tolerance))\n            .powf(1.0 / 6.0)\n            .ceil()\n            .max(1.0) as u32\n    }\n\n    /// Find out the t value for the point where the curve is intersected with the base line.\n    /// The base line is the line from P0 to P3.\n    /// If the curve only has two intersection points with the base line, they should be 0.0 and 1.0.\n    /// In this case, the \"fill\" will be simple since the curve is a convex line.\n    /// If the curve has more than two intersection points with the base line, the \"fill\" will be a problem.\n    /// We need to find out where is the 3rd t value (0<t<1)\n    /// And the original cubic curve will be split into two curves (0.0..t and t..1.0).\n    /// B(t) = (1-t)^3*P0 + 3*t*(1-t)^2*P1 + 3*t^2*(1-t)*P2 + t^3*P3\n    /// or B(t) = (P3 - 3*P2 + 3*P1 - P0)*t^3 + (3*P2 - 6*P1 + 3*P0)*t^2 + (3*P1 - 3*P0)*t + P0\n    /// this B(t) should be on the line between P0 and P3. Therefore:\n    /// (B.x - P0.x)/(P3.x - P0.x) = (B.y - P0.y)/(P3.y - P0.y), or:\n    /// B.x * (P3.y - P0.y) - B.y * (P3.x - P0.x) + P0.x * (P0.y - P3.y) + P0.y * (P3.x - P0.x) = 0\n    /// B.x = (P3.x - 3 * P2.x + 3 * P1.x - P0.x) * t^3 + (3 * P2.x - 6 * P1.x + 3 * P0.x) * t^2 + (3 * P1.x - 3 * P0.x) * t + P0.x\n    /// B.y = (P3.y - 3 * P2.y + 3 * P1.y - P0.y) * t^3 + (3 * P2.y - 6 * P1.y + 3 * P0.y) * t^2 + (3 * P1.y - 3 * P0.y) * t + P0.y\n    /// Combine the above three equations and iliminate B.x and B.y, we get:\n    /// ```text\n    /// t^3 * ( (P3.x - 3*P2.x + 3*P1.x - P0.x) * (P3.y - P0.y) - (P3.y - 3*P2.y + 3*P1.y - P0.y) * (P3.x - P0.x))\n    /// + t^2 * ( (3 * P2.x - 6 * P1.x + 3 * P0.x) * (P3.y - P0.y) - (3 * P2.y - 6 * P1.y + 3 * P0.y) * (P3.x - P0.x))\n    /// + t^1 * ( (3 * P1.x - 3 * P0.x) * (P3.y - P0.y) - (3 * P1.y - 3 * P0.y) * (P3.x - P0.x))\n    /// + (P0.x * (P3.y - P0.y) - P0.y * (P3.x - P0.x)) + P0.x * (P0.y - P3.y) + P0.y * (P3.x - P0.x)\n    /// = 0\n    /// ```\n    /// or `a * t^3 + b * t^2 + c * t + d = 0`\n    ///\n    /// let x = t - b / (3 * a), then we have:\n    /// ```text\n    /// x^3 + p * x + q = 0, where:\n    /// p = (3.0 * a * c - b^2) / (3.0 * a^2)\n    /// q = (2.0 * b^3 - 9.0 * a * b * c + 27.0 * a^2 * d) / (27.0 * a^3)\n    /// ```\n    ///\n    /// when p > 0, there will be one real root, two complex roots\n    /// when p = 0, there will be two real roots, when p=q=0, there will be three real roots but all 0.\n    /// when p < 0, there will be three unique real roots. this is what we need. (x1, x2, x3)\n    ///  t = x + b / (3 * a), then we have: t1, t2, t3.\n    /// the one between 0.0 and 1.0 is what we need.\n    /// <`https://baike.baidu.com/item/%E4%B8%80%E5%85%83%E4%B8%89%E6%AC%A1%E6%96%B9%E7%A8%8B/8388473 /`>\n    ///\n    pub fn find_cross_t(&self, epsilon: f32) -> Option<f32> {\n        let p0 = self.points[0];\n        let p1 = self.points[1];\n        let p2 = self.points[2];\n        let p3 = self.points[3];\n\n        let a = (p3.x - 3.0 * p2.x + 3.0 * p1.x - p0.x) * (p3.y - p0.y)\n            - (p3.y - 3.0 * p2.y + 3.0 * p1.y - p0.y) * (p3.x - p0.x);\n        let b = (3.0 * p2.x - 6.0 * p1.x + 3.0 * p0.x) * (p3.y - p0.y)\n            - (3.0 * p2.y - 6.0 * p1.y + 3.0 * p0.y) * (p3.x - p0.x);\n        let c =\n            (3.0 * p1.x - 3.0 * p0.x) * (p3.y - p0.y) - (3.0 * p1.y - 3.0 * p0.y) * (p3.x - p0.x);\n        let d = p0.x * (p3.y - p0.y) - p0.y * (p3.x - p0.x)\n            + p0.x * (p0.y - p3.y)\n            + p0.y * (p3.x - p0.x);\n\n        let h = -b / (3.0 * a);\n        let p = (3.0 * a * c - b * b) / (3.0 * a * a);\n        let q = (2.0 * b * b * b - 9.0 * a * b * c + 27.0 * a * a * d) / (27.0 * a * a * a);\n\n        if p > 0.0 {\n            return None;\n        }\n        let r = (-(p / 3.0).powi(3)).sqrt();\n        let theta = (-q / (2.0 * r)).acos() / 3.0;\n\n        let t1 = 2.0 * r.cbrt() * theta.cos() + h;\n        let t2 = 2.0 * r.cbrt() * (theta + 120.0 * std::f32::consts::PI / 180.0).cos() + h;\n        let t3 = 2.0 * r.cbrt() * (theta + 240.0 * std::f32::consts::PI / 180.0).cos() + h;\n\n        if t1 > epsilon && t1 < 1.0 - epsilon {\n            return Some(t1);\n        }\n        if t2 > epsilon && t2 < 1.0 - epsilon {\n            return Some(t2);\n        }\n        if t3 > epsilon && t3 < 1.0 - epsilon {\n            return Some(t3);\n        }\n        None\n    }\n\n    /// Calculate the point (x,y) at t based on the cubic Bézier curve equation.\n    /// t is in [0.0,1.0]\n    /// [Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves)\n    ///\n    pub fn sample(&self, t: f32) -> Pos2 {\n        debug_assert!(\n            t >= 0.0 && t <= 1.0,\n            \"the sample value should be in [0.0,1.0]\"\n        );\n\n        let h = 1.0 - t;\n        let a = t * t * t;\n        let b = 3.0 * t * t * h;\n        let c = 3.0 * t * h * h;\n        let d = h * h * h;\n        let result = self.points[3].to_vec2() * a\n            + self.points[2].to_vec2() * b\n            + self.points[1].to_vec2() * c\n            + self.points[0].to_vec2() * d;\n        result.to_pos2()\n    }\n\n    /// find a set of points that approximate the cubic Bézier curve.\n    /// the number of points is determined by the tolerance.\n    /// the points may not be evenly distributed in the range [0.0,1.0] (t value)\n    pub fn flatten(&self, tolerance: Option<f32>) -> Vec<Pos2> {\n        let tolerance =\n            tolerance.unwrap_or_else(|| (self.points[0].x - self.points[3].x).abs() * 0.001);\n        let mut result = vec![self.points[0]];\n        self.for_each_flattened_with_t(tolerance, &mut |p, _t| {\n            result.push(p);\n        });\n        result\n    }\n\n    /// find a set of points that approximate the cubic Bézier curve.\n    /// the number of points is determined by the tolerance.\n    /// the points may not be evenly distributed in the range [0.0,1.0] (t value)\n    /// this api will check whether the curve will cross the base line or not when closed = true.\n    /// The result will be a vec of vec of Pos2. it will store two closed aren in different vec.\n    /// The epsilon is used to compare a float value.\n    pub fn flatten_closed(&self, tolerance: Option<f32>, epsilon: Option<f32>) -> Vec<Vec<Pos2>> {\n        let tolerance =\n            tolerance.unwrap_or_else(|| (self.points[0].x - self.points[3].x).abs() * 0.001);\n        let epsilon = epsilon.unwrap_or(1.0e-5);\n        let mut result = Vec::new();\n        let mut first_half = Vec::new();\n        let mut second_half = Vec::new();\n        let mut flipped = false;\n        first_half.push(self.points[0]);\n\n        let cross = self.find_cross_t(epsilon);\n        match cross {\n            Some(cross) => {\n                if self.closed {\n                    self.for_each_flattened_with_t(tolerance, &mut |p, t| {\n                        if t < cross {\n                            first_half.push(p);\n                        } else {\n                            if !flipped {\n                                // when just crossed the base line, flip the order of the points\n                                // add the cross point to the first half as the last point\n                                // and add the cross point to the second half as the first point\n                                flipped = true;\n                                let cross_point = self.sample(cross);\n                                first_half.push(cross_point);\n                                second_half.push(cross_point);\n                            }\n                            second_half.push(p);\n                        }\n                    });\n                } else {\n                    self.for_each_flattened_with_t(tolerance, &mut |p, _t| {\n                        first_half.push(p);\n                    });\n                }\n            }\n            None => {\n                self.for_each_flattened_with_t(tolerance, &mut |p, _t| {\n                    first_half.push(p);\n                });\n            }\n        }\n\n        result.push(first_half);\n        if !second_half.is_empty() {\n            result.push(second_half);\n        }\n        result\n    }\n    // from lyon_geom::cubic_bezier.rs\n    /// Iterates through the curve invoking a callback at each point.\n    pub fn for_each_flattened_with_t<F: FnMut(Pos2, f32)>(&self, tolerance: f32, callback: &mut F) {\n        flatten_cubic_bezier_with_t(self, tolerance, callback);\n    }\n}\n\nimpl From<CubicBezierShape> for Shape {\n    #[inline(always)]\n    fn from(shape: CubicBezierShape) -> Self {\n        Self::CubicBezier(shape)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A quadratic [Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).\n///\n/// See also [`CubicBezierShape`].\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct QuadraticBezierShape {\n    /// The first point is the starting point and the last one is the ending point of the curve.\n    /// The middle point is the control points.\n    pub points: [Pos2; 3],\n    pub closed: bool,\n\n    pub fill: Color32,\n    pub stroke: PathStroke,\n}\n\nimpl QuadraticBezierShape {\n    /// Create a new quadratic Bézier shape based on the 3 points and stroke.\n    ///\n    /// The first point is the starting point and the last one is the ending point of the curve.\n    /// The middle point is the control points.\n    /// The points should be in the order [start, control, end]\n    pub fn from_points_stroke(\n        points: [Pos2; 3],\n        closed: bool,\n        fill: Color32,\n        stroke: impl Into<PathStroke>,\n    ) -> Self {\n        Self {\n            points,\n            closed,\n            fill,\n            stroke: stroke.into(),\n        }\n    }\n\n    /// Transform the curve with the given transform.\n    pub fn transform(&self, transform: &RectTransform) -> Self {\n        let mut points = [Pos2::default(); 3];\n        for (i, origin_point) in self.points.iter().enumerate() {\n            points[i] = transform * *origin_point;\n        }\n        Self {\n            points,\n            closed: self.closed,\n            fill: self.fill,\n            stroke: self.stroke.clone(),\n        }\n    }\n\n    /// Convert the quadratic Bézier curve to one [`PathShape`].\n    /// The `tolerance` will be used to control the max distance between the curve and the base line.\n    pub fn to_path_shape(&self, tolerance: Option<f32>) -> PathShape {\n        let points = self.flatten(tolerance);\n        PathShape {\n            points,\n            closed: self.closed,\n            fill: self.fill,\n            stroke: self.stroke.clone(),\n        }\n    }\n\n    /// The visual bounding rectangle (includes stroke width)\n    pub fn visual_bounding_rect(&self) -> Rect {\n        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {\n            Rect::NOTHING\n        } else {\n            self.logical_bounding_rect().expand(self.stroke.width / 2.0)\n        }\n    }\n\n    /// Logical bounding rectangle (ignoring stroke width)\n    pub fn logical_bounding_rect(&self) -> Rect {\n        let (mut min_x, mut max_x) = if self.points[0].x < self.points[2].x {\n            (self.points[0].x, self.points[2].x)\n        } else {\n            (self.points[2].x, self.points[0].x)\n        };\n        let (mut min_y, mut max_y) = if self.points[0].y < self.points[2].y {\n            (self.points[0].y, self.points[2].y)\n        } else {\n            (self.points[2].y, self.points[0].y)\n        };\n\n        quadratic_for_each_local_extremum(\n            self.points[0].x,\n            self.points[1].x,\n            self.points[2].x,\n            &mut |t| {\n                let x = self.sample(t).x;\n                if x < min_x {\n                    min_x = x;\n                }\n                if x > max_x {\n                    max_x = x;\n                }\n            },\n        );\n\n        quadratic_for_each_local_extremum(\n            self.points[0].y,\n            self.points[1].y,\n            self.points[2].y,\n            &mut |t| {\n                let y = self.sample(t).y;\n                if y < min_y {\n                    min_y = y;\n                }\n                if y > max_y {\n                    max_y = y;\n                }\n            },\n        );\n\n        Rect {\n            min: Pos2 { x: min_x, y: min_y },\n            max: Pos2 { x: max_x, y: max_y },\n        }\n    }\n\n    /// Calculate the point (x,y) at t based on the quadratic Bézier curve equation.\n    /// t is in [0.0,1.0]\n    /// [Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B.C3.A9zier_curves)\n    ///\n    pub fn sample(&self, t: f32) -> Pos2 {\n        debug_assert!(\n            t >= 0.0 && t <= 1.0,\n            \"the sample value should be in [0.0,1.0]\"\n        );\n\n        let h = 1.0 - t;\n        let a = t * t;\n        let b = 2.0 * t * h;\n        let c = h * h;\n        let result = self.points[2].to_vec2() * a\n            + self.points[1].to_vec2() * b\n            + self.points[0].to_vec2() * c;\n        result.to_pos2()\n    }\n\n    /// find a set of points that approximate the quadratic Bézier curve.\n    /// the number of points is determined by the tolerance.\n    /// the points may not be evenly distributed in the range [0.0,1.0] (t value)\n    pub fn flatten(&self, tolerance: Option<f32>) -> Vec<Pos2> {\n        let tolerance =\n            tolerance.unwrap_or_else(|| (self.points[0].x - self.points[2].x).abs() * 0.001);\n        let mut result = vec![self.points[0]];\n        self.for_each_flattened_with_t(tolerance, &mut |p, _t| {\n            result.push(p);\n        });\n        result\n    }\n\n    // copied from https://docs.rs/lyon_geom/latest/lyon_geom/\n    /// Compute a flattened approximation of the curve, invoking a callback at\n    /// each step.\n    ///\n    /// The callback takes the point and corresponding curve parameter at each step.\n    ///\n    /// This implements the algorithm described by Raph Levien at\n    /// <https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html>\n    pub fn for_each_flattened_with_t<F>(&self, tolerance: f32, callback: &mut F)\n    where\n        F: FnMut(Pos2, f32),\n    {\n        let params = FlatteningParameters::from_curve(self, tolerance);\n        if params.is_point {\n            return;\n        }\n\n        let count = params.count as u32;\n        for index in 1..count {\n            let t = params.t_at_iteration(index as f32);\n\n            callback(self.sample(t), t);\n        }\n\n        callback(self.sample(1.0), 1.0);\n    }\n}\n\nimpl From<QuadraticBezierShape> for Shape {\n    #[inline(always)]\n    fn from(shape: QuadraticBezierShape) -> Self {\n        Self::QuadraticBezier(shape)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n// lyon_geom::flatten_cubic.rs\n// copied from https://docs.rs/lyon_geom/latest/lyon_geom/\nfn flatten_cubic_bezier_with_t<F: FnMut(Pos2, f32)>(\n    curve: &CubicBezierShape,\n    tolerance: f32,\n    callback: &mut F,\n) {\n    // debug_assert!(tolerance >= S::EPSILON * S::EPSILON);\n    let quadratics_tolerance = tolerance * 0.2;\n    let flattening_tolerance = tolerance * 0.8;\n\n    let num_quadratics = curve.num_quadratics(quadratics_tolerance);\n    let step = 1.0 / num_quadratics as f32;\n    let n = num_quadratics;\n    let mut t0 = 0.0;\n    for _ in 0..(n - 1) {\n        let t1 = t0 + step;\n\n        let quadratic = single_curve_approximation(&curve.split_range(t0..t1));\n        quadratic.for_each_flattened_with_t(flattening_tolerance, &mut |point, t_sub| {\n            let t = t0 + step * t_sub;\n            callback(point, t);\n        });\n\n        t0 = t1;\n    }\n\n    // Do the last step manually to make sure we finish at t = 1.0 exactly.\n    let quadratic = single_curve_approximation(&curve.split_range(t0..1.0));\n    quadratic.for_each_flattened_with_t(flattening_tolerance, &mut |point, t_sub| {\n        let t = t0 + step * t_sub;\n        callback(point, t);\n    });\n}\n\n// from lyon_geom::quadratic_bezier.rs\n// copied from https://docs.rs/lyon_geom/latest/lyon_geom/\nstruct FlatteningParameters {\n    count: f32,\n    integral_from: f32,\n    integral_step: f32,\n    inv_integral_from: f32,\n    div_inv_integral_diff: f32,\n    is_point: bool,\n}\n\nimpl FlatteningParameters {\n    // https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html\n    pub fn from_curve(curve: &QuadraticBezierShape, tolerance: f32) -> Self {\n        #![expect(clippy::useless_let_if_seq)]\n\n        // Map the quadratic bézier segment to y = x^2 parabola.\n        let from = curve.points[0];\n        let ctrl = curve.points[1];\n        let to = curve.points[2];\n\n        let ddx = 2.0 * ctrl.x - from.x - to.x;\n        let ddy = 2.0 * ctrl.y - from.y - to.y;\n        let cross = (to.x - from.x) * ddy - (to.y - from.y) * ddx;\n        let inv_cross = 1.0 / cross;\n        let parabola_from = ((ctrl.x - from.x) * ddx + (ctrl.y - from.y) * ddy) * inv_cross;\n        let parabola_to = ((to.x - ctrl.x) * ddx + (to.y - ctrl.y) * ddy) * inv_cross;\n        // Note, scale can be NaN, for example with straight lines. When it happens the NaN will\n        // propagate to other parameters. We catch it all by setting the iteration count to zero\n        // and leave the rest as garbage.\n        let scale = cross.abs() / (ddx.hypot(ddy) * (parabola_to - parabola_from).abs());\n\n        let integral_from = approx_parabola_integral(parabola_from);\n        let integral_to = approx_parabola_integral(parabola_to);\n        let integral_diff = integral_to - integral_from;\n\n        let inv_integral_from = approx_parabola_inv_integral(integral_from);\n        let inv_integral_to = approx_parabola_inv_integral(integral_to);\n        let div_inv_integral_diff = 1.0 / (inv_integral_to - inv_integral_from);\n\n        // the original author thinks it can be stored as integer if it's not generic.\n        // but if so, we have to handle the edge case of the integral being infinite.\n        let mut count = (0.5 * integral_diff.abs() * (scale / tolerance).sqrt()).ceil();\n        let mut is_point = false;\n        // If count is NaN the curve can be approximated by a single straight line or a point.\n        if !count.is_finite() {\n            count = 0.0;\n            is_point = (to.x - from.x).hypot(to.y - from.y) < tolerance * tolerance;\n        }\n\n        let integral_step = integral_diff / count;\n\n        Self {\n            count,\n            integral_from,\n            integral_step,\n            inv_integral_from,\n            div_inv_integral_diff,\n            is_point,\n        }\n    }\n\n    fn t_at_iteration(&self, iteration: f32) -> f32 {\n        let u = approx_parabola_inv_integral(self.integral_from + self.integral_step * iteration);\n        (u - self.inv_integral_from) * self.div_inv_integral_diff\n    }\n}\n\n/// Compute an approximation to integral (1 + 4x^2) ^ -0.25 dx used in the flattening code.\nfn approx_parabola_integral(x: f32) -> f32 {\n    let d: f32 = 0.67;\n    let quarter = 0.25;\n    x / (1.0 - d + (d.powi(4) + quarter * x * x).sqrt().sqrt())\n}\n\n/// Approximate the inverse of the function above.\nfn approx_parabola_inv_integral(x: f32) -> f32 {\n    let b = 0.39;\n    let quarter = 0.25;\n    x * (1.0 - b + (b * b + quarter * x * x).sqrt())\n}\n\nfn single_curve_approximation(curve: &CubicBezierShape) -> QuadraticBezierShape {\n    let c1_x = (curve.points[1].x * 3.0 - curve.points[0].x) * 0.5;\n    let c1_y = (curve.points[1].y * 3.0 - curve.points[0].y) * 0.5;\n    let c2_x = (curve.points[2].x * 3.0 - curve.points[3].x) * 0.5;\n    let c2_y = (curve.points[2].y * 3.0 - curve.points[3].y) * 0.5;\n    let c = Pos2 {\n        x: (c1_x + c2_x) * 0.5,\n        y: (c1_y + c2_y) * 0.5,\n    };\n    QuadraticBezierShape {\n        points: [curve.points[0], c, curve.points[3]],\n        closed: curve.closed,\n        fill: curve.fill,\n        stroke: curve.stroke.clone(),\n    }\n}\n\nfn quadratic_for_each_local_extremum<F: FnMut(f32)>(p0: f32, p1: f32, p2: f32, cb: &mut F) {\n    // A quadratic Bézier curve can be derived by a linear function:\n    // p(t) = p0 + t(p1 - p0) + t^2(p2 - 2p1 + p0)\n    // The derivative is:\n    // p'(t) = (p1 - p0) + 2(p2 - 2p1 + p0)t or:\n    // f(x) = a* x + b\n    let a = p2 - 2.0 * p1 + p0;\n    // let b = p1 - p0;\n    // no need to check for zero, since we're only interested in local extrema\n    if a == 0.0 {\n        return;\n    }\n\n    let t = (p0 - p1) / a;\n    if t > 0.0 && t < 1.0 {\n        cb(t);\n    }\n}\n\nfn cubic_for_each_local_extremum<F: FnMut(f32)>(p0: f32, p1: f32, p2: f32, p3: f32, cb: &mut F) {\n    // See www.faculty.idc.ac.il/arik/quality/appendixa.html for an explanation\n    // A cubic Bézier curve can be derived by the following equation:\n    // B'(t) = 3(1-t)^2(p1-p0) + 6(1-t)t(p2-p1) + 3t^2(p3-p2) or\n    // f(x) = a * x² + b * x + c\n    let a = 3.0 * (p3 + 3.0 * (p1 - p2) - p0);\n    let b = 6.0 * (p2 - 2.0 * p1 + p0);\n    let c = 3.0 * (p1 - p0);\n\n    let in_range = |t: f32| t <= 1.0 && t >= 0.0;\n\n    // linear situation\n    if a == 0.0 {\n        if b != 0.0 {\n            let t = -c / b;\n            if in_range(t) {\n                cb(t);\n            }\n        }\n        return;\n    }\n\n    let discr = b * b - 4.0 * a * c;\n    // no Real solution\n    if discr < 0.0 {\n        return;\n    }\n\n    // one Real solution\n    if discr == 0.0 {\n        let t = -b / (2.0 * a);\n        if in_range(t) {\n            cb(t);\n        }\n        return;\n    }\n\n    // two Real solutions\n    let discr = discr.sqrt();\n    let t1 = (-b - discr) / (2.0 * a);\n    let t2 = (-b + discr) / (2.0 * a);\n    if in_range(t1) {\n        cb(t1);\n    }\n    if in_range(t2) {\n        cb(t2);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use emath::pos2;\n\n    use super::*;\n\n    #[test]\n    fn test_quadratic_bounding_box() {\n        let curve = QuadraticBezierShape {\n            points: [\n                Pos2 { x: 110.0, y: 170.0 },\n                Pos2 { x: 10.0, y: 10.0 },\n                Pos2 { x: 180.0, y: 30.0 },\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n        let bbox = curve.logical_bounding_rect();\n        assert!((bbox.min.x - 72.96).abs() < 0.01);\n        assert!((bbox.min.y - 27.78).abs() < 0.01);\n\n        assert!((bbox.max.x - 180.0).abs() < 0.01);\n        assert!((bbox.max.y - 170.0).abs() < 0.01);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.1, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 26);\n\n        let curve = QuadraticBezierShape {\n            points: [\n                Pos2 { x: 110.0, y: 170.0 },\n                Pos2 { x: 180.0, y: 30.0 },\n                Pos2 { x: 10.0, y: 10.0 },\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n        let bbox = curve.logical_bounding_rect();\n        assert!((bbox.min.x - 10.0).abs() < 0.01);\n        assert!((bbox.min.y - 10.0).abs() < 0.01);\n\n        assert!((bbox.max.x - 130.42).abs() < 0.01);\n        assert!((bbox.max.y - 170.0).abs() < 0.01);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.1, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 25);\n    }\n\n    #[test]\n    fn test_quadratic_different_tolerance() {\n        let curve = QuadraticBezierShape {\n            points: [\n                Pos2 { x: 110.0, y: 170.0 },\n                Pos2 { x: 180.0, y: 30.0 },\n                Pos2 { x: 10.0, y: 10.0 },\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(1.0, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 9);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.1, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 25);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.01, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 77);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.001, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 240);\n    }\n\n    #[test]\n    fn test_cubic_bounding_box() {\n        let curve = CubicBezierShape {\n            points: [\n                pos2(10.0, 10.0),\n                pos2(110.0, 170.0),\n                pos2(180.0, 30.0),\n                pos2(270.0, 210.0),\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let bbox = curve.logical_bounding_rect();\n        assert_eq!(bbox.min.x, 10.0);\n        assert_eq!(bbox.min.y, 10.0);\n        assert_eq!(bbox.max.x, 270.0);\n        assert_eq!(bbox.max.y, 210.0);\n\n        let curve = CubicBezierShape {\n            points: [\n                pos2(10.0, 10.0),\n                pos2(110.0, 170.0),\n                pos2(270.0, 210.0),\n                pos2(180.0, 30.0),\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let bbox = curve.logical_bounding_rect();\n        assert_eq!(bbox.min.x, 10.0);\n        assert_eq!(bbox.min.y, 10.0);\n        assert!((bbox.max.x - 206.50).abs() < 0.01);\n        assert!((bbox.max.y - 148.48).abs() < 0.01);\n\n        let curve = CubicBezierShape {\n            points: [\n                pos2(110.0, 170.0),\n                pos2(10.0, 10.0),\n                pos2(270.0, 210.0),\n                pos2(180.0, 30.0),\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let bbox = curve.logical_bounding_rect();\n        assert!((bbox.min.x - 86.71).abs() < 0.01);\n        assert!((bbox.min.y - 30.0).abs() < 0.01);\n\n        assert!((bbox.max.x - 199.27).abs() < 0.01);\n        assert!((bbox.max.y - 170.0).abs() < 0.01);\n    }\n\n    #[test]\n    fn test_cubic_different_tolerance_flattening() {\n        let curve = CubicBezierShape {\n            points: [\n                pos2(0.0, 0.0),\n                pos2(100.0, 0.0),\n                pos2(100.0, 100.0),\n                pos2(100.0, 200.0),\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(1.0, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 10);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.5, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 13);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.1, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 28);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.01, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 83);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.001, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 248);\n    }\n\n    #[test]\n    fn test_cubic_different_shape_flattening() {\n        let curve = CubicBezierShape {\n            points: [\n                pos2(90.0, 110.0),\n                pos2(30.0, 170.0),\n                pos2(210.0, 170.0),\n                pos2(170.0, 110.0),\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.01, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 117);\n\n        let curve = CubicBezierShape {\n            points: [\n                pos2(90.0, 110.0),\n                pos2(90.0, 170.0),\n                pos2(170.0, 170.0),\n                pos2(170.0, 110.0),\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.01, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 91);\n\n        let curve = CubicBezierShape {\n            points: [\n                pos2(90.0, 110.0),\n                pos2(110.0, 170.0),\n                pos2(150.0, 170.0),\n                pos2(170.0, 110.0),\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.01, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 75);\n\n        let curve = CubicBezierShape {\n            points: [\n                pos2(90.0, 110.0),\n                pos2(110.0, 170.0),\n                pos2(230.0, 110.0),\n                pos2(170.0, 110.0),\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.01, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 100);\n\n        let curve = CubicBezierShape {\n            points: [\n                pos2(90.0, 110.0),\n                pos2(110.0, 170.0),\n                pos2(210.0, 70.0),\n                pos2(170.0, 110.0),\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.01, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 71);\n\n        let curve = CubicBezierShape {\n            points: [\n                pos2(90.0, 110.0),\n                pos2(110.0, 170.0),\n                pos2(150.0, 50.0),\n                pos2(170.0, 110.0),\n            ],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.01, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 88);\n    }\n\n    #[test]\n    fn test_quadratic_flattening() {\n        let curve = QuadraticBezierShape {\n            points: [pos2(0.0, 0.0), pos2(80.0, 200.0), pos2(100.0, 30.0)],\n            closed: false,\n            fill: Default::default(),\n            stroke: Default::default(),\n        };\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(1.0, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 9);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.5, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 11);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.1, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 24);\n\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.01, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 72);\n        let mut result = vec![curve.points[0]]; //add the start point\n        curve.for_each_flattened_with_t(0.001, &mut |pos, _t| {\n            result.push(pos);\n        });\n\n        assert_eq!(result.len(), 223);\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/shapes/circle_shape.rs",
    "content": "use crate::{Color32, Pos2, Rect, Shape, Stroke, Vec2};\n\n/// How to paint a circle.\n#[derive(Copy, Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct CircleShape {\n    pub center: Pos2,\n    pub radius: f32,\n    pub fill: Color32,\n    pub stroke: Stroke,\n}\n\nimpl CircleShape {\n    #[inline]\n    pub fn filled(center: Pos2, radius: f32, fill_color: impl Into<Color32>) -> Self {\n        Self {\n            center,\n            radius,\n            fill: fill_color.into(),\n            stroke: Default::default(),\n        }\n    }\n\n    #[inline]\n    pub fn stroke(center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> Self {\n        Self {\n            center,\n            radius,\n            fill: Default::default(),\n            stroke: stroke.into(),\n        }\n    }\n\n    /// The visual bounding rectangle (includes stroke width)\n    pub fn visual_bounding_rect(&self) -> Rect {\n        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {\n            Rect::NOTHING\n        } else {\n            Rect::from_center_size(\n                self.center,\n                Vec2::splat(self.radius * 2.0 + self.stroke.width),\n            )\n        }\n    }\n}\n\nimpl From<CircleShape> for Shape {\n    #[inline(always)]\n    fn from(shape: CircleShape) -> Self {\n        Self::Circle(shape)\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/shapes/ellipse_shape.rs",
    "content": "use crate::*;\n\n/// How to paint an ellipse.\n#[derive(Copy, Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct EllipseShape {\n    pub center: Pos2,\n\n    /// Radius is the vector (a, b) where the width of the Ellipse is 2a and the height is 2b\n    pub radius: Vec2,\n    pub fill: Color32,\n    pub stroke: Stroke,\n}\n\nimpl EllipseShape {\n    #[inline]\n    pub fn filled(center: Pos2, radius: Vec2, fill_color: impl Into<Color32>) -> Self {\n        Self {\n            center,\n            radius,\n            fill: fill_color.into(),\n            stroke: Default::default(),\n        }\n    }\n\n    #[inline]\n    pub fn stroke(center: Pos2, radius: Vec2, stroke: impl Into<Stroke>) -> Self {\n        Self {\n            center,\n            radius,\n            fill: Default::default(),\n            stroke: stroke.into(),\n        }\n    }\n\n    /// The visual bounding rectangle (includes stroke width)\n    pub fn visual_bounding_rect(&self) -> Rect {\n        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {\n            Rect::NOTHING\n        } else {\n            Rect::from_center_size(\n                self.center,\n                self.radius * 2.0 + Vec2::splat(self.stroke.width),\n            )\n        }\n    }\n}\n\nimpl From<EllipseShape> for Shape {\n    #[inline(always)]\n    fn from(shape: EllipseShape) -> Self {\n        Self::Ellipse(shape)\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/shapes/mod.rs",
    "content": "mod bezier_shape;\nmod circle_shape;\nmod ellipse_shape;\nmod paint_callback;\nmod path_shape;\nmod rect_shape;\nmod shape;\nmod text_shape;\n\npub use self::{\n    bezier_shape::{CubicBezierShape, QuadraticBezierShape},\n    circle_shape::CircleShape,\n    ellipse_shape::EllipseShape,\n    paint_callback::{PaintCallback, PaintCallbackInfo},\n    path_shape::PathShape,\n    rect_shape::RectShape,\n    shape::Shape,\n    text_shape::TextShape,\n};\n"
  },
  {
    "path": "crates/epaint/src/shapes/paint_callback.rs",
    "content": "use std::{any::Any, sync::Arc};\n\nuse crate::*;\n\n/// Information passed along with [`PaintCallback`] ([`Shape::Callback`]).\npub struct PaintCallbackInfo {\n    /// Viewport in points.\n    ///\n    /// This specifies where on the screen to paint, and the borders of this\n    /// Rect is the [-1, +1] of the Normalized Device Coordinates.\n    ///\n    /// Note than only a portion of this may be visible due to [`Self::clip_rect`].\n    ///\n    /// This comes from [`PaintCallback::rect`].\n    pub viewport: Rect,\n\n    /// Clip rectangle in points.\n    pub clip_rect: Rect,\n\n    /// Pixels per point.\n    pub pixels_per_point: f32,\n\n    /// Full size of the screen, in pixels.\n    pub screen_size_px: [u32; 2],\n}\n\n#[test]\nfn test_viewport_rounding() {\n    for i in 0..=10_000 {\n        // Two adjacent viewports should never overlap:\n        let x = i as f32 / 97.0;\n        let left = Rect::from_min_max(pos2(0.0, 0.0), pos2(100.0, 100.0)).with_max_x(x);\n        let right = Rect::from_min_max(pos2(0.0, 0.0), pos2(100.0, 100.0)).with_min_x(x);\n\n        for pixels_per_point in [0.618, 1.0, std::f32::consts::PI] {\n            let left = ViewportInPixels::from_points(&left, pixels_per_point, [100, 100]);\n            let right = ViewportInPixels::from_points(&right, pixels_per_point, [100, 100]);\n            assert_eq!(left.left_px + left.width_px, right.left_px);\n        }\n    }\n}\n\nimpl PaintCallbackInfo {\n    /// The viewport rectangle. This is what you would use in e.g. `glViewport`.\n    pub fn viewport_in_pixels(&self) -> ViewportInPixels {\n        ViewportInPixels::from_points(&self.viewport, self.pixels_per_point, self.screen_size_px)\n    }\n\n    /// The \"scissor\" or \"clip\" rectangle. This is what you would use in e.g. `glScissor`.\n    pub fn clip_rect_in_pixels(&self) -> ViewportInPixels {\n        ViewportInPixels::from_points(&self.clip_rect, self.pixels_per_point, self.screen_size_px)\n    }\n}\n\n/// If you want to paint some 3D shapes inside an egui region, you can use this.\n///\n/// This is advanced usage, and is backend specific.\n#[derive(Clone)]\npub struct PaintCallback {\n    /// Where to paint.\n    ///\n    /// This will become [`PaintCallbackInfo::viewport`].\n    pub rect: Rect,\n\n    /// Paint something custom (e.g. 3D stuff).\n    ///\n    /// The concrete value of `callback` depends on the rendering backend used. For instance, the\n    /// `glow` backend requires that callback be an `egui_glow::CallbackFn` while the `wgpu`\n    /// backend requires a `egui_wgpu::Callback`.\n    ///\n    /// If the type cannot be downcast to the type expected by the current backend the callback\n    /// will not be drawn.\n    ///\n    /// The rendering backend is responsible for first setting the active viewport to\n    /// [`Self::rect`].\n    ///\n    /// The rendering backend is also responsible for restoring any state, such as the bound shader\n    /// program, vertex array, etc.\n    ///\n    /// Shape has to be clone, therefore this has to be an `Arc` instead of a `Box`.\n    pub callback: Arc<dyn Any + Send + Sync>,\n}\n\nimpl std::fmt::Debug for PaintCallback {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"CustomShape\")\n            .field(\"rect\", &self.rect)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl std::cmp::PartialEq for PaintCallback {\n    fn eq(&self, other: &Self) -> bool {\n        self.rect.eq(&other.rect) && Arc::ptr_eq(&self.callback, &other.callback)\n    }\n}\n\nimpl From<PaintCallback> for Shape {\n    #[inline(always)]\n    fn from(shape: PaintCallback) -> Self {\n        Self::Callback(shape)\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/shapes/path_shape.rs",
    "content": "use crate::*;\n\n/// A path which can be stroked and/or filled (if closed).\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct PathShape {\n    /// Filled paths should prefer clockwise order.\n    pub points: Vec<Pos2>,\n\n    /// If true, connect the first and last of the points together.\n    /// This is required if `fill != TRANSPARENT`.\n    pub closed: bool,\n\n    /// Fill is only supported for convex polygons.\n    pub fill: Color32,\n\n    /// Color and thickness of the line.\n    pub stroke: PathStroke,\n    // TODO(emilk): Add texture support either by supplying uv for each point,\n    // or by some transform from points to uv (e.g. a callback or a linear transform matrix).\n}\n\nimpl PathShape {\n    /// A line through many points.\n    ///\n    /// Use [`Shape::line_segment`] instead if your line only connects two points.\n    #[inline]\n    pub fn line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {\n        Self {\n            points,\n            closed: false,\n            fill: Default::default(),\n            stroke: stroke.into(),\n        }\n    }\n\n    /// A line that closes back to the start point again.\n    #[inline]\n    pub fn closed_line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {\n        Self {\n            points,\n            closed: true,\n            fill: Default::default(),\n            stroke: stroke.into(),\n        }\n    }\n\n    /// A convex polygon with a fill and optional stroke.\n    ///\n    /// The most performant winding order is clockwise.\n    #[inline]\n    pub fn convex_polygon(\n        points: Vec<Pos2>,\n        fill: impl Into<Color32>,\n        stroke: impl Into<PathStroke>,\n    ) -> Self {\n        Self {\n            points,\n            closed: true,\n            fill: fill.into(),\n            stroke: stroke.into(),\n        }\n    }\n\n    /// The visual bounding rectangle (includes stroke width)\n    #[inline]\n    pub fn visual_bounding_rect(&self) -> Rect {\n        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {\n            Rect::NOTHING\n        } else {\n            Rect::from_points(&self.points).expand(self.stroke.width / 2.0)\n        }\n    }\n}\n\nimpl From<PathShape> for Shape {\n    #[inline(always)]\n    fn from(shape: PathShape) -> Self {\n        Self::Path(shape)\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/shapes/rect_shape.rs",
    "content": "use std::sync::Arc;\n\nuse crate::*;\n\n/// How to paint a rectangle.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct RectShape {\n    pub rect: Rect,\n\n    /// How rounded the corners of the rectangle are.\n    ///\n    /// Use [`CornerRadius::ZERO`] for for sharp corners.\n    ///\n    /// This is the corner radii of the rectangle.\n    /// If there is a stroke, then the stroke will have an inner and outer corner radius,\n    /// and those will depend on [`StrokeKind`] and the stroke width.\n    ///\n    /// For [`StrokeKind::Inside`], the outside of the stroke coincides with the rectangle,\n    /// so the rounding will in this case specify the outer corner radius.\n    pub corner_radius: CornerRadius,\n\n    /// How to fill the rectangle.\n    pub fill: Color32,\n\n    /// The thickness and color of the outline.\n    ///\n    /// Whether or not the stroke is inside or outside the edge of [`Self::rect`],\n    /// is controlled by [`Self::stroke_kind`].\n    pub stroke: Stroke,\n\n    /// Is the stroke on the inside, outside, or centered on the rectangle?\n    ///\n    /// If you want to perfectly tile rectangles, use [`StrokeKind::Inside`].\n    pub stroke_kind: StrokeKind,\n\n    /// Snap the rectangle to pixels?\n    ///\n    /// Rounding produces sharper rectangles.\n    ///\n    /// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used.\n    pub round_to_pixels: Option<bool>,\n\n    /// If larger than zero, the edges of the rectangle\n    /// (for both fill and stroke) will be blurred.\n    ///\n    /// This can be used to produce shadows and glow effects.\n    ///\n    /// The blur is currently implemented using a simple linear blur in sRGBA gamma space.\n    pub blur_width: f32,\n\n    /// Controls texturing, if any.\n    ///\n    /// Since most rectangles do not have a texture, this is optional and in an `Arc`,\n    /// so that [`RectShape`] is kept small..\n    pub brush: Option<Arc<Brush>>,\n}\n\n#[test]\nfn rect_shape_size() {\n    assert_eq!(\n        std::mem::size_of::<RectShape>(),\n        48,\n        \"RectShape changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it.\"\n    );\n    assert!(\n        std::mem::size_of::<RectShape>() <= 64,\n        \"RectShape is getting way too big!\"\n    );\n}\n\nimpl RectShape {\n    /// See also [`Self::filled`] and [`Self::stroke`].\n    #[inline]\n    pub fn new(\n        rect: Rect,\n        corner_radius: impl Into<CornerRadius>,\n        fill_color: impl Into<Color32>,\n        stroke: impl Into<Stroke>,\n        stroke_kind: StrokeKind,\n    ) -> Self {\n        Self {\n            rect,\n            corner_radius: corner_radius.into(),\n            fill: fill_color.into(),\n            stroke: stroke.into(),\n            stroke_kind,\n            round_to_pixels: None,\n            blur_width: 0.0,\n            brush: Default::default(),\n        }\n    }\n\n    #[inline]\n    pub fn filled(\n        rect: Rect,\n        corner_radius: impl Into<CornerRadius>,\n        fill_color: impl Into<Color32>,\n    ) -> Self {\n        Self::new(\n            rect,\n            corner_radius,\n            fill_color,\n            Stroke::NONE,\n            StrokeKind::Outside, // doesn't matter\n        )\n    }\n\n    #[inline]\n    pub fn stroke(\n        rect: Rect,\n        corner_radius: impl Into<CornerRadius>,\n        stroke: impl Into<Stroke>,\n        stroke_kind: StrokeKind,\n    ) -> Self {\n        let fill = Color32::TRANSPARENT;\n        Self::new(rect, corner_radius, fill, stroke, stroke_kind)\n    }\n\n    /// Set if the stroke is on the inside, outside, or centered on the rectangle.\n    #[inline]\n    pub fn with_stroke_kind(mut self, stroke_kind: StrokeKind) -> Self {\n        self.stroke_kind = stroke_kind;\n        self\n    }\n\n    /// Snap the rectangle to pixels?\n    ///\n    /// Rounding produces sharper rectangles.\n    ///\n    /// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used.\n    #[inline]\n    pub fn with_round_to_pixels(mut self, round_to_pixels: bool) -> Self {\n        self.round_to_pixels = Some(round_to_pixels);\n        self\n    }\n\n    /// If larger than zero, the edges of the rectangle\n    /// (for both fill and stroke) will be blurred.\n    ///\n    /// This can be used to produce shadows and glow effects.\n    ///\n    /// The blur is currently implemented using a simple linear blur in `sRGBA` gamma space.\n    #[inline]\n    pub fn with_blur_width(mut self, blur_width: f32) -> Self {\n        self.blur_width = blur_width;\n        self\n    }\n\n    /// Set the texture to use when painting this rectangle, if any.\n    #[inline]\n    pub fn with_texture(mut self, fill_texture_id: TextureId, uv: Rect) -> Self {\n        self.brush = Some(Arc::new(Brush {\n            fill_texture_id,\n            uv,\n        }));\n        self\n    }\n\n    /// The visual bounding rectangle (includes stroke width)\n    #[inline]\n    pub fn visual_bounding_rect(&self) -> Rect {\n        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {\n            Rect::NOTHING\n        } else {\n            let expand = match self.stroke_kind {\n                StrokeKind::Inside => 0.0,\n                StrokeKind::Middle => self.stroke.width / 2.0,\n                StrokeKind::Outside => self.stroke.width,\n            };\n            self.rect.expand(expand + self.blur_width / 2.0)\n        }\n    }\n\n    /// The texture to use when painting this rectangle, if any.\n    ///\n    /// If no texture is set, this will return [`TextureId::default`].\n    pub fn fill_texture_id(&self) -> TextureId {\n        self.brush\n            .as_ref()\n            .map_or_else(TextureId::default, |brush| brush.fill_texture_id)\n    }\n}\n\nimpl From<RectShape> for Shape {\n    #[inline(always)]\n    fn from(shape: RectShape) -> Self {\n        Self::Rect(shape)\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/shapes/shape.rs",
    "content": "//! The different shapes that can be painted.\n\nuse std::sync::Arc;\n\nuse emath::{Align2, Pos2, Rangef, Rect, TSTransform, Vec2, pos2};\n\nuse crate::{\n    Color32, CornerRadius, Mesh, Stroke, StrokeKind, TextureId,\n    stroke::PathStroke,\n    text::{FontId, FontsView, Galley},\n};\n\nuse super::{\n    CircleShape, CubicBezierShape, EllipseShape, PaintCallback, PathShape, QuadraticBezierShape,\n    RectShape, TextShape,\n};\n\n/// A paint primitive such as a circle or a piece of text.\n/// Coordinates are all screen space points (not physical pixels).\n///\n/// You should generally recreate your [`Shape`]s each frame,\n/// but storing them should also be fine with one exception:\n/// [`Shape::Text`] depends on the current `pixels_per_point` (dpi scale)\n/// and so must be recreated every time `pixels_per_point` changes.\n#[must_use = \"Add a Shape to a Painter\"]\n#[derive(Clone, Debug, PartialEq)]\npub enum Shape {\n    /// Paint nothing. This can be useful as a placeholder.\n    Noop,\n\n    /// Recursively nest more shapes - sometimes a convenience to be able to do.\n    /// For performance reasons it is better to avoid it.\n    Vec(Vec<Self>),\n\n    /// Circle with optional outline and fill.\n    Circle(CircleShape),\n\n    /// Ellipse with optional outline and fill.\n    Ellipse(EllipseShape),\n\n    /// A line between two points.\n    LineSegment { points: [Pos2; 2], stroke: Stroke },\n\n    /// A series of lines between points.\n    /// The path can have a stroke and/or fill (if closed).\n    Path(PathShape),\n\n    /// Rectangle with optional outline and fill.\n    Rect(RectShape),\n\n    /// Text.\n    ///\n    /// This needs to be recreated if `pixels_per_point` (dpi scale) changes.\n    Text(TextShape),\n\n    /// A general triangle mesh.\n    ///\n    /// Can be used to display images.\n    ///\n    /// Wrapped in an [`Arc`] to minimize the size of [`Shape`].\n    Mesh(Arc<Mesh>),\n\n    /// A quadratic [Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).\n    QuadraticBezier(QuadraticBezierShape),\n\n    /// A cubic [Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).\n    CubicBezier(CubicBezierShape),\n\n    /// Backend-specific painting.\n    Callback(PaintCallback),\n}\n\n#[test]\nfn shape_size() {\n    assert_eq!(\n        std::mem::size_of::<Shape>(),\n        64,\n        \"Shape changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it.\"\n    );\n    assert!(\n        std::mem::size_of::<Shape>() <= 64,\n        \"Shape is getting way too big!\"\n    );\n}\n\n#[test]\nfn shape_impl_send_sync() {\n    fn assert_send_sync<T: Send + Sync>() {}\n    assert_send_sync::<Shape>();\n}\n\nimpl From<Vec<Self>> for Shape {\n    #[inline(always)]\n    fn from(shapes: Vec<Self>) -> Self {\n        Self::Vec(shapes)\n    }\n}\n\nimpl From<Mesh> for Shape {\n    #[inline(always)]\n    fn from(mesh: Mesh) -> Self {\n        Self::Mesh(mesh.into())\n    }\n}\n\nimpl From<Arc<Mesh>> for Shape {\n    #[inline(always)]\n    fn from(mesh: Arc<Mesh>) -> Self {\n        Self::Mesh(mesh)\n    }\n}\n\n/// ## Constructors\nimpl Shape {\n    /// A line between two points.\n    /// More efficient than calling [`Self::line`].\n    #[inline]\n    pub fn line_segment(points: [Pos2; 2], stroke: impl Into<Stroke>) -> Self {\n        Self::LineSegment {\n            points,\n            stroke: stroke.into(),\n        }\n    }\n\n    /// A horizontal line.\n    pub fn hline(x: impl Into<Rangef>, y: f32, stroke: impl Into<Stroke>) -> Self {\n        let x = x.into();\n        Self::LineSegment {\n            points: [pos2(x.min, y), pos2(x.max, y)],\n            stroke: stroke.into(),\n        }\n    }\n\n    /// A vertical line.\n    pub fn vline(x: f32, y: impl Into<Rangef>, stroke: impl Into<Stroke>) -> Self {\n        let y = y.into();\n        Self::LineSegment {\n            points: [pos2(x, y.min), pos2(x, y.max)],\n            stroke: stroke.into(),\n        }\n    }\n\n    /// A line through many points.\n    ///\n    /// Use [`Self::line_segment`] instead if your line only connects two points.\n    #[inline]\n    pub fn line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {\n        Self::Path(PathShape::line(points, stroke))\n    }\n\n    /// A line that closes back to the start point again.\n    #[inline]\n    pub fn closed_line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {\n        Self::Path(PathShape::closed_line(points, stroke))\n    }\n\n    /// Turn a line into equally spaced dots.\n    pub fn dotted_line(\n        path: &[Pos2],\n        color: impl Into<Color32>,\n        spacing: f32,\n        radius: f32,\n    ) -> Vec<Self> {\n        let mut shapes = Vec::new();\n        points_from_line(path, spacing, radius, color.into(), &mut shapes);\n        shapes\n    }\n\n    /// Turn a line into dashes.\n    pub fn dashed_line(\n        path: &[Pos2],\n        stroke: impl Into<Stroke>,\n        dash_length: f32,\n        gap_length: f32,\n    ) -> Vec<Self> {\n        let mut shapes = Vec::new();\n        dashes_from_line(\n            path,\n            stroke.into(),\n            &[dash_length],\n            &[gap_length],\n            &mut shapes,\n            0.,\n        );\n        shapes\n    }\n\n    /// Turn a line into dashes with different dash/gap lengths and a start offset.\n    pub fn dashed_line_with_offset(\n        path: &[Pos2],\n        stroke: impl Into<Stroke>,\n        dash_lengths: &[f32],\n        gap_lengths: &[f32],\n        dash_offset: f32,\n    ) -> Vec<Self> {\n        let mut shapes = Vec::new();\n        dashes_from_line(\n            path,\n            stroke.into(),\n            dash_lengths,\n            gap_lengths,\n            &mut shapes,\n            dash_offset,\n        );\n        shapes\n    }\n\n    /// Turn a line into dashes. If you need to create many dashed lines use this instead of\n    /// [`Self::dashed_line`].\n    pub fn dashed_line_many(\n        points: &[Pos2],\n        stroke: impl Into<Stroke>,\n        dash_length: f32,\n        gap_length: f32,\n        shapes: &mut Vec<Self>,\n    ) {\n        dashes_from_line(\n            points,\n            stroke.into(),\n            &[dash_length],\n            &[gap_length],\n            shapes,\n            0.,\n        );\n    }\n\n    /// Turn a line into dashes with different dash/gap lengths and a start offset. If you need to\n    /// create many dashed lines use this instead of [`Self::dashed_line_with_offset`].\n    pub fn dashed_line_many_with_offset(\n        points: &[Pos2],\n        stroke: impl Into<Stroke>,\n        dash_lengths: &[f32],\n        gap_lengths: &[f32],\n        dash_offset: f32,\n        shapes: &mut Vec<Self>,\n    ) {\n        dashes_from_line(\n            points,\n            stroke.into(),\n            dash_lengths,\n            gap_lengths,\n            shapes,\n            dash_offset,\n        );\n    }\n\n    /// A convex polygon with a fill and optional stroke.\n    ///\n    /// The most performant winding order is clockwise.\n    #[inline]\n    pub fn convex_polygon(\n        points: Vec<Pos2>,\n        fill: impl Into<Color32>,\n        stroke: impl Into<PathStroke>,\n    ) -> Self {\n        Self::Path(PathShape::convex_polygon(points, fill, stroke))\n    }\n\n    #[inline]\n    pub fn circle_filled(center: Pos2, radius: f32, fill_color: impl Into<Color32>) -> Self {\n        Self::Circle(CircleShape::filled(center, radius, fill_color))\n    }\n\n    #[inline]\n    pub fn circle_stroke(center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> Self {\n        Self::Circle(CircleShape::stroke(center, radius, stroke))\n    }\n\n    #[inline]\n    pub fn ellipse_filled(center: Pos2, radius: Vec2, fill_color: impl Into<Color32>) -> Self {\n        Self::Ellipse(EllipseShape::filled(center, radius, fill_color))\n    }\n\n    #[inline]\n    pub fn ellipse_stroke(center: Pos2, radius: Vec2, stroke: impl Into<Stroke>) -> Self {\n        Self::Ellipse(EllipseShape::stroke(center, radius, stroke))\n    }\n\n    /// See also [`Self::rect_stroke`].\n    #[inline]\n    pub fn rect_filled(\n        rect: Rect,\n        corner_radius: impl Into<CornerRadius>,\n        fill_color: impl Into<Color32>,\n    ) -> Self {\n        Self::Rect(RectShape::filled(rect, corner_radius, fill_color))\n    }\n\n    /// See also [`Self::rect_filled`].\n    #[inline]\n    pub fn rect_stroke(\n        rect: Rect,\n        corner_radius: impl Into<CornerRadius>,\n        stroke: impl Into<Stroke>,\n        stroke_kind: StrokeKind,\n    ) -> Self {\n        Self::Rect(RectShape::stroke(rect, corner_radius, stroke, stroke_kind))\n    }\n\n    #[expect(clippy::needless_pass_by_value)]\n    pub fn text(\n        fonts: &mut FontsView<'_>,\n        pos: Pos2,\n        anchor: Align2,\n        text: impl ToString,\n        font_id: FontId,\n        color: Color32,\n    ) -> Self {\n        let galley = fonts.layout_no_wrap(text.to_string(), font_id, color);\n        let rect = anchor.anchor_size(pos, galley.size());\n        Self::galley(rect.min, galley, color)\n    }\n\n    /// Any uncolored parts of the [`Galley`] (using [`Color32::PLACEHOLDER`]) will be replaced with the given color.\n    ///\n    /// Any non-placeholder color in the galley takes precedence over this fallback color.\n    #[inline]\n    pub fn galley(pos: Pos2, galley: Arc<Galley>, fallback_color: Color32) -> Self {\n        TextShape::new(pos, galley, fallback_color).into()\n    }\n\n    /// All text color in the [`Galley`] will be replaced with the given color.\n    #[inline]\n    pub fn galley_with_override_text_color(\n        pos: Pos2,\n        galley: Arc<Galley>,\n        text_color: Color32,\n    ) -> Self {\n        TextShape::new(pos, galley, text_color)\n            .with_override_text_color(text_color)\n            .into()\n    }\n\n    #[inline]\n    #[deprecated = \"Use `Shape::galley` or `Shape::galley_with_override_text_color` instead\"]\n    pub fn galley_with_color(pos: Pos2, galley: Arc<Galley>, text_color: Color32) -> Self {\n        Self::galley_with_override_text_color(pos, galley, text_color)\n    }\n\n    #[inline]\n    pub fn mesh(mesh: impl Into<Arc<Mesh>>) -> Self {\n        let mesh = mesh.into();\n        debug_assert!(mesh.is_valid(), \"Invalid mesh: {mesh:#?}\");\n        Self::Mesh(mesh)\n    }\n\n    /// An image at the given position.\n    ///\n    /// `uv` should normally be `Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0))`\n    /// unless you want to crop or flip the image.\n    ///\n    /// `tint` is a color multiplier. Use [`Color32::WHITE`] if you don't want to tint the image.\n    pub fn image(texture_id: TextureId, rect: Rect, uv: Rect, tint: Color32) -> Self {\n        let mut mesh = Mesh::with_texture(texture_id);\n        mesh.add_rect_with_uv(rect, uv, tint);\n        Self::mesh(mesh)\n    }\n\n    /// The visual bounding rectangle (includes stroke widths)\n    pub fn visual_bounding_rect(&self) -> Rect {\n        match self {\n            Self::Noop => Rect::NOTHING,\n            Self::Vec(shapes) => {\n                let mut rect = Rect::NOTHING;\n                for shape in shapes {\n                    rect |= shape.visual_bounding_rect();\n                }\n                rect\n            }\n            Self::Circle(circle_shape) => circle_shape.visual_bounding_rect(),\n            Self::Ellipse(ellipse_shape) => ellipse_shape.visual_bounding_rect(),\n            Self::LineSegment { points, stroke } => {\n                if stroke.is_empty() {\n                    Rect::NOTHING\n                } else {\n                    Rect::from_two_pos(points[0], points[1]).expand(stroke.width / 2.0)\n                }\n            }\n            Self::Path(path_shape) => path_shape.visual_bounding_rect(),\n            Self::Rect(rect_shape) => rect_shape.visual_bounding_rect(),\n            Self::Text(text_shape) => text_shape.visual_bounding_rect(),\n            Self::Mesh(mesh) => mesh.calc_bounds(),\n            Self::QuadraticBezier(bezier) => bezier.visual_bounding_rect(),\n            Self::CubicBezier(bezier) => bezier.visual_bounding_rect(),\n            Self::Callback(custom) => custom.rect,\n        }\n    }\n}\n\n/// ## Inspection and transforms\nimpl Shape {\n    #[inline(always)]\n    pub fn texture_id(&self) -> crate::TextureId {\n        if let Self::Mesh(mesh) = self {\n            mesh.texture_id\n        } else if let Self::Rect(rect_shape) = self {\n            rect_shape.fill_texture_id()\n        } else {\n            crate::TextureId::default()\n        }\n    }\n\n    /// Scale the shape by `factor`, in-place.\n    ///\n    /// A wrapper around [`Self::transform`].\n    #[inline(always)]\n    pub fn scale(&mut self, factor: f32) {\n        self.transform(TSTransform::from_scaling(factor));\n    }\n\n    /// Move the shape by `delta`, in-place.\n    ///\n    /// A wrapper around [`Self::transform`].\n    #[inline(always)]\n    pub fn translate(&mut self, delta: Vec2) {\n        self.transform(TSTransform::from_translation(delta));\n    }\n\n    /// Transform (move/scale) the shape in-place.\n    ///\n    /// If using a [`PaintCallback`], note that only the rect is scaled as opposed\n    /// to other shapes where the stroke is also scaled.\n    pub fn transform(&mut self, transform: TSTransform) {\n        match self {\n            Self::Noop => {}\n            Self::Vec(shapes) => {\n                for shape in shapes {\n                    shape.transform(transform);\n                }\n            }\n            Self::Circle(circle_shape) => {\n                circle_shape.center = transform * circle_shape.center;\n                circle_shape.radius *= transform.scaling;\n                circle_shape.stroke.width *= transform.scaling;\n            }\n            Self::Ellipse(ellipse_shape) => {\n                ellipse_shape.center = transform * ellipse_shape.center;\n                ellipse_shape.radius *= transform.scaling;\n                ellipse_shape.stroke.width *= transform.scaling;\n            }\n            Self::LineSegment { points, stroke } => {\n                for p in points {\n                    *p = transform * *p;\n                }\n                stroke.width *= transform.scaling;\n            }\n            Self::Path(path_shape) => {\n                for p in &mut path_shape.points {\n                    *p = transform * *p;\n                }\n                path_shape.stroke.width *= transform.scaling;\n            }\n            Self::Rect(rect_shape) => {\n                rect_shape.rect = transform * rect_shape.rect;\n                rect_shape.corner_radius *= transform.scaling;\n                rect_shape.stroke.width *= transform.scaling;\n                rect_shape.blur_width *= transform.scaling;\n            }\n            Self::Text(text_shape) => {\n                text_shape.transform(transform);\n            }\n            Self::Mesh(mesh) => {\n                Arc::make_mut(mesh).transform(transform);\n            }\n            Self::QuadraticBezier(bezier) => {\n                for p in &mut bezier.points {\n                    *p = transform * *p;\n                }\n                bezier.stroke.width *= transform.scaling;\n            }\n            Self::CubicBezier(bezier) => {\n                for p in &mut bezier.points {\n                    *p = transform * *p;\n                }\n                bezier.stroke.width *= transform.scaling;\n            }\n            Self::Callback(shape) => {\n                shape.rect = transform * shape.rect;\n            }\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Creates equally spaced filled circles from a line.\nfn points_from_line(\n    path: &[Pos2],\n    spacing: f32,\n    radius: f32,\n    color: Color32,\n    shapes: &mut Vec<Shape>,\n) {\n    let mut position_on_segment = 0.0;\n    for window in path.windows(2) {\n        let (start, end) = (window[0], window[1]);\n        let vector = end - start;\n        let segment_length = vector.length();\n        while position_on_segment < segment_length {\n            let new_point = start + vector * (position_on_segment / segment_length);\n            shapes.push(Shape::circle_filled(new_point, radius, color));\n            position_on_segment += spacing;\n        }\n        position_on_segment -= segment_length;\n    }\n}\n\n/// Creates dashes from a line.\nfn dashes_from_line(\n    path: &[Pos2],\n    stroke: Stroke,\n    dash_lengths: &[f32],\n    gap_lengths: &[f32],\n    shapes: &mut Vec<Shape>,\n    dash_offset: f32,\n) {\n    assert_eq!(\n        dash_lengths.len(),\n        gap_lengths.len(),\n        \"Mismatched dash and gap lengths, got dash_lengths: {}, gap_lengths: {}\",\n        dash_lengths.len(),\n        gap_lengths.len()\n    );\n    let mut position_on_segment = dash_offset;\n    let mut drawing_dash = false;\n    let mut step = 0;\n    let steps = dash_lengths.len();\n    for window in path.windows(2) {\n        let (start, end) = (window[0], window[1]);\n        let vector = end - start;\n        let segment_length = vector.length();\n\n        let mut start_point = start;\n        while position_on_segment < segment_length {\n            let new_point = start + vector * (position_on_segment / segment_length);\n            if drawing_dash {\n                // This is the end point.\n                shapes.push(Shape::line_segment([start_point, new_point], stroke));\n                position_on_segment += gap_lengths[step];\n                // Increment step counter\n                step += 1;\n                if step >= steps {\n                    step = 0;\n                }\n            } else {\n                // Start a new dash.\n                start_point = new_point;\n                position_on_segment += dash_lengths[step];\n            }\n            drawing_dash = !drawing_dash;\n        }\n\n        // If the segment ends and the dash is not finished, add the segment's end point.\n        if drawing_dash {\n            shapes.push(Shape::line_segment([start_point, end], stroke));\n        }\n\n        position_on_segment -= segment_length;\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/shapes/text_shape.rs",
    "content": "use std::sync::Arc;\n\nuse emath::{Align2, Rot2};\n\nuse crate::*;\n\n/// How to paint some text on screen.\n///\n/// This needs to be recreated if `pixels_per_point` (dpi scale) changes.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct TextShape {\n    /// Where the origin of [`Self::galley`] is.\n    ///\n    /// Usually the top left corner of the first character.\n    pub pos: Pos2,\n\n    /// The laid out text, from [`FontsView::layout_job`].\n    pub galley: Arc<Galley>,\n\n    /// Add this underline to the whole text.\n    /// You can also set an underline when creating the galley.\n    pub underline: Stroke,\n\n    /// Any [`Color32::PLACEHOLDER`] in the galley will be replaced by the given color.\n    /// Affects everything: backgrounds, glyphs, strikethrough, underline, etc.\n    pub fallback_color: Color32,\n\n    /// If set, the text color in the galley will be ignored and replaced\n    /// with the given color.\n    ///\n    /// This only affects the glyphs and will NOT replace background color nor strikethrough/underline color.\n    pub override_text_color: Option<Color32>,\n\n    /// If set, the text will be rendered with the given opacity in gamma space\n    /// Affects everything: backgrounds, glyphs, strikethrough, underline, etc.\n    pub opacity_factor: f32,\n\n    /// Rotate text by this many radians clockwise.\n    /// The pivot is `pos` (the upper left corner of the text).\n    pub angle: f32,\n}\n\nimpl TextShape {\n    /// The given fallback color will be used for any uncolored part of the galley (using [`Color32::PLACEHOLDER`]).\n    ///\n    /// Any non-placeholder color in the galley takes precedence over this fallback color.\n    #[inline]\n    pub fn new(pos: Pos2, galley: Arc<Galley>, fallback_color: Color32) -> Self {\n        Self {\n            pos,\n            galley,\n            underline: Stroke::NONE,\n            fallback_color,\n            override_text_color: None,\n            opacity_factor: 1.0,\n            angle: 0.0,\n        }\n    }\n\n    /// The visual bounding rectangle\n    #[inline]\n    pub fn visual_bounding_rect(&self) -> Rect {\n        self.galley\n            .mesh_bounds\n            .rotate_bb(emath::Rot2::from_angle(self.angle))\n            .translate(self.pos.to_vec2())\n    }\n\n    #[inline]\n    pub fn with_underline(mut self, underline: Stroke) -> Self {\n        self.underline = underline;\n        self\n    }\n\n    /// Use the given color for the text, regardless of what color is already in the galley.\n    #[inline]\n    pub fn with_override_text_color(mut self, override_text_color: Color32) -> Self {\n        self.override_text_color = Some(override_text_color);\n        self\n    }\n\n    /// Set text rotation to `angle` radians clockwise.\n    /// The pivot is `pos` (the upper left corner of the text).\n    #[inline]\n    pub fn with_angle(mut self, angle: f32) -> Self {\n        self.angle = angle;\n        self\n    }\n\n    /// Set the text rotation to the `angle` radians clockwise.\n    /// The pivot is determined by the given `anchor` point on the text bounding box.\n    #[inline]\n    pub fn with_angle_and_anchor(mut self, angle: f32, anchor: Align2) -> Self {\n        self.angle = angle;\n        let a0 = anchor.pos_in_rect(&self.galley.rect).to_vec2();\n        let a1 = Rot2::from_angle(angle) * a0;\n        self.pos += a0 - a1;\n        self\n    }\n\n    /// Render text with this opacity in gamma space\n    #[inline]\n    pub fn with_opacity_factor(mut self, opacity_factor: f32) -> Self {\n        self.opacity_factor = opacity_factor;\n        self\n    }\n\n    /// Move the shape by this many points, in-place.\n    pub fn transform(&mut self, transform: emath::TSTransform) {\n        let Self {\n            pos,\n            galley,\n            underline,\n            fallback_color: _,\n            override_text_color: _,\n            opacity_factor: _,\n            angle: _,\n        } = self;\n\n        *pos = transform * *pos;\n        underline.width *= transform.scaling;\n\n        let Galley {\n            job: _,\n            rows,\n            elided: _,\n            rect,\n            mesh_bounds,\n            num_vertices: _,\n            num_indices: _,\n            pixels_per_point: _,\n            intrinsic_size,\n        } = Arc::make_mut(galley);\n\n        *rect = transform.scaling * *rect;\n        *mesh_bounds = transform.scaling * *mesh_bounds;\n        *intrinsic_size = transform.scaling * *intrinsic_size;\n\n        for text::PlacedRow {\n            pos,\n            row,\n            ends_with_newline: _,\n        } in rows\n        {\n            *pos *= transform.scaling;\n\n            let text::Row {\n                section_index_at_start: _,\n                glyphs: _, // TODO(emilk): would it make sense to transform these?\n                size,\n                visuals,\n            } = Arc::make_mut(row);\n\n            *size *= transform.scaling;\n\n            let text::RowVisuals {\n                mesh,\n                mesh_bounds,\n                glyph_index_start: _,\n                glyph_vertex_range: _,\n            } = visuals;\n\n            *mesh_bounds = transform.scaling * *mesh_bounds;\n\n            for v in &mut mesh.vertices {\n                v.pos *= transform.scaling;\n            }\n        }\n    }\n}\n\nimpl From<TextShape> for Shape {\n    #[inline(always)]\n    fn from(shape: TextShape) -> Self {\n        Self::Text(shape)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{super::*, *};\n    use crate::text::FontDefinitions;\n    use emath::almost_equal;\n\n    #[test]\n    fn text_bounding_box_under_rotation() {\n        let mut fonts = Fonts::new(TextOptions::default(), FontDefinitions::default());\n        let font = FontId::monospace(12.0);\n\n        let mut t = crate::Shape::text(\n            &mut fonts.with_pixels_per_point(1.0),\n            Pos2::ZERO,\n            emath::Align2::CENTER_CENTER,\n            \"testing123\",\n            font,\n            Color32::BLACK,\n        );\n\n        let size_orig = t.visual_bounding_rect().size();\n\n        // 90 degree rotation\n        if let Shape::Text(ts) = &mut t {\n            ts.angle = std::f32::consts::PI / 2.0;\n        }\n\n        let size_rot = t.visual_bounding_rect().size();\n\n        // make sure the box is actually rotated\n        assert!(almost_equal(size_orig.x, size_rot.y, 1e-4));\n        assert!(almost_equal(size_orig.y, size_rot.x, 1e-4));\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/stats.rs",
    "content": "//! Collect statistics about what is being painted.\n\nuse crate::{ClippedShape, Galley, Mesh, Primitive, Shape};\n\n/// Size of the elements in a vector/array.\n#[derive(Clone, Copy, Default, PartialEq)]\nenum ElementSize {\n    #[default]\n    Unknown,\n    Homogeneous(usize),\n    Heterogenous,\n}\n\n/// Aggregate information about a bunch of allocations.\n#[derive(Clone, Copy, Default, PartialEq)]\npub struct AllocInfo {\n    element_size: ElementSize,\n    num_allocs: usize,\n    num_elements: usize,\n    num_bytes: usize,\n}\n\nimpl<T> From<&[T]> for AllocInfo {\n    fn from(slice: &[T]) -> Self {\n        Self::from_slice(slice)\n    }\n}\n\nimpl std::ops::Add for AllocInfo {\n    type Output = Self;\n\n    fn add(self, rhs: Self) -> Self {\n        use ElementSize::{Heterogenous, Homogeneous, Unknown};\n        let element_size = match (self.element_size, rhs.element_size) {\n            (Heterogenous, _) | (_, Heterogenous) => Heterogenous,\n            (Unknown, other) | (other, Unknown) => other,\n            (Homogeneous(lhs), Homogeneous(rhs)) if lhs == rhs => Homogeneous(lhs),\n            _ => Heterogenous,\n        };\n\n        Self {\n            element_size,\n            num_allocs: self.num_allocs + rhs.num_allocs,\n            num_elements: self.num_elements + rhs.num_elements,\n            num_bytes: self.num_bytes + rhs.num_bytes,\n        }\n    }\n}\n\nimpl std::ops::AddAssign for AllocInfo {\n    fn add_assign(&mut self, rhs: Self) {\n        *self = *self + rhs;\n    }\n}\n\nimpl std::iter::Sum for AllocInfo {\n    fn sum<I>(iter: I) -> Self\n    where\n        I: Iterator<Item = Self>,\n    {\n        let mut sum = Self::default();\n        for value in iter {\n            sum += value;\n        }\n        sum\n    }\n}\n\nimpl AllocInfo {\n    // pub fn from_shape(shape: &Shape) -> Self {\n    //     match shape {\n    //         Shape::Noop\n    //         Shape::Vec(shapes) => Self::from_shapes(shapes)\n    //         | Shape::Circle { .. }\n    //         | Shape::LineSegment { .. }\n    //         | Shape::Rect { .. } => Self::default(),\n    //         Shape::Path { points, .. } => Self::from_slice(points),\n    //         Shape::Text { galley, .. } => Self::from_galley(galley),\n    //         Shape::Mesh(mesh) => Self::from_mesh(mesh),\n    //     }\n    // }\n\n    pub fn from_galley(galley: &Galley) -> Self {\n        Self::from_slice(galley.text().as_bytes())\n            + Self::from_slice(&galley.rows)\n            + galley.rows.iter().map(Self::from_galley_row).sum()\n    }\n\n    fn from_galley_row(row: &crate::text::PlacedRow) -> Self {\n        Self::from_mesh(&row.visuals.mesh) + Self::from_slice(&row.glyphs)\n    }\n\n    pub fn from_mesh(mesh: &Mesh) -> Self {\n        Self::from_slice(&mesh.indices) + Self::from_slice(&mesh.vertices)\n    }\n\n    pub fn from_slice<T>(slice: &[T]) -> Self {\n        use std::mem::size_of;\n        let element_size = size_of::<T>();\n        Self {\n            element_size: ElementSize::Homogeneous(element_size),\n            num_allocs: 1,\n            num_elements: slice.len(),\n            num_bytes: std::mem::size_of_val(slice),\n        }\n    }\n\n    pub fn num_elements(&self) -> usize {\n        assert!(\n            self.element_size != ElementSize::Heterogenous,\n            \"Heterogenous element size\"\n        );\n        self.num_elements\n    }\n\n    pub fn num_allocs(&self) -> usize {\n        self.num_allocs\n    }\n\n    pub fn num_bytes(&self) -> usize {\n        self.num_bytes\n    }\n\n    pub fn megabytes(&self) -> String {\n        megabytes(self.num_bytes())\n    }\n\n    pub fn format(&self, what: &str) -> String {\n        if self.num_allocs() == 0 {\n            format!(\"{:6} {:16}\", 0, what)\n        } else if self.num_allocs() == 1 {\n            format!(\n                \"{:6} {:16}  {}       1 allocation\",\n                self.num_elements,\n                what,\n                self.megabytes()\n            )\n        } else if self.element_size != ElementSize::Heterogenous {\n            format!(\n                \"{:6} {:16}  {}     {:3} allocations\",\n                self.num_elements(),\n                what,\n                self.megabytes(),\n                self.num_allocs()\n            )\n        } else {\n            format!(\n                \"{:6} {:16}  {}     {:3} allocations\",\n                \"\",\n                what,\n                self.megabytes(),\n                self.num_allocs()\n            )\n        }\n    }\n}\n\n/// Collected allocation statistics for shapes and meshes.\n#[derive(Clone, Copy, Default)]\npub struct PaintStats {\n    pub shapes: AllocInfo,\n    pub shape_text: AllocInfo,\n    pub shape_path: AllocInfo,\n    pub shape_mesh: AllocInfo,\n    pub shape_vec: AllocInfo,\n    pub num_callbacks: usize,\n\n    pub text_shape_vertices: AllocInfo,\n    pub text_shape_indices: AllocInfo,\n\n    /// Number of separate clip rectangles\n    pub clipped_primitives: AllocInfo,\n    pub vertices: AllocInfo,\n    pub indices: AllocInfo,\n}\n\nimpl PaintStats {\n    pub fn from_shapes(shapes: &[ClippedShape]) -> Self {\n        let mut stats = Self::default();\n        stats.shape_path.element_size = ElementSize::Heterogenous; // nicer display later\n        stats.shape_vec.element_size = ElementSize::Heterogenous; // nicer display later\n\n        stats.shapes = AllocInfo::from_slice(shapes);\n        for ClippedShape { shape, .. } in shapes {\n            stats.add(shape);\n        }\n        stats\n    }\n\n    fn add(&mut self, shape: &Shape) {\n        match shape {\n            Shape::Vec(shapes) => {\n                // self += PaintStats::from_shapes(&shapes); // TODO(emilk)\n                self.shapes += AllocInfo::from_slice(shapes);\n                self.shape_vec += AllocInfo::from_slice(shapes);\n                for shape in shapes {\n                    self.add(shape);\n                }\n            }\n            Shape::Noop\n            | Shape::Circle { .. }\n            | Shape::Ellipse { .. }\n            | Shape::LineSegment { .. }\n            | Shape::Rect { .. }\n            | Shape::CubicBezier(_)\n            | Shape::QuadraticBezier(_) => {}\n            Shape::Path(path_shape) => {\n                self.shape_path += AllocInfo::from_slice(&path_shape.points);\n            }\n            Shape::Text(text_shape) => {\n                self.shape_text += AllocInfo::from_galley(&text_shape.galley);\n\n                for row in &text_shape.galley.rows {\n                    self.text_shape_indices += AllocInfo::from_slice(&row.visuals.mesh.indices);\n                    self.text_shape_vertices += AllocInfo::from_slice(&row.visuals.mesh.vertices);\n                }\n            }\n            Shape::Mesh(mesh) => {\n                self.shape_mesh += AllocInfo::from_mesh(mesh);\n            }\n            Shape::Callback(_) => {\n                self.num_callbacks += 1;\n            }\n        }\n    }\n\n    pub fn with_clipped_primitives(\n        mut self,\n        clipped_primitives: &[crate::ClippedPrimitive],\n    ) -> Self {\n        self.clipped_primitives += AllocInfo::from_slice(clipped_primitives);\n        for clipped_primitive in clipped_primitives {\n            if let Primitive::Mesh(mesh) = &clipped_primitive.primitive {\n                self.vertices += AllocInfo::from_slice(&mesh.vertices);\n                self.indices += AllocInfo::from_slice(&mesh.indices);\n            }\n        }\n        self\n    }\n}\n\nfn megabytes(size: usize) -> String {\n    format!(\"{:.2} MB\", size as f64 / 1e6)\n}\n"
  },
  {
    "path": "crates/epaint/src/stroke.rs",
    "content": "use std::{fmt::Debug, sync::Arc};\n\nuse emath::GuiRounding as _;\n\nuse super::{Color32, ColorMode, Pos2, Rect, emath};\n\n/// Describes the width and color of a line.\n///\n/// The default stroke is the same as [`Stroke::NONE`].\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Stroke {\n    pub width: f32,\n    pub color: Color32,\n}\n\nimpl Stroke {\n    /// Same as [`Stroke::default`].\n    pub const NONE: Self = Self {\n        width: 0.0,\n        color: Color32::TRANSPARENT,\n    };\n\n    #[inline]\n    pub fn new(width: impl Into<f32>, color: impl Into<Color32>) -> Self {\n        Self {\n            width: width.into(),\n            color: color.into(),\n        }\n    }\n\n    /// True if width is zero or color is transparent\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.width <= 0.0 || self.color == Color32::TRANSPARENT\n    }\n\n    /// For vertical or horizontal lines:\n    /// round the stroke center to produce a sharp, pixel-aligned line.\n    pub fn round_center_to_pixel(&self, pixels_per_point: f32, coord: &mut f32) {\n        // If the stroke is an odd number of pixels wide,\n        // we want to round the center of it to the center of a pixel.\n        //\n        // If however it is an even number of pixels wide,\n        // we want to round the center to be between two pixels.\n        //\n        // We also want to treat strokes that are _almost_ odd as it it was odd,\n        // to make it symmetric. Same for strokes that are _almost_ even.\n        //\n        // For strokes less than a pixel wide we also round to the center,\n        // because it will rendered as a single row of pixels by the tessellator.\n\n        let pixel_size = 1.0 / pixels_per_point;\n\n        if self.width <= pixel_size || is_nearest_integer_odd(pixels_per_point * self.width) {\n            *coord = coord.round_to_pixel_center(pixels_per_point);\n        } else {\n            *coord = coord.round_to_pixels(pixels_per_point);\n        }\n    }\n\n    pub(crate) fn round_rect_to_pixel(&self, pixels_per_point: f32, rect: &mut Rect) {\n        // We put odd-width strokes in the center of pixels.\n        // To understand why, see `fn round_center_to_pixel`.\n\n        let pixel_size = 1.0 / pixels_per_point;\n\n        let width = self.width;\n        if width <= 0.0 {\n            *rect = rect.round_to_pixels(pixels_per_point);\n        } else if width <= pixel_size || is_nearest_integer_odd(pixels_per_point * width) {\n            *rect = rect.round_to_pixel_center(pixels_per_point);\n        } else {\n            *rect = rect.round_to_pixels(pixels_per_point);\n        }\n    }\n}\n\nimpl<Color> From<(f32, Color)> for Stroke\nwhere\n    Color: Into<Color32>,\n{\n    #[inline(always)]\n    fn from((width, color): (f32, Color)) -> Self {\n        Self::new(width, color)\n    }\n}\n\nimpl std::hash::Hash for Stroke {\n    #[inline(always)]\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        let Self { width, color } = *self;\n        emath::OrderedFloat(width).hash(state);\n        color.hash(state);\n    }\n}\n\n/// Describes how the stroke of a shape should be painted.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum StrokeKind {\n    /// The stroke should be painted entirely inside of the shape\n    Inside,\n\n    /// The stroke should be painted right on the edge of the shape, half inside and half outside.\n    Middle,\n\n    /// The stroke should be painted entirely outside of the shape\n    Outside,\n}\n\n/// Describes the width and color of paths. The color can either be solid or provided by a callback. For more information, see [`ColorMode`]\n///\n/// The default stroke is the same as [`Stroke::NONE`].\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct PathStroke {\n    pub width: f32,\n    pub color: ColorMode,\n    pub kind: StrokeKind,\n}\n\nimpl Default for PathStroke {\n    #[inline]\n    fn default() -> Self {\n        Self::NONE\n    }\n}\n\nimpl PathStroke {\n    /// Same as [`PathStroke::default`].\n    pub const NONE: Self = Self {\n        width: 0.0,\n        color: ColorMode::TRANSPARENT,\n        kind: StrokeKind::Middle,\n    };\n\n    #[inline]\n    pub fn new(width: impl Into<f32>, color: impl Into<Color32>) -> Self {\n        Self {\n            width: width.into(),\n            color: ColorMode::Solid(color.into()),\n            kind: StrokeKind::Middle,\n        }\n    }\n\n    /// Create a new `PathStroke` with a UV function\n    ///\n    /// The bounding box passed to the callback will have a margin of [`TessellationOptions::feathering_size_in_pixels`](`crate::tessellator::TessellationOptions::feathering_size_in_pixels`)\n    #[inline]\n    pub fn new_uv(\n        width: impl Into<f32>,\n        callback: impl Fn(Rect, Pos2) -> Color32 + Send + Sync + 'static,\n    ) -> Self {\n        Self {\n            width: width.into(),\n            color: ColorMode::UV(Arc::new(callback)),\n            kind: StrokeKind::Middle,\n        }\n    }\n\n    #[inline]\n    pub fn with_kind(self, kind: StrokeKind) -> Self {\n        Self { kind, ..self }\n    }\n\n    /// Set the stroke to be painted right on the edge of the shape, half inside and half outside.\n    #[inline]\n    pub fn middle(self) -> Self {\n        Self {\n            kind: StrokeKind::Middle,\n            ..self\n        }\n    }\n\n    /// Set the stroke to be painted entirely outside of the shape\n    #[inline]\n    pub fn outside(self) -> Self {\n        Self {\n            kind: StrokeKind::Outside,\n            ..self\n        }\n    }\n\n    /// Set the stroke to be painted entirely inside of the shape\n    #[inline]\n    pub fn inside(self) -> Self {\n        Self {\n            kind: StrokeKind::Inside,\n            ..self\n        }\n    }\n\n    /// True if width is zero or color is solid and transparent\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.width <= 0.0 || self.color == ColorMode::TRANSPARENT\n    }\n}\n\nimpl<Color> From<(f32, Color)> for PathStroke\nwhere\n    Color: Into<Color32>,\n{\n    #[inline(always)]\n    fn from((width, color): (f32, Color)) -> Self {\n        Self::new(width, color)\n    }\n}\n\nimpl From<Stroke> for PathStroke {\n    fn from(value: Stroke) -> Self {\n        if value.is_empty() {\n            // Important, since we use the stroke color when doing feathering of the fill!\n            Self::NONE\n        } else {\n            Self {\n                width: value.width,\n                color: ColorMode::Solid(value.color),\n                kind: StrokeKind::Middle,\n            }\n        }\n    }\n}\n\n/// Returns true if the nearest integer is odd.\nfn is_nearest_integer_odd(x: f32) -> bool {\n    (x * 0.5 + 0.25).fract() > 0.5\n}\n\n#[test]\nfn test_is_nearest_integer_odd() {\n    assert!(is_nearest_integer_odd(0.6));\n    assert!(is_nearest_integer_odd(1.0));\n    assert!(is_nearest_integer_odd(1.4));\n    assert!(!is_nearest_integer_odd(1.6));\n    assert!(!is_nearest_integer_odd(2.0));\n    assert!(!is_nearest_integer_odd(2.4));\n    assert!(is_nearest_integer_odd(2.6));\n    assert!(is_nearest_integer_odd(3.0));\n    assert!(is_nearest_integer_odd(3.4));\n}\n"
  },
  {
    "path": "crates/epaint/src/tessellator.rs",
    "content": "//! Converts graphics primitives into textured triangles.\n//!\n//! This module converts lines, circles, text and more represented by [`Shape`]\n//! into textured triangles represented by [`Mesh`].\n\n#![expect(clippy::identity_op)]\n\nuse emath::{GuiRounding as _, NumExt as _, Pos2, Rect, Rot2, Vec2, pos2, remap, vec2};\n\nuse crate::{\n    CircleShape, ClippedPrimitive, ClippedShape, Color32, CornerRadiusF32, CubicBezierShape,\n    EllipseShape, Mesh, PathShape, Primitive, QuadraticBezierShape, RectShape, Shape, Stroke,\n    StrokeKind, TextShape, TextureId, Vertex, WHITE_UV, color::ColorMode, emath,\n    stroke::PathStroke, texture_atlas::PreparedDisc,\n};\n\n// ----------------------------------------------------------------------------\n\n#[expect(clippy::approx_constant)]\nmod precomputed_vertices {\n    // fn main() {\n    //     let n = 64;\n    //     println!(\"pub const CIRCLE_{}: [Vec2; {}] = [\", n, n+1);\n    //     for i in 0..=n {\n    //         let a = std::f64::consts::TAU * i as f64 / n as f64;\n    //         println!(\"    vec2({:.06}, {:.06}),\", a.cos(), a.sin());\n    //     }\n    //     println!(\"];\")\n    // }\n\n    use emath::{Vec2, vec2};\n\n    pub const CIRCLE_8: [Vec2; 9] = [\n        vec2(1.000000, 0.000000),\n        vec2(0.707107, 0.707107),\n        vec2(0.000000, 1.000000),\n        vec2(-0.707107, 0.707107),\n        vec2(-1.000000, 0.000000),\n        vec2(-0.707107, -0.707107),\n        vec2(0.000000, -1.000000),\n        vec2(0.707107, -0.707107),\n        vec2(1.000000, 0.000000),\n    ];\n\n    pub const CIRCLE_16: [Vec2; 17] = [\n        vec2(1.000000, 0.000000),\n        vec2(0.923880, 0.382683),\n        vec2(0.707107, 0.707107),\n        vec2(0.382683, 0.923880),\n        vec2(0.000000, 1.000000),\n        vec2(-0.382684, 0.923880),\n        vec2(-0.707107, 0.707107),\n        vec2(-0.923880, 0.382683),\n        vec2(-1.000000, 0.000000),\n        vec2(-0.923880, -0.382683),\n        vec2(-0.707107, -0.707107),\n        vec2(-0.382684, -0.923880),\n        vec2(0.000000, -1.000000),\n        vec2(0.382684, -0.923879),\n        vec2(0.707107, -0.707107),\n        vec2(0.923880, -0.382683),\n        vec2(1.000000, 0.000000),\n    ];\n\n    pub const CIRCLE_32: [Vec2; 33] = [\n        vec2(1.000000, 0.000000),\n        vec2(0.980785, 0.195090),\n        vec2(0.923880, 0.382683),\n        vec2(0.831470, 0.555570),\n        vec2(0.707107, 0.707107),\n        vec2(0.555570, 0.831470),\n        vec2(0.382683, 0.923880),\n        vec2(0.195090, 0.980785),\n        vec2(0.000000, 1.000000),\n        vec2(-0.195090, 0.980785),\n        vec2(-0.382683, 0.923880),\n        vec2(-0.555570, 0.831470),\n        vec2(-0.707107, 0.707107),\n        vec2(-0.831470, 0.555570),\n        vec2(-0.923880, 0.382683),\n        vec2(-0.980785, 0.195090),\n        vec2(-1.000000, 0.000000),\n        vec2(-0.980785, -0.195090),\n        vec2(-0.923880, -0.382683),\n        vec2(-0.831470, -0.555570),\n        vec2(-0.707107, -0.707107),\n        vec2(-0.555570, -0.831470),\n        vec2(-0.382683, -0.923880),\n        vec2(-0.195090, -0.980785),\n        vec2(-0.000000, -1.000000),\n        vec2(0.195090, -0.980785),\n        vec2(0.382683, -0.923880),\n        vec2(0.555570, -0.831470),\n        vec2(0.707107, -0.707107),\n        vec2(0.831470, -0.555570),\n        vec2(0.923880, -0.382683),\n        vec2(0.980785, -0.195090),\n        vec2(1.000000, -0.000000),\n    ];\n\n    pub const CIRCLE_64: [Vec2; 65] = [\n        vec2(1.000000, 0.000000),\n        vec2(0.995185, 0.098017),\n        vec2(0.980785, 0.195090),\n        vec2(0.956940, 0.290285),\n        vec2(0.923880, 0.382683),\n        vec2(0.881921, 0.471397),\n        vec2(0.831470, 0.555570),\n        vec2(0.773010, 0.634393),\n        vec2(0.707107, 0.707107),\n        vec2(0.634393, 0.773010),\n        vec2(0.555570, 0.831470),\n        vec2(0.471397, 0.881921),\n        vec2(0.382683, 0.923880),\n        vec2(0.290285, 0.956940),\n        vec2(0.195090, 0.980785),\n        vec2(0.098017, 0.995185),\n        vec2(0.000000, 1.000000),\n        vec2(-0.098017, 0.995185),\n        vec2(-0.195090, 0.980785),\n        vec2(-0.290285, 0.956940),\n        vec2(-0.382683, 0.923880),\n        vec2(-0.471397, 0.881921),\n        vec2(-0.555570, 0.831470),\n        vec2(-0.634393, 0.773010),\n        vec2(-0.707107, 0.707107),\n        vec2(-0.773010, 0.634393),\n        vec2(-0.831470, 0.555570),\n        vec2(-0.881921, 0.471397),\n        vec2(-0.923880, 0.382683),\n        vec2(-0.956940, 0.290285),\n        vec2(-0.980785, 0.195090),\n        vec2(-0.995185, 0.098017),\n        vec2(-1.000000, 0.000000),\n        vec2(-0.995185, -0.098017),\n        vec2(-0.980785, -0.195090),\n        vec2(-0.956940, -0.290285),\n        vec2(-0.923880, -0.382683),\n        vec2(-0.881921, -0.471397),\n        vec2(-0.831470, -0.555570),\n        vec2(-0.773010, -0.634393),\n        vec2(-0.707107, -0.707107),\n        vec2(-0.634393, -0.773010),\n        vec2(-0.555570, -0.831470),\n        vec2(-0.471397, -0.881921),\n        vec2(-0.382683, -0.923880),\n        vec2(-0.290285, -0.956940),\n        vec2(-0.195090, -0.980785),\n        vec2(-0.098017, -0.995185),\n        vec2(-0.000000, -1.000000),\n        vec2(0.098017, -0.995185),\n        vec2(0.195090, -0.980785),\n        vec2(0.290285, -0.956940),\n        vec2(0.382683, -0.923880),\n        vec2(0.471397, -0.881921),\n        vec2(0.555570, -0.831470),\n        vec2(0.634393, -0.773010),\n        vec2(0.707107, -0.707107),\n        vec2(0.773010, -0.634393),\n        vec2(0.831470, -0.555570),\n        vec2(0.881921, -0.471397),\n        vec2(0.923880, -0.382683),\n        vec2(0.956940, -0.290285),\n        vec2(0.980785, -0.195090),\n        vec2(0.995185, -0.098017),\n        vec2(1.000000, -0.000000),\n    ];\n\n    pub const CIRCLE_128: [Vec2; 129] = [\n        vec2(1.000000, 0.000000),\n        vec2(0.998795, 0.049068),\n        vec2(0.995185, 0.098017),\n        vec2(0.989177, 0.146730),\n        vec2(0.980785, 0.195090),\n        vec2(0.970031, 0.242980),\n        vec2(0.956940, 0.290285),\n        vec2(0.941544, 0.336890),\n        vec2(0.923880, 0.382683),\n        vec2(0.903989, 0.427555),\n        vec2(0.881921, 0.471397),\n        vec2(0.857729, 0.514103),\n        vec2(0.831470, 0.555570),\n        vec2(0.803208, 0.595699),\n        vec2(0.773010, 0.634393),\n        vec2(0.740951, 0.671559),\n        vec2(0.707107, 0.707107),\n        vec2(0.671559, 0.740951),\n        vec2(0.634393, 0.773010),\n        vec2(0.595699, 0.803208),\n        vec2(0.555570, 0.831470),\n        vec2(0.514103, 0.857729),\n        vec2(0.471397, 0.881921),\n        vec2(0.427555, 0.903989),\n        vec2(0.382683, 0.923880),\n        vec2(0.336890, 0.941544),\n        vec2(0.290285, 0.956940),\n        vec2(0.242980, 0.970031),\n        vec2(0.195090, 0.980785),\n        vec2(0.146730, 0.989177),\n        vec2(0.098017, 0.995185),\n        vec2(0.049068, 0.998795),\n        vec2(0.000000, 1.000000),\n        vec2(-0.049068, 0.998795),\n        vec2(-0.098017, 0.995185),\n        vec2(-0.146730, 0.989177),\n        vec2(-0.195090, 0.980785),\n        vec2(-0.242980, 0.970031),\n        vec2(-0.290285, 0.956940),\n        vec2(-0.336890, 0.941544),\n        vec2(-0.382683, 0.923880),\n        vec2(-0.427555, 0.903989),\n        vec2(-0.471397, 0.881921),\n        vec2(-0.514103, 0.857729),\n        vec2(-0.555570, 0.831470),\n        vec2(-0.595699, 0.803208),\n        vec2(-0.634393, 0.773010),\n        vec2(-0.671559, 0.740951),\n        vec2(-0.707107, 0.707107),\n        vec2(-0.740951, 0.671559),\n        vec2(-0.773010, 0.634393),\n        vec2(-0.803208, 0.595699),\n        vec2(-0.831470, 0.555570),\n        vec2(-0.857729, 0.514103),\n        vec2(-0.881921, 0.471397),\n        vec2(-0.903989, 0.427555),\n        vec2(-0.923880, 0.382683),\n        vec2(-0.941544, 0.336890),\n        vec2(-0.956940, 0.290285),\n        vec2(-0.970031, 0.242980),\n        vec2(-0.980785, 0.195090),\n        vec2(-0.989177, 0.146730),\n        vec2(-0.995185, 0.098017),\n        vec2(-0.998795, 0.049068),\n        vec2(-1.000000, 0.000000),\n        vec2(-0.998795, -0.049068),\n        vec2(-0.995185, -0.098017),\n        vec2(-0.989177, -0.146730),\n        vec2(-0.980785, -0.195090),\n        vec2(-0.970031, -0.242980),\n        vec2(-0.956940, -0.290285),\n        vec2(-0.941544, -0.336890),\n        vec2(-0.923880, -0.382683),\n        vec2(-0.903989, -0.427555),\n        vec2(-0.881921, -0.471397),\n        vec2(-0.857729, -0.514103),\n        vec2(-0.831470, -0.555570),\n        vec2(-0.803208, -0.595699),\n        vec2(-0.773010, -0.634393),\n        vec2(-0.740951, -0.671559),\n        vec2(-0.707107, -0.707107),\n        vec2(-0.671559, -0.740951),\n        vec2(-0.634393, -0.773010),\n        vec2(-0.595699, -0.803208),\n        vec2(-0.555570, -0.831470),\n        vec2(-0.514103, -0.857729),\n        vec2(-0.471397, -0.881921),\n        vec2(-0.427555, -0.903989),\n        vec2(-0.382683, -0.923880),\n        vec2(-0.336890, -0.941544),\n        vec2(-0.290285, -0.956940),\n        vec2(-0.242980, -0.970031),\n        vec2(-0.195090, -0.980785),\n        vec2(-0.146730, -0.989177),\n        vec2(-0.098017, -0.995185),\n        vec2(-0.049068, -0.998795),\n        vec2(-0.000000, -1.000000),\n        vec2(0.049068, -0.998795),\n        vec2(0.098017, -0.995185),\n        vec2(0.146730, -0.989177),\n        vec2(0.195090, -0.980785),\n        vec2(0.242980, -0.970031),\n        vec2(0.290285, -0.956940),\n        vec2(0.336890, -0.941544),\n        vec2(0.382683, -0.923880),\n        vec2(0.427555, -0.903989),\n        vec2(0.471397, -0.881921),\n        vec2(0.514103, -0.857729),\n        vec2(0.555570, -0.831470),\n        vec2(0.595699, -0.803208),\n        vec2(0.634393, -0.773010),\n        vec2(0.671559, -0.740951),\n        vec2(0.707107, -0.707107),\n        vec2(0.740951, -0.671559),\n        vec2(0.773010, -0.634393),\n        vec2(0.803208, -0.595699),\n        vec2(0.831470, -0.555570),\n        vec2(0.857729, -0.514103),\n        vec2(0.881921, -0.471397),\n        vec2(0.903989, -0.427555),\n        vec2(0.923880, -0.382683),\n        vec2(0.941544, -0.336890),\n        vec2(0.956940, -0.290285),\n        vec2(0.970031, -0.242980),\n        vec2(0.980785, -0.195090),\n        vec2(0.989177, -0.146730),\n        vec2(0.995185, -0.098017),\n        vec2(0.998795, -0.049068),\n        vec2(1.000000, -0.000000),\n    ];\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\nstruct PathPoint {\n    pos: Pos2,\n\n    /// For filled paths the normal is used for anti-aliasing (both strokes and filled areas).\n    ///\n    /// For strokes the normal is also used for giving thickness to the path\n    /// (i.e. in what direction to expand).\n    ///\n    /// The normal could be estimated by differences between successive points,\n    /// but that would be less accurate (and in some cases slower).\n    ///\n    /// Normals are normally unit-length.\n    normal: Vec2,\n}\n\n/// A connected line (without thickness or gaps) which can be tessellated\n/// to either to a stroke (with thickness) or a filled convex area.\n/// Used as a scratch-pad during tessellation.\n#[derive(Clone, Debug, Default)]\npub struct Path(Vec<PathPoint>);\n\nimpl Path {\n    #[inline(always)]\n    pub fn clear(&mut self) {\n        self.0.clear();\n    }\n\n    #[inline(always)]\n    pub fn reserve(&mut self, additional: usize) {\n        self.0.reserve(additional);\n    }\n\n    #[inline(always)]\n    pub fn add_point(&mut self, pos: Pos2, normal: Vec2) {\n        self.0.push(PathPoint { pos, normal });\n    }\n\n    pub fn add_circle(&mut self, center: Pos2, radius: f32) {\n        use precomputed_vertices::{CIRCLE_8, CIRCLE_16, CIRCLE_32, CIRCLE_64, CIRCLE_128};\n\n        // These cutoffs are based on a high-dpi display. TODO(emilk): use pixels_per_point here?\n        // same cutoffs as in add_circle_quadrant\n\n        if radius <= 2.0 {\n            self.0.extend(CIRCLE_8.iter().map(|&n| PathPoint {\n                pos: center + radius * n,\n                normal: n,\n            }));\n        } else if radius <= 5.0 {\n            self.0.extend(CIRCLE_16.iter().map(|&n| PathPoint {\n                pos: center + radius * n,\n                normal: n,\n            }));\n        } else if radius < 18.0 {\n            self.0.extend(CIRCLE_32.iter().map(|&n| PathPoint {\n                pos: center + radius * n,\n                normal: n,\n            }));\n        } else if radius < 50.0 {\n            self.0.extend(CIRCLE_64.iter().map(|&n| PathPoint {\n                pos: center + radius * n,\n                normal: n,\n            }));\n        } else {\n            self.0.extend(CIRCLE_128.iter().map(|&n| PathPoint {\n                pos: center + radius * n,\n                normal: n,\n            }));\n        }\n    }\n\n    pub fn add_line_segment(&mut self, points: [Pos2; 2]) {\n        self.reserve(2);\n        let normal = (points[1] - points[0]).normalized().rot90();\n        self.add_point(points[0], normal);\n        self.add_point(points[1], normal);\n    }\n\n    pub fn add_open_points(&mut self, points: &[Pos2]) {\n        let n = points.len();\n        assert!(n >= 2, \"A path needs at least two points, but got {n}\");\n\n        if n == 2 {\n            // Common case optimization:\n            self.add_line_segment([points[0], points[1]]);\n        } else {\n            self.reserve(n);\n            self.add_point(points[0], (points[1] - points[0]).normalized().rot90());\n            let mut n0 = (points[1] - points[0]).normalized().rot90();\n            for i in 1..n - 1 {\n                let mut n1 = (points[i + 1] - points[i]).normalized().rot90();\n\n                // Handle duplicated points (but not triplicated…):\n                if n0 == Vec2::ZERO {\n                    n0 = n1;\n                } else if n1 == Vec2::ZERO {\n                    n1 = n0;\n                }\n\n                let normal = (n0 + n1) / 2.0;\n                let length_sq = normal.length_sq();\n                let right_angle_length_sq = 0.5;\n                let sharper_than_a_right_angle = length_sq < right_angle_length_sq;\n                if sharper_than_a_right_angle {\n                    // cut off the sharp corner\n                    let center_normal = normal.normalized();\n                    let n0c = (n0 + center_normal) / 2.0;\n                    let n1c = (n1 + center_normal) / 2.0;\n                    self.add_point(points[i], n0c / n0c.length_sq());\n                    self.add_point(points[i], n1c / n1c.length_sq());\n                } else {\n                    // miter join\n                    self.add_point(points[i], normal / length_sq);\n                }\n\n                n0 = n1;\n            }\n            self.add_point(\n                points[n - 1],\n                (points[n - 1] - points[n - 2]).normalized().rot90(),\n            );\n        }\n    }\n\n    pub fn add_line_loop(&mut self, points: &[Pos2]) {\n        let n = points.len();\n        assert!(n >= 2, \"A path needs at least two points, but got {n}\");\n        self.reserve(n);\n\n        let mut n0 = (points[0] - points[n - 1]).normalized().rot90();\n\n        for i in 0..n {\n            let next_i = if i + 1 == n { 0 } else { i + 1 };\n            let mut n1 = (points[next_i] - points[i]).normalized().rot90();\n\n            // Handle duplicated points (but not triplicated…):\n            if n0 == Vec2::ZERO {\n                n0 = n1;\n            } else if n1 == Vec2::ZERO {\n                n1 = n0;\n            }\n\n            let normal = (n0 + n1) / 2.0;\n            let length_sq = normal.length_sq();\n\n            // We can't just cut off corners for filled shapes like this,\n            // because the feather will both expand and contract the corner along the provided normals\n            // to make sure it doesn't grow, and the shrinking will make the inner points cross each other.\n            //\n            // A better approach is to shrink the vertices in by half the feather-width here\n            // and then only expand during feathering.\n            //\n            // See https://github.com/emilk/egui/issues/1226\n            const CUT_OFF_SHARP_CORNERS: bool = false;\n\n            let right_angle_length_sq = 0.5;\n            let sharper_than_a_right_angle = length_sq < right_angle_length_sq;\n            if CUT_OFF_SHARP_CORNERS && sharper_than_a_right_angle {\n                // cut off the sharp corner\n                let center_normal = normal.normalized();\n                let n0c = (n0 + center_normal) / 2.0;\n                let n1c = (n1 + center_normal) / 2.0;\n                self.add_point(points[i], n0c / n0c.length_sq());\n                self.add_point(points[i], n1c / n1c.length_sq());\n            } else {\n                // miter join\n                self.add_point(points[i], normal / length_sq);\n            }\n\n            n0 = n1;\n        }\n    }\n\n    /// The path is taken to be closed (i.e. returning to the start again).\n    ///\n    /// Calling this may reverse the vertices in the path if they are wrong winding order.\n    /// The preferred winding order is clockwise.\n    pub fn fill_and_stroke(\n        &mut self,\n        feathering: f32,\n        fill: Color32,\n        stroke: &PathStroke,\n        out: &mut Mesh,\n    ) {\n        stroke_and_fill_path(feathering, &mut self.0, PathType::Closed, stroke, fill, out);\n    }\n\n    /// Open-ended.\n    pub fn stroke_open(&mut self, feathering: f32, stroke: &PathStroke, out: &mut Mesh) {\n        stroke_path(feathering, &mut self.0, PathType::Open, stroke, out);\n    }\n\n    /// A closed path (returning to the first point).\n    pub fn stroke_closed(&mut self, feathering: f32, stroke: &PathStroke, out: &mut Mesh) {\n        stroke_path(feathering, &mut self.0, PathType::Closed, stroke, out);\n    }\n\n    pub fn stroke(\n        &mut self,\n        feathering: f32,\n        path_type: PathType,\n        stroke: &PathStroke,\n        out: &mut Mesh,\n    ) {\n        stroke_path(feathering, &mut self.0, path_type, stroke, out);\n    }\n\n    /// The path is taken to be closed (i.e. returning to the start again).\n    ///\n    /// Calling this may reverse the vertices in the path if they are wrong winding order.\n    /// The preferred winding order is clockwise.\n    pub fn fill(&mut self, feathering: f32, color: Color32, out: &mut Mesh) {\n        fill_closed_path(feathering, &mut self.0, color, out);\n    }\n\n    /// Like [`Self::fill`] but with texturing.\n    ///\n    /// The `uv_from_pos` is called for each vertex position.\n    pub fn fill_with_uv(\n        &mut self,\n        feathering: f32,\n        color: Color32,\n        texture_id: TextureId,\n        uv_from_pos: impl Fn(Pos2) -> Pos2,\n        out: &mut Mesh,\n    ) {\n        fill_closed_path_with_uv(feathering, &mut self.0, color, texture_id, uv_from_pos, out);\n    }\n}\n\npub mod path {\n    //! Helpers for constructing paths\n    use crate::CornerRadiusF32;\n    use emath::{Pos2, Rect, pos2};\n\n    /// overwrites existing points\n    pub fn rounded_rectangle(path: &mut Vec<Pos2>, rect: Rect, cr: CornerRadiusF32) {\n        path.clear();\n\n        let min = rect.min;\n        let max = rect.max;\n\n        let cr = clamp_corner_radius(cr, rect);\n\n        if cr == CornerRadiusF32::ZERO {\n            path.reserve(4);\n            path.push(pos2(min.x, min.y)); // left top\n            path.push(pos2(max.x, min.y)); // right top\n            path.push(pos2(max.x, max.y)); // right bottom\n            path.push(pos2(min.x, max.y)); // left bottom\n        } else {\n            // We need to avoid duplicated vertices, because that leads to visual artifacts later.\n            // Duplicated vertices can happen when one side is all rounding, with no straight edge between.\n            let eps = f32::EPSILON * rect.size().max_elem();\n\n            add_circle_quadrant(path, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0); // south east\n\n            if rect.width() <= cr.se + cr.sw + eps {\n                path.pop(); // avoid duplicated vertex\n            }\n\n            add_circle_quadrant(path, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0); // south west\n\n            if rect.height() <= cr.sw + cr.nw + eps {\n                path.pop(); // avoid duplicated vertex\n            }\n\n            add_circle_quadrant(path, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0); // north west\n\n            if rect.width() <= cr.nw + cr.ne + eps {\n                path.pop(); // avoid duplicated vertex\n            }\n\n            add_circle_quadrant(path, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0); // north east\n\n            if rect.height() <= cr.ne + cr.se + eps {\n                path.pop(); // avoid duplicated vertex\n            }\n        }\n    }\n\n    /// Add one quadrant of a circle\n    ///\n    /// * quadrant 0: right bottom\n    /// * quadrant 1: left bottom\n    /// * quadrant 2: left top\n    /// * quadrant 3: right top\n    //\n    // Derivation:\n    //\n    // * angle 0 * TAU / 4 = right\n    //   - quadrant 0: right bottom\n    // * angle 1 * TAU / 4 = bottom\n    //   - quadrant 1: left bottom\n    // * angle 2 * TAU / 4 = left\n    //   - quadrant 2: left top\n    // * angle 3 * TAU / 4 = top\n    //   - quadrant 3: right top\n    // * angle 4 * TAU / 4 = right\n    pub fn add_circle_quadrant(path: &mut Vec<Pos2>, center: Pos2, radius: f32, quadrant: f32) {\n        use super::precomputed_vertices::{CIRCLE_8, CIRCLE_16, CIRCLE_32, CIRCLE_64, CIRCLE_128};\n\n        // These cutoffs are based on a high-dpi display. TODO(emilk): use pixels_per_point here?\n        // same cutoffs as in add_circle\n\n        if radius <= 0.0 {\n            path.push(center);\n        } else if radius <= 2.0 {\n            let offset = quadrant as usize * 2;\n            let quadrant_vertices = &CIRCLE_8[offset..=offset + 2];\n            path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));\n        } else if radius <= 5.0 {\n            let offset = quadrant as usize * 4;\n            let quadrant_vertices = &CIRCLE_16[offset..=offset + 4];\n            path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));\n        } else if radius < 18.0 {\n            let offset = quadrant as usize * 8;\n            let quadrant_vertices = &CIRCLE_32[offset..=offset + 8];\n            path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));\n        } else if radius < 50.0 {\n            let offset = quadrant as usize * 16;\n            let quadrant_vertices = &CIRCLE_64[offset..=offset + 16];\n            path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));\n        } else {\n            let offset = quadrant as usize * 32;\n            let quadrant_vertices = &CIRCLE_128[offset..=offset + 32];\n            path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));\n        }\n    }\n\n    // Ensures the radius of each corner is within a valid range\n    fn clamp_corner_radius(cr: CornerRadiusF32, rect: Rect) -> CornerRadiusF32 {\n        let half_width = rect.width() * 0.5;\n        let half_height = rect.height() * 0.5;\n        let max_cr = half_width.min(half_height);\n        cr.at_most(max_cr).at_least(0.0)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Clone, Copy, PartialEq, Eq)]\npub enum PathType {\n    Open,\n    Closed,\n}\n\n/// Tessellation quality options\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct TessellationOptions {\n    /// Use \"feathering\" to smooth out the edges of shapes as a form of anti-aliasing.\n    ///\n    /// Feathering works by making each edge into a thin gradient into transparency.\n    /// The size of this edge is controlled by [`Self::feathering_size_in_pixels`].\n    ///\n    /// This makes shapes appear smoother, but requires more triangles and is therefore slower.\n    ///\n    /// This setting does not affect text.\n    ///\n    /// Default: `true`.\n    pub feathering: bool,\n\n    /// The size of the feathering, in physical pixels.\n    ///\n    /// The default, and suggested, value for this is `1.0`.\n    /// If you use a larger value, edges will appear blurry.\n    pub feathering_size_in_pixels: f32,\n\n    /// If `true` (default) cull certain primitives before tessellating them.\n    /// This likely makes\n    pub coarse_tessellation_culling: bool,\n\n    /// If `true`, small filled circled will be optimized by using pre-rasterized circled\n    /// from the font atlas.\n    pub prerasterized_discs: bool,\n\n    /// If `true` (default) align text to the physical pixel grid.\n    /// This makes the text sharper on most platforms.\n    pub round_text_to_pixels: bool,\n\n    /// If `true` (default), align right-angled line segments to the physical pixel grid.\n    ///\n    /// This makes the line segments appear crisp on any display.\n    pub round_line_segments_to_pixels: bool,\n\n    /// If `true` (default), align rectangles to the physical pixel grid.\n    ///\n    /// This makes the rectangle strokes more crisp,\n    /// and makes filled rectangles tile perfectly (without feathering).\n    ///\n    /// You can override this with [`crate::RectShape::round_to_pixels`].\n    pub round_rects_to_pixels: bool,\n\n    /// Output the clip rectangles to be painted.\n    pub debug_paint_clip_rects: bool,\n\n    /// Output the text-containing rectangles.\n    pub debug_paint_text_rects: bool,\n\n    /// If true, no clipping will be done.\n    pub debug_ignore_clip_rects: bool,\n\n    /// The maximum distance between the original curve and the flattened curve.\n    pub bezier_tolerance: f32,\n\n    /// The default value will be 1.0e-5, it will be used during float compare.\n    pub epsilon: f32,\n\n    /// If `rayon` feature is activated, should we parallelize tessellation?\n    pub parallel_tessellation: bool,\n\n    /// If `true`, invalid meshes will be silently ignored.\n    /// If `false`, invalid meshes will cause a panic.\n    ///\n    /// The default is `false` to save performance.\n    pub validate_meshes: bool,\n}\n\nimpl Default for TessellationOptions {\n    fn default() -> Self {\n        Self {\n            feathering: true,\n            feathering_size_in_pixels: 1.0,\n            coarse_tessellation_culling: true,\n            prerasterized_discs: true,\n            round_text_to_pixels: true,\n            round_line_segments_to_pixels: true,\n            round_rects_to_pixels: true,\n            debug_paint_text_rects: false,\n            debug_paint_clip_rects: false,\n            debug_ignore_clip_rects: false,\n            bezier_tolerance: 0.1,\n            epsilon: 1.0e-5,\n            parallel_tessellation: true,\n            validate_meshes: false,\n        }\n    }\n}\n\nfn cw_signed_area(path: &[PathPoint]) -> f64 {\n    if let Some(last) = path.last() {\n        let mut previous = last.pos;\n        let mut area = 0.0;\n        for p in path {\n            area += (previous.x * p.pos.y - p.pos.x * previous.y) as f64;\n            previous = p.pos;\n        }\n        area\n    } else {\n        0.0\n    }\n}\n\n/// Tessellate the given convex area into a polygon.\n///\n/// Calling this may reverse the vertices in the path if they are wrong winding order.\n///\n/// The preferred winding order is clockwise.\nfn fill_closed_path(feathering: f32, path: &mut [PathPoint], fill_color: Color32, out: &mut Mesh) {\n    if fill_color == Color32::TRANSPARENT {\n        return;\n    }\n\n    let n = path.len() as u32;\n    if n < 3 {\n        return;\n    }\n\n    if 0.0 < feathering {\n        if cw_signed_area(path) < 0.0 {\n            // Wrong winding order - fix:\n            path.reverse();\n            for point in &mut *path {\n                point.normal = -point.normal;\n            }\n        }\n\n        out.reserve_triangles(3 * n as usize);\n        out.reserve_vertices(2 * n as usize);\n        let idx_inner = out.vertices.len() as u32;\n        let idx_outer = idx_inner + 1;\n\n        // The fill:\n        for i in 2..n {\n            out.add_triangle(idx_inner + 2 * (i - 1), idx_inner, idx_inner + 2 * i);\n        }\n\n        // The feathering:\n        let mut i0 = n - 1;\n        for i1 in 0..n {\n            let p1 = &path[i1 as usize];\n            let dm = 0.5 * feathering * p1.normal;\n\n            let pos_inner = p1.pos - dm;\n            let pos_outer = p1.pos + dm;\n\n            out.colored_vertex(pos_inner, fill_color);\n            out.colored_vertex(pos_outer, Color32::TRANSPARENT);\n            out.add_triangle(idx_inner + i1 * 2, idx_inner + i0 * 2, idx_outer + 2 * i0);\n            out.add_triangle(idx_outer + i0 * 2, idx_outer + i1 * 2, idx_inner + 2 * i1);\n            i0 = i1;\n        }\n    } else {\n        out.reserve_triangles(n as usize);\n        let idx = out.vertices.len() as u32;\n        out.vertices.extend(path.iter().map(|p| Vertex {\n            pos: p.pos,\n            uv: WHITE_UV,\n            color: fill_color,\n        }));\n        for i in 2..n {\n            out.add_triangle(idx, idx + i - 1, idx + i);\n        }\n    }\n}\n\n/// Like [`fill_closed_path`] but with texturing.\n///\n/// The `uv_from_pos` is called for each vertex position.\nfn fill_closed_path_with_uv(\n    feathering: f32,\n    path: &mut [PathPoint],\n    color: Color32,\n    texture_id: TextureId,\n    uv_from_pos: impl Fn(Pos2) -> Pos2,\n    out: &mut Mesh,\n) {\n    if color == Color32::TRANSPARENT {\n        return;\n    }\n\n    if out.is_empty() {\n        out.texture_id = texture_id;\n    } else {\n        assert_eq!(\n            out.texture_id, texture_id,\n            \"Mixing different `texture_id` in the same \"\n        );\n    }\n\n    let n = path.len() as u32;\n    if 0.0 < feathering {\n        if cw_signed_area(path) < 0.0 {\n            // Wrong winding order - fix:\n            path.reverse();\n            for point in &mut *path {\n                point.normal = -point.normal;\n            }\n        }\n\n        out.reserve_triangles(3 * n as usize);\n        out.reserve_vertices(2 * n as usize);\n        let color_outer = Color32::TRANSPARENT;\n        let idx_inner = out.vertices.len() as u32;\n        let idx_outer = idx_inner + 1;\n\n        // The fill:\n        for i in 2..n {\n            out.add_triangle(idx_inner + 2 * (i - 1), idx_inner, idx_inner + 2 * i);\n        }\n\n        // The feathering:\n        let mut i0 = n - 1;\n        for i1 in 0..n {\n            let p1 = &path[i1 as usize];\n            let dm = 0.5 * feathering * p1.normal;\n\n            let pos = p1.pos - dm;\n            out.vertices.push(Vertex {\n                pos,\n                uv: uv_from_pos(pos),\n                color,\n            });\n\n            let pos = p1.pos + dm;\n            out.vertices.push(Vertex {\n                pos,\n                uv: uv_from_pos(pos),\n                color: color_outer,\n            });\n\n            out.add_triangle(idx_inner + i1 * 2, idx_inner + i0 * 2, idx_outer + 2 * i0);\n            out.add_triangle(idx_outer + i0 * 2, idx_outer + i1 * 2, idx_inner + 2 * i1);\n            i0 = i1;\n        }\n    } else {\n        out.reserve_triangles(n as usize);\n        let idx = out.vertices.len() as u32;\n        out.vertices.extend(path.iter().map(|p| Vertex {\n            pos: p.pos,\n            uv: uv_from_pos(p.pos),\n            color,\n        }));\n        for i in 2..n {\n            out.add_triangle(idx, idx + i - 1, idx + i);\n        }\n    }\n}\n\n/// Tessellate the given path as a stroke with thickness.\nfn stroke_path(\n    feathering: f32,\n    path: &mut [PathPoint],\n    path_type: PathType,\n    stroke: &PathStroke,\n    out: &mut Mesh,\n) {\n    let fill = Color32::TRANSPARENT;\n    stroke_and_fill_path(feathering, path, path_type, stroke, fill, out);\n}\n\n/// Tessellate the given path as a stroke with thickness, with optional fill color.\n///\n/// Calling this may reverse the vertices in the path if they are wrong winding order.\n///\n/// The preferred winding order is clockwise.\nfn stroke_and_fill_path(\n    feathering: f32,\n    path: &mut [PathPoint],\n    path_type: PathType,\n    stroke: &PathStroke,\n    color_fill: Color32,\n    out: &mut Mesh,\n) {\n    let n = path.len() as u32;\n\n    if n < 2 {\n        return;\n    }\n\n    if stroke.width == 0.0 {\n        // Skip the stroke, just fill.\n        return fill_closed_path(feathering, path, color_fill, out);\n    }\n\n    if color_fill != Color32::TRANSPARENT && cw_signed_area(path) < 0.0 {\n        // Wrong winding order - fix:\n        path.reverse();\n        for point in &mut *path {\n            point.normal = -point.normal;\n        }\n    }\n\n    if stroke.color == ColorMode::TRANSPARENT {\n        // Skip the stroke, just fill. But subtract the width from the path:\n        match stroke.kind {\n            StrokeKind::Inside => {\n                for point in &mut *path {\n                    point.pos -= stroke.width * point.normal;\n                }\n            }\n            StrokeKind::Middle => {\n                for point in &mut *path {\n                    point.pos -= 0.5 * stroke.width * point.normal;\n                }\n            }\n            StrokeKind::Outside => {}\n        }\n\n        // Skip the stroke, just fill.\n        return fill_closed_path(feathering, path, color_fill, out);\n    }\n\n    let idx = out.vertices.len() as u32;\n\n    // Move the points so that the stroke is on middle of the path.\n    match stroke.kind {\n        StrokeKind::Inside => {\n            for point in &mut *path {\n                point.pos -= 0.5 * stroke.width * point.normal;\n            }\n        }\n        StrokeKind::Middle => {\n            // correct\n        }\n        StrokeKind::Outside => {\n            for point in &mut *path {\n                point.pos += 0.5 * stroke.width * point.normal;\n            }\n        }\n    }\n\n    // Expand the bounding box to include the thickness of the path\n    let uv_bbox = if matches!(stroke.color, ColorMode::UV(_)) {\n        Rect::from_points(&path.iter().map(|p| p.pos).collect::<Vec<Pos2>>())\n            .expand((stroke.width / 2.0) + feathering)\n    } else {\n        Rect::NAN\n    };\n    let get_color = |col: &ColorMode, pos: Pos2| match col {\n        ColorMode::Solid(col) => *col,\n        ColorMode::UV(fun) => fun(uv_bbox, pos),\n    };\n\n    if 0.0 < feathering {\n        let color_outer = Color32::TRANSPARENT;\n        let color_middle = &stroke.color;\n\n        // We add a bit of an epsilon here, because when we round to pixels,\n        // we can get rounding errors (unless pixels_per_point is an integer).\n        // And it's better to err on the side of the nicer rendering with line caps\n        // (the thin-line optimization has no line caps).\n        let thin_line = stroke.width <= 0.9 * feathering;\n        if thin_line {\n            // If the stroke is painted smaller than the pixel width (=feathering width),\n            // then we risk severe aliasing.\n            // Instead, we paint the stroke as a triangular ridge, two feather-widths wide,\n            // and lessen the opacity of the middle part instead of making it thinner.\n            if color_fill != Color32::TRANSPARENT && stroke.width < feathering {\n                // If this is filled shape, then we need to also compensate so that the\n                // filled area remains the same as it would have been without the\n                // artificially wide line.\n                for point in &mut *path {\n                    point.pos += 0.5 * (feathering - stroke.width) * point.normal;\n                }\n            }\n\n            // TODO(emilk): add line caps (if this is an open line).\n\n            let opacity = stroke.width / feathering;\n\n            /*\n            We paint the line using three edges: outer, middle, fill.\n\n            .       o   m   i      outer, middle, fill\n            .       |---|          feathering (pixel width)\n            */\n\n            out.reserve_triangles(4 * n as usize);\n            out.reserve_vertices(3 * n as usize);\n\n            let mut i0 = n - 1;\n            for i1 in 0..n {\n                let connect_with_previous = path_type == PathType::Closed || i1 > 0;\n                let p1 = path[i1 as usize];\n                let p = p1.pos;\n                let n = p1.normal;\n                out.colored_vertex(p + n * feathering, color_outer);\n                out.colored_vertex(p, mul_color(get_color(color_middle, p), opacity));\n                out.colored_vertex(p - n * feathering, color_fill);\n\n                if connect_with_previous {\n                    out.add_triangle(idx + 3 * i0 + 0, idx + 3 * i0 + 1, idx + 3 * i1 + 0);\n                    out.add_triangle(idx + 3 * i0 + 1, idx + 3 * i1 + 0, idx + 3 * i1 + 1);\n\n                    out.add_triangle(idx + 3 * i0 + 1, idx + 3 * i0 + 2, idx + 3 * i1 + 1);\n                    out.add_triangle(idx + 3 * i0 + 2, idx + 3 * i1 + 1, idx + 3 * i1 + 2);\n                }\n\n                i0 = i1;\n            }\n\n            if color_fill != Color32::TRANSPARENT {\n                out.reserve_triangles(n as usize - 2);\n                let idx_fill = idx + 2;\n                for i in 2..n {\n                    out.add_triangle(idx_fill + 3 * (i - 1), idx_fill, idx_fill + 3 * i);\n                }\n            }\n        } else {\n            // thick anti-aliased line\n\n            /*\n            We paint the line using four edges: outer, middle, middle, fill\n\n            .       o   m     p    m   f   outer, middle, point, middle, fill\n            .       |---|                  feathering (pixel width)\n            .         |--------------|     width\n            .       |---------|            outer_rad\n            .           |-----|            inner_rad\n            */\n\n            let inner_rad = 0.5 * (stroke.width - feathering);\n            let outer_rad = 0.5 * (stroke.width + feathering);\n\n            match path_type {\n                PathType::Closed => {\n                    out.reserve_triangles(6 * n as usize);\n                    out.reserve_vertices(4 * n as usize);\n\n                    let mut i0 = n - 1;\n                    for i1 in 0..n {\n                        let p1 = path[i1 as usize];\n                        let p = p1.pos;\n                        let n = p1.normal;\n                        out.colored_vertex(p + n * outer_rad, color_outer);\n                        out.colored_vertex(\n                            p + n * inner_rad,\n                            get_color(color_middle, p + n * inner_rad),\n                        );\n                        out.colored_vertex(\n                            p - n * inner_rad,\n                            get_color(color_middle, p - n * inner_rad),\n                        );\n                        out.colored_vertex(p - n * outer_rad, color_fill);\n\n                        out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);\n                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);\n\n                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);\n                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);\n\n                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);\n                        out.add_triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);\n\n                        i0 = i1;\n                    }\n\n                    if color_fill != Color32::TRANSPARENT {\n                        out.reserve_triangles(n as usize - 2);\n                        let idx_fill = idx + 3;\n                        for i in 2..n {\n                            out.add_triangle(idx_fill + 4 * (i - 1), idx_fill, idx_fill + 4 * i);\n                        }\n                    }\n                }\n                PathType::Open => {\n                    // Anti-alias the ends by extruding the outer edge and adding\n                    // two more triangles to each end:\n\n                    //   | aa |       | aa |\n                    //    _________________   ___\n                    //   | \\    added    / |  feathering\n                    //   |   \\ ___p___ /   |  ___\n                    //   |    |       |    |\n                    //   |    |  opa  |    |\n                    //   |    |  que  |    |\n                    //   |    |       |    |\n\n                    // (in the future it would be great with an option to add a circular end instead)\n\n                    // TODO(emilk): we should probably shrink before adding the line caps,\n                    // so that we don't add to the area of the line.\n                    // TODO(emilk): make line caps optional.\n\n                    out.reserve_triangles(6 * n as usize + 4);\n                    out.reserve_vertices(4 * n as usize);\n\n                    {\n                        let end = path[0];\n                        let p = end.pos;\n                        let n = end.normal;\n                        let back_extrude = n.rot90() * feathering;\n                        out.colored_vertex(p + n * outer_rad + back_extrude, color_outer);\n                        out.colored_vertex(\n                            p + n * inner_rad,\n                            get_color(color_middle, p + n * inner_rad),\n                        );\n                        out.colored_vertex(\n                            p - n * inner_rad,\n                            get_color(color_middle, p - n * inner_rad),\n                        );\n                        out.colored_vertex(p - n * outer_rad + back_extrude, color_outer);\n\n                        out.add_triangle(idx + 0, idx + 1, idx + 2);\n                        out.add_triangle(idx + 0, idx + 2, idx + 3);\n                    }\n\n                    let mut i0 = 0;\n                    for i1 in 1..n - 1 {\n                        let point = path[i1 as usize];\n                        let p = point.pos;\n                        let n = point.normal;\n                        out.colored_vertex(p + n * outer_rad, color_outer);\n                        out.colored_vertex(\n                            p + n * inner_rad,\n                            get_color(color_middle, p + n * inner_rad),\n                        );\n                        out.colored_vertex(\n                            p - n * inner_rad,\n                            get_color(color_middle, p - n * inner_rad),\n                        );\n                        out.colored_vertex(p - n * outer_rad, color_outer);\n\n                        out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);\n                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);\n\n                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);\n                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);\n\n                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);\n                        out.add_triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);\n\n                        i0 = i1;\n                    }\n\n                    {\n                        let i1 = n - 1;\n                        let end = path[i1 as usize];\n                        let p = end.pos;\n                        let n = end.normal;\n                        let back_extrude = -n.rot90() * feathering;\n                        out.colored_vertex(p + n * outer_rad + back_extrude, color_outer);\n                        out.colored_vertex(\n                            p + n * inner_rad,\n                            get_color(color_middle, p + n * inner_rad),\n                        );\n                        out.colored_vertex(\n                            p - n * inner_rad,\n                            get_color(color_middle, p - n * inner_rad),\n                        );\n                        out.colored_vertex(p - n * outer_rad + back_extrude, color_outer);\n\n                        out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);\n                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);\n\n                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);\n                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);\n\n                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);\n                        out.add_triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);\n\n                        // The extension:\n                        out.add_triangle(idx + 4 * i1 + 0, idx + 4 * i1 + 1, idx + 4 * i1 + 2);\n                        out.add_triangle(idx + 4 * i1 + 0, idx + 4 * i1 + 2, idx + 4 * i1 + 3);\n                    }\n                }\n            }\n        }\n    } else {\n        // not anti-aliased:\n        out.reserve_triangles(2 * n as usize);\n        out.reserve_vertices(2 * n as usize);\n\n        let last_index = if path_type == PathType::Closed {\n            n\n        } else {\n            n - 1\n        };\n        for i in 0..last_index {\n            out.add_triangle(\n                idx + (2 * i + 0) % (2 * n),\n                idx + (2 * i + 1) % (2 * n),\n                idx + (2 * i + 2) % (2 * n),\n            );\n            out.add_triangle(\n                idx + (2 * i + 2) % (2 * n),\n                idx + (2 * i + 1) % (2 * n),\n                idx + (2 * i + 3) % (2 * n),\n            );\n        }\n\n        let thin_line = stroke.width <= feathering;\n        if thin_line {\n            // Fade out thin lines rather than making them thinner\n            let opacity = stroke.width / feathering;\n            let radius = feathering / 2.0;\n            for p in path.iter_mut() {\n                out.colored_vertex(\n                    p.pos + radius * p.normal,\n                    mul_color(get_color(&stroke.color, p.pos + radius * p.normal), opacity),\n                );\n                out.colored_vertex(\n                    p.pos - radius * p.normal,\n                    mul_color(get_color(&stroke.color, p.pos - radius * p.normal), opacity),\n                );\n            }\n        } else {\n            let radius = stroke.width / 2.0;\n            for p in path.iter_mut() {\n                out.colored_vertex(\n                    p.pos + radius * p.normal,\n                    get_color(&stroke.color, p.pos + radius * p.normal),\n                );\n                out.colored_vertex(\n                    p.pos - radius * p.normal,\n                    get_color(&stroke.color, p.pos - radius * p.normal),\n                );\n            }\n        }\n\n        if color_fill != Color32::TRANSPARENT {\n            // We Need to create new vertices, because the ones we used for the stroke\n            // has the wrong color.\n\n            // Shrink to ignore the stroke…\n            for point in &mut *path {\n                point.pos -= 0.5 * stroke.width * point.normal;\n            }\n            // …then fill:\n            fill_closed_path(feathering, path, color_fill, out);\n        }\n    }\n}\n\nfn mul_color(color: Color32, factor: f32) -> Color32 {\n    // The fast gamma-space multiply also happens to be perceptually better.\n    // Win-win!\n    color.gamma_multiply(factor)\n}\n\n// ----------------------------------------------------------------------------\n\n/// Converts [`Shape`]s into triangles ([`Mesh`]).\n///\n/// For performance reasons it is smart to reuse the same [`Tessellator`].\n#[derive(Clone)]\npub struct Tessellator {\n    pixels_per_point: f32,\n    options: TessellationOptions,\n    font_tex_size: [usize; 2],\n\n    /// See [`crate::TextureAtlas::prepared_discs`].\n    prepared_discs: Vec<PreparedDisc>,\n\n    /// size of feathering in points. normally the size of a physical pixel. 0.0 if disabled\n    feathering: f32,\n\n    /// Only used for culling\n    clip_rect: Rect,\n\n    scratchpad_points: Vec<Pos2>,\n    scratchpad_path: Path,\n}\n\nimpl Tessellator {\n    /// Create a new [`Tessellator`].\n    ///\n    /// * `pixels_per_point`: number of physical pixels to each logical point\n    /// * `options`: tessellation quality\n    /// * `shapes`: what to tessellate\n    /// * `font_tex_size`: size of the font texture. Required to normalize glyph uv rectangles when tessellating text.\n    /// * `prepared_discs`: What [`crate::TextureAtlas::prepared_discs`] returns. Can safely be set to an empty vec.\n    pub fn new(\n        pixels_per_point: f32,\n        options: TessellationOptions,\n        font_tex_size: [usize; 2],\n        prepared_discs: Vec<PreparedDisc>,\n    ) -> Self {\n        let feathering = if options.feathering {\n            let pixel_size = 1.0 / pixels_per_point;\n            options.feathering_size_in_pixels * pixel_size\n        } else {\n            0.0\n        };\n        Self {\n            pixels_per_point,\n            options,\n            font_tex_size,\n            prepared_discs,\n            feathering,\n            clip_rect: Rect::EVERYTHING,\n            scratchpad_points: Default::default(),\n            scratchpad_path: Default::default(),\n        }\n    }\n\n    /// Set the [`Rect`] to use for culling.\n    pub fn set_clip_rect(&mut self, clip_rect: Rect) {\n        self.clip_rect = clip_rect;\n    }\n\n    /// Tessellate a clipped shape into a list of primitives.\n    pub fn tessellate_clipped_shape(\n        &mut self,\n        clipped_shape: ClippedShape,\n        out_primitives: &mut Vec<ClippedPrimitive>,\n    ) {\n        let ClippedShape { clip_rect, shape } = clipped_shape;\n\n        if !clip_rect.is_positive() {\n            return; // skip empty clip rectangles\n        }\n\n        if let Shape::Vec(shapes) = shape {\n            for shape in shapes {\n                self.tessellate_clipped_shape(ClippedShape { clip_rect, shape }, out_primitives);\n            }\n            return;\n        }\n\n        if let Shape::Callback(callback) = shape {\n            out_primitives.push(ClippedPrimitive {\n                clip_rect,\n                primitive: Primitive::Callback(callback),\n            });\n            return;\n        }\n\n        let start_new_mesh = match out_primitives.last() {\n            None => true,\n            Some(output_clipped_primitive) => {\n                output_clipped_primitive.clip_rect != clip_rect\n                    || match &output_clipped_primitive.primitive {\n                        Primitive::Mesh(output_mesh) => {\n                            output_mesh.texture_id != shape.texture_id()\n                        }\n                        Primitive::Callback(_) => true,\n                    }\n            }\n        };\n\n        if start_new_mesh {\n            out_primitives.push(ClippedPrimitive {\n                clip_rect,\n                primitive: Primitive::Mesh(Mesh::default()),\n            });\n        }\n\n        #[expect(clippy::unwrap_used)] // it's never empty\n        let out = out_primitives.last_mut().unwrap();\n\n        if let Primitive::Mesh(out_mesh) = &mut out.primitive {\n            self.clip_rect = clip_rect;\n            self.tessellate_shape(shape, out_mesh);\n        } else {\n            unreachable!();\n        }\n    }\n\n    /// Tessellate a single [`Shape`] into a [`Mesh`].\n    ///\n    /// This call can panic the given shape is of [`Shape::Vec`] or [`Shape::Callback`].\n    /// For that, use [`Self::tessellate_clipped_shape`] instead.\n    /// * `shape`: the shape to tessellate.\n    /// * `out`: triangles are appended to this.\n    pub fn tessellate_shape(&mut self, shape: Shape, out: &mut Mesh) {\n        match shape {\n            Shape::Noop => {}\n            Shape::Vec(vec) => {\n                for shape in vec {\n                    self.tessellate_shape(shape, out);\n                }\n            }\n            Shape::Circle(circle) => {\n                self.tessellate_circle(circle, out);\n            }\n            Shape::Ellipse(ellipse) => {\n                self.tessellate_ellipse(ellipse, out);\n            }\n            Shape::Mesh(mesh) => {\n                profiling::scope!(\"mesh\");\n\n                if self.options.validate_meshes && !mesh.is_valid() {\n                    debug_assert!(false, \"Invalid Mesh in Shape::Mesh\");\n                    return;\n                }\n                // note: `append` still checks if the mesh is valid if extra asserts are enabled.\n\n                if self.options.coarse_tessellation_culling\n                    && !self.clip_rect.intersects(mesh.calc_bounds())\n                {\n                    return;\n                }\n\n                out.append_ref(&mesh);\n            }\n            Shape::LineSegment { points, stroke } => {\n                self.tessellate_line_segment(points, stroke, out);\n            }\n            Shape::Path(path_shape) => {\n                self.tessellate_path(&path_shape, out);\n            }\n            Shape::Rect(rect_shape) => {\n                self.tessellate_rect(&rect_shape, out);\n            }\n            Shape::Text(text_shape) => {\n                if self.options.debug_paint_text_rects {\n                    let rect = text_shape.galley.rect.translate(text_shape.pos.to_vec2());\n                    self.tessellate_rect(\n                        &RectShape::stroke(rect, 2.0, (0.5, Color32::GREEN), StrokeKind::Outside),\n                        out,\n                    );\n                }\n                self.tessellate_text(&text_shape, out);\n            }\n            Shape::QuadraticBezier(quadratic_shape) => {\n                self.tessellate_quadratic_bezier(&quadratic_shape, out);\n            }\n            Shape::CubicBezier(cubic_shape) => self.tessellate_cubic_bezier(&cubic_shape, out),\n            Shape::Callback(_) => {\n                panic!(\"Shape::Callback passed to Tessellator\");\n            }\n        }\n    }\n\n    /// Tessellate a single [`CircleShape`] into a [`Mesh`].\n    ///\n    /// * `shape`: the circle to tessellate.\n    /// * `out`: triangles are appended to this.\n    pub fn tessellate_circle(&mut self, shape: CircleShape, out: &mut Mesh) {\n        let CircleShape {\n            center,\n            radius,\n            mut fill,\n            stroke,\n        } = shape;\n\n        if radius <= 0.0 {\n            return;\n        }\n\n        if self.options.coarse_tessellation_culling\n            && !self\n                .clip_rect\n                .expand(radius + stroke.width)\n                .contains(center)\n        {\n            return;\n        }\n\n        if self.options.prerasterized_discs && fill != Color32::TRANSPARENT {\n            let radius_px = radius * self.pixels_per_point;\n            // strike the right balance between some circles becoming too blurry, and some too sharp.\n            let cutoff_radius = radius_px * 2.0_f32.powf(0.25);\n\n            // Find the right disc radius for a crisp edge:\n            // TODO(emilk): perhaps we can do something faster than this linear search.\n            for disc in &self.prepared_discs {\n                if cutoff_radius <= disc.r {\n                    let side = radius_px * disc.w / (self.pixels_per_point * disc.r);\n                    let rect = Rect::from_center_size(center, Vec2::splat(side));\n                    out.add_rect_with_uv(rect, disc.uv, fill);\n\n                    if stroke.is_empty() {\n                        return; // we are done\n                    } else {\n                        // we still need to do the stroke\n                        fill = Color32::TRANSPARENT; // don't fill again below\n                        break;\n                    }\n                }\n            }\n        }\n\n        let path_stroke = PathStroke::from(stroke).outside();\n        self.scratchpad_path.clear();\n        self.scratchpad_path.add_circle(center, radius);\n        self.scratchpad_path\n            .fill_and_stroke(self.feathering, fill, &path_stroke, out);\n    }\n\n    /// Tessellate a single [`EllipseShape`] into a [`Mesh`].\n    ///\n    /// * `shape`: the ellipse to tessellate.\n    /// * `out`: triangles are appended to this.\n    pub fn tessellate_ellipse(&mut self, shape: EllipseShape, out: &mut Mesh) {\n        let EllipseShape {\n            center,\n            radius,\n            fill,\n            stroke,\n        } = shape;\n\n        if radius.x <= 0.0 || radius.y <= 0.0 {\n            return;\n        }\n\n        if self.options.coarse_tessellation_culling\n            && !self\n                .clip_rect\n                .expand2(radius + Vec2::splat(stroke.width))\n                .contains(center)\n        {\n            return;\n        }\n\n        // Get the max pixel radius\n        let max_radius = (radius.max_elem() * self.pixels_per_point) as u32;\n\n        // Ensure there is at least 8 points in each quarter of the ellipse\n        let num_points = u32::max(8, max_radius / 16);\n\n        // Create an ease ratio based the ellipses a and b\n        let ratio = ((radius.y / radius.x) / 2.0).clamp(0.0, 1.0);\n\n        // Generate points between the 0 to pi/2\n        let quarter: Vec<Vec2> = (1..num_points)\n            .map(|i| {\n                let percent = i as f32 / num_points as f32;\n\n                // Ease the percent value, concentrating points around tight bends\n                let eased = 2.0 * (percent - percent.powf(2.0)) * ratio + percent.powf(2.0);\n\n                // Scale the ease to the quarter\n                let t = eased * std::f32::consts::FRAC_PI_2;\n                Vec2::new(radius.x * f32::cos(t), radius.y * f32::sin(t))\n            })\n            .collect();\n\n        // Build the ellipse from the 4 known vertices filling arcs between\n        // them by mirroring the points between 0 and pi/2\n        let mut points = Vec::new();\n        points.push(center + Vec2::new(radius.x, 0.0));\n        points.extend(quarter.iter().map(|p| center + *p));\n        points.push(center + Vec2::new(0.0, radius.y));\n        points.extend(quarter.iter().rev().map(|p| center + Vec2::new(-p.x, p.y)));\n        points.push(center + Vec2::new(-radius.x, 0.0));\n        points.extend(quarter.iter().map(|p| center - *p));\n        points.push(center + Vec2::new(0.0, -radius.y));\n        points.extend(quarter.iter().rev().map(|p| center + Vec2::new(p.x, -p.y)));\n\n        let path_stroke = PathStroke::from(stroke).outside();\n        self.scratchpad_path.clear();\n        self.scratchpad_path.add_line_loop(&points);\n        self.scratchpad_path\n            .fill_and_stroke(self.feathering, fill, &path_stroke, out);\n    }\n\n    /// Tessellate a single [`Mesh`] into a [`Mesh`].\n    ///\n    /// * `mesh`: the mesh to tessellate.\n    /// * `out`: triangles are appended to this.\n    pub fn tessellate_mesh(&self, mesh: &Mesh, out: &mut Mesh) {\n        if !mesh.is_valid() {\n            debug_assert!(false, \"Invalid Mesh in Shape::Mesh\");\n            return;\n        }\n\n        if self.options.coarse_tessellation_culling\n            && !self.clip_rect.intersects(mesh.calc_bounds())\n        {\n            return;\n        }\n\n        out.append_ref(mesh);\n    }\n\n    /// Tessellate a line segment between the two points with the given stroke into a [`Mesh`].\n    ///\n    /// * `shape`: the mesh to tessellate.\n    /// * `out`: triangles are appended to this.\n    pub fn tessellate_line_segment(\n        &mut self,\n        mut points: [Pos2; 2],\n        stroke: impl Into<Stroke>,\n        out: &mut Mesh,\n    ) {\n        let stroke = stroke.into();\n        if stroke.is_empty() {\n            return;\n        }\n\n        if self.options.coarse_tessellation_culling\n            && !self\n                .clip_rect\n                .intersects(Rect::from_two_pos(points[0], points[1]).expand(stroke.width))\n        {\n            return;\n        }\n\n        if self.options.round_line_segments_to_pixels {\n            let feathering = self.feathering;\n            let pixels_per_point = self.pixels_per_point;\n\n            let quarter_pixel = 0.25 * feathering; // Used to avoid fence post problem.\n\n            let [a, b] = &mut points;\n            if a.x == b.x {\n                // Vertical line\n                let mut x = a.x;\n                stroke.round_center_to_pixel(self.pixels_per_point, &mut x);\n                a.x = x;\n                b.x = x;\n\n                // Often the ends of the line are exactly on a pixel boundary,\n                // but we extend line segments with a cap that is a pixel wide…\n                // Solution: first shrink the line segment (on each end),\n                // then round to pixel center!\n                // We shrink by half-a-pixel n total (a quarter on each end),\n                // so that on average we avoid the fence-post-problem after rounding.\n                if a.y < b.y {\n                    a.y = (a.y + quarter_pixel).round_to_pixel_center(pixels_per_point);\n                    b.y = (b.y - quarter_pixel).round_to_pixel_center(pixels_per_point);\n                } else {\n                    a.y = (a.y - quarter_pixel).round_to_pixel_center(pixels_per_point);\n                    b.y = (b.y + quarter_pixel).round_to_pixel_center(pixels_per_point);\n                }\n            }\n            if a.y == b.y {\n                // Horizontal line\n                let mut y = a.y;\n                stroke.round_center_to_pixel(self.pixels_per_point, &mut y);\n                a.y = y;\n                b.y = y;\n\n                // See earlier comment for vertical lines\n                if a.x < b.x {\n                    a.x = (a.x + quarter_pixel).round_to_pixel_center(pixels_per_point);\n                    b.x = (b.x - quarter_pixel).round_to_pixel_center(pixels_per_point);\n                } else {\n                    a.x = (a.x - quarter_pixel).round_to_pixel_center(pixels_per_point);\n                    b.x = (b.x + quarter_pixel).round_to_pixel_center(pixels_per_point);\n                }\n            }\n        }\n\n        self.scratchpad_path.clear();\n        self.scratchpad_path.add_line_segment(points);\n        self.scratchpad_path\n            .stroke_open(self.feathering, &stroke.into(), out);\n    }\n\n    #[deprecated = \"Use `tessellate_line_segment` instead\"]\n    pub fn tessellate_line(\n        &mut self,\n        points: [Pos2; 2],\n        stroke: impl Into<Stroke>,\n        out: &mut Mesh,\n    ) {\n        self.tessellate_line_segment(points, stroke, out);\n    }\n\n    /// Tessellate a single [`PathShape`] into a [`Mesh`].\n    ///\n    /// * `path_shape`: the path to tessellate.\n    /// * `out`: triangles are appended to this.\n    pub fn tessellate_path(&mut self, path_shape: &PathShape, out: &mut Mesh) {\n        if path_shape.points.len() < 2 {\n            return;\n        }\n\n        if self.options.coarse_tessellation_culling\n            && !path_shape.visual_bounding_rect().intersects(self.clip_rect)\n        {\n            return;\n        }\n\n        profiling::function_scope!();\n\n        let PathShape {\n            points,\n            closed,\n            fill,\n            stroke,\n        } = path_shape;\n\n        self.scratchpad_path.clear();\n\n        if *closed {\n            self.scratchpad_path.add_line_loop(points);\n\n            self.scratchpad_path\n                .fill_and_stroke(self.feathering, *fill, stroke, out);\n        } else {\n            debug_assert_eq!(\n                *fill,\n                Color32::TRANSPARENT,\n                \"You asked to fill a path that is not closed. That makes no sense.\"\n            );\n\n            self.scratchpad_path.add_open_points(points);\n\n            self.scratchpad_path\n                .stroke(self.feathering, PathType::Open, stroke, out);\n        }\n    }\n\n    /// Tessellate a single [`Rect`] into a [`Mesh`].\n    ///\n    /// * `rect`: the rectangle to tessellate.\n    /// * `out`: triangles are appended to this.\n    pub fn tessellate_rect(&mut self, rect_shape: &RectShape, out: &mut Mesh) {\n        if self.options.coarse_tessellation_culling\n            && !rect_shape.visual_bounding_rect().intersects(self.clip_rect)\n        {\n            return;\n        }\n\n        let brush = rect_shape.brush.as_ref();\n        let RectShape {\n            mut rect,\n            corner_radius,\n            mut fill,\n            mut stroke,\n            mut stroke_kind,\n            round_to_pixels,\n            mut blur_width,\n            brush: _, // brush is extracted on its own, because it is not Copy\n        } = *rect_shape;\n\n        let mut corner_radius = CornerRadiusF32::from(corner_radius);\n        let round_to_pixels = round_to_pixels.unwrap_or(self.options.round_rects_to_pixels);\n\n        if stroke.width == 0.0 {\n            stroke.color = Color32::TRANSPARENT;\n        }\n\n        // It is common to (sometimes accidentally) create an infinitely sized rectangle.\n        // Make sure we can handle that:\n        rect.min = rect.min.at_least(pos2(-1e7, -1e7));\n        rect.max = rect.max.at_most(pos2(1e7, 1e7));\n\n        if !stroke.is_empty() {\n            // Check if the stroke covers the whole rectangle\n            let rect_with_stroke = match stroke_kind {\n                StrokeKind::Inside => rect,\n                StrokeKind::Middle => rect.expand(stroke.width / 2.0),\n                StrokeKind::Outside => rect.expand(stroke.width),\n            };\n\n            if rect_with_stroke.size().min_elem() <= 2.0 * stroke.width + 0.5 * self.feathering {\n                // The stroke covers the fill.\n                // Change this to be a fill-only shape, using the stroke color as the new fill color.\n                rect = rect_with_stroke;\n\n                // We blend so that if the stroke is semi-transparent,\n                // the fill still shines through.\n                fill = stroke.color;\n\n                stroke = Stroke::NONE;\n            }\n        }\n\n        if stroke.is_empty() && out.texture_id == TextureId::default() {\n            // Approximate thin rectangles with line segments.\n            // This is important so that thin rectangles look good.\n            if rect.width() <= 2.0 * self.feathering {\n                return self.tessellate_line_segment(\n                    [rect.center_top(), rect.center_bottom()],\n                    (rect.width(), fill),\n                    out,\n                );\n            }\n            if rect.height() <= 2.0 * self.feathering {\n                return self.tessellate_line_segment(\n                    [rect.left_center(), rect.right_center()],\n                    (rect.height(), fill),\n                    out,\n                );\n            }\n        }\n\n        // Important: round to pixels BEFORE modifying/applying stroke_kind\n        if round_to_pixels {\n            // The rounding is aware of the stroke kind.\n            // It is designed to be clever in trying to divine the intentions of the user.\n            match stroke_kind {\n                StrokeKind::Inside => {\n                    // The stroke is inside the rect, so the rect defines the _outside_ of the stroke.\n                    // We round the outside of the stroke on a pixel boundary.\n                    // This will make the outside of the stroke crisp.\n                    //\n                    // Will make each stroke asymmetric if not an even multiple of physical pixels,\n                    // but the left stroke will always be the mirror image of the right stroke,\n                    // and the top stroke will always be the mirror image of the bottom stroke.\n                    //\n                    // This is so that a user can tile rectangles with `StrokeKind::Inside`,\n                    // and get no pixel overlap between them.\n                    rect = rect.round_to_pixels(self.pixels_per_point);\n                }\n                StrokeKind::Middle => {\n                    // On this path we optimize for crisp and symmetric strokes.\n                    stroke.round_rect_to_pixel(self.pixels_per_point, &mut rect);\n                }\n                StrokeKind::Outside => {\n                    // Put the inside of the stroke on a pixel boundary.\n                    // Makes the inside of the stroke and the filled rect crisp,\n                    // but the outside of the stroke may become feathered (blurry).\n                    //\n                    // Will make each stroke asymmetric if not an even multiple of physical pixels,\n                    // but the left stroke will always be the mirror image of the right stroke,\n                    // and the top stroke will always be the mirror image of the bottom stroke.\n                    rect = rect.round_to_pixels(self.pixels_per_point);\n                }\n            }\n        }\n\n        let old_feathering = self.feathering;\n\n        if self.feathering < blur_width {\n            // We accomplish the blur by using a larger-than-normal feathering.\n            // Feathering is usually used to make the edges of a shape softer for anti-aliasing.\n\n            // The tessellator can't handle blurring/feathering larger than the smallest side of the rect.\n            let eps = 0.1; // avoid numerical problems\n            blur_width = blur_width\n                .at_most(rect.size().min_elem() - eps - 2.0 * stroke.width)\n                .at_least(0.0);\n\n            corner_radius += 0.5 * blur_width;\n\n            self.feathering = self.feathering.max(blur_width);\n        }\n\n        {\n            // Modify `rect` so that it represents the OUTER border\n            // We do this because `path::rounded_rectangle` uses the\n            // corner radius to pick the fidelity/resolution of the corner.\n\n            let original_cr = corner_radius;\n\n            match stroke_kind {\n                StrokeKind::Inside => {}\n                StrokeKind::Middle => {\n                    rect = rect.expand(stroke.width / 2.0);\n                    corner_radius += stroke.width / 2.0;\n                }\n                StrokeKind::Outside => {\n                    rect = rect.expand(stroke.width);\n                    corner_radius += stroke.width;\n                }\n            }\n\n            stroke_kind = StrokeKind::Inside;\n\n            // A small corner_radius is incompatible with a wide stroke,\n            // because the small bend will be extruded inwards and cross itself.\n            // There are two ways to solve this (wile maintaining constant stroke width):\n            // either we increase the corner_radius, or we set it to zero.\n            // We choose the former: if the user asks for _any_ corner_radius, they should get it.\n\n            let min_inside_cr = 0.1; // Large enough to avoid numerical issues\n            let min_outside_cr = stroke.width + min_inside_cr;\n\n            let extra_cr_tweak = 0.4; // Otherwise is doesn't _feels_  enough.\n\n            if original_cr.nw == 0.0 {\n                corner_radius.nw = 0.0;\n            } else {\n                corner_radius.nw += extra_cr_tweak;\n                corner_radius.nw = corner_radius.nw.at_least(min_outside_cr);\n            }\n            if original_cr.ne == 0.0 {\n                corner_radius.ne = 0.0;\n            } else {\n                corner_radius.ne += extra_cr_tweak;\n                corner_radius.ne = corner_radius.ne.at_least(min_outside_cr);\n            }\n            if original_cr.sw == 0.0 {\n                corner_radius.sw = 0.0;\n            } else {\n                corner_radius.sw += extra_cr_tweak;\n                corner_radius.sw = corner_radius.sw.at_least(min_outside_cr);\n            }\n            if original_cr.se == 0.0 {\n                corner_radius.se = 0.0;\n            } else {\n                corner_radius.se += extra_cr_tweak;\n                corner_radius.se = corner_radius.se.at_least(min_outside_cr);\n            }\n        }\n\n        let path = &mut self.scratchpad_path;\n        path.clear();\n        path::rounded_rectangle(&mut self.scratchpad_points, rect, corner_radius);\n        path.add_line_loop(&self.scratchpad_points);\n\n        let path_stroke = PathStroke::from(stroke).with_kind(stroke_kind);\n\n        if let Some(brush) = brush {\n            // Textured fill\n\n            let fill_rect = match stroke_kind {\n                StrokeKind::Inside => rect.shrink(stroke.width),\n                StrokeKind::Middle => rect.shrink(stroke.width / 2.0),\n                StrokeKind::Outside => rect,\n            };\n\n            if fill_rect.is_positive() {\n                let crate::Brush {\n                    fill_texture_id,\n                    uv,\n                } = **brush;\n                let uv_from_pos = |p: Pos2| {\n                    pos2(\n                        remap(p.x, rect.x_range(), uv.x_range()),\n                        remap(p.y, rect.y_range(), uv.y_range()),\n                    )\n                };\n                path.fill_with_uv(self.feathering, fill, fill_texture_id, uv_from_pos, out);\n            }\n\n            if !stroke.is_empty() {\n                path.stroke_closed(self.feathering, &path_stroke, out);\n            }\n        } else {\n            // Stroke and maybe fill\n            path.fill_and_stroke(self.feathering, fill, &path_stroke, out);\n        }\n\n        self.feathering = old_feathering; // restore\n    }\n\n    /// Tessellate a single [`TextShape`] into a [`Mesh`].\n    /// * `text_shape`: the text to tessellate.\n    /// * `out`: triangles are appended to this.\n    pub fn tessellate_text(&mut self, text_shape: &TextShape, out: &mut Mesh) {\n        let TextShape {\n            pos: galley_pos,\n            galley,\n            underline,\n            override_text_color,\n            fallback_color,\n            opacity_factor,\n            angle,\n        } = text_shape;\n\n        if galley.is_empty() {\n            return;\n        }\n\n        if *opacity_factor <= 0.0 {\n            return;\n        }\n\n        if galley.pixels_per_point != self.pixels_per_point {\n            log::warn!(\n                \"epaint: WARNING: pixels_per_point (dpi scale) have changed between text layout and tessellation. \\\n                       You must recreate your text shapes if pixels_per_point changes.\"\n            );\n        }\n\n        out.vertices.reserve(galley.num_vertices);\n        out.indices.reserve(galley.num_indices);\n\n        // The contents of the galley are already snapped to pixel coordinates,\n        // but we need to make sure the galley ends up on the start of a physical pixel:\n        let galley_pos = if self.options.round_text_to_pixels {\n            galley_pos.round_to_pixels(self.pixels_per_point)\n        } else {\n            *galley_pos\n        };\n\n        let uv_normalizer = vec2(\n            1.0 / self.font_tex_size[0] as f32,\n            1.0 / self.font_tex_size[1] as f32,\n        );\n\n        let rotator = Rot2::from_angle(*angle);\n\n        for row in &galley.rows {\n            if row.visuals.mesh.is_empty() {\n                continue;\n            }\n\n            let final_row_pos = galley_pos + rotator * row.pos.to_vec2();\n\n            let mut row_rect = row.visuals.mesh_bounds;\n            if *angle != 0.0 {\n                row_rect = row_rect.rotate_bb(rotator);\n            }\n            row_rect = row_rect.translate(final_row_pos.to_vec2());\n\n            if self.options.coarse_tessellation_culling && !self.clip_rect.intersects(row_rect) {\n                // culling individual lines of text is important, since a single `Shape::Text`\n                // can span hundreds of lines.\n                continue;\n            }\n\n            let index_offset = out.vertices.len() as u32;\n\n            out.indices.extend(\n                row.visuals\n                    .mesh\n                    .indices\n                    .iter()\n                    .map(|index| index + index_offset),\n            );\n\n            out.vertices.extend(\n                row.visuals\n                    .mesh\n                    .vertices\n                    .iter()\n                    .enumerate()\n                    .map(|(i, vertex)| {\n                        let Vertex { pos, uv, mut color } = *vertex;\n\n                        if let Some(override_text_color) = override_text_color {\n                            // Only override the glyph color (not background color, strike-through color, etc)\n                            if row.visuals.glyph_vertex_range.contains(&i) {\n                                color = *override_text_color;\n                            }\n                        } else if color == Color32::PLACEHOLDER {\n                            color = *fallback_color;\n                        }\n\n                        if *opacity_factor < 1.0 {\n                            color = color.gamma_multiply(*opacity_factor);\n                        }\n\n                        debug_assert!(color != Color32::PLACEHOLDER, \"A placeholder color made it to the tessellator. You forgot to set a fallback color.\");\n\n                        let offset = if *angle == 0.0 {\n                            pos.to_vec2()\n                        } else {\n                            rotator * pos.to_vec2()\n                        };\n\n                        Vertex {\n                            pos: final_row_pos + offset,\n                            uv: (uv.to_vec2() * uv_normalizer).to_pos2(),\n                            color,\n                        }\n                    }),\n            );\n\n            if *underline != Stroke::NONE {\n                self.tessellate_line_segment(\n                    [row_rect.left_bottom(), row_rect.right_bottom()],\n                    *underline,\n                    out,\n                );\n            }\n        }\n    }\n\n    /// Tessellate a single [`QuadraticBezierShape`] into a [`Mesh`].\n    ///\n    /// * `quadratic_shape`: the shape to tessellate.\n    /// * `out`: triangles are appended to this.\n    pub fn tessellate_quadratic_bezier(\n        &mut self,\n        quadratic_shape: &QuadraticBezierShape,\n        out: &mut Mesh,\n    ) {\n        let options = &self.options;\n        let clip_rect = self.clip_rect;\n\n        if options.coarse_tessellation_culling\n            && !quadratic_shape.visual_bounding_rect().intersects(clip_rect)\n        {\n            return;\n        }\n\n        let points = quadratic_shape.flatten(Some(options.bezier_tolerance));\n\n        self.tessellate_bezier_complete(\n            &points,\n            quadratic_shape.fill,\n            quadratic_shape.closed,\n            &quadratic_shape.stroke,\n            out,\n        );\n    }\n\n    /// Tessellate a single [`CubicBezierShape`] into a [`Mesh`].\n    ///\n    /// * `cubic_shape`: the shape to tessellate.\n    /// * `out`: triangles are appended to this.\n    pub fn tessellate_cubic_bezier(&mut self, cubic_shape: &CubicBezierShape, out: &mut Mesh) {\n        let options = &self.options;\n        let clip_rect = self.clip_rect;\n        if options.coarse_tessellation_culling\n            && !cubic_shape.visual_bounding_rect().intersects(clip_rect)\n        {\n            return;\n        }\n\n        let points_vec =\n            cubic_shape.flatten_closed(Some(options.bezier_tolerance), Some(options.epsilon));\n\n        for points in points_vec {\n            self.tessellate_bezier_complete(\n                &points,\n                cubic_shape.fill,\n                cubic_shape.closed,\n                &cubic_shape.stroke,\n                out,\n            );\n        }\n    }\n\n    fn tessellate_bezier_complete(\n        &mut self,\n        points: &[Pos2],\n        fill: Color32,\n        closed: bool,\n        stroke: &PathStroke,\n        out: &mut Mesh,\n    ) {\n        if points.len() < 2 {\n            return;\n        }\n\n        self.scratchpad_path.clear();\n        if closed {\n            self.scratchpad_path.add_line_loop(points);\n\n            self.scratchpad_path\n                .fill_and_stroke(self.feathering, fill, stroke, out);\n        } else {\n            debug_assert_eq!(\n                fill,\n                Color32::TRANSPARENT,\n                \"You asked to fill a bezier path that is not closed. That makes no sense.\"\n            );\n\n            self.scratchpad_path.add_open_points(points);\n\n            self.scratchpad_path\n                .stroke(self.feathering, PathType::Open, stroke, out);\n        }\n    }\n}\n\nimpl Tessellator {\n    /// Turns [`Shape`]:s into sets of triangles.\n    ///\n    /// The given shapes will tessellated in the same order as they are given.\n    /// They will be batched together by clip rectangle.\n    ///\n    /// * `pixels_per_point`: number of physical pixels to each logical point\n    /// * `options`: tessellation quality\n    /// * `shapes`: what to tessellate\n    /// * `font_tex_size`: size of the font texture. Required to normalize glyph uv rectangles when tessellating text.\n    /// * `prepared_discs`: What [`crate::TextureAtlas::prepared_discs`] returns. Can safely be set to an empty vec.\n    ///\n    /// The implementation uses a [`Tessellator`].\n    ///\n    /// ## Returns\n    /// A list of clip rectangles with matching [`Mesh`].\n    #[allow(clippy::allow_attributes, unused_mut)]\n    pub fn tessellate_shapes(&mut self, mut shapes: Vec<ClippedShape>) -> Vec<ClippedPrimitive> {\n        profiling::function_scope!();\n\n        #[cfg(feature = \"rayon\")]\n        if self.options.parallel_tessellation {\n            self.parallel_tessellation_of_large_shapes(&mut shapes);\n        }\n\n        let mut clipped_primitives: Vec<ClippedPrimitive> = Vec::default();\n\n        {\n            profiling::scope!(\"tessellate\");\n            for clipped_shape in shapes {\n                self.tessellate_clipped_shape(clipped_shape, &mut clipped_primitives);\n            }\n        }\n\n        if self.options.debug_paint_clip_rects {\n            clipped_primitives = self.add_clip_rects(clipped_primitives);\n        }\n\n        if self.options.debug_ignore_clip_rects {\n            for clipped_primitive in &mut clipped_primitives {\n                clipped_primitive.clip_rect = Rect::EVERYTHING;\n            }\n        }\n\n        clipped_primitives.retain(|p| {\n            p.clip_rect.is_positive()\n                && match &p.primitive {\n                    Primitive::Mesh(mesh) => !mesh.is_empty(),\n                    Primitive::Callback(_) => true,\n                }\n        });\n\n        for clipped_primitive in &clipped_primitives {\n            if let Primitive::Mesh(mesh) = &clipped_primitive.primitive {\n                debug_assert!(mesh.is_valid(), \"Tessellator generated invalid Mesh\");\n            }\n        }\n\n        clipped_primitives\n    }\n\n    /// Find large shapes and throw them on the rayon thread pool,\n    /// then replace the original shape with their tessellated meshes.\n    #[cfg(feature = \"rayon\")]\n    fn parallel_tessellation_of_large_shapes(&self, shapes: &mut [ClippedShape]) {\n        profiling::function_scope!();\n\n        use rayon::prelude::*;\n\n        // We only parallelize large/slow stuff, because each tessellation job\n        // will allocate a new Mesh, and so it creates a lot of extra memory fragmentation\n        // and allocations that is only worth it for large shapes.\n        fn should_parallelize(shape: &Shape) -> bool {\n            match shape {\n                Shape::Vec(shapes) => 4 < shapes.len() || shapes.iter().any(should_parallelize),\n\n                Shape::Path(path_shape) => 32 < path_shape.points.len(),\n\n                Shape::QuadraticBezier(_) | Shape::CubicBezier(_) | Shape::Ellipse(_) => true,\n\n                Shape::Noop\n                | Shape::Text(_)\n                | Shape::Circle(_)\n                | Shape::Mesh(_)\n                | Shape::LineSegment { .. }\n                | Shape::Rect(_)\n                | Shape::Callback(_) => false,\n            }\n        }\n\n        let tessellated: Vec<(usize, Mesh)> = shapes\n            .par_iter()\n            .enumerate()\n            .filter(|(_, clipped_shape)| should_parallelize(&clipped_shape.shape))\n            .map(|(index, clipped_shape)| {\n                profiling::scope!(\"tessellate_big_shape\");\n                // TODO(emilk): reuse tessellator in a thread local\n                let mut tessellator = (*self).clone();\n                let mut mesh = Mesh::default();\n                tessellator.tessellate_shape(clipped_shape.shape.clone(), &mut mesh);\n                (index, mesh)\n            })\n            .collect();\n\n        profiling::scope!(\"distribute results\", tessellated.len().to_string());\n        for (index, mesh) in tessellated {\n            shapes[index].shape = Shape::Mesh(mesh.into());\n        }\n    }\n\n    fn add_clip_rects(\n        &mut self,\n        clipped_primitives: Vec<ClippedPrimitive>,\n    ) -> Vec<ClippedPrimitive> {\n        self.clip_rect = Rect::EVERYTHING;\n        let stroke = Stroke::new(2.0, Color32::from_rgb(150, 255, 150));\n\n        clipped_primitives\n            .into_iter()\n            .flat_map(|clipped_primitive| {\n                let mut clip_rect_mesh = Mesh::default();\n                self.tessellate_shape(\n                    Shape::rect_stroke(\n                        clipped_primitive.clip_rect,\n                        0.0,\n                        stroke,\n                        StrokeKind::Outside,\n                    ),\n                    &mut clip_rect_mesh,\n                );\n\n                [\n                    clipped_primitive,\n                    ClippedPrimitive {\n                        clip_rect: Rect::EVERYTHING, // whatever\n                        primitive: Primitive::Mesh(clip_rect_mesh),\n                    },\n                ]\n            })\n            .collect()\n    }\n}\n\n#[test]\nfn test_tessellator() {\n    use crate::*;\n\n    let mut shapes = Vec::with_capacity(2);\n\n    let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));\n    let uv = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));\n\n    let mut mesh = Mesh::with_texture(TextureId::Managed(1));\n    mesh.add_rect_with_uv(rect, uv, Color32::WHITE);\n    shapes.push(Shape::mesh(mesh));\n\n    let mut mesh = Mesh::with_texture(TextureId::Managed(2));\n    mesh.add_rect_with_uv(rect, uv, Color32::WHITE);\n    shapes.push(Shape::mesh(mesh));\n\n    let shape = Shape::Vec(shapes);\n    let clipped_shapes = vec![ClippedShape {\n        clip_rect: rect,\n        shape,\n    }];\n\n    let font_tex_size = [1024, 1024]; // unused\n    let prepared_discs = vec![]; // unused\n\n    let primitives = Tessellator::new(1.0, Default::default(), font_tex_size, prepared_discs)\n        .tessellate_shapes(clipped_shapes);\n\n    assert_eq!(primitives.len(), 2);\n}\n\n#[test]\nfn path_bounding_box() {\n    use crate::*;\n\n    for i in 1..=100 {\n        let width = i as f32;\n\n        let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(10.0, 10.0));\n        let expected_rect = rect.expand((width / 2.0) + 1.5);\n\n        let mut mesh = Mesh::default();\n\n        let mut path = Path::default();\n        path.add_open_points(&[\n            pos2(0.0, 0.0),\n            pos2(2.0, 0.0),\n            pos2(5.0, 5.0),\n            pos2(0.0, 5.0),\n            pos2(0.0, 7.0),\n            pos2(10.0, 10.0),\n        ]);\n\n        path.stroke(\n            1.5,\n            PathType::Closed,\n            &PathStroke::new_uv(width, move |r, p| {\n                assert_eq!(r, expected_rect);\n                // see https://github.com/emilk/egui/pull/4353#discussion_r1573879940 for why .contains() isn't used here.\n                // TL;DR rounding errors.\n                assert!(\n                    r.distance_to_pos(p) <= 0.55,\n                    \"passed rect {r:?} didn't contain point {p:?} (distance: {})\",\n                    r.distance_to_pos(p)\n                );\n                assert!(\n                    expected_rect.distance_to_pos(p) <= 0.55,\n                    \"expected rect {expected_rect:?} didn't contain point {p:?}\"\n                );\n                Color32::WHITE\n            }),\n            &mut mesh,\n        );\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/text/cursor.rs",
    "content": "//! Different types of text cursors, i.e. ways to point into a [`super::Galley`].\n\n/// Character cursor.\n///\n/// The default cursor is zero.\n#[derive(Clone, Copy, Debug, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct CCursor {\n    /// Character offset (NOT byte offset!).\n    pub index: usize,\n\n    /// If this cursors sits right at the border of a wrapped row break (NOT paragraph break)\n    /// do we prefer the next row?\n    /// This is *almost* always what you want, *except* for when\n    /// explicitly clicking the end of a row or pressing the end key.\n    pub prefer_next_row: bool,\n}\n\nimpl CCursor {\n    #[inline]\n    pub fn new(index: usize) -> Self {\n        Self {\n            index,\n            prefer_next_row: false,\n        }\n    }\n}\n\n/// Two `CCursor`s are considered equal if they refer to the same character boundary,\n/// even if one prefers the start of the next row.\nimpl PartialEq for CCursor {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.index == other.index\n    }\n}\n\nimpl std::ops::Add<usize> for CCursor {\n    type Output = Self;\n\n    fn add(self, rhs: usize) -> Self::Output {\n        Self {\n            index: self.index.saturating_add(rhs),\n            prefer_next_row: self.prefer_next_row,\n        }\n    }\n}\n\nimpl std::ops::Sub<usize> for CCursor {\n    type Output = Self;\n\n    fn sub(self, rhs: usize) -> Self::Output {\n        Self {\n            index: self.index.saturating_sub(rhs),\n            prefer_next_row: self.prefer_next_row,\n        }\n    }\n}\n\nimpl std::ops::AddAssign<usize> for CCursor {\n    fn add_assign(&mut self, rhs: usize) {\n        self.index = self.index.saturating_add(rhs);\n    }\n}\n\nimpl std::ops::SubAssign<usize> for CCursor {\n    fn sub_assign(&mut self, rhs: usize) {\n        self.index = self.index.saturating_sub(rhs);\n    }\n}\n\n/// Row/column cursor.\n///\n/// This refers to rows and columns in layout terms--text wrapping creates multiple rows.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct LayoutCursor {\n    /// 0 is first row, and so on.\n    /// Note that a single paragraph can span multiple rows.\n    /// (a paragraph is text separated by `\\n`).\n    pub row: usize,\n\n    /// Character based (NOT bytes).\n    /// It is fine if this points to something beyond the end of the current row.\n    /// When moving up/down it may again be within the next row.\n    pub column: usize,\n}\n"
  },
  {
    "path": "crates/epaint/src/text/font.rs",
    "content": "#![expect(clippy::mem_forget)]\n\nuse emath::{GuiRounding as _, OrderedFloat, Vec2, vec2};\nuse self_cell::self_cell;\nuse skrifa::{\n    MetadataProvider as _,\n    raw::{TableProvider as _, tables::kern::SubtableKind},\n};\nuse std::collections::BTreeMap;\nuse vello_cpu::{color, kurbo};\n\nuse crate::{\n    TextOptions, TextureAtlas,\n    text::{\n        FontTweak, VariationCoords,\n        fonts::{Blob, CachedFamily, FontFaceKey},\n    },\n};\n\n// ----------------------------------------------------------------------------\n\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct UvRect {\n    /// X/Y offset for nice rendering (unit: points).\n    pub offset: Vec2,\n\n    /// Screen size (in points) of this glyph.\n    /// Note that the height is different from the font height.\n    pub size: Vec2,\n\n    /// Top left corner UV in texture.\n    pub min: [u16; 2],\n\n    /// Bottom right corner (exclusive).\n    pub max: [u16; 2],\n}\n\nimpl UvRect {\n    pub fn is_nothing(&self) -> bool {\n        self.min == self.max\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\npub struct GlyphInfo {\n    /// Used for pair-kerning.\n    ///\n    /// Doesn't need to be unique.\n    ///\n    /// Is `None` for a special \"invisible\" glyph.\n    pub(crate) id: Option<skrifa::GlyphId>,\n\n    /// In [`skrifa`]s \"unscaled\" coordinate system.\n    pub advance_width_unscaled: OrderedFloat<f32>,\n}\n\nimpl GlyphInfo {\n    /// A valid, but invisible, glyph of zero-width.\n    pub const INVISIBLE: Self = Self {\n        id: None,\n        advance_width_unscaled: OrderedFloat(0.0),\n    };\n}\n\n// Subpixel binning, taken from cosmic-text:\n// https://github.com/pop-os/cosmic-text/blob/974ddaed96b334f560b606ebe5d2ca2d2f9f23ef/src/glyph_cache.rs\n\n/// Bin for subpixel positioning of glyphs.\n///\n/// For accurate glyph positioning, we want to render each glyph at a subpixel coordinate. However, we also want to\n/// cache each glyph's bitmap. As a compromise, we bin each subpixel offset into one of four fractional values. This\n/// means one glyph can have up to four subpixel-positioned bitmaps in the cache.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]\npub(super) enum SubpixelBin {\n    #[default]\n    Zero,\n    One,\n    Two,\n    Three,\n}\n\nimpl SubpixelBin {\n    /// Bin the given position and return the new integral coordinate.\n    fn new(pos: f32) -> (i32, Self) {\n        let trunc = pos as i32;\n        let fract = pos - trunc as f32;\n\n        #[expect(clippy::collapsible_else_if)]\n        if pos.is_sign_negative() {\n            if fract > -0.125 {\n                (trunc, Self::Zero)\n            } else if fract > -0.375 {\n                (trunc - 1, Self::Three)\n            } else if fract > -0.625 {\n                (trunc - 1, Self::Two)\n            } else if fract > -0.875 {\n                (trunc - 1, Self::One)\n            } else {\n                (trunc - 1, Self::Zero)\n            }\n        } else {\n            if fract < 0.125 {\n                (trunc, Self::Zero)\n            } else if fract < 0.375 {\n                (trunc, Self::One)\n            } else if fract < 0.625 {\n                (trunc, Self::Two)\n            } else if fract < 0.875 {\n                (trunc, Self::Three)\n            } else {\n                (trunc + 1, Self::Zero)\n            }\n        }\n    }\n\n    pub fn as_float(&self) -> f32 {\n        match self {\n            Self::Zero => 0.0,\n            Self::One => 0.25,\n            Self::Two => 0.5,\n            Self::Three => 0.75,\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Default)]\npub struct GlyphAllocation {\n    /// Used for pair-kerning.\n    ///\n    /// Doesn't need to be unique.\n    /// Use [`skrifa::GlyphId::NOTDEF`] if you just want to have an id, and don't care.\n    pub(crate) id: skrifa::GlyphId,\n\n    /// Unit: screen pixels.\n    pub advance_width_px: f32,\n\n    /// UV rectangle for drawing.\n    pub uv_rect: UvRect,\n}\n\n#[derive(Hash, PartialEq, Eq)]\nstruct GlyphCacheKey(u64);\n\nimpl nohash_hasher::IsEnabled for GlyphCacheKey {}\n\nimpl GlyphCacheKey {\n    fn new(glyph_id: skrifa::GlyphId, metrics: &StyledMetrics, bin: SubpixelBin) -> Self {\n        let StyledMetrics {\n            pixels_per_point,\n            px_scale_factor,\n            ..\n        } = *metrics;\n        debug_assert!(\n            0.0 < pixels_per_point && pixels_per_point.is_finite(),\n            \"Bad pixels_per_point {pixels_per_point}\"\n        );\n        debug_assert!(\n            0.0 < px_scale_factor && px_scale_factor.is_finite(),\n            \"Bad px_scale_factor: {px_scale_factor}\"\n        );\n        Self(crate::util::hash((\n            glyph_id,\n            pixels_per_point.to_bits(),\n            px_scale_factor.to_bits(),\n            bin,\n        )))\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nstruct DependentFontData<'a> {\n    skrifa: skrifa::FontRef<'a>,\n    charmap: skrifa::charmap::Charmap<'a>,\n    outline_glyphs: skrifa::outline::OutlineGlyphCollection<'a>,\n    metrics: skrifa::metrics::Metrics,\n    glyph_metrics: skrifa::metrics::GlyphMetrics<'a>,\n    hinting_instance: Option<skrifa::outline::HintingInstance>,\n}\n\nself_cell! {\n    struct FontCell {\n        owner: Blob,\n\n        #[covariant]\n        dependent: DependentFontData,\n    }\n}\n\nimpl FontCell {\n    fn px_scale_factor(&self, scale: f32) -> f32 {\n        let units_per_em = self.borrow_dependent().metrics.units_per_em as f32;\n        scale / units_per_em\n    }\n\n    fn allocate_glyph_uncached(\n        &mut self,\n        atlas: &mut TextureAtlas,\n        metrics: &StyledMetrics,\n        glyph_info: &GlyphInfo,\n        bin: SubpixelBin,\n        location: skrifa::instance::LocationRef<'_>,\n    ) -> Option<GlyphAllocation> {\n        let glyph_id = glyph_info.id?;\n\n        debug_assert!(\n            glyph_id != skrifa::GlyphId::NOTDEF,\n            \"Can't allocate glyph for id 0\"\n        );\n\n        let mut path = kurbo::BezPath::new();\n        let mut pen = VelloPen {\n            path: &mut path,\n            x_offset: bin.as_float() as f64,\n        };\n\n        self.with_dependent_mut(|_, font_data| {\n            let outline = font_data.outline_glyphs.get(glyph_id)?;\n\n            if let Some(hinting_instance) = &mut font_data.hinting_instance {\n                let size = skrifa::instance::Size::new(metrics.scale);\n                if hinting_instance.size() != size {\n                    hinting_instance\n                        .reconfigure(\n                            &font_data.outline_glyphs,\n                            size,\n                            location,\n                            skrifa::outline::Target::Smooth {\n                                mode: skrifa::outline::SmoothMode::Normal,\n                                symmetric_rendering: true,\n                                preserve_linear_metrics: true,\n                            },\n                        )\n                        .ok()?;\n                }\n                let draw_settings = skrifa::outline::DrawSettings::hinted(hinting_instance, false);\n                outline.draw(draw_settings, &mut pen).ok()?;\n            } else {\n                let draw_settings = skrifa::outline::DrawSettings::unhinted(\n                    skrifa::instance::Size::new(metrics.scale),\n                    location,\n                );\n                outline.draw(draw_settings, &mut pen).ok()?;\n            }\n\n            Some(())\n        })?;\n\n        let bounds = path.control_box().expand();\n        let width = bounds.width() as u16;\n        let height = bounds.height() as u16;\n\n        let mut ctx = vello_cpu::RenderContext::new(width, height);\n        ctx.set_transform(kurbo::Affine::translate((-bounds.x0, -bounds.y0)));\n        ctx.set_paint(color::OpaqueColor::<color::Srgb>::WHITE);\n        ctx.fill_path(&path);\n        let mut dest = vello_cpu::Pixmap::new(width, height);\n        ctx.render_to_pixmap(&mut dest);\n        let uv_rect = if width == 0 || height == 0 {\n            UvRect::default()\n        } else {\n            let glyph_pos = {\n                let alpha_from_coverage = atlas.options().alpha_from_coverage;\n                let (glyph_pos, image) = atlas.allocate((width as usize, height as usize));\n                let pixels = dest.data_as_u8_slice();\n                for y in 0..height as usize {\n                    for x in 0..width as usize {\n                        image[(x + glyph_pos.0, y + glyph_pos.1)] = alpha_from_coverage\n                            .color_from_coverage(\n                                pixels[((y * width as usize) + x) * 4 + 3] as f32 / 255.0,\n                            );\n                    }\n                }\n                glyph_pos\n            };\n            let offset_in_pixels = vec2(bounds.x0 as f32, bounds.y0 as f32);\n            let offset =\n                offset_in_pixels / metrics.pixels_per_point + metrics.y_offset_in_points * Vec2::Y;\n            UvRect {\n                offset,\n                size: vec2(width as f32, height as f32) / metrics.pixels_per_point,\n                min: [glyph_pos.0 as u16, glyph_pos.1 as u16],\n                max: [\n                    (glyph_pos.0 + width as usize) as u16,\n                    (glyph_pos.1 + height as usize) as u16,\n                ],\n            }\n        };\n\n        Some(GlyphAllocation {\n            id: glyph_id,\n            advance_width_px: glyph_info.advance_width_unscaled.0 * metrics.px_scale_factor,\n            uv_rect,\n        })\n    }\n}\n\nstruct VelloPen<'a> {\n    path: &'a mut kurbo::BezPath,\n    x_offset: f64,\n}\n\nimpl skrifa::outline::OutlinePen for VelloPen<'_> {\n    fn move_to(&mut self, x: f32, y: f32) {\n        self.path.move_to((x as f64 + self.x_offset, -y as f64));\n    }\n\n    fn line_to(&mut self, x: f32, y: f32) {\n        self.path.line_to((x as f64 + self.x_offset, -y as f64));\n    }\n\n    fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {\n        self.path.quad_to(\n            (cx0 as f64 + self.x_offset, -cy0 as f64),\n            (x as f64 + self.x_offset, -y as f64),\n        );\n    }\n\n    fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {\n        self.path.curve_to(\n            (cx0 as f64 + self.x_offset, -cy0 as f64),\n            (cx1 as f64 + self.x_offset, -cy1 as f64),\n            (x as f64 + self.x_offset, -y as f64),\n        );\n    }\n\n    fn close(&mut self) {\n        self.path.close_path();\n    }\n}\n\n/// A specific font face.\n/// The interface uses points as the unit for everything.\npub struct FontFace {\n    name: String,\n    font: FontCell,\n    tweak: FontTweak,\n\n    glyph_info_cache: ahash::HashMap<char, GlyphInfo>,\n    glyph_alloc_cache: ahash::HashMap<GlyphCacheKey, GlyphAllocation>,\n}\n\nimpl FontFace {\n    pub fn new(\n        options: TextOptions,\n        name: String,\n        font_data: Blob,\n        index: u32,\n        tweak: FontTweak,\n    ) -> Result<Self, Box<dyn std::error::Error>> {\n        let font = FontCell::try_new(font_data, |font_data| {\n            let skrifa_font =\n                skrifa::FontRef::from_index(AsRef::<[u8]>::as_ref(font_data.as_ref()), index)?;\n\n            let charmap = skrifa_font.charmap();\n            let glyphs = skrifa_font.outline_glyphs();\n\n            // Note: We use default location here during initialization because\n            // the actual weight will be applied via the stored location during rendering.\n            // The metrics won't be significantly different at this unscaled size.\n            let metrics = skrifa_font.metrics(\n                skrifa::instance::Size::unscaled(),\n                skrifa::instance::LocationRef::default(),\n            );\n            let glyph_metrics = skrifa_font.glyph_metrics(\n                skrifa::instance::Size::unscaled(),\n                skrifa::instance::LocationRef::default(),\n            );\n\n            let hinting_enabled = tweak.hinting_override.unwrap_or(options.font_hinting);\n            let hinting_instance = hinting_enabled\n                .then(|| {\n                    // It doesn't really matter what we put here for options. Since the size is `unscaled()`, we will\n                    // always reconfigure this hinting instance with the real options when rendering for the first time.\n                    skrifa::outline::HintingInstance::new(\n                        &glyphs,\n                        skrifa::instance::Size::unscaled(),\n                        skrifa::instance::LocationRef::default(),\n                        skrifa::outline::Target::default(),\n                    )\n                    .ok()\n                })\n                .flatten();\n\n            Ok::<DependentFontData<'_>, Box<dyn std::error::Error>>(DependentFontData {\n                skrifa: skrifa_font,\n                charmap,\n                outline_glyphs: glyphs,\n                metrics,\n                glyph_metrics,\n                hinting_instance,\n            })\n        })?;\n\n        Ok(Self {\n            name,\n            font,\n            tweak,\n            glyph_info_cache: Default::default(),\n            glyph_alloc_cache: Default::default(),\n        })\n    }\n\n    /// Code points that will always be replaced by the replacement character.\n    ///\n    /// See also [`invisible_char`].\n    fn ignore_character(&self, chr: char) -> bool {\n        use crate::text::FontDefinitions;\n\n        if !FontDefinitions::builtin_font_names().contains(&self.name.as_str()) {\n            return false;\n        }\n\n        matches!(\n            chr,\n            // Strip out a religious symbol with secondary nefarious interpretation:\n            '\\u{534d}' | '\\u{5350}' |\n\n            // Ignore ubuntu-specific stuff in `Ubuntu-Light.ttf`:\n            '\\u{E0FF}' | '\\u{EFFD}' | '\\u{F0FF}' | '\\u{F200}'\n        )\n    }\n\n    /// An un-ordered iterator over all supported characters.\n    fn characters(&self) -> impl Iterator<Item = char> + '_ {\n        self.font\n            .borrow_dependent()\n            .charmap\n            .mappings()\n            .filter_map(|(chr, _)| char::from_u32(chr).filter(|c| !self.ignore_character(*c)))\n    }\n\n    /// `\\n` will result in `None`\n    pub(super) fn glyph_info(&mut self, c: char) -> Option<GlyphInfo> {\n        if let Some(glyph_info) = self.glyph_info_cache.get(&c) {\n            return Some(*glyph_info);\n        }\n\n        if self.ignore_character(c) {\n            return None; // these will result in the replacement character when rendering\n        }\n\n        if c == '\\t'\n            && let Some(space) = self.glyph_info(' ')\n        {\n            let glyph_info = GlyphInfo {\n                advance_width_unscaled: (crate::text::TAB_SIZE as f32\n                    * space.advance_width_unscaled.0)\n                    .into(),\n                ..space\n            };\n            self.glyph_info_cache.insert(c, glyph_info);\n            return Some(glyph_info);\n        }\n\n        if c == '\\u{2009}' {\n            // Thin space, often used as thousands deliminator: 1 234 567 890\n            // https://www.compart.com/en/unicode/U+2009\n            // https://en.wikipedia.org/wiki/Thin_space\n\n            if let Some(space) = self.glyph_info(' ') {\n                let em = self.font.borrow_dependent().metrics.units_per_em as f32;\n                let advance_width = f32::min(em / 6.0, space.advance_width_unscaled.0 * 0.5); // TODO(emilk): make configurable\n                let glyph_info = GlyphInfo {\n                    advance_width_unscaled: advance_width.into(),\n                    ..space\n                };\n                self.glyph_info_cache.insert(c, glyph_info);\n                return Some(glyph_info);\n            }\n        }\n\n        if invisible_char(c) {\n            let glyph_info = GlyphInfo::INVISIBLE;\n            self.glyph_info_cache.insert(c, glyph_info);\n            return Some(glyph_info);\n        }\n\n        let font_data = self.font.borrow_dependent();\n\n        // Add new character:\n        let glyph_id = font_data\n            .charmap\n            .map(c)\n            .filter(|id| *id != skrifa::GlyphId::NOTDEF)?;\n\n        let glyph_info = GlyphInfo {\n            id: Some(glyph_id),\n            advance_width_unscaled: font_data\n                .glyph_metrics\n                .advance_width(glyph_id)\n                .unwrap_or_default()\n                .into(),\n        };\n        self.glyph_info_cache.insert(c, glyph_info);\n        Some(glyph_info)\n    }\n\n    #[inline]\n    pub(super) fn pair_kerning_pixels(\n        &self,\n        metrics: &StyledMetrics,\n        last_glyph_id: skrifa::GlyphId,\n        glyph_id: skrifa::GlyphId,\n    ) -> f32 {\n        let skrifa_font = &self.font.borrow_dependent().skrifa;\n        let Ok(kern) = skrifa_font.kern() else {\n            return 0.0;\n        };\n        kern.subtables()\n            .find_map(|st| match st.ok()?.kind().ok()? {\n                SubtableKind::Format0(table_ref) => table_ref.kerning(last_glyph_id, glyph_id),\n                SubtableKind::Format1(_) => None,\n                SubtableKind::Format2(subtable2) => subtable2.kerning(last_glyph_id, glyph_id),\n                SubtableKind::Format3(table_ref) => table_ref.kerning(last_glyph_id, glyph_id),\n            })\n            .unwrap_or_default() as f32\n            * metrics.px_scale_factor\n    }\n\n    #[inline]\n    pub fn pair_kerning(\n        &self,\n        metrics: &StyledMetrics,\n        last_glyph_id: skrifa::GlyphId,\n        glyph_id: skrifa::GlyphId,\n    ) -> f32 {\n        self.pair_kerning_pixels(metrics, last_glyph_id, glyph_id) / metrics.pixels_per_point\n    }\n\n    #[inline(always)]\n    pub fn styled_metrics(\n        &self,\n        pixels_per_point: f32,\n        font_size: f32,\n        coords: &VariationCoords,\n    ) -> StyledMetrics {\n        let pt_scale_factor = self.font.px_scale_factor(font_size * self.tweak.scale);\n        let font_data = self.font.borrow_dependent();\n        let ascent = (font_data.metrics.ascent * pt_scale_factor).round_ui();\n        let descent = (font_data.metrics.descent * pt_scale_factor).round_ui();\n        let line_gap = (font_data.metrics.leading * pt_scale_factor).round_ui();\n\n        let scale = font_size * self.tweak.scale * pixels_per_point;\n        let px_scale_factor = self.font.px_scale_factor(scale);\n\n        let y_offset_in_points = ((font_size * self.tweak.scale * self.tweak.y_offset_factor)\n            + self.tweak.y_offset)\n            .round_ui();\n\n        let axes = font_data.skrifa.axes();\n        // Override the default coordinates with ones specified via FontTweak, then the ones specified directly via the\n        // argument (probably from TextFormat).\n        let settings = self\n            .tweak\n            .coords\n            .as_ref()\n            .iter()\n            .chain(coords.as_ref().iter());\n        let location = axes.location(settings);\n\n        StyledMetrics {\n            pixels_per_point,\n            px_scale_factor,\n            scale,\n            y_offset_in_points,\n            ascent,\n            row_height: ascent - descent + line_gap,\n            location,\n        }\n    }\n\n    pub fn allocate_glyph(\n        &mut self,\n        atlas: &mut TextureAtlas,\n        metrics: &StyledMetrics,\n        glyph_info: GlyphInfo,\n        chr: char,\n        h_pos: f32,\n    ) -> (GlyphAllocation, i32) {\n        let advance_width_px = glyph_info.advance_width_unscaled.0 * metrics.px_scale_factor;\n\n        let Some(glyph_id) = glyph_info.id else {\n            // Invisible.\n            return (GlyphAllocation::default(), h_pos as i32);\n        };\n\n        // CJK scripts contain a lot of characters and could hog the glyph atlas if we stored 4 subpixel offsets per\n        // glyph.\n        let (h_pos_round, bin) = if is_cjk(chr) {\n            (h_pos.round() as i32, SubpixelBin::Zero)\n        } else {\n            SubpixelBin::new(h_pos)\n        };\n\n        let entry = match self\n            .glyph_alloc_cache\n            .entry(GlyphCacheKey::new(glyph_id, metrics, bin))\n        {\n            std::collections::hash_map::Entry::Occupied(glyph_alloc) => {\n                let mut glyph_alloc = *glyph_alloc.get();\n                glyph_alloc.advance_width_px = advance_width_px; // Hack to get `\\t` and thin space to work, since they use the same glyph id as ` ` (space).\n                return (glyph_alloc, h_pos_round);\n            }\n            std::collections::hash_map::Entry::Vacant(entry) => entry,\n        };\n\n        let allocation = self\n            .font\n            .allocate_glyph_uncached(atlas, metrics, &glyph_info, bin, (&metrics.location).into())\n            .unwrap_or_default();\n\n        entry.insert(allocation);\n        (allocation, h_pos_round)\n    }\n}\n\n// TODO(emilk): rename?\n/// Wrapper over multiple [`FontFace`] (e.g. a primary + fallbacks for emojis)\npub struct Font<'a> {\n    pub(super) fonts_by_id: &'a mut nohash_hasher::IntMap<FontFaceKey, FontFace>,\n    pub(super) cached_family: &'a mut CachedFamily,\n    pub(super) atlas: &'a mut TextureAtlas,\n}\n\nimpl Font<'_> {\n    pub fn preload_characters(&mut self, s: &str) {\n        for c in s.chars() {\n            self.glyph_info(c);\n        }\n    }\n\n    /// All supported characters, and in which font they are available in.\n    pub fn characters(&mut self) -> &BTreeMap<char, Vec<String>> {\n        self.cached_family.characters.get_or_insert_with(|| {\n            let mut characters: BTreeMap<char, Vec<String>> = Default::default();\n            for font_id in &self.cached_family.fonts {\n                let font = self.fonts_by_id.get(font_id).expect(\"Nonexistent font ID\");\n                for chr in font.characters() {\n                    characters.entry(chr).or_default().push(font.name.clone());\n                }\n            }\n            characters\n        })\n    }\n\n    pub fn styled_metrics(\n        &self,\n        pixels_per_point: f32,\n        font_size: f32,\n        coords: &VariationCoords,\n    ) -> StyledMetrics {\n        self.cached_family\n            .fonts\n            .first()\n            .and_then(|key| self.fonts_by_id.get(key))\n            .map(|font_face| font_face.styled_metrics(pixels_per_point, font_size, coords))\n            .unwrap_or_default()\n    }\n\n    /// Width of this character in points.\n    pub fn glyph_width(&mut self, c: char, font_size: f32) -> f32 {\n        let (key, glyph_info) = self.glyph_info(c);\n        if let Some(font) = &self.fonts_by_id.get(&key) {\n            glyph_info.advance_width_unscaled.0 * font.font.px_scale_factor(font_size)\n        } else {\n            0.0\n        }\n    }\n\n    /// Can we display this glyph?\n    pub fn has_glyph(&mut self, c: char) -> bool {\n        self.glyph_info(c) != self.cached_family.replacement_glyph // TODO(emilk): this is a false negative if the user asks about the replacement character itself 🤦‍♂️\n    }\n\n    /// Can we display all the glyphs in this text?\n    pub fn has_glyphs(&mut self, s: &str) -> bool {\n        s.chars().all(|c| self.has_glyph(c))\n    }\n\n    /// `\\n` will (intentionally) show up as the replacement character.\n    pub(crate) fn glyph_info(&mut self, c: char) -> (FontFaceKey, GlyphInfo) {\n        if let Some(font_index_glyph_info) = self.cached_family.glyph_info_cache.get(&c) {\n            return *font_index_glyph_info;\n        }\n\n        let font_index_glyph_info = self\n            .cached_family\n            .glyph_info_no_cache_or_fallback(c, self.fonts_by_id);\n        let font_index_glyph_info =\n            font_index_glyph_info.unwrap_or(self.cached_family.replacement_glyph);\n        self.cached_family\n            .glyph_info_cache\n            .insert(c, font_index_glyph_info);\n        font_index_glyph_info\n    }\n}\n\n/// Metrics for a font at a specific screen-space scale.\n#[derive(Clone, Debug, PartialEq, Default)]\npub struct StyledMetrics {\n    /// The DPI part of the screen-space scale.\n    pub pixels_per_point: f32,\n\n    /// Scale factor, relative to the font's units per em (so, probably much less than 1).\n    ///\n    /// Translates \"unscaled\" units to physical (screen) pixels.\n    pub px_scale_factor: f32,\n\n    /// Absolute scale in screen pixels, for skrifa.\n    pub scale: f32,\n\n    /// Vertical offset, in UI points (not screen-space).\n    pub y_offset_in_points: f32,\n\n    /// This is the distance from the top to the baseline.\n    ///\n    /// Unit: points.\n    pub ascent: f32,\n\n    /// Height of one row of text in points.\n    ///\n    /// Returns a value rounded to [`emath::GUI_ROUNDING`].\n    pub row_height: f32,\n\n    /// Resolved variation coordinates.\n    pub location: skrifa::instance::Location,\n}\n\n/// Code points that will always be invisible (zero width).\n///\n/// See also [`FontFace::ignore_character`].\n#[inline]\nfn invisible_char(c: char) -> bool {\n    if c == '\\r' {\n        // A character most vile and pernicious. Don't display it.\n        return true;\n    }\n\n    // See https://github.com/emilk/egui/issues/336\n\n    // From https://www.fileformat.info/info/unicode/category/Cf/list.htm\n\n    // TODO(emilk): heed bidi characters\n\n    matches!(\n        c,\n        '\\u{200B}' // ZERO WIDTH SPACE\n            | '\\u{200C}' // ZERO WIDTH NON-JOINER\n            | '\\u{200D}' // ZERO WIDTH JOINER\n            | '\\u{200E}' // LEFT-TO-RIGHT MARK\n            | '\\u{200F}' // RIGHT-TO-LEFT MARK\n            | '\\u{202A}' // LEFT-TO-RIGHT EMBEDDING\n            | '\\u{202B}' // RIGHT-TO-LEFT EMBEDDING\n            | '\\u{202C}' // POP DIRECTIONAL FORMATTING\n            | '\\u{202D}' // LEFT-TO-RIGHT OVERRIDE\n            | '\\u{202E}' // RIGHT-TO-LEFT OVERRIDE\n            | '\\u{2060}' // WORD JOINER\n            | '\\u{2061}' // FUNCTION APPLICATION\n            | '\\u{2062}' // INVISIBLE TIMES\n            | '\\u{2063}' // INVISIBLE SEPARATOR\n            | '\\u{2064}' // INVISIBLE PLUS\n            | '\\u{2066}' // LEFT-TO-RIGHT ISOLATE\n            | '\\u{2067}' // RIGHT-TO-LEFT ISOLATE\n            | '\\u{2068}' // FIRST STRONG ISOLATE\n            | '\\u{2069}' // POP DIRECTIONAL ISOLATE\n            | '\\u{206A}' // INHIBIT SYMMETRIC SWAPPING\n            | '\\u{206B}' // ACTIVATE SYMMETRIC SWAPPING\n            | '\\u{206C}' // INHIBIT ARABIC FORM SHAPING\n            | '\\u{206D}' // ACTIVATE ARABIC FORM SHAPING\n            | '\\u{206E}' // NATIONAL DIGIT SHAPES\n            | '\\u{206F}' // NOMINAL DIGIT SHAPES\n            | '\\u{FEFF}' // ZERO WIDTH NO-BREAK SPACE\n    )\n}\n\n#[inline]\npub(super) fn is_cjk_ideograph(c: char) -> bool {\n    ('\\u{4E00}' <= c && c <= '\\u{9FFF}')\n        || ('\\u{3400}' <= c && c <= '\\u{4DBF}')\n        || ('\\u{2B740}' <= c && c <= '\\u{2B81F}')\n}\n\n#[inline]\npub(super) fn is_kana(c: char) -> bool {\n    ('\\u{3040}' <= c && c <= '\\u{309F}') // Hiragana block\n        || ('\\u{30A0}' <= c && c <= '\\u{30FF}') // Katakana block\n}\n\n#[inline]\npub(super) fn is_cjk(c: char) -> bool {\n    // TODO(bigfarts): Add support for Korean Hangul.\n    is_cjk_ideograph(c) || is_kana(c)\n}\n\n#[inline]\npub(super) fn is_cjk_break_allowed(c: char) -> bool {\n    // See: https://en.wikipedia.org/wiki/Line_breaking_rules_in_East_Asian_languages#Characters_not_permitted_on_the_start_of_a_line.\n    !\")]｝〕〉》」』】〙〗〟'\\\"｠»ヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻‐゠–〜?!‼⁇⁈⁉・、:;,。.\".contains(c)\n}\n"
  },
  {
    "path": "crates/epaint/src/text/fonts.rs",
    "content": "use std::{\n    borrow::Cow,\n    collections::BTreeMap,\n    sync::{\n        Arc,\n        atomic::{AtomicU64, Ordering},\n    },\n};\n\nuse crate::{\n    TextureAtlas,\n    text::{\n        Galley, LayoutJob, LayoutSection, TextOptions, VariationCoords,\n        font::{Font, FontFace, GlyphInfo},\n    },\n};\nuse emath::{NumExt as _, OrderedFloat};\n\n#[cfg(feature = \"default_fonts\")]\nuse epaint_default_fonts::{EMOJI_ICON, HACK_REGULAR, NOTO_EMOJI_REGULAR, UBUNTU_LIGHT};\n\n// ----------------------------------------------------------------------------\n\n/// How to select a sized font.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct FontId {\n    /// Height in points.\n    pub size: f32,\n\n    /// What font family to use.\n    pub family: FontFamily,\n    // TODO(emilk): weight (bold), italics, …\n}\n\nimpl Default for FontId {\n    #[inline]\n    fn default() -> Self {\n        Self {\n            size: 14.0,\n            family: FontFamily::Proportional,\n        }\n    }\n}\n\nimpl FontId {\n    #[inline]\n    pub const fn new(size: f32, family: FontFamily) -> Self {\n        Self { size, family }\n    }\n\n    #[inline]\n    pub const fn proportional(size: f32) -> Self {\n        Self::new(size, FontFamily::Proportional)\n    }\n\n    #[inline]\n    pub const fn monospace(size: f32) -> Self {\n        Self::new(size, FontFamily::Monospace)\n    }\n}\n\nimpl std::hash::Hash for FontId {\n    #[inline(always)]\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        let Self { size, family } = self;\n        emath::OrderedFloat(*size).hash(state);\n        family.hash(state);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Font of unknown size.\n///\n/// Which style of font: [`Monospace`][`FontFamily::Monospace`], [`Proportional`][`FontFamily::Proportional`],\n/// or by user-chosen name.\n#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum FontFamily {\n    /// A font where some characters are wider than other (e.g. 'w' is wider than 'i').\n    ///\n    /// Proportional fonts are easier to read and should be the preferred choice in most situations.\n    #[default]\n    Proportional,\n\n    /// A font where each character is the same width (`w` is the same width as `i`).\n    ///\n    /// Useful for code snippets, or when you need to align numbers or text.\n    Monospace,\n\n    /// One of the names in [`FontDefinitions::families`].\n    ///\n    /// ```\n    /// # use epaint::FontFamily;\n    /// // User-chosen names:\n    /// FontFamily::Name(\"arial\".into());\n    /// FontFamily::Name(\"serif\".into());\n    /// ```\n    Name(Arc<str>),\n}\n\nimpl std::fmt::Display for FontFamily {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Monospace => \"Monospace\".fmt(f),\n            Self::Proportional => \"Proportional\".fmt(f),\n            Self::Name(name) => (*name).fmt(f),\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// A `.ttf` or `.otf` file and a font face index.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct FontData {\n    /// The content of a `.ttf` or `.otf` file.\n    pub font: Cow<'static, [u8]>,\n\n    /// Which font face in the file to use.\n    /// When in doubt, use `0`.\n    pub index: u32,\n\n    /// Extra scale and vertical tweak to apply to all text of this font.\n    pub tweak: FontTweak,\n}\n\nimpl FontData {\n    pub fn from_static(font: &'static [u8]) -> Self {\n        Self {\n            font: Cow::Borrowed(font),\n            index: 0,\n            tweak: Default::default(),\n        }\n    }\n\n    pub fn from_owned(font: Vec<u8>) -> Self {\n        Self {\n            font: Cow::Owned(font),\n            index: 0,\n            tweak: Default::default(),\n        }\n    }\n\n    pub fn tweak(self, tweak: FontTweak) -> Self {\n        Self { tweak, ..self }\n    }\n}\n\nimpl AsRef<[u8]> for FontData {\n    fn as_ref(&self) -> &[u8] {\n        self.font.as_ref()\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Extra scale and vertical tweak to apply to all text of a certain font.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct FontTweak {\n    /// Scale the font's glyphs by this much.\n    /// this is only a visual effect and does not affect the text layout.\n    ///\n    /// Default: `1.0` (no scaling).\n    pub scale: f32,\n\n    /// Shift font's glyphs downwards by this fraction of the font size (in points).\n    /// this is only a visual effect and does not affect the text layout.\n    ///\n    /// Affects larger font sizes more.\n    ///\n    /// A positive value shifts the text downwards.\n    /// A negative value shifts it upwards.\n    ///\n    /// Example value: `-0.2`.\n    pub y_offset_factor: f32,\n\n    /// Shift font's glyphs downwards by this amount of logical points.\n    /// this is only a visual effect and does not affect the text layout.\n    ///\n    /// Affects all font sizes equally.\n    ///\n    /// Example value: `2.0`.\n    pub y_offset: f32,\n\n    /// Override the global font hinting setting for this specific font.\n    ///\n    /// `None` means use the global setting.\n    pub hinting_override: Option<bool>,\n\n    /// Override the font's default variation coordinates.\n    pub coords: VariationCoords,\n}\n\nimpl Default for FontTweak {\n    fn default() -> Self {\n        Self {\n            scale: 1.0,\n            y_offset_factor: 0.0,\n            y_offset: 0.0,\n            hinting_override: None,\n            coords: VariationCoords::default(),\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\npub type Blob = Arc<dyn AsRef<[u8]> + Send + Sync>;\n\nfn blob_from_font_data(data: &FontData) -> Blob {\n    match data.clone().font {\n        Cow::Borrowed(bytes) => Arc::new(bytes) as Blob,\n        Cow::Owned(bytes) => Arc::new(bytes) as Blob,\n    }\n}\n\n/// Describes the font data and the sizes to use.\n///\n/// Often you would start with [`FontDefinitions::default()`] and then add/change the contents.\n///\n/// This is how you install your own custom fonts:\n/// ```\n/// # use {epaint::text::{FontDefinitions, FontFamily, FontData}};\n/// # struct FakeEguiCtx {};\n/// # impl FakeEguiCtx { fn set_fonts(&self, _: FontDefinitions) {} }\n/// # let egui_ctx = FakeEguiCtx {};\n/// let mut fonts = FontDefinitions::default();\n///\n/// // Install my own font (maybe supporting non-latin characters):\n/// fonts.font_data.insert(\"my_font\".to_owned(),\n///    std::sync::Arc::new(\n///        // .ttf and .otf supported\n///        FontData::from_static(include_bytes!(\"../../../epaint_default_fonts/fonts/Ubuntu-Light.ttf\"))\n///    )\n/// );\n///\n/// // Put my font first (highest priority):\n/// fonts.families.get_mut(&FontFamily::Proportional).unwrap()\n///     .insert(0, \"my_font\".to_owned());\n///\n/// // Put my font as last fallback for monospace:\n/// fonts.families.get_mut(&FontFamily::Monospace).unwrap()\n///     .push(\"my_font\".to_owned());\n///\n/// egui_ctx.set_fonts(fonts);\n/// ```\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[cfg_attr(feature = \"serde\", serde(default))]\npub struct FontDefinitions {\n    /// List of font names and their definitions.\n    ///\n    /// `epaint` has built-in-default for these, but you can override them if you like.\n    pub font_data: BTreeMap<String, Arc<FontData>>,\n\n    /// Which fonts (names) to use for each [`FontFamily`].\n    ///\n    /// The list should be a list of keys into [`Self::font_data`].\n    /// When looking for a character glyph `epaint` will start with\n    /// the first font and then move to the second, and so on.\n    /// So the first font is the primary, and then comes a list of fallbacks in order of priority.\n    pub families: BTreeMap<FontFamily, Vec<String>>,\n}\n\n#[derive(Debug, Clone)]\npub struct FontInsert {\n    /// Font name\n    pub name: String,\n\n    /// A `.ttf` or `.otf` file and a font face index.\n    pub data: FontData,\n\n    /// Sets the font family and priority\n    pub families: Vec<InsertFontFamily>,\n}\n\n#[derive(Debug, Clone)]\npub struct InsertFontFamily {\n    /// Font family\n    pub family: FontFamily,\n\n    /// Fallback or Primary font\n    pub priority: FontPriority,\n}\n\n#[derive(Debug, Clone)]\npub enum FontPriority {\n    /// Prefer this font before all existing ones.\n    ///\n    /// If a desired glyph exists in this font, it will be used.\n    Highest,\n\n    /// Use this font as a fallback, after all existing ones.\n    ///\n    /// This font will only be used if the glyph is not found in any of the previously installed fonts.\n    Lowest,\n}\n\nimpl FontInsert {\n    pub fn new(name: &str, data: FontData, families: Vec<InsertFontFamily>) -> Self {\n        Self {\n            name: name.to_owned(),\n            data,\n            families,\n        }\n    }\n}\n\nimpl Default for FontDefinitions {\n    /// Specifies the default fonts if the feature `default_fonts` is enabled,\n    /// otherwise this is the same as [`Self::empty`].\n    #[cfg(not(feature = \"default_fonts\"))]\n    fn default() -> Self {\n        Self::empty()\n    }\n\n    /// Specifies the default fonts if the feature `default_fonts` is enabled,\n    /// otherwise this is the same as [`Self::empty`].\n    #[cfg(feature = \"default_fonts\")]\n    fn default() -> Self {\n        let mut font_data: BTreeMap<String, Arc<FontData>> = BTreeMap::new();\n\n        let mut families = BTreeMap::new();\n\n        font_data.insert(\n            \"Hack\".to_owned(),\n            Arc::new(FontData::from_static(HACK_REGULAR)),\n        );\n\n        // Some good looking emojis. Use as first priority:\n        font_data.insert(\n            \"NotoEmoji-Regular\".to_owned(),\n            Arc::new(FontData::from_static(NOTO_EMOJI_REGULAR).tweak(FontTweak {\n                scale: 0.81, // Make smaller\n                ..Default::default()\n            })),\n        );\n\n        font_data.insert(\n            \"Ubuntu-Light\".to_owned(),\n            Arc::new(FontData::from_static(UBUNTU_LIGHT)),\n        );\n\n        // Bigger emojis, and more. <http://jslegers.github.io/emoji-icon-font/>:\n        font_data.insert(\n            \"emoji-icon-font\".to_owned(),\n            Arc::new(FontData::from_static(EMOJI_ICON).tweak(FontTweak {\n                scale: 0.90, // Make smaller\n                ..Default::default()\n            })),\n        );\n\n        families.insert(\n            FontFamily::Monospace,\n            vec![\n                \"Hack\".to_owned(),\n                \"Ubuntu-Light\".to_owned(), // fallback for √ etc\n                \"NotoEmoji-Regular\".to_owned(),\n                \"emoji-icon-font\".to_owned(),\n            ],\n        );\n        families.insert(\n            FontFamily::Proportional,\n            vec![\n                \"Ubuntu-Light\".to_owned(),\n                \"NotoEmoji-Regular\".to_owned(),\n                \"emoji-icon-font\".to_owned(),\n            ],\n        );\n\n        Self {\n            font_data,\n            families,\n        }\n    }\n}\n\nimpl FontDefinitions {\n    /// No fonts.\n    pub fn empty() -> Self {\n        let mut families = BTreeMap::new();\n        families.insert(FontFamily::Monospace, vec![]);\n        families.insert(FontFamily::Proportional, vec![]);\n\n        Self {\n            font_data: Default::default(),\n            families,\n        }\n    }\n\n    /// List of all the builtin font names used by `epaint`.\n    #[cfg(feature = \"default_fonts\")]\n    pub fn builtin_font_names() -> &'static [&'static str] {\n        &[\n            \"Ubuntu-Light\",\n            \"NotoEmoji-Regular\",\n            \"emoji-icon-font\",\n            \"Hack\",\n        ]\n    }\n\n    /// List of all the builtin font names used by `epaint`.\n    #[cfg(not(feature = \"default_fonts\"))]\n    pub fn builtin_font_names() -> &'static [&'static str] {\n        &[]\n    }\n}\n\n/// Unique ID for looking up a single font face/file.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\npub(crate) struct FontFaceKey(u64);\n\nimpl FontFaceKey {\n    pub const INVALID: Self = Self(0);\n\n    fn new() -> Self {\n        static KEY_COUNTER: AtomicU64 = AtomicU64::new(1);\n        Self(crate::util::hash(\n            KEY_COUNTER.fetch_add(1, Ordering::Relaxed),\n        ))\n    }\n}\n\n// Safe, because we hash the value in the constructor.\nimpl nohash_hasher::IsEnabled for FontFaceKey {}\n\n/// Cached data for working with a font family (e.g. doing character lookups).\n#[derive(Debug)]\npub(super) struct CachedFamily {\n    pub fonts: Vec<FontFaceKey>,\n\n    /// Lazily calculated.\n    pub characters: Option<BTreeMap<char, Vec<String>>>,\n\n    pub replacement_glyph: (FontFaceKey, GlyphInfo),\n\n    pub glyph_info_cache: ahash::HashMap<char, (FontFaceKey, GlyphInfo)>,\n}\n\nimpl CachedFamily {\n    fn new(\n        fonts: Vec<FontFaceKey>,\n        fonts_by_id: &mut nohash_hasher::IntMap<FontFaceKey, FontFace>,\n    ) -> Self {\n        if fonts.is_empty() {\n            return Self {\n                fonts,\n                characters: None,\n                replacement_glyph: (FontFaceKey::INVALID, GlyphInfo::INVISIBLE),\n                glyph_info_cache: Default::default(),\n            };\n        }\n\n        let mut slf = Self {\n            fonts,\n            characters: None,\n            replacement_glyph: (FontFaceKey::INVALID, GlyphInfo::INVISIBLE),\n            glyph_info_cache: Default::default(),\n        };\n\n        const PRIMARY_REPLACEMENT_CHAR: char = '◻'; // white medium square\n        const FALLBACK_REPLACEMENT_CHAR: char = '?'; // fallback for the fallback\n\n        let replacement_glyph = slf\n            .glyph_info_no_cache_or_fallback(PRIMARY_REPLACEMENT_CHAR, fonts_by_id)\n            .or_else(|| slf.glyph_info_no_cache_or_fallback(FALLBACK_REPLACEMENT_CHAR, fonts_by_id))\n            .unwrap_or_else(|| {\n                log::warn!(\n                    \"Failed to find replacement characters {PRIMARY_REPLACEMENT_CHAR:?} or {FALLBACK_REPLACEMENT_CHAR:?}. Will use empty glyph.\"\n                );\n                (FontFaceKey::INVALID, GlyphInfo::INVISIBLE)\n            });\n        slf.replacement_glyph = replacement_glyph;\n\n        slf\n    }\n\n    pub(crate) fn glyph_info_no_cache_or_fallback(\n        &mut self,\n        c: char,\n        fonts_by_id: &mut nohash_hasher::IntMap<FontFaceKey, FontFace>,\n    ) -> Option<(FontFaceKey, GlyphInfo)> {\n        for font_key in &self.fonts {\n            let font_face = fonts_by_id.get_mut(font_key).expect(\"Nonexistent font ID\");\n            if let Some(glyph_info) = font_face.glyph_info(c) {\n                self.glyph_info_cache.insert(c, (*font_key, glyph_info));\n                return Some((*font_key, glyph_info));\n            }\n        }\n        None\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// The collection of fonts used by `epaint`.\n///\n/// Required in order to paint text. Create one and reuse. Cheap to clone.\n///\n/// Each [`Fonts`] comes with a font atlas textures that needs to be used when painting.\n///\n/// If you are using `egui`, use `egui::Context::set_fonts` and `egui::Context::fonts`.\n///\n/// You need to call [`Self::begin_pass`] and [`Self::font_image_delta`] once every frame.\npub struct Fonts {\n    pub fonts: FontsImpl,\n    galley_cache: GalleyCache,\n}\n\nimpl Fonts {\n    /// Create a new [`Fonts`] for text layout.\n    /// This call is expensive, so only create one [`Fonts`] and then reuse it.\n    pub fn new(options: TextOptions, definitions: FontDefinitions) -> Self {\n        Self {\n            fonts: FontsImpl::new(options, definitions),\n            galley_cache: Default::default(),\n        }\n    }\n\n    /// Call at the start of each frame with the latest known [`TextOptions`].\n    ///\n    /// Call after painting the previous frame, but before using [`Fonts`] for the new frame.\n    ///\n    /// This function will react to changes in [`TextOptions`],\n    /// as well as notice when the font atlas is getting full, and handle that.\n    pub fn begin_pass(&mut self, options: TextOptions) {\n        let text_options_changed = self.fonts.options() != &options;\n        let font_atlas_almost_full = self.fonts.atlas.fill_ratio() > 0.8;\n        let needs_recreate = text_options_changed || font_atlas_almost_full;\n\n        if needs_recreate {\n            let definitions = self.fonts.definitions.clone();\n\n            *self = Self {\n                fonts: FontsImpl::new(options, definitions),\n                galley_cache: Default::default(),\n            };\n        }\n\n        self.galley_cache.flush_cache();\n    }\n\n    /// Call at the end of each frame (before painting) to get the change to the font texture since last call.\n    pub fn font_image_delta(&mut self) -> Option<crate::ImageDelta> {\n        self.fonts.atlas.take_delta()\n    }\n\n    #[inline]\n    pub fn options(&self) -> &TextOptions {\n        self.texture_atlas().options()\n    }\n\n    #[inline]\n    pub fn definitions(&self) -> &FontDefinitions {\n        &self.fonts.definitions\n    }\n\n    /// The font atlas.\n    /// Pass this to [`crate::Tessellator`].\n    pub fn texture_atlas(&self) -> &TextureAtlas {\n        &self.fonts.atlas\n    }\n\n    /// The full font atlas image.\n    #[inline]\n    pub fn image(&self) -> crate::ColorImage {\n        self.fonts.atlas.image().clone()\n    }\n\n    /// Current size of the font image.\n    /// Pass this to [`crate::Tessellator`].\n    pub fn font_image_size(&self) -> [usize; 2] {\n        self.fonts.atlas.size()\n    }\n\n    /// Can we display this glyph?\n    pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {\n        self.fonts.font(&font_id.family).has_glyph(c)\n    }\n\n    /// Can we display all the glyphs in this text?\n    pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {\n        self.fonts.font(&font_id.family).has_glyphs(s)\n    }\n\n    pub fn num_galleys_in_cache(&self) -> usize {\n        self.galley_cache.num_galleys_in_cache()\n    }\n\n    /// How full is the font atlas?\n    ///\n    /// This increases as new fonts and/or glyphs are used,\n    /// but can also decrease in a call to [`Self::begin_pass`].\n    pub fn font_atlas_fill_ratio(&self) -> f32 {\n        self.fonts.atlas.fill_ratio()\n    }\n\n    /// Returns a [`FontsView`] with the given `pixels_per_point` that can be used to do text layout.\n    pub fn with_pixels_per_point(&mut self, pixels_per_point: f32) -> FontsView<'_> {\n        FontsView {\n            fonts: &mut self.fonts,\n            galley_cache: &mut self.galley_cache,\n            pixels_per_point,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// The context's collection of fonts, with this context's `pixels_per_point`. This is what you use to do text layout.\npub struct FontsView<'a> {\n    pub fonts: &'a mut FontsImpl,\n    galley_cache: &'a mut GalleyCache,\n    pixels_per_point: f32,\n}\n\nimpl FontsView<'_> {\n    #[inline]\n    pub fn options(&self) -> &TextOptions {\n        self.fonts.options()\n    }\n\n    #[inline]\n    pub fn definitions(&self) -> &FontDefinitions {\n        &self.fonts.definitions\n    }\n\n    /// The full font atlas image.\n    #[inline]\n    pub fn image(&self) -> crate::ColorImage {\n        self.fonts.atlas.image().clone()\n    }\n\n    /// Current size of the font image.\n    /// Pass this to [`crate::Tessellator`].\n    pub fn font_image_size(&self) -> [usize; 2] {\n        self.fonts.atlas.size()\n    }\n\n    /// Width of this character in points.\n    ///\n    /// If the font doesn't exist, this will return `0.0`.\n    pub fn glyph_width(&mut self, font_id: &FontId, c: char) -> f32 {\n        self.fonts\n            .font(&font_id.family)\n            .glyph_width(c, font_id.size)\n    }\n\n    /// Can we display this glyph?\n    pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {\n        self.fonts.font(&font_id.family).has_glyph(c)\n    }\n\n    /// Can we display all the glyphs in this text?\n    pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {\n        self.fonts.font(&font_id.family).has_glyphs(s)\n    }\n\n    /// Height of one row of text in points.\n    ///\n    /// Returns a value rounded to [`emath::GUI_ROUNDING`].\n    #[inline]\n    pub fn row_height(&mut self, font_id: &FontId) -> f32 {\n        self.fonts\n            .font(&font_id.family)\n            .styled_metrics(\n                self.pixels_per_point,\n                font_id.size,\n                // TODO(valadaptive): use font variation coords when calculating row height\n                &VariationCoords::default(),\n            )\n            .row_height\n    }\n\n    /// List of all known font families.\n    pub fn families(&self) -> Vec<FontFamily> {\n        self.fonts.definitions.families.keys().cloned().collect()\n    }\n\n    /// Layout some text.\n    ///\n    /// This is the most advanced layout function.\n    /// See also [`Self::layout`], [`Self::layout_no_wrap`] and\n    /// [`Self::layout_delayed_color`].\n    ///\n    /// The implementation uses memoization so repeated calls are cheap.\n    #[inline]\n    pub fn layout_job(&mut self, job: LayoutJob) -> Arc<Galley> {\n        let allow_split_paragraphs = true; // Optimization for editing text with many paragraphs.\n        self.galley_cache.layout(\n            self.fonts,\n            self.pixels_per_point,\n            job,\n            allow_split_paragraphs,\n        )\n    }\n\n    pub fn num_galleys_in_cache(&self) -> usize {\n        self.galley_cache.num_galleys_in_cache()\n    }\n\n    /// How full is the font atlas?\n    ///\n    /// This increases as new fonts and/or glyphs are used,\n    /// but can also decrease in a call to [`Fonts::begin_pass`].\n    pub fn font_atlas_fill_ratio(&self) -> f32 {\n        self.fonts.atlas.fill_ratio()\n    }\n\n    /// Will wrap text at the given width and line break at `\\n`.\n    ///\n    /// The implementation uses memoization so repeated calls are cheap.\n    #[inline]\n    pub fn layout(\n        &mut self,\n        text: String,\n        font_id: FontId,\n        color: crate::Color32,\n        wrap_width: f32,\n    ) -> Arc<Galley> {\n        let job = LayoutJob::simple(text, font_id, color, wrap_width);\n        self.layout_job(job)\n    }\n\n    /// Will line break at `\\n`.\n    ///\n    /// The implementation uses memoization so repeated calls are cheap.\n    #[inline]\n    pub fn layout_no_wrap(\n        &mut self,\n        text: String,\n        font_id: FontId,\n        color: crate::Color32,\n    ) -> Arc<Galley> {\n        let job = LayoutJob::simple(text, font_id, color, f32::INFINITY);\n        self.layout_job(job)\n    }\n\n    /// Like [`Self::layout`], made for when you want to pick a color for the text later.\n    ///\n    /// The implementation uses memoization so repeated calls are cheap.\n    #[inline]\n    pub fn layout_delayed_color(\n        &mut self,\n        text: String,\n        font_id: FontId,\n        wrap_width: f32,\n    ) -> Arc<Galley> {\n        self.layout(text, font_id, crate::Color32::PLACEHOLDER, wrap_width)\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// The collection of fonts used by `epaint`.\n///\n/// Required in order to paint text.\npub struct FontsImpl {\n    definitions: FontDefinitions,\n    atlas: TextureAtlas,\n    fonts_by_id: nohash_hasher::IntMap<FontFaceKey, FontFace>,\n    fonts_by_name: ahash::HashMap<String, FontFaceKey>,\n    family_cache: ahash::HashMap<FontFamily, CachedFamily>,\n}\n\nimpl FontsImpl {\n    /// Create a new [`FontsImpl`] for text layout.\n    /// This call is expensive, so only create one [`FontsImpl`] and then reuse it.\n    pub fn new(options: TextOptions, definitions: FontDefinitions) -> Self {\n        let texture_width = options.max_texture_side.at_most(16 * 1024);\n        let initial_height = 32; // Keep initial font atlas small, so it is fast to upload to GPU. This will expand as needed anyways.\n        let atlas = TextureAtlas::new([texture_width, initial_height], options);\n\n        let mut fonts_by_id: nohash_hasher::IntMap<FontFaceKey, FontFace> = Default::default();\n        let mut fonts_by_name: ahash::HashMap<String, FontFaceKey> = Default::default();\n        for (name, font_data) in &definitions.font_data {\n            let blob = blob_from_font_data(font_data);\n            let font_face = FontFace::new(\n                options,\n                name.clone(),\n                blob,\n                font_data.index,\n                font_data.tweak.clone(),\n            )\n            .unwrap_or_else(|err| panic!(\"Error parsing {name:?} TTF/OTF font file: {err}\"));\n            let key = FontFaceKey::new();\n            fonts_by_id.insert(key, font_face);\n            fonts_by_name.insert(name.clone(), key);\n        }\n\n        Self {\n            definitions,\n            atlas,\n            fonts_by_id,\n            fonts_by_name,\n            family_cache: Default::default(),\n        }\n    }\n\n    pub fn options(&self) -> &TextOptions {\n        self.atlas.options()\n    }\n\n    /// Get the right font implementation from [`FontFamily`].\n    pub fn font(&mut self, family: &FontFamily) -> Font<'_> {\n        let cached_family = self.family_cache.entry(family.clone()).or_insert_with(|| {\n            let fonts = &self.definitions.families.get(family);\n            let fonts =\n                fonts.unwrap_or_else(|| panic!(\"FontFamily::{family:?} is not bound to any fonts\"));\n\n            let fonts: Vec<FontFaceKey> = fonts\n                .iter()\n                .map(|font_name| {\n                    *self\n                        .fonts_by_name\n                        .get(font_name)\n                        .unwrap_or_else(|| panic!(\"No font data found for {font_name:?}\"))\n                })\n                .collect();\n\n            CachedFamily::new(fonts, &mut self.fonts_by_id)\n        });\n        Font {\n            fonts_by_id: &mut self.fonts_by_id,\n            cached_family,\n            atlas: &mut self.atlas,\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nstruct CachedGalley {\n    /// When it was last used\n    last_used: u32,\n\n    /// Hashes of all other entries this one depends on for quick re-layout.\n    /// Their `last_used`s should be updated alongside this one to make sure they're\n    /// not evicted.\n    children: Option<Arc<[u64]>>,\n\n    galley: Arc<Galley>,\n}\n\n#[derive(Default)]\nstruct GalleyCache {\n    /// Frame counter used to do garbage collection on the cache\n    generation: u32,\n    cache: nohash_hasher::IntMap<u64, CachedGalley>,\n}\n\nimpl GalleyCache {\n    fn layout_internal(\n        &mut self,\n        fonts: &mut FontsImpl,\n        mut job: LayoutJob,\n        pixels_per_point: f32,\n        allow_split_paragraphs: bool,\n    ) -> (u64, Arc<Galley>) {\n        if job.wrap.max_width.is_finite() {\n            // Protect against rounding errors in egui layout code.\n\n            // Say the user asks to wrap at width 200.0.\n            // The text layout wraps, and reports that the final width was 196.0 points.\n            // This then trickles up the `Ui` chain and gets stored as the width for a tooltip (say).\n            // On the next frame, this is then set as the max width for the tooltip,\n            // and we end up calling the text layout code again, this time with a wrap width of 196.0.\n            // Except, somewhere in the `Ui` chain with added margins etc, a rounding error was introduced,\n            // so that we actually set a wrap-width of 195.9997 instead.\n            // Now the text that fit perfrectly at 196.0 needs to wrap one word earlier,\n            // and so the text re-wraps and reports a new width of 185.0 points.\n            // And then the cycle continues.\n\n            // So we limit max_width to integers.\n\n            // Related issues:\n            // * https://github.com/emilk/egui/issues/4927\n            // * https://github.com/emilk/egui/issues/4928\n            // * https://github.com/emilk/egui/issues/5084\n            // * https://github.com/emilk/egui/issues/5163\n\n            job.wrap.max_width = job.wrap.max_width.round();\n        }\n\n        let hash = crate::util::hash((&job, OrderedFloat(pixels_per_point))); // TODO(emilk): even faster hasher?\n\n        let galley = match self.cache.entry(hash) {\n            std::collections::hash_map::Entry::Occupied(entry) => {\n                // The job was found in cache - no need to re-layout.\n                let cached = entry.into_mut();\n                cached.last_used = self.generation;\n\n                let galley = Arc::clone(&cached.galley);\n                if let Some(children) = &cached.children {\n                    // The point of `allow_split_paragraphs` is to split large jobs into paragraph,\n                    // and then cache each paragraph individually.\n                    // That way, if we edit a single paragraph, only that paragraph will be re-layouted.\n                    // For that to work we need to keep all the child/paragraph\n                    // galleys alive while the parent galley is alive:\n                    for child_hash in Arc::clone(children).iter() {\n                        if let Some(cached_child) = self.cache.get_mut(child_hash) {\n                            cached_child.last_used = self.generation;\n                        }\n                    }\n                }\n\n                galley\n            }\n            std::collections::hash_map::Entry::Vacant(entry) => {\n                let job = Arc::new(job);\n                if allow_split_paragraphs && should_cache_each_paragraph_individually(&job) {\n                    let (child_galleys, child_hashes) =\n                        self.layout_each_paragraph_individually(fonts, &job, pixels_per_point);\n                    debug_assert_eq!(\n                        child_hashes.len(),\n                        child_galleys.len(),\n                        \"Bug in `layout_each_paragraph_individually`\"\n                    );\n                    let galley = Arc::new(Galley::concat(job, &child_galleys, pixels_per_point));\n\n                    self.cache.insert(\n                        hash,\n                        CachedGalley {\n                            last_used: self.generation,\n                            children: Some(child_hashes.into()),\n                            galley: Arc::clone(&galley),\n                        },\n                    );\n                    galley\n                } else {\n                    let galley = super::layout(fonts, pixels_per_point, job);\n                    let galley = Arc::new(galley);\n                    entry.insert(CachedGalley {\n                        last_used: self.generation,\n                        children: None,\n                        galley: Arc::clone(&galley),\n                    });\n                    galley\n                }\n            }\n        };\n\n        (hash, galley)\n    }\n\n    fn layout(\n        &mut self,\n        fonts: &mut FontsImpl,\n        pixels_per_point: f32,\n        job: LayoutJob,\n        allow_split_paragraphs: bool,\n    ) -> Arc<Galley> {\n        self.layout_internal(fonts, job, pixels_per_point, allow_split_paragraphs)\n            .1\n    }\n\n    /// Split on `\\n` and lay out (and cache) each paragraph individually.\n    fn layout_each_paragraph_individually(\n        &mut self,\n        fonts: &mut FontsImpl,\n        job: &LayoutJob,\n        pixels_per_point: f32,\n    ) -> (Vec<Arc<Galley>>, Vec<u64>) {\n        profiling::function_scope!();\n\n        let mut current_section = 0;\n        let mut start = 0;\n        let mut max_rows_remaining = job.wrap.max_rows;\n        let mut child_galleys = Vec::new();\n        let mut child_hashes = Vec::new();\n\n        while start < job.text.len() {\n            let is_first_paragraph = start == 0;\n            // `end` will not include the `\\n` since we don't want to create an empty row in our\n            // split galley\n            let mut end = job.text[start..]\n                .find('\\n')\n                .map_or(job.text.len(), |i| start + i);\n            if end == job.text.len() - 1 && job.text.ends_with('\\n') {\n                end += 1; // If the text ends with a newline, we include it in the last paragraph.\n            }\n\n            let mut paragraph_job = LayoutJob {\n                text: job.text[start..end].to_owned(),\n                wrap: crate::text::TextWrapping {\n                    max_rows: max_rows_remaining,\n                    ..job.wrap\n                },\n                sections: Vec::new(),\n                break_on_newline: job.break_on_newline,\n                halign: job.halign,\n                justify: job.justify,\n                first_row_min_height: if is_first_paragraph {\n                    job.first_row_min_height\n                } else {\n                    0.0\n                },\n                round_output_to_gui: job.round_output_to_gui,\n            };\n\n            // Add overlapping sections:\n            for section in &job.sections[current_section..job.sections.len()] {\n                let LayoutSection {\n                    leading_space,\n                    byte_range: section_range,\n                    format,\n                } = section;\n\n                // `start` and `end` are the byte range of the current paragraph.\n                // How does the current section overlap with the paragraph range?\n\n                if section_range.end <= start {\n                    // The section is behind us\n                    current_section += 1;\n                } else if end < section_range.start {\n                    break; // Haven't reached this one yet.\n                } else {\n                    // Section range overlaps with paragraph range\n                    debug_assert!(\n                        section_range.start <= section_range.end,\n                        \"Bad byte_range: {section_range:?}\"\n                    );\n                    let new_range = section_range.start.saturating_sub(start)\n                        ..(section_range.end.at_most(end)).saturating_sub(start);\n                    debug_assert!(\n                        new_range.start <= new_range.end,\n                        \"Bad new section range: {new_range:?}\"\n                    );\n                    paragraph_job.sections.push(LayoutSection {\n                        leading_space: if start <= section_range.start {\n                            *leading_space\n                        } else {\n                            0.0\n                        },\n                        byte_range: new_range,\n                        format: format.clone(),\n                    });\n                }\n            }\n\n            // TODO(emilk): we could lay out each paragraph in parallel to get a nice speedup on multicore machines.\n            let (hash, galley) =\n                self.layout_internal(fonts, paragraph_job, pixels_per_point, false);\n            child_hashes.push(hash);\n\n            // This will prevent us from invalidating cache entries unnecessarily:\n            if max_rows_remaining != usize::MAX {\n                max_rows_remaining -= galley.rows.len();\n            }\n\n            let elided = galley.elided;\n            child_galleys.push(galley);\n            if elided {\n                break;\n            }\n\n            start = end + 1;\n        }\n\n        (child_galleys, child_hashes)\n    }\n\n    pub fn num_galleys_in_cache(&self) -> usize {\n        self.cache.len()\n    }\n\n    /// Must be called once per frame to clear the [`Galley`] cache.\n    pub fn flush_cache(&mut self) {\n        let current_generation = self.generation;\n        self.cache.retain(|_key, cached| {\n            cached.last_used == current_generation // only keep those that were used this frame\n        });\n        self.generation = self.generation.wrapping_add(1);\n    }\n}\n\n/// If true, lay out and cache each paragraph (sections separated by newlines) individually.\n///\n/// This makes it much faster to re-layout the full text when only a portion of it has changed since last frame, i.e. when editing somewhere in a file with thousands of lines/paragraphs.\nfn should_cache_each_paragraph_individually(job: &LayoutJob) -> bool {\n    // We currently don't support this elided text, i.e. when `max_rows` is set.\n    // Most often, elided text is elided to one row,\n    // and so will always be fast to lay out.\n    job.break_on_newline && job.wrap.max_rows == usize::MAX && job.text.contains('\\n')\n}\n\n#[cfg(feature = \"default_fonts\")]\n#[cfg(test)]\nmod tests {\n    use core::f32;\n\n    use super::*;\n    use crate::text::{TextWrapping, layout};\n    use crate::{Stroke, text::TextFormat};\n    use ecolor::Color32;\n    use emath::Align;\n\n    fn jobs() -> Vec<LayoutJob> {\n        vec![\n            LayoutJob::simple(\n                String::default(),\n                FontId::new(14.0, FontFamily::Monospace),\n                Color32::WHITE,\n                f32::INFINITY,\n            ),\n            LayoutJob::simple(\n                \"ends with newlines\\n\\n\".to_owned(),\n                FontId::new(14.0, FontFamily::Monospace),\n                Color32::WHITE,\n                f32::INFINITY,\n            ),\n            LayoutJob::simple(\n                \"Simple test.\".to_owned(),\n                FontId::new(14.0, FontFamily::Monospace),\n                Color32::WHITE,\n                f32::INFINITY,\n            ),\n            {\n                let mut job = LayoutJob::simple(\n                    \"hi\".to_owned(),\n                    FontId::default(),\n                    Color32::WHITE,\n                    f32::INFINITY,\n                );\n                job.append(\"\\n\", 0.0, TextFormat::default());\n                job.append(\"\\n\", 0.0, TextFormat::default());\n                job.append(\"world\", 0.0, TextFormat::default());\n                job.wrap.max_rows = 2;\n                job\n            },\n            {\n                let mut job = LayoutJob::simple(\n                    \"Test text with a lot of words\\n and a newline.\".to_owned(),\n                    FontId::new(14.0, FontFamily::Monospace),\n                    Color32::WHITE,\n                    40.0,\n                );\n                job.first_row_min_height = 30.0;\n                job\n            },\n            LayoutJob::simple(\n                \"This some text that may be long.\\nDet kanske också finns lite ÅÄÖ här.\".to_owned(),\n                FontId::new(14.0, FontFamily::Proportional),\n                Color32::WHITE,\n                50.0,\n            ),\n            {\n                let mut job = LayoutJob {\n                    first_row_min_height: 20.0,\n                    ..Default::default()\n                };\n                job.append(\n                    \"1st paragraph has underline and strikethrough, and has some non-ASCII characters:\\n ÅÄÖ.\",\n                    0.0,\n                    TextFormat {\n                        font_id: FontId::new(15.0, FontFamily::Monospace),\n                        underline: Stroke::new(1.0, Color32::RED),\n                        strikethrough: Stroke::new(1.0, Color32::GREEN),\n                        ..Default::default()\n                    },\n                );\n                job.append(\n                    \"2nd paragraph has some leading space.\\n\",\n                    16.0,\n                    TextFormat {\n                        font_id: FontId::new(14.0, FontFamily::Proportional),\n                        ..Default::default()\n                    },\n                );\n                job.append(\n                    \"3rd paragraph is kind of boring, but has italics.\\nAnd a newline\",\n                    0.0,\n                    TextFormat {\n                        font_id: FontId::new(10.0, FontFamily::Proportional),\n                        italics: true,\n                        ..Default::default()\n                    },\n                );\n\n                job\n            },\n            {\n                // Regression test for <https://github.com/emilk/egui/issues/7378>\n                let mut job = LayoutJob::default();\n                job.append(\"\\n\", 0.0, TextFormat::default());\n                job.append(\"\", 0.0, TextFormat::default());\n                job\n            },\n        ]\n    }\n\n    #[expect(clippy::print_stdout)]\n    #[test]\n    fn test_split_paragraphs() {\n        for pixels_per_point in [1.0, 2.0_f32.sqrt(), 2.0] {\n            let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());\n\n            for halign in [Align::Min, Align::Center, Align::Max] {\n                for justify in [false, true] {\n                    for mut job in jobs() {\n                        job.halign = halign;\n                        job.justify = justify;\n\n                        let whole = GalleyCache::default().layout(\n                            &mut fonts,\n                            pixels_per_point,\n                            job.clone(),\n                            false,\n                        );\n\n                        let split = GalleyCache::default().layout(\n                            &mut fonts,\n                            pixels_per_point,\n                            job.clone(),\n                            true,\n                        );\n\n                        for (i, row) in whole.rows.iter().enumerate() {\n                            println!(\n                                \"Whole row {i}: section_index_at_start={}, first glyph section_index: {:?}\",\n                                row.row.section_index_at_start,\n                                row.row.glyphs.first().map(|g| g.section_index)\n                            );\n                        }\n                        for (i, row) in split.rows.iter().enumerate() {\n                            println!(\n                                \"Split row {i}: section_index_at_start={}, first glyph section_index: {:?}\",\n                                row.row.section_index_at_start,\n                                row.row.glyphs.first().map(|g| g.section_index)\n                            );\n                        }\n\n                        // Don't compare for equaliity; but format with a specific precision and make sure we hit that.\n                        // NOTE: we use a rather low precision, because as long as we're within a pixel I think it's good enough.\n                        similar_asserts::assert_eq!(\n                            format!(\"{:#.1?}\", split),\n                            format!(\"{:#.1?}\", whole),\n                            \"pixels_per_point: {pixels_per_point:.2}, input text: '{}'\",\n                            job.text\n                        );\n                    }\n                }\n            }\n        }\n    }\n\n    #[test]\n    fn test_intrinsic_size() {\n        let pixels_per_point = [1.0, 1.3, 2.0, 0.867];\n        let max_widths = [40.0, 80.0, 133.0, 200.0];\n        let rounded_output_to_gui = [false, true];\n\n        for pixels_per_point in pixels_per_point {\n            let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());\n\n            for &max_width in &max_widths {\n                for round_output_to_gui in rounded_output_to_gui {\n                    for mut job in jobs() {\n                        job.wrap = TextWrapping::wrap_at_width(max_width);\n\n                        job.round_output_to_gui = round_output_to_gui;\n\n                        let galley_wrapped =\n                            layout(&mut fonts, pixels_per_point, job.clone().into());\n\n                        job.wrap = TextWrapping::no_max_width();\n\n                        let text = job.text.clone();\n                        let galley_unwrapped = layout(&mut fonts, pixels_per_point, job.into());\n\n                        let intrinsic_size = galley_wrapped.intrinsic_size();\n                        let unwrapped_size = galley_unwrapped.size();\n\n                        let difference = (intrinsic_size - unwrapped_size).length().abs();\n                        similar_asserts::assert_eq!(\n                            format!(\"{intrinsic_size:.4?}\"),\n                            format!(\"{unwrapped_size:.4?}\"),\n                            \"Wrapped intrinsic size should almost match unwrapped size. Intrinsic: {intrinsic_size:.8?} vs unwrapped: {unwrapped_size:.8?}\n                                Difference: {difference:.8?}\n                                wrapped rows: {}, unwrapped rows: {}\n                                pixels_per_point: {pixels_per_point}, text: {text:?}, max_width: {max_width}, round_output_to_gui: {round_output_to_gui}\",\n                            galley_wrapped.rows.len(),\n                            galley_unwrapped.rows.len()\n                            );\n                        similar_asserts::assert_eq!(\n                            format!(\"{intrinsic_size:.4?}\"),\n                            format!(\"{unwrapped_size:.4?}\"),\n                            \"Unwrapped galley intrinsic size should exactly match its size. \\\n                                {:.8?} vs {:8?}\",\n                            galley_unwrapped.intrinsic_size(),\n                            galley_unwrapped.size(),\n                        );\n                    }\n                }\n            }\n        }\n    }\n\n    #[test]\n    fn test_fallback_glyph_width() {\n        let mut fonts = Fonts::new(TextOptions::default(), FontDefinitions::empty());\n        let mut view = fonts.with_pixels_per_point(1.0);\n\n        let width = view.glyph_width(&FontId::new(12.0, FontFamily::Proportional), ' ');\n        assert_eq!(width, 0.0);\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/text/mod.rs",
    "content": "//! Everything related to text, fonts, text layout, cursors etc.\n\npub mod cursor;\nmod font;\nmod fonts;\nmod text_layout;\nmod text_layout_types;\n\n/// One `\\t` character is this many spaces wide.\npub const TAB_SIZE: usize = 4;\n\npub use {\n    fonts::{\n        FontData, FontDefinitions, FontFamily, FontId, FontInsert, FontPriority, FontTweak, Fonts,\n        FontsImpl, FontsView, InsertFontFamily,\n    },\n    text_layout::*,\n    text_layout_types::*,\n};\n\n/// Suggested character to use to replace those in password text fields.\npub const PASSWORD_REPLACEMENT_CHAR: char = '•';\n\n/// Controls how we render text\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct TextOptions {\n    /// Maximum size of the font texture.\n    pub max_texture_side: usize,\n\n    /// Controls how to convert glyph coverage to alpha.\n    pub alpha_from_coverage: crate::AlphaFromCoverage,\n\n    /// Whether to enable font hinting\n    ///\n    /// (round some font coordinates to pixels for sharper text).\n    ///\n    /// Default is `true`.\n    pub font_hinting: bool,\n}\n\nimpl Default for TextOptions {\n    fn default() -> Self {\n        Self {\n            max_texture_side: 2048, // Small but portable\n            alpha_from_coverage: crate::AlphaFromCoverage::default(),\n            font_hinting: true,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/text/text_layout.rs",
    "content": "#![expect(clippy::unwrap_used)] // TODO(emilk): remove unwraps\n\nuse std::sync::Arc;\n\nuse emath::{Align, GuiRounding as _, NumExt as _, Pos2, Rect, Vec2, pos2, vec2};\n\nuse crate::{\n    Color32, Mesh, Stroke, Vertex,\n    stroke::PathStroke,\n    text::{\n        font::{StyledMetrics, is_cjk, is_cjk_break_allowed},\n        fonts::FontFaceKey,\n    },\n};\n\nuse super::{FontsImpl, Galley, Glyph, LayoutJob, LayoutSection, PlacedRow, Row, RowVisuals};\n\n// ----------------------------------------------------------------------------\n\n/// Represents GUI scale and convenience methods for rounding to pixels.\n#[derive(Clone, Copy)]\nstruct PointScale {\n    pub pixels_per_point: f32,\n}\n\nimpl PointScale {\n    #[inline(always)]\n    pub fn new(pixels_per_point: f32) -> Self {\n        Self { pixels_per_point }\n    }\n\n    #[inline(always)]\n    pub fn pixels_per_point(&self) -> f32 {\n        self.pixels_per_point\n    }\n\n    #[inline(always)]\n    pub fn round_to_pixel(&self, point: f32) -> f32 {\n        (point * self.pixels_per_point).round() / self.pixels_per_point\n    }\n\n    #[inline(always)]\n    pub fn floor_to_pixel(&self, point: f32) -> f32 {\n        (point * self.pixels_per_point).floor() / self.pixels_per_point\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Temporary storage before line-wrapping.\n#[derive(Clone)]\nstruct Paragraph {\n    /// Start of the next glyph to be added. In screen-space / physical pixels.\n    pub cursor_x_px: f32,\n\n    /// This is included in case there are no glyphs\n    pub section_index_at_start: u32,\n\n    pub glyphs: Vec<Glyph>,\n\n    /// In case of an empty paragraph (\"\\n\"), use this as height.\n    pub empty_paragraph_height: f32,\n}\n\nimpl Paragraph {\n    pub fn from_section_index(section_index_at_start: u32) -> Self {\n        Self {\n            cursor_x_px: 0.0,\n            section_index_at_start,\n            glyphs: vec![],\n            empty_paragraph_height: 0.0,\n        }\n    }\n}\n\n/// Layout text into a [`Galley`].\n///\n/// In most cases you should use [`crate::FontsView::layout_job`] instead\n/// since that memoizes the input, making subsequent layouting of the same text much faster.\npub fn layout(fonts: &mut FontsImpl, pixels_per_point: f32, job: Arc<LayoutJob>) -> Galley {\n    profiling::function_scope!();\n\n    if job.wrap.max_rows == 0 {\n        // Early-out: no text\n        return Galley {\n            job,\n            rows: Default::default(),\n            rect: Rect::ZERO,\n            mesh_bounds: Rect::NOTHING,\n            num_vertices: 0,\n            num_indices: 0,\n            pixels_per_point,\n            elided: true,\n            intrinsic_size: Vec2::ZERO,\n        };\n    }\n\n    // For most of this we ignore the y coordinate:\n\n    let mut paragraphs = vec![Paragraph::from_section_index(0)];\n    for (section_index, section) in job.sections.iter().enumerate() {\n        layout_section(\n            fonts,\n            pixels_per_point,\n            &job,\n            section_index as u32,\n            section,\n            &mut paragraphs,\n        );\n    }\n\n    let point_scale = PointScale::new(pixels_per_point);\n\n    let intrinsic_size = calculate_intrinsic_size(point_scale, &job, &paragraphs);\n\n    let mut elided = false;\n    let mut rows = rows_from_paragraphs(paragraphs, &job, pixels_per_point, &mut elided);\n    if elided && let Some(last_placed) = rows.last_mut() {\n        let last_row = Arc::make_mut(&mut last_placed.row);\n        replace_last_glyph_with_overflow_character(fonts, pixels_per_point, &job, last_row);\n        if let Some(last) = last_row.glyphs.last() {\n            last_row.size.x = last.max_x();\n        }\n    }\n\n    let justify = job.justify && job.wrap.max_width.is_finite();\n\n    if justify || job.halign != Align::LEFT {\n        let num_rows = rows.len();\n        for (i, placed_row) in rows.iter_mut().enumerate() {\n            let is_last_row = i + 1 == num_rows;\n            let justify_row = justify && !placed_row.ends_with_newline && !is_last_row;\n            halign_and_justify_row(\n                point_scale,\n                placed_row,\n                job.halign,\n                job.wrap.max_width,\n                justify_row,\n            );\n        }\n    }\n\n    // Calculate the Y positions and tessellate the text:\n    galley_from_rows(point_scale, job, rows, elided, intrinsic_size)\n}\n\n// Ignores the Y coordinate.\nfn layout_section(\n    fonts: &mut FontsImpl,\n    pixels_per_point: f32,\n    job: &LayoutJob,\n    section_index: u32,\n    section: &LayoutSection,\n    out_paragraphs: &mut Vec<Paragraph>,\n) {\n    let LayoutSection {\n        leading_space,\n        byte_range,\n        format,\n    } = section;\n    let mut font = fonts.font(&format.font_id.family);\n    let font_size = format.font_id.size;\n    let font_metrics = font.styled_metrics(pixels_per_point, font_size, &format.coords);\n    let line_height = section\n        .format\n        .line_height\n        .unwrap_or(font_metrics.row_height);\n    let extra_letter_spacing = section.format.extra_letter_spacing;\n\n    let mut paragraph = out_paragraphs.last_mut().unwrap();\n    if paragraph.glyphs.is_empty() {\n        paragraph.empty_paragraph_height = line_height; // TODO(emilk): replace this hack with actually including `\\n` in the glyphs?\n    }\n\n    paragraph.cursor_x_px += leading_space * pixels_per_point;\n\n    let mut last_glyph_id = None;\n\n    // Optimization: only recompute `ScaledMetrics` when the concrete `FontImpl` changes.\n    let mut current_font = FontFaceKey::INVALID;\n    let mut current_font_face_metrics = StyledMetrics::default();\n\n    for chr in job.text[byte_range.clone()].chars() {\n        if job.break_on_newline && chr == '\\n' {\n            out_paragraphs.push(Paragraph::from_section_index(section_index));\n            paragraph = out_paragraphs.last_mut().unwrap();\n            paragraph.empty_paragraph_height = line_height; // TODO(emilk): replace this hack with actually including `\\n` in the glyphs?\n        } else {\n            let (font_id, glyph_info) = font.glyph_info(chr);\n            let mut font_face = font.fonts_by_id.get_mut(&font_id);\n            if current_font != font_id {\n                current_font = font_id;\n                current_font_face_metrics = font_face\n                    .as_ref()\n                    .map(|font_face| {\n                        font_face.styled_metrics(pixels_per_point, font_size, &format.coords)\n                    })\n                    .unwrap_or_default();\n            }\n\n            if let (Some(font_face), Some(last_glyph_id), Some(glyph_id)) =\n                (&font_face, last_glyph_id, glyph_info.id)\n            {\n                paragraph.cursor_x_px += font_face.pair_kerning_pixels(\n                    &current_font_face_metrics,\n                    last_glyph_id,\n                    glyph_id,\n                );\n\n                // Only apply extra_letter_spacing to glyphs after the first one:\n                paragraph.cursor_x_px += extra_letter_spacing * pixels_per_point;\n            }\n\n            let (glyph_alloc, physical_x) = if let Some(font_face) = font_face.as_mut() {\n                font_face.allocate_glyph(\n                    font.atlas,\n                    &current_font_face_metrics,\n                    glyph_info,\n                    chr,\n                    paragraph.cursor_x_px,\n                )\n            } else {\n                Default::default()\n            };\n\n            paragraph.glyphs.push(Glyph {\n                chr,\n                pos: pos2(physical_x as f32 / pixels_per_point, f32::NAN),\n                advance_width: glyph_alloc.advance_width_px / pixels_per_point,\n                line_height,\n                font_face_height: current_font_face_metrics.row_height,\n                font_face_ascent: current_font_face_metrics.ascent,\n                font_height: font_metrics.row_height,\n                font_ascent: font_metrics.ascent,\n                uv_rect: glyph_alloc.uv_rect,\n                section_index,\n                first_vertex: 0, // filled in later\n            });\n\n            paragraph.cursor_x_px += glyph_alloc.advance_width_px;\n            last_glyph_id = Some(glyph_alloc.id);\n        }\n    }\n}\n\n/// Calculate the intrinsic size of the text.\n///\n/// The result is eventually passed to `Response::intrinsic_size`.\n/// This works by calculating the size of each `Paragraph` (instead of each `Row`).\nfn calculate_intrinsic_size(\n    point_scale: PointScale,\n    job: &LayoutJob,\n    paragraphs: &[Paragraph],\n) -> Vec2 {\n    let mut intrinsic_size = Vec2::ZERO;\n    for (idx, paragraph) in paragraphs.iter().enumerate() {\n        // Use the precise cursor position instead of `last_glyph.max_x()`,\n        // because glyph positions are pixel-snapped but the cursor tracks\n        // the exact subpixel advance. This ensures that when two galleys are\n        // placed side-by-side, the gap matches what it would be within a\n        // single galley.\n        let width = paragraph.cursor_x_px / point_scale.pixels_per_point;\n        intrinsic_size.x = f32::max(intrinsic_size.x, width);\n\n        let mut height = paragraph\n            .glyphs\n            .iter()\n            .map(|g| g.line_height)\n            .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))\n            .unwrap_or(paragraph.empty_paragraph_height);\n        if idx == 0 {\n            height = f32::max(height, job.first_row_min_height);\n        }\n        intrinsic_size.y += point_scale.round_to_pixel(height);\n    }\n    intrinsic_size\n}\n\n// Ignores the Y coordinate.\nfn rows_from_paragraphs(\n    paragraphs: Vec<Paragraph>,\n    job: &LayoutJob,\n    pixels_per_point: f32,\n    elided: &mut bool,\n) -> Vec<PlacedRow> {\n    let num_paragraphs = paragraphs.len();\n\n    let mut rows = vec![];\n\n    for (i, paragraph) in paragraphs.into_iter().enumerate() {\n        if job.wrap.max_rows <= rows.len() {\n            *elided = true;\n            break;\n        }\n\n        let is_last_paragraph = (i + 1) == num_paragraphs;\n\n        if paragraph.glyphs.is_empty() {\n            rows.push(PlacedRow {\n                pos: pos2(0.0, f32::NAN),\n                row: Arc::new(Row {\n                    section_index_at_start: paragraph.section_index_at_start,\n                    glyphs: vec![],\n                    visuals: Default::default(),\n                    size: vec2(0.0, paragraph.empty_paragraph_height),\n                }),\n                ends_with_newline: !is_last_paragraph,\n            });\n        } else {\n            // Use precise cursor position for width instead of pixel-snapped\n            // `last_glyph.max_x()`, so that side-by-side galleys have the same\n            // spacing as characters within a single galley.\n            let paragraph_width = paragraph.cursor_x_px / pixels_per_point;\n            if paragraph_width <= job.effective_wrap_width() {\n                // Early-out optimization: the whole paragraph fits on one row.\n                rows.push(PlacedRow {\n                    pos: pos2(0.0, f32::NAN),\n                    row: Arc::new(Row {\n                        section_index_at_start: paragraph.section_index_at_start,\n                        glyphs: paragraph.glyphs,\n                        visuals: Default::default(),\n                        size: vec2(paragraph_width, 0.0),\n                    }),\n                    ends_with_newline: !is_last_paragraph,\n                });\n            } else {\n                line_break(&paragraph, job, &mut rows, elided);\n                let placed_row = rows.last_mut().unwrap();\n                placed_row.ends_with_newline = !is_last_paragraph;\n            }\n        }\n    }\n\n    rows\n}\n\nfn line_break(\n    paragraph: &Paragraph,\n    job: &LayoutJob,\n    out_rows: &mut Vec<PlacedRow>,\n    elided: &mut bool,\n) {\n    let wrap_width = job.effective_wrap_width();\n\n    // Keeps track of good places to insert row break if we exceed `wrap_width`.\n    let mut row_break_candidates = RowBreakCandidates::default();\n\n    let mut first_row_indentation = paragraph.glyphs[0].pos.x;\n    let mut row_start_x = 0.0;\n    let mut row_start_idx = 0;\n\n    for i in 0..paragraph.glyphs.len() {\n        if job.wrap.max_rows <= out_rows.len() {\n            *elided = true;\n            break;\n        }\n\n        let potential_row_width = paragraph.glyphs[i].max_x() - row_start_x;\n\n        if wrap_width < potential_row_width {\n            // Row break:\n\n            if first_row_indentation > 0.0\n                && !row_break_candidates.has_good_candidate(job.wrap.break_anywhere)\n            {\n                // Allow the first row to be completely empty, because we know there will be more space on the next row:\n                // TODO(emilk): this records the height of this first row as zero, though that is probably fine since first_row_indentation usually comes with a first_row_min_height.\n                out_rows.push(PlacedRow {\n                    pos: pos2(0.0, f32::NAN),\n                    row: Arc::new(Row {\n                        section_index_at_start: paragraph.section_index_at_start,\n                        glyphs: vec![],\n                        visuals: Default::default(),\n                        size: Vec2::ZERO,\n                    }),\n                    ends_with_newline: false,\n                });\n                row_start_x += first_row_indentation;\n                first_row_indentation = 0.0;\n            } else if let Some(last_kept_index) = row_break_candidates.get(job.wrap.break_anywhere)\n            {\n                let glyphs: Vec<Glyph> = paragraph.glyphs[row_start_idx..=last_kept_index]\n                    .iter()\n                    .copied()\n                    .map(|mut glyph| {\n                        glyph.pos.x -= row_start_x;\n                        glyph\n                    })\n                    .collect();\n\n                let section_index_at_start = glyphs[0].section_index;\n                let paragraph_max_x = glyphs.last().unwrap().max_x();\n\n                out_rows.push(PlacedRow {\n                    pos: pos2(0.0, f32::NAN),\n                    row: Arc::new(Row {\n                        section_index_at_start,\n                        glyphs,\n                        visuals: Default::default(),\n                        size: vec2(paragraph_max_x, 0.0),\n                    }),\n                    ends_with_newline: false,\n                });\n\n                // Start a new row:\n                row_start_idx = last_kept_index + 1;\n                row_start_x = paragraph.glyphs[row_start_idx].pos.x;\n                row_break_candidates.forget_before_idx(row_start_idx);\n            } else {\n                // Found no place to break, so we have to overrun wrap_width.\n            }\n        }\n\n        row_break_candidates.add(i, &paragraph.glyphs[i..]);\n    }\n\n    if row_start_idx < paragraph.glyphs.len() {\n        // Final row of text:\n\n        if job.wrap.max_rows <= out_rows.len() {\n            *elided = true; // can't fit another row\n        } else {\n            let glyphs: Vec<Glyph> = paragraph.glyphs[row_start_idx..]\n                .iter()\n                .copied()\n                .map(|mut glyph| {\n                    glyph.pos.x -= row_start_x;\n                    glyph\n                })\n                .collect();\n\n            let section_index_at_start = glyphs[0].section_index;\n            let paragraph_min_x = glyphs[0].pos.x;\n            let paragraph_max_x = glyphs.last().unwrap().max_x();\n\n            out_rows.push(PlacedRow {\n                pos: pos2(paragraph_min_x, 0.0),\n                row: Arc::new(Row {\n                    section_index_at_start,\n                    glyphs,\n                    visuals: Default::default(),\n                    size: vec2(paragraph_max_x - paragraph_min_x, 0.0),\n                }),\n                ends_with_newline: false,\n            });\n        }\n    }\n}\n\n/// Trims the last glyphs in the row and replaces it with an overflow character (e.g. `…`).\n///\n/// Called before we have any Y coordinates.\nfn replace_last_glyph_with_overflow_character(\n    fonts: &mut FontsImpl,\n    pixels_per_point: f32,\n    job: &LayoutJob,\n    row: &mut Row,\n) {\n    let Some(overflow_character) = job.wrap.overflow_character else {\n        return;\n    };\n\n    let mut section_index = row\n        .glyphs\n        .last()\n        .map(|g| g.section_index)\n        .unwrap_or(row.section_index_at_start);\n    loop {\n        let section = &job.sections[section_index as usize];\n        let extra_letter_spacing = section.format.extra_letter_spacing;\n        let mut font = fonts.font(&section.format.font_id.family);\n        let font_size = section.format.font_id.size;\n\n        let (font_id, glyph_info) = font.glyph_info(overflow_character);\n        let mut font_face = font.fonts_by_id.get_mut(&font_id);\n        let font_face_metrics = font_face\n            .as_mut()\n            .map(|f| f.styled_metrics(pixels_per_point, font_size, &section.format.coords))\n            .unwrap_or_default();\n\n        let overflow_glyph_x = if let Some(prev_glyph) = row.glyphs.last() {\n            // Kern the overflow character properly\n            let pair_kerning = font_face\n                .as_mut()\n                .map(|font_face| {\n                    if let (Some(prev_glyph_id), Some(overflow_glyph_id)) = (\n                        font_face.glyph_info(prev_glyph.chr).and_then(|g| g.id),\n                        font_face.glyph_info(overflow_character).and_then(|g| g.id),\n                    ) {\n                        font_face.pair_kerning(&font_face_metrics, prev_glyph_id, overflow_glyph_id)\n                    } else {\n                        0.0\n                    }\n                })\n                .unwrap_or_default();\n\n            prev_glyph.max_x() + extra_letter_spacing + pair_kerning\n        } else {\n            0.0 // TODO(emilk): heed paragraph leading_space 😬\n        };\n\n        let replacement_glyph_width = font_face\n            .as_mut()\n            .and_then(|f| f.glyph_info(overflow_character))\n            .map(|i| {\n                i.advance_width_unscaled.0 * font_face_metrics.px_scale_factor / pixels_per_point\n            })\n            .unwrap_or_default();\n\n        // Check if we're within width budget:\n        if overflow_glyph_x + replacement_glyph_width <= job.effective_wrap_width()\n            || row.glyphs.is_empty()\n        {\n            // we are done\n\n            let (replacement_glyph_alloc, physical_x) = font_face\n                .as_mut()\n                .map(|f| {\n                    f.allocate_glyph(\n                        font.atlas,\n                        &font_face_metrics,\n                        glyph_info,\n                        overflow_character,\n                        overflow_glyph_x * pixels_per_point,\n                    )\n                })\n                .unwrap_or_default();\n\n            let font_metrics =\n                font.styled_metrics(pixels_per_point, font_size, &section.format.coords);\n            let line_height = section\n                .format\n                .line_height\n                .unwrap_or(font_metrics.row_height);\n\n            row.glyphs.push(Glyph {\n                chr: overflow_character,\n                pos: pos2(physical_x as f32 / pixels_per_point, f32::NAN),\n                advance_width: replacement_glyph_alloc.advance_width_px / pixels_per_point,\n                line_height,\n                font_face_height: font_face_metrics.row_height,\n                font_face_ascent: font_face_metrics.ascent,\n                font_height: font_metrics.row_height,\n                font_ascent: font_metrics.ascent,\n                uv_rect: replacement_glyph_alloc.uv_rect,\n                section_index,\n                first_vertex: 0, // filled in later\n            });\n            return;\n        }\n\n        // We didn't fit - pop the last glyph and try again.\n        if let Some(last_glyph) = row.glyphs.pop() {\n            section_index = last_glyph.section_index;\n        } else {\n            section_index = row.section_index_at_start;\n        }\n    }\n}\n\n/// Horizontally aligned the text on a row.\n///\n/// Ignores the Y coordinate.\nfn halign_and_justify_row(\n    point_scale: PointScale,\n    placed_row: &mut PlacedRow,\n    halign: Align,\n    wrap_width: f32,\n    justify: bool,\n) {\n    #![expect(clippy::useless_let_if_seq)] // False positive\n\n    let row = Arc::make_mut(&mut placed_row.row);\n\n    if row.glyphs.is_empty() {\n        return;\n    }\n\n    let num_leading_spaces = row\n        .glyphs\n        .iter()\n        .take_while(|glyph| glyph.chr.is_whitespace())\n        .count();\n\n    let glyph_range = if num_leading_spaces == row.glyphs.len() {\n        // There is only whitespace\n        (0, row.glyphs.len())\n    } else {\n        let num_trailing_spaces = row\n            .glyphs\n            .iter()\n            .rev()\n            .take_while(|glyph| glyph.chr.is_whitespace())\n            .count();\n\n        (num_leading_spaces, row.glyphs.len() - num_trailing_spaces)\n    };\n    let num_glyphs_in_range = glyph_range.1 - glyph_range.0;\n    assert!(num_glyphs_in_range > 0, \"Should have at least one glyph\");\n\n    let original_min_x = row.glyphs[glyph_range.0].logical_rect().min.x;\n    let original_max_x = row.glyphs[glyph_range.1 - 1].logical_rect().max.x;\n    let original_width = original_max_x - original_min_x;\n\n    let target_width = if justify && num_glyphs_in_range > 1 {\n        wrap_width\n    } else {\n        original_width\n    };\n\n    let (target_min_x, target_max_x) = match halign {\n        Align::LEFT => (0.0, target_width),\n        Align::Center => (-target_width / 2.0, target_width / 2.0),\n        Align::RIGHT => (-target_width, 0.0),\n    };\n\n    let num_spaces_in_range = row.glyphs[glyph_range.0..glyph_range.1]\n        .iter()\n        .filter(|glyph| glyph.chr.is_whitespace())\n        .count();\n\n    let mut extra_x_per_glyph = if num_glyphs_in_range == 1 {\n        0.0\n    } else {\n        (target_width - original_width) / (num_glyphs_in_range as f32 - 1.0)\n    };\n    extra_x_per_glyph = extra_x_per_glyph.at_least(0.0); // Don't contract\n\n    let mut extra_x_per_space = 0.0;\n    if 0 < num_spaces_in_range && num_spaces_in_range < num_glyphs_in_range {\n        // Add an integral number of pixels between each glyph,\n        // and add the balance to the spaces:\n\n        extra_x_per_glyph = point_scale.floor_to_pixel(extra_x_per_glyph);\n\n        extra_x_per_space = (target_width\n            - original_width\n            - extra_x_per_glyph * (num_glyphs_in_range as f32 - 1.0))\n            / (num_spaces_in_range as f32);\n    }\n\n    placed_row.pos.x = point_scale.round_to_pixel(target_min_x);\n    let mut translate_x = -original_min_x - extra_x_per_glyph * glyph_range.0 as f32;\n\n    for glyph in &mut row.glyphs {\n        glyph.pos.x += translate_x;\n        glyph.pos.x = point_scale.round_to_pixel(glyph.pos.x);\n        translate_x += extra_x_per_glyph;\n        if glyph.chr.is_whitespace() {\n            translate_x += extra_x_per_space;\n        }\n    }\n\n    // Note we ignore the leading/trailing whitespace here!\n    row.size.x = target_max_x - target_min_x;\n}\n\n/// Calculate the Y positions and tessellate the text.\nfn galley_from_rows(\n    point_scale: PointScale,\n    job: Arc<LayoutJob>,\n    mut rows: Vec<PlacedRow>,\n    elided: bool,\n    intrinsic_size: Vec2,\n) -> Galley {\n    let mut first_row_min_height = job.first_row_min_height;\n    let mut cursor_y = 0.0;\n\n    for placed_row in &mut rows {\n        let mut max_row_height = first_row_min_height.at_least(placed_row.height());\n        let row = Arc::make_mut(&mut placed_row.row);\n\n        first_row_min_height = 0.0;\n        for glyph in &row.glyphs {\n            max_row_height = max_row_height.at_least(glyph.line_height);\n        }\n        max_row_height = point_scale.round_to_pixel(max_row_height);\n\n        // Now position each glyph vertically:\n        for glyph in &mut row.glyphs {\n            let format = &job.sections[glyph.section_index as usize].format;\n\n            glyph.pos.y = glyph.font_face_ascent\n\n                // Apply valign to the different in height of the entire row, and the height of this `Font`:\n                + format.valign.to_factor() * (max_row_height - glyph.line_height)\n\n                // When mixing different `FontImpl` (e.g. latin and emojis),\n                // we always center the difference:\n                + 0.5 * (glyph.font_height - glyph.font_face_height);\n\n            glyph.pos.y = point_scale.round_to_pixel(glyph.pos.y);\n        }\n\n        placed_row.pos.y = cursor_y;\n        row.size.y = max_row_height;\n\n        cursor_y += max_row_height;\n        cursor_y = point_scale.round_to_pixel(cursor_y); // TODO(emilk): it would be better to do the calculations in pixels instead.\n    }\n\n    let format_summary = format_summary(&job);\n\n    let mut rect = Rect::ZERO;\n    let mut mesh_bounds = Rect::NOTHING;\n    let mut num_vertices = 0;\n    let mut num_indices = 0;\n\n    for placed_row in &mut rows {\n        rect |= placed_row.rect();\n\n        let row = Arc::make_mut(&mut placed_row.row);\n        row.visuals = tessellate_row(point_scale, &job, &format_summary, row);\n\n        mesh_bounds |= row.visuals.mesh_bounds.translate(placed_row.pos.to_vec2());\n        num_vertices += row.visuals.mesh.vertices.len();\n        num_indices += row.visuals.mesh.indices.len();\n\n        row.section_index_at_start = u32::MAX; // No longer in use.\n        for glyph in &mut row.glyphs {\n            glyph.section_index = u32::MAX; // No longer in use.\n        }\n    }\n\n    let mut galley = Galley {\n        job,\n        rows,\n        elided,\n        rect,\n        mesh_bounds,\n        num_vertices,\n        num_indices,\n        pixels_per_point: point_scale.pixels_per_point,\n        intrinsic_size,\n    };\n\n    if galley.job.round_output_to_gui {\n        galley.round_output_to_gui();\n    }\n\n    galley\n}\n\n#[derive(Default)]\nstruct FormatSummary {\n    any_background: bool,\n    any_underline: bool,\n    any_strikethrough: bool,\n}\n\nfn format_summary(job: &LayoutJob) -> FormatSummary {\n    let mut format_summary = FormatSummary::default();\n    for section in &job.sections {\n        format_summary.any_background |= section.format.background != Color32::TRANSPARENT;\n        format_summary.any_underline |= section.format.underline != Stroke::NONE;\n        format_summary.any_strikethrough |= section.format.strikethrough != Stroke::NONE;\n    }\n    format_summary\n}\n\nfn tessellate_row(\n    point_scale: PointScale,\n    job: &LayoutJob,\n    format_summary: &FormatSummary,\n    row: &mut Row,\n) -> RowVisuals {\n    if row.glyphs.is_empty() {\n        return Default::default();\n    }\n\n    let mut mesh = Mesh::default();\n\n    mesh.reserve_triangles(row.glyphs.len() * 2);\n    mesh.reserve_vertices(row.glyphs.len() * 4);\n\n    if format_summary.any_background {\n        add_row_backgrounds(point_scale, job, row, &mut mesh);\n    }\n\n    let glyph_index_start = mesh.indices.len();\n    let glyph_vertex_start = mesh.vertices.len();\n    tessellate_glyphs(point_scale, job, row, &mut mesh);\n    let glyph_vertex_end = mesh.vertices.len();\n\n    if format_summary.any_underline {\n        add_row_hline(point_scale, row, &mut mesh, |glyph| {\n            let format = &job.sections[glyph.section_index as usize].format;\n            let stroke = format.underline;\n            let y = glyph.logical_rect().bottom();\n            (stroke, y)\n        });\n    }\n\n    if format_summary.any_strikethrough {\n        add_row_hline(point_scale, row, &mut mesh, |glyph| {\n            let format = &job.sections[glyph.section_index as usize].format;\n            let stroke = format.strikethrough;\n            let y = glyph.logical_rect().center().y;\n            (stroke, y)\n        });\n    }\n\n    let mesh_bounds = mesh.calc_bounds();\n\n    RowVisuals {\n        mesh,\n        mesh_bounds,\n        glyph_index_start,\n        glyph_vertex_range: glyph_vertex_start..glyph_vertex_end,\n    }\n}\n\n/// Create background for glyphs that have them.\n/// Creates as few rectangular regions as possible.\nfn add_row_backgrounds(point_scale: PointScale, job: &LayoutJob, row: &Row, mesh: &mut Mesh) {\n    if row.glyphs.is_empty() {\n        return;\n    }\n\n    let mut end_run = |start: Option<(Color32, Rect, f32)>, stop_x: f32| {\n        if let Some((color, start_rect, expand)) = start {\n            let rect = Rect::from_min_max(start_rect.left_top(), pos2(stop_x, start_rect.bottom()));\n            let rect = rect.expand(expand);\n            let rect = rect.round_to_pixels(point_scale.pixels_per_point());\n            mesh.add_colored_rect(rect, color);\n        }\n    };\n\n    let mut run_start = None;\n    let mut last_rect = Rect::NAN;\n\n    for glyph in &row.glyphs {\n        let format = &job.sections[glyph.section_index as usize].format;\n        let color = format.background;\n        let rect = glyph.logical_rect();\n\n        if color == Color32::TRANSPARENT {\n            end_run(run_start.take(), last_rect.right());\n        } else if let Some((existing_color, start, expand)) = run_start {\n            if existing_color == color\n                && start.top() == rect.top()\n                && start.bottom() == rect.bottom()\n                && format.expand_bg == expand\n            {\n                // continue the same background rectangle\n            } else {\n                end_run(run_start.take(), last_rect.right());\n                run_start = Some((color, rect, format.expand_bg));\n            }\n        } else {\n            run_start = Some((color, rect, format.expand_bg));\n        }\n\n        last_rect = rect;\n    }\n\n    end_run(run_start.take(), last_rect.right());\n}\n\nfn tessellate_glyphs(point_scale: PointScale, job: &LayoutJob, row: &mut Row, mesh: &mut Mesh) {\n    for glyph in &mut row.glyphs {\n        glyph.first_vertex = mesh.vertices.len() as u32;\n        let uv_rect = glyph.uv_rect;\n        if !uv_rect.is_nothing() {\n            let mut left_top = glyph.pos + uv_rect.offset;\n            left_top.x = point_scale.round_to_pixel(left_top.x);\n            left_top.y = point_scale.round_to_pixel(left_top.y);\n\n            let rect = Rect::from_min_max(left_top, left_top + uv_rect.size);\n            let uv = Rect::from_min_max(\n                pos2(uv_rect.min[0] as f32, uv_rect.min[1] as f32),\n                pos2(uv_rect.max[0] as f32, uv_rect.max[1] as f32),\n            );\n\n            let format = &job.sections[glyph.section_index as usize].format;\n\n            let color = format.color;\n\n            if format.italics {\n                let idx = mesh.vertices.len() as u32;\n                mesh.add_triangle(idx, idx + 1, idx + 2);\n                mesh.add_triangle(idx + 2, idx + 1, idx + 3);\n\n                let top_offset = rect.height() * 0.25 * Vec2::X;\n\n                mesh.vertices.push(Vertex {\n                    pos: rect.left_top() + top_offset,\n                    uv: uv.left_top(),\n                    color,\n                });\n                mesh.vertices.push(Vertex {\n                    pos: rect.right_top() + top_offset,\n                    uv: uv.right_top(),\n                    color,\n                });\n                mesh.vertices.push(Vertex {\n                    pos: rect.left_bottom(),\n                    uv: uv.left_bottom(),\n                    color,\n                });\n                mesh.vertices.push(Vertex {\n                    pos: rect.right_bottom(),\n                    uv: uv.right_bottom(),\n                    color,\n                });\n            } else {\n                mesh.add_rect_with_uv(rect, uv, color);\n            }\n        }\n    }\n}\n\n/// Add a horizontal line over a row of glyphs with a stroke and y decided by a callback.\nfn add_row_hline(\n    point_scale: PointScale,\n    row: &Row,\n    mesh: &mut Mesh,\n    stroke_and_y: impl Fn(&Glyph) -> (Stroke, f32),\n) {\n    let mut path = crate::tessellator::Path::default(); // reusing path to avoid re-allocations.\n\n    let mut end_line = |start: Option<(Stroke, Pos2)>, stop_x: f32| {\n        if let Some((stroke, start)) = start {\n            let stop = pos2(stop_x, start.y);\n            path.clear();\n            path.add_line_segment([start, stop]);\n            let feathering = 1.0 / point_scale.pixels_per_point();\n            path.stroke_open(feathering, &PathStroke::from(stroke), mesh);\n        }\n    };\n\n    let mut line_start = None;\n    let mut last_right_x = f32::NAN;\n\n    for glyph in &row.glyphs {\n        let (stroke, mut y) = stroke_and_y(glyph);\n        stroke.round_center_to_pixel(point_scale.pixels_per_point, &mut y);\n\n        if stroke.is_empty() {\n            end_line(line_start.take(), last_right_x);\n        } else if let Some((existing_stroke, start)) = line_start {\n            if existing_stroke == stroke && start.y == y {\n                // continue the same line\n            } else {\n                end_line(line_start.take(), last_right_x);\n                line_start = Some((stroke, pos2(glyph.pos.x, y)));\n            }\n        } else {\n            line_start = Some((stroke, pos2(glyph.pos.x, y)));\n        }\n\n        last_right_x = glyph.max_x();\n    }\n\n    end_line(line_start.take(), last_right_x);\n}\n\n// ----------------------------------------------------------------------------\n\n/// Keeps track of good places to break a long row of text.\n/// Will focus primarily on spaces, secondarily on things like `-`\n#[derive(Clone, Copy, Default)]\nstruct RowBreakCandidates {\n    /// Breaking at ` ` or other whitespace\n    /// is always the primary candidate.\n    space: Option<usize>,\n\n    /// Logograms (single character representing a whole word) or kana (Japanese hiragana and katakana) are good candidates for line break.\n    cjk: Option<usize>,\n\n    /// Breaking anywhere before a CJK character is acceptable too.\n    pre_cjk: Option<usize>,\n\n    /// Breaking at a dash is a super-\n    /// good idea.\n    dash: Option<usize>,\n\n    /// This is nicer for things like URLs, e.g. www.\n    /// example.com.\n    punctuation: Option<usize>,\n\n    /// Breaking after just random character is some\n    /// times necessary.\n    any: Option<usize>,\n}\n\nimpl RowBreakCandidates {\n    fn add(&mut self, index: usize, glyphs: &[Glyph]) {\n        let chr = glyphs[0].chr;\n        const NON_BREAKING_SPACE: char = '\\u{A0}';\n        if chr.is_whitespace() && chr != NON_BREAKING_SPACE {\n            self.space = Some(index);\n        } else if is_cjk(chr) && (glyphs.len() == 1 || is_cjk_break_allowed(glyphs[1].chr)) {\n            self.cjk = Some(index);\n        } else if chr == '-' {\n            self.dash = Some(index);\n        } else if chr.is_ascii_punctuation() {\n            self.punctuation = Some(index);\n        } else if glyphs.len() > 1 && is_cjk(glyphs[1].chr) {\n            self.pre_cjk = Some(index);\n        }\n        self.any = Some(index);\n    }\n\n    fn word_boundary(&self) -> Option<usize> {\n        [self.space, self.cjk, self.pre_cjk]\n            .into_iter()\n            .max()\n            .flatten()\n    }\n\n    fn has_good_candidate(&self, break_anywhere: bool) -> bool {\n        if break_anywhere {\n            self.any.is_some()\n        } else {\n            self.word_boundary().is_some()\n        }\n    }\n\n    fn get(&self, break_anywhere: bool) -> Option<usize> {\n        if break_anywhere {\n            self.any\n        } else {\n            self.word_boundary()\n                .or(self.dash)\n                .or(self.punctuation)\n                .or(self.any)\n        }\n    }\n\n    fn forget_before_idx(&mut self, index: usize) {\n        let Self {\n            space,\n            cjk,\n            pre_cjk,\n            dash,\n            punctuation,\n            any,\n        } = self;\n        if space.is_some_and(|s| s < index) {\n            *space = None;\n        }\n        if cjk.is_some_and(|s| s < index) {\n            *cjk = None;\n        }\n        if pre_cjk.is_some_and(|s| s < index) {\n            *pre_cjk = None;\n        }\n        if dash.is_some_and(|s| s < index) {\n            *dash = None;\n        }\n        if punctuation.is_some_and(|s| s < index) {\n            *punctuation = None;\n        }\n        if any.is_some_and(|s| s < index) {\n            *any = None;\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[cfg(test)]\nmod tests {\n\n    use super::{super::*, *};\n\n    #[test]\n    fn test_zero_max_width() {\n        let pixels_per_point = 1.0;\n        let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());\n        let mut layout_job = LayoutJob::single_section(\"W\".into(), TextFormat::default());\n        layout_job.wrap.max_width = 0.0;\n        let galley = layout(&mut fonts, pixels_per_point, layout_job.into());\n        assert_eq!(galley.rows.len(), 1);\n    }\n\n    #[test]\n    fn test_truncate_with_newline() {\n        // No matter where we wrap, we should be appending the newline character.\n\n        let pixels_per_point = 1.0;\n\n        let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());\n        let text_format = TextFormat {\n            font_id: FontId::monospace(12.0),\n            ..Default::default()\n        };\n\n        for text in [\"Hello\\nworld\", \"\\nfoo\"] {\n            for break_anywhere in [false, true] {\n                for max_width in [0.0, 5.0, 10.0, 20.0, f32::INFINITY] {\n                    let mut layout_job =\n                        LayoutJob::single_section(text.into(), text_format.clone());\n                    layout_job.wrap.max_width = max_width;\n                    layout_job.wrap.max_rows = 1;\n                    layout_job.wrap.break_anywhere = break_anywhere;\n\n                    let galley = layout(&mut fonts, pixels_per_point, layout_job.into());\n\n                    assert!(galley.elided);\n                    assert_eq!(galley.rows.len(), 1);\n                    let row_text = galley.rows[0].text();\n                    assert!(\n                        row_text.ends_with('…'),\n                        \"Expected row to end with `…`, got {row_text:?} when line-breaking the text {text:?} with max_width {max_width} and break_anywhere {break_anywhere}.\",\n                    );\n                }\n            }\n        }\n\n        {\n            let mut layout_job = LayoutJob::single_section(\"Hello\\nworld\".into(), text_format);\n            layout_job.wrap.max_width = 50.0;\n            layout_job.wrap.max_rows = 1;\n            layout_job.wrap.break_anywhere = false;\n\n            let galley = layout(&mut fonts, pixels_per_point, layout_job.into());\n\n            assert!(galley.elided);\n            assert_eq!(galley.rows.len(), 1);\n            let row_text = galley.rows[0].text();\n            assert_eq!(row_text, \"Hello…\");\n        }\n    }\n\n    #[test]\n    fn test_cjk() {\n        let pixels_per_point = 1.0;\n        let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());\n        let mut layout_job = LayoutJob::single_section(\n            \"日本語とEnglishの混在した文章\".into(),\n            TextFormat::default(),\n        );\n        layout_job.wrap.max_width = 90.0;\n        let galley = layout(&mut fonts, pixels_per_point, layout_job.into());\n        assert_eq!(\n            galley.rows.iter().map(|row| row.text()).collect::<Vec<_>>(),\n            vec![\"日本語と\", \"Englishの混在\", \"した文章\"]\n        );\n    }\n\n    #[test]\n    fn test_pre_cjk() {\n        let pixels_per_point = 1.0;\n        let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());\n        let mut layout_job = LayoutJob::single_section(\n            \"日本語とEnglishの混在した文章\".into(),\n            TextFormat::default(),\n        );\n        layout_job.wrap.max_width = 110.0;\n        let galley = layout(&mut fonts, pixels_per_point, layout_job.into());\n        assert_eq!(\n            galley.rows.iter().map(|row| row.text()).collect::<Vec<_>>(),\n            vec![\"日本語とEnglish\", \"の混在した文章\"]\n        );\n    }\n\n    #[test]\n    fn test_truncate_width() {\n        let pixels_per_point = 1.0;\n        let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());\n        let mut layout_job =\n            LayoutJob::single_section(\"# DNA\\nMore text\".into(), TextFormat::default());\n        layout_job.wrap.max_width = f32::INFINITY;\n        layout_job.wrap.max_rows = 1;\n        layout_job.round_output_to_gui = false;\n        let galley = layout(&mut fonts, pixels_per_point, layout_job.into());\n        assert!(galley.elided);\n        assert_eq!(\n            galley.rows.iter().map(|row| row.text()).collect::<Vec<_>>(),\n            vec![\"# DNA…\"]\n        );\n        let row = &galley.rows[0];\n        assert_eq!(row.pos, Pos2::ZERO);\n        assert_eq!(row.rect().max.x, row.glyphs.last().unwrap().max_x());\n    }\n\n    #[test]\n    fn test_truncate_with_pixels_per_point() {\n        let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());\n\n        for pixels_per_point in [\n            0.33, 0.5, 0.67, 1.0, 1.25, 1.33, 1.5, 1.75, 2.0, 3.0, 4.0, 5.0,\n        ] {\n            for ch in ['W', 'A', 'n', 't', 'i'] {\n                let target_width = 50.0;\n                let text = (0..20).map(|_| ch).collect::<String>();\n\n                let mut job = LayoutJob::single_section(text, TextFormat::default());\n                job.wrap.max_width = target_width;\n                job.wrap.max_rows = 1;\n                let elided_galley = layout(&mut fonts, pixels_per_point, job.into());\n                assert!(elided_galley.elided);\n\n                let test_galley = layout(\n                    &mut fonts,\n                    pixels_per_point,\n                    Arc::new(LayoutJob::single_section(\n                        (0..elided_galley.rows[0].char_count_excluding_newline())\n                            .map(|_| ch)\n                            .chain(std::iter::once('…'))\n                            .collect::<String>(),\n                        TextFormat::default(),\n                    )),\n                );\n\n                assert!(elided_galley.size().x >= 0.0);\n                assert!(elided_galley.size().x <= target_width);\n                assert!(test_galley.size().x > target_width);\n            }\n        }\n    }\n\n    #[test]\n    fn test_empty_row() {\n        let pixels_per_point = 1.0;\n        let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());\n\n        let font_id = FontId::default();\n        let font_height = fonts\n            .font(&font_id.family)\n            .styled_metrics(pixels_per_point, font_id.size, &VariationCoords::default())\n            .row_height;\n\n        let job = LayoutJob::simple(String::new(), font_id, Color32::WHITE, f32::INFINITY);\n\n        let galley = layout(&mut fonts, pixels_per_point, job.into());\n\n        assert_eq!(galley.rows.len(), 1, \"Expected one row\");\n        assert_eq!(\n            galley.rows[0].row.glyphs.len(),\n            0,\n            \"Expected no glyphs in the empty row\"\n        );\n        assert_eq!(\n            galley.size(),\n            Vec2::new(0.0, font_height.round()),\n            \"Unexpected galley size\"\n        );\n        assert_eq!(\n            galley.intrinsic_size(),\n            Vec2::new(0.0, font_height.round()),\n            \"Unexpected intrinsic size\"\n        );\n    }\n\n    #[test]\n    fn test_end_with_newline() {\n        let pixels_per_point = 1.0;\n        let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default());\n\n        let font_id = FontId::default();\n        let font_height = fonts\n            .font(&font_id.family)\n            .styled_metrics(pixels_per_point, font_id.size, &VariationCoords::default())\n            .row_height;\n\n        let job = LayoutJob::simple(\"Hi!\\n\".to_owned(), font_id, Color32::WHITE, f32::INFINITY);\n\n        let galley = layout(&mut fonts, pixels_per_point, job.into());\n\n        assert_eq!(galley.rows.len(), 2, \"Expected two rows\");\n        assert_eq!(\n            galley.rows[1].row.glyphs.len(),\n            0,\n            \"Expected no glyphs in the empty row\"\n        );\n        assert_eq!(\n            galley.size().round(),\n            Vec2::new(17.0, font_height.round() * 2.0),\n            \"Unexpected galley size\"\n        );\n        assert_eq!(\n            galley.intrinsic_size().round(),\n            Vec2::new(17.0, font_height.round() * 2.0),\n            \"Unexpected intrinsic size\"\n        );\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/text/text_layout_types.rs",
    "content": "use std::sync::Arc;\nuse std::{ops::Range, str::FromStr as _};\n\nuse super::{\n    cursor::{CCursor, LayoutCursor},\n    font::UvRect,\n};\nuse crate::{Color32, FontId, Mesh, Stroke, text::FontsView};\nuse emath::{Align, GuiRounding as _, NumExt as _, OrderedFloat, Pos2, Rect, Vec2, pos2, vec2};\npub use font_types::Tag;\nuse smallvec::SmallVec;\n\n/// Describes the task of laying out text.\n///\n/// This supports mixing different fonts, color and formats (underline etc).\n///\n/// Pass this to [`crate::FontsView::layout_job`] or [`crate::text::layout`].\n///\n/// ## Example:\n/// ```\n/// use epaint::{Color32, text::{LayoutJob, TextFormat}, FontFamily, FontId};\n///\n/// let mut job = LayoutJob::default();\n/// job.append(\n///     \"Hello \",\n///     0.0,\n///     TextFormat {\n///         font_id: FontId::new(14.0, FontFamily::Proportional),\n///         color: Color32::WHITE,\n///         ..Default::default()\n///     },\n/// );\n/// job.append(\n///     \"World!\",\n///     0.0,\n///     TextFormat {\n///         font_id: FontId::new(14.0, FontFamily::Monospace),\n///         color: Color32::BLACK,\n///         ..Default::default()\n///     },\n/// );\n/// ```\n///\n/// As you can see, constructing a [`LayoutJob`] is currently a lot of work.\n/// It would be nice to have a helper macro for it!\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct LayoutJob {\n    /// The complete text of this job, referenced by [`LayoutSection`].\n    pub text: String,\n\n    /// The different section, which can have different fonts, colors, etc.\n    pub sections: Vec<LayoutSection>,\n\n    /// Controls the text wrapping and elision.\n    pub wrap: TextWrapping,\n\n    /// The first row must be at least this high.\n    /// This is in case we lay out text that is the continuation\n    /// of some earlier text (sharing the same row),\n    /// in which case this will be the height of the earlier text.\n    /// In other cases, set this to `0.0`.\n    pub first_row_min_height: f32,\n\n    /// If `true`, all `\\n` characters will result in a new _paragraph_,\n    /// starting on a new row.\n    ///\n    /// If `false`, all `\\n` characters will be ignored\n    /// and show up as the replacement character.\n    ///\n    /// Default: `true`.\n    pub break_on_newline: bool,\n\n    /// How to horizontally align the text (`Align::LEFT`, `Align::Center`, `Align::RIGHT`).\n    pub halign: Align,\n\n    /// Justify text so that word-wrapped rows fill the whole [`TextWrapping::max_width`].\n    pub justify: bool,\n\n    /// Round output sizes using [`emath::GuiRounding`], to avoid rounding errors in layout code.\n    pub round_output_to_gui: bool,\n}\n\nimpl Default for LayoutJob {\n    #[inline]\n    fn default() -> Self {\n        Self {\n            text: Default::default(),\n            sections: Default::default(),\n            wrap: Default::default(),\n            first_row_min_height: 0.0,\n            break_on_newline: true,\n            halign: Align::LEFT,\n            justify: false,\n            round_output_to_gui: true,\n        }\n    }\n}\n\nimpl LayoutJob {\n    /// Break on `\\n` and at the given wrap width.\n    #[inline]\n    pub fn simple(text: String, font_id: FontId, color: Color32, wrap_width: f32) -> Self {\n        Self {\n            sections: vec![LayoutSection {\n                leading_space: 0.0,\n                byte_range: 0..text.len(),\n                format: TextFormat::simple(font_id, color),\n            }],\n            text,\n            wrap: TextWrapping {\n                max_width: wrap_width,\n                ..Default::default()\n            },\n            break_on_newline: true,\n            ..Default::default()\n        }\n    }\n\n    /// Break on `\\n`\n    #[inline]\n    pub fn simple_format(text: String, format: TextFormat) -> Self {\n        Self {\n            sections: vec![LayoutSection {\n                leading_space: 0.0,\n                byte_range: 0..text.len(),\n                format,\n            }],\n            text,\n            break_on_newline: true,\n            ..Default::default()\n        }\n    }\n\n    /// Does not break on `\\n`, but shows the replacement character instead.\n    #[inline]\n    pub fn simple_singleline(text: String, font_id: FontId, color: Color32) -> Self {\n        Self {\n            sections: vec![LayoutSection {\n                leading_space: 0.0,\n                byte_range: 0..text.len(),\n                format: TextFormat::simple(font_id, color),\n            }],\n            text,\n            wrap: Default::default(),\n            break_on_newline: false,\n            ..Default::default()\n        }\n    }\n\n    #[inline]\n    pub fn single_section(text: String, format: TextFormat) -> Self {\n        Self {\n            sections: vec![LayoutSection {\n                leading_space: 0.0,\n                byte_range: 0..text.len(),\n                format,\n            }],\n            text,\n            wrap: Default::default(),\n            break_on_newline: true,\n            ..Default::default()\n        }\n    }\n\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.sections.is_empty()\n    }\n\n    /// Helper for adding a new section when building a [`LayoutJob`].\n    pub fn append(&mut self, text: &str, leading_space: f32, format: TextFormat) {\n        let start = self.text.len();\n        self.text += text;\n        let byte_range = start..self.text.len();\n        self.sections.push(LayoutSection {\n            leading_space,\n            byte_range,\n            format,\n        });\n    }\n\n    /// The height of the tallest font used in the job.\n    ///\n    /// Returns a value rounded to [`emath::GUI_ROUNDING`].\n    pub fn font_height(&self, fonts: &mut FontsView<'_>) -> f32 {\n        let mut max_height = 0.0_f32;\n        for section in &self.sections {\n            max_height = max_height.max(fonts.row_height(&section.format.font_id));\n        }\n        max_height\n    }\n\n    /// The wrap with, with a small margin in some cases.\n    pub fn effective_wrap_width(&self) -> f32 {\n        if self.round_output_to_gui {\n            // On a previous pass we may have rounded down by at most 0.5 and reported that as a width.\n            // egui may then set that width as the max width for subsequent frames, and it is important\n            // that we then don't wrap earlier.\n            self.wrap.max_width + 0.5\n        } else {\n            self.wrap.max_width\n        }\n    }\n}\n\nimpl std::hash::Hash for LayoutJob {\n    #[inline]\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        let Self {\n            text,\n            sections,\n            wrap,\n            first_row_min_height,\n            break_on_newline,\n            halign,\n            justify,\n            round_output_to_gui,\n        } = self;\n\n        text.hash(state);\n        sections.hash(state);\n        wrap.hash(state);\n        emath::OrderedFloat(*first_row_min_height).hash(state);\n        break_on_newline.hash(state);\n        halign.hash(state);\n        justify.hash(state);\n        round_output_to_gui.hash(state);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct LayoutSection {\n    /// Can be used for first row indentation.\n    pub leading_space: f32,\n\n    /// Range into the galley text\n    pub byte_range: Range<usize>,\n\n    pub format: TextFormat,\n}\n\nimpl std::hash::Hash for LayoutSection {\n    #[inline]\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        let Self {\n            leading_space,\n            byte_range,\n            format,\n        } = self;\n        OrderedFloat(*leading_space).hash(state);\n        byte_range.hash(state);\n        format.hash(state);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Helper trait for all types that can be parsed as a [`font_types::Tag`].\npub trait IntoTag {\n    fn into_tag(self) -> font_types::Tag;\n}\n\nimpl IntoTag for font_types::Tag {\n    #[inline(always)]\n    fn into_tag(self) -> font_types::Tag {\n        self\n    }\n}\n\nimpl IntoTag for u32 {\n    #[inline(always)]\n    fn into_tag(self) -> font_types::Tag {\n        font_types::Tag::from_u32(self)\n    }\n}\n\nimpl IntoTag for [u8; 4] {\n    #[inline(always)]\n    fn into_tag(self) -> font_types::Tag {\n        font_types::Tag::new_checked(&self).expect(\"Invalid variation axis tag\")\n    }\n}\n\nimpl IntoTag for &[u8; 4] {\n    #[inline(always)]\n    fn into_tag(self) -> font_types::Tag {\n        font_types::Tag::new_checked(self).expect(\"Invalid variation axis tag\")\n    }\n}\n\nimpl IntoTag for &str {\n    #[inline(always)]\n    fn into_tag(self) -> font_types::Tag {\n        font_types::Tag::from_str(self).expect(\"Invalid variation axis tag\")\n    }\n}\n\n/// List of font variation coordinates by axis tag. If more than one coordinate for a given axis is provided, the last\n/// one added is used.\n#[derive(Clone, Debug, PartialEq, Default)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct VariationCoords(SmallVec<[(font_types::Tag, f32); 2]>);\n\nimpl VariationCoords {\n    /// Create a list of variation coordinates from a sequence of (tag, value) pairs.\n    ///\n    /// ## Example:\n    /// ```\n    /// use epaint::text::VariationCoords;\n    ///\n    /// let coords = VariationCoords::new([\n    ///     (b\"wght\", 500.0),\n    ///     (b\"wdth\", 75.0),\n    /// ]);\n    /// ```\n    pub fn new<T: IntoTag>(values: impl IntoIterator<Item = (T, f32)>) -> Self {\n        Self(values.into_iter().map(|(t, c)| (t.into_tag(), c)).collect())\n    }\n\n    /// Add a variation coordinate to the list.\n    #[inline(always)]\n    pub fn push(&mut self, tag: impl IntoTag, coord: f32) {\n        self.0.push((tag.into_tag(), coord));\n    }\n\n    /// Remove the coordinate at the given index.\n    pub fn remove(&mut self, index: usize) {\n        self.0.remove(index);\n    }\n\n    pub fn clear(&mut self) {\n        self.0.clear();\n    }\n}\n\nimpl AsRef<[(font_types::Tag, f32)]> for VariationCoords {\n    #[inline(always)]\n    fn as_ref(&self) -> &[(font_types::Tag, f32)] {\n        &self.0\n    }\n}\n\nimpl AsMut<[(font_types::Tag, f32)]> for VariationCoords {\n    fn as_mut(&mut self) -> &mut [(font_types::Tag, f32)] {\n        &mut self.0\n    }\n}\n\nimpl std::hash::Hash for VariationCoords {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.0.len().hash(state);\n        for (tag, coord) in &self.0 {\n            tag.hash(state);\n            OrderedFloat(*coord).hash(state);\n        }\n    }\n}\n\n/// Formatting option for a section of text.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct TextFormat {\n    pub font_id: FontId,\n\n    /// Extra spacing between letters, in points.\n    ///\n    /// Default: 0.0.\n    pub extra_letter_spacing: f32,\n\n    /// Explicit line height of the text in points.\n    ///\n    /// This is the distance between the bottom row of two subsequent lines of text.\n    ///\n    /// If `None` (the default), the line height is determined by the font.\n    ///\n    /// For even text it is recommended you round this to an even number of _pixels_.\n    pub line_height: Option<f32>,\n\n    /// Text color\n    pub color: Color32,\n\n    pub background: Color32,\n\n    /// Amount to expand background fill by.\n    ///\n    /// Default: 1.0\n    pub expand_bg: f32,\n\n    pub coords: VariationCoords,\n\n    pub italics: bool,\n\n    pub underline: Stroke,\n\n    pub strikethrough: Stroke,\n\n    /// If you use a small font and [`Align::TOP`] you\n    /// can get the effect of raised text.\n    ///\n    /// If you use a small font and [`Align::BOTTOM`]\n    /// you get the effect of a subscript.\n    ///\n    /// If you use [`Align::Center`], you get text that is centered\n    /// around a common center-line, which is nice when mixining emojis\n    /// and normal text in e.g. a button.\n    pub valign: Align,\n}\n\nimpl Default for TextFormat {\n    #[inline]\n    fn default() -> Self {\n        Self {\n            font_id: FontId::default(),\n            extra_letter_spacing: 0.0,\n            line_height: None,\n            color: Color32::GRAY,\n            background: Color32::TRANSPARENT,\n            expand_bg: 1.0,\n            coords: VariationCoords::default(),\n            italics: false,\n            underline: Stroke::NONE,\n            strikethrough: Stroke::NONE,\n            valign: Align::BOTTOM,\n        }\n    }\n}\n\nimpl std::hash::Hash for TextFormat {\n    #[inline]\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        let Self {\n            font_id,\n            extra_letter_spacing,\n            line_height,\n            color,\n            background,\n            expand_bg,\n            coords,\n            italics,\n            underline,\n            strikethrough,\n            valign,\n        } = self;\n        font_id.hash(state);\n        emath::OrderedFloat(*extra_letter_spacing).hash(state);\n        if let Some(line_height) = *line_height {\n            emath::OrderedFloat(line_height).hash(state);\n        }\n        color.hash(state);\n        background.hash(state);\n        emath::OrderedFloat(*expand_bg).hash(state);\n        coords.hash(state);\n        italics.hash(state);\n        underline.hash(state);\n        strikethrough.hash(state);\n        valign.hash(state);\n    }\n}\n\nimpl TextFormat {\n    #[inline]\n    pub fn simple(font_id: FontId, color: Color32) -> Self {\n        Self {\n            font_id,\n            color,\n            ..Default::default()\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// How to wrap and elide text.\n///\n/// This enum is used in high-level APIs where providing a [`TextWrapping`] is too verbose.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum TextWrapMode {\n    /// The text should expand the `Ui` size when reaching its boundary.\n    Extend,\n\n    /// The text should wrap to the next line when reaching the `Ui` boundary.\n    Wrap,\n\n    /// The text should be elided using \"…\" when reaching the `Ui` boundary.\n    ///\n    /// Note that using [`TextWrapping`] and [`LayoutJob`] offers more control over the elision.\n    Truncate,\n}\n\n/// Controls the text wrapping and elision of a [`LayoutJob`].\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct TextWrapping {\n    /// Wrap text so that no row is wider than this.\n    ///\n    /// If you would rather truncate text that doesn't fit, set [`Self::max_rows`] to `1`.\n    ///\n    /// Set `max_width` to [`f32::INFINITY`] to turn off wrapping and elision.\n    ///\n    /// Note that `\\n` always produces a new row\n    /// if [`LayoutJob::break_on_newline`] is `true`.\n    pub max_width: f32,\n\n    /// Maximum amount of rows the text galley should have.\n    ///\n    /// If this limit is reached, text will be truncated\n    /// and [`Self::overflow_character`] appended to the final row.\n    /// You can detect this by checking [`Galley::elided`].\n    ///\n    /// If set to `0`, no text will be outputted.\n    ///\n    /// If set to `1`, a single row will be outputted,\n    /// eliding the text after [`Self::max_width`] is reached.\n    /// When you set `max_rows = 1`, it is recommended you also set [`Self::break_anywhere`] to `true`.\n    ///\n    /// Default value: `usize::MAX`.\n    pub max_rows: usize,\n\n    /// If `true`: Allow breaking between any characters.\n    /// If `false` (default): prefer breaking between words, etc.\n    ///\n    /// NOTE: Due to limitations in the current implementation,\n    /// when truncating text using [`Self::max_rows`] the text may be truncated\n    /// in the middle of a word even if [`Self::break_anywhere`] is `false`.\n    /// Therefore it is recommended to set [`Self::break_anywhere`] to `true`\n    /// whenever [`Self::max_rows`] is set to `1`.\n    pub break_anywhere: bool,\n\n    /// Character to use to represent elided text.\n    ///\n    /// The default is `…`.\n    ///\n    /// If not set, no character will be used (but the text will still be elided).\n    pub overflow_character: Option<char>,\n}\n\nimpl std::hash::Hash for TextWrapping {\n    #[inline]\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        let Self {\n            max_width,\n            max_rows,\n            break_anywhere,\n            overflow_character,\n        } = self;\n        emath::OrderedFloat(*max_width).hash(state);\n        max_rows.hash(state);\n        break_anywhere.hash(state);\n        overflow_character.hash(state);\n    }\n}\n\nimpl Default for TextWrapping {\n    fn default() -> Self {\n        Self {\n            max_width: f32::INFINITY,\n            max_rows: usize::MAX,\n            break_anywhere: false,\n            overflow_character: Some('…'),\n        }\n    }\n}\n\nimpl TextWrapping {\n    /// Create a [`TextWrapping`] from a [`TextWrapMode`] and an available width.\n    pub fn from_wrap_mode_and_width(mode: TextWrapMode, max_width: f32) -> Self {\n        match mode {\n            TextWrapMode::Extend => Self::no_max_width(),\n            TextWrapMode::Wrap => Self::wrap_at_width(max_width),\n            TextWrapMode::Truncate => Self::truncate_at_width(max_width),\n        }\n    }\n\n    /// A row can be as long as it need to be.\n    pub fn no_max_width() -> Self {\n        Self {\n            max_width: f32::INFINITY,\n            ..Default::default()\n        }\n    }\n\n    /// A row can be at most `max_width` wide but can wrap in any number of lines.\n    pub fn wrap_at_width(max_width: f32) -> Self {\n        Self {\n            max_width,\n            ..Default::default()\n        }\n    }\n\n    /// Elide text that doesn't fit within the given width, replaced with `…`.\n    pub fn truncate_at_width(max_width: f32) -> Self {\n        Self {\n            max_width,\n            max_rows: 1,\n            break_anywhere: true,\n            ..Default::default()\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// Text that has been laid out, ready for painting.\n///\n/// You can create a [`Galley`] using [`crate::FontsView::layout_job`];\n///\n/// Needs to be recreated if the underlying font atlas texture changes, which\n/// happens under the following conditions:\n/// - `pixels_per_point` or `max_texture_size` change. These parameters are set\n///   in [`crate::text::Fonts::begin_pass`]. When using `egui` they are set\n///   from `egui::InputState` and can change at any time.\n/// - The atlas has become full. This can happen any time a new glyph is added\n///   to the atlas, which in turn can happen any time new text is laid out.\n///\n/// The name comes from typography, where a \"galley\" is a metal tray\n/// containing a column of set type, usually the size of a page of text.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Galley {\n    /// The job that this galley is the result of.\n    /// Contains the original string and style sections.\n    pub job: Arc<LayoutJob>,\n\n    /// Rows of text, from top to bottom, and their offsets.\n    ///\n    /// The number of characters in all rows sum up to `job.text.chars().count()`\n    /// unless [`Self::elided`] is `true`.\n    ///\n    /// Note that a paragraph (a piece of text separated with `\\n`)\n    /// can be split up into multiple rows.\n    pub rows: Vec<PlacedRow>,\n\n    /// Set to true the text was truncated due to [`TextWrapping::max_rows`].\n    pub elided: bool,\n\n    /// Bounding rect.\n    ///\n    /// `rect.top()` is always 0.0.\n    ///\n    /// With [`LayoutJob::halign`]:\n    /// * [`Align::LEFT`]: `rect.left() == 0.0`\n    /// * [`Align::Center`]: `rect.center() == 0.0`\n    /// * [`Align::RIGHT`]: `rect.right() == 0.0`\n    pub rect: Rect,\n\n    /// Tight bounding box around all the meshes in all the rows.\n    /// Can be used for culling.\n    pub mesh_bounds: Rect,\n\n    /// Total number of vertices in all the row meshes.\n    pub num_vertices: usize,\n\n    /// Total number of indices in all the row meshes.\n    pub num_indices: usize,\n\n    /// The number of physical pixels for each logical point.\n    /// Since this affects the layout, we keep track of it\n    /// so that we can warn if this has changed once we get to\n    /// tessellation.\n    pub pixels_per_point: f32,\n\n    pub(crate) intrinsic_size: Vec2,\n}\n\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct PlacedRow {\n    /// The position of this [`Row`] relative to the galley.\n    ///\n    /// This is rounded to the closest _pixel_ in order to produce crisp, pixel-perfect text.\n    pub pos: Pos2,\n\n    /// The underlying unpositioned [`Row`].\n    pub row: Arc<Row>,\n\n    /// If true, this [`PlacedRow`] came from a paragraph ending with a `\\n`.\n    /// The `\\n` itself is omitted from row's [`Row::glyphs`].\n    /// A `\\n` in the input text always creates a new [`PlacedRow`] below it,\n    /// so that text that ends with `\\n` has an empty [`PlacedRow`] last.\n    /// This also implies that the last [`PlacedRow`] in a [`Galley`] always has `ends_with_newline == false`.\n    pub ends_with_newline: bool,\n}\n\nimpl PlacedRow {\n    /// Logical bounding rectangle on font heights etc.\n    ///\n    /// This ignores / includes the `LayoutSection::leading_space`.\n    pub fn rect(&self) -> Rect {\n        Rect::from_min_size(self.pos, self.row.size)\n    }\n\n    /// Same as [`Self::rect`] but excluding the `LayoutSection::leading_space`.\n    pub fn rect_without_leading_space(&self) -> Rect {\n        let x = self.glyphs.first().map_or(self.pos.x, |g| g.pos.x);\n        let size_x = self.size.x - x;\n        Rect::from_min_size(Pos2::new(x, self.pos.y), Vec2::new(size_x, self.size.y))\n    }\n}\n\nimpl std::ops::Deref for PlacedRow {\n    type Target = Row;\n\n    fn deref(&self) -> &Self::Target {\n        &self.row\n    }\n}\n\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Row {\n    /// This is included in case there are no glyphs.\n    ///\n    /// Only used during layout, then set to an invalid value in order to\n    /// enable the paragraph-concat optimization path without having to\n    /// adjust `section_index` when concatting.\n    pub(crate) section_index_at_start: u32,\n\n    /// One for each `char`.\n    pub glyphs: Vec<Glyph>,\n\n    /// Logical size based on font heights etc.\n    /// Includes leading and trailing whitespace.\n    pub size: Vec2,\n\n    /// The mesh, ready to be rendered.\n    pub visuals: RowVisuals,\n}\n\n/// The tessellated output of a row.\n#[derive(Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct RowVisuals {\n    /// The tessellated text, using non-normalized (texel) UV coordinates.\n    /// That is, you need to divide the uv coordinates by the texture size.\n    pub mesh: Mesh,\n\n    /// Bounds of the mesh, and can be used for culling.\n    /// Does NOT include leading or trailing whitespace glyphs!!\n    pub mesh_bounds: Rect,\n\n    /// The number of triangle indices added before the first glyph triangle.\n    ///\n    /// This can be used to insert more triangles after the background but before the glyphs,\n    /// i.e. for text selection visualization.\n    pub glyph_index_start: usize,\n\n    /// The range of vertices in the mesh that contain glyphs (as opposed to background, underlines, strikethorugh, etc).\n    ///\n    /// The glyph vertices comes after backgrounds (if any), but before any underlines and strikethrough.\n    pub glyph_vertex_range: Range<usize>,\n}\n\nimpl Default for RowVisuals {\n    fn default() -> Self {\n        Self {\n            mesh: Default::default(),\n            mesh_bounds: Rect::NOTHING,\n            glyph_index_start: 0,\n            glyph_vertex_range: 0..0,\n        }\n    }\n}\n\n#[derive(Copy, Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct Glyph {\n    /// The character this glyph represents.\n    pub chr: char,\n\n    /// Baseline position, relative to the row.\n    /// Logical position: pos.y is the same for all chars of the same [`TextFormat`].\n    pub pos: Pos2,\n\n    /// Logical width of the glyph.\n    pub advance_width: f32,\n\n    /// Height of this row of text.\n    ///\n    /// Usually same as [`Self::font_height`],\n    /// unless explicitly overridden by [`TextFormat::line_height`].\n    pub line_height: f32,\n\n    /// The ascent of this font.\n    pub font_ascent: f32,\n\n    /// The row/line height of this font.\n    pub font_height: f32,\n\n    /// The ascent of the sub-font within the font (`FontFace`).\n    pub font_face_ascent: f32,\n\n    /// The row/line height of the sub-font within the font (`FontFace`).\n    pub font_face_height: f32,\n\n    /// Position and size of the glyph in the font texture, in texels.\n    pub uv_rect: UvRect,\n\n    /// Index into [`LayoutJob::sections`]. Decides color etc.\n    ///\n    /// Only used during layout, then set to an invalid value in order to\n    /// enable the paragraph-concat optimization path without having to\n    /// adjust `section_index` when concatting.\n    pub(crate) section_index: u32,\n\n    /// Which is our first vertex in [`RowVisuals::mesh`].\n    pub first_vertex: u32,\n}\n\nimpl Glyph {\n    #[inline]\n    pub fn size(&self) -> Vec2 {\n        Vec2::new(self.advance_width, self.line_height)\n    }\n\n    #[inline]\n    pub fn max_x(&self) -> f32 {\n        self.pos.x + self.advance_width\n    }\n\n    /// Same y range for all characters with the same [`TextFormat`].\n    #[inline]\n    pub fn logical_rect(&self) -> Rect {\n        Rect::from_min_size(self.pos - vec2(0.0, self.font_ascent), self.size())\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nimpl Row {\n    /// The text on this row, excluding the implicit `\\n` if any.\n    pub fn text(&self) -> String {\n        self.glyphs.iter().map(|g| g.chr).collect()\n    }\n\n    /// Excludes the implicit `\\n` after the [`Row`], if any.\n    #[inline]\n    pub fn char_count_excluding_newline(&self) -> usize {\n        self.glyphs.len()\n    }\n\n    /// Closest char at the desired x coordinate in row-relative coordinates.\n    /// Returns something in the range `[0, char_count_excluding_newline()]`.\n    pub fn char_at(&self, desired_x: f32) -> usize {\n        for (i, glyph) in self.glyphs.iter().enumerate() {\n            if desired_x < glyph.logical_rect().center().x {\n                return i;\n            }\n        }\n        self.char_count_excluding_newline()\n    }\n\n    pub fn x_offset(&self, column: usize) -> f32 {\n        if let Some(glyph) = self.glyphs.get(column) {\n            glyph.pos.x\n        } else {\n            self.size.x\n        }\n    }\n\n    #[inline]\n    pub fn height(&self) -> f32 {\n        self.size.y\n    }\n}\n\nimpl PlacedRow {\n    #[inline]\n    pub fn min_y(&self) -> f32 {\n        self.rect().top()\n    }\n\n    #[inline]\n    pub fn max_y(&self) -> f32 {\n        self.rect().bottom()\n    }\n\n    /// Includes the implicit `\\n` after the [`PlacedRow`], if any.\n    #[inline]\n    pub fn char_count_including_newline(&self) -> usize {\n        self.row.glyphs.len() + (self.ends_with_newline as usize)\n    }\n}\n\nimpl Galley {\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.job.is_empty()\n    }\n\n    /// The full, non-elided text of the input job.\n    #[inline]\n    pub fn text(&self) -> &str {\n        &self.job.text\n    }\n\n    #[inline]\n    pub fn size(&self) -> Vec2 {\n        self.rect.size()\n    }\n\n    /// This is the size that a non-wrapped, non-truncated, non-justified version of the text\n    /// would have.\n    ///\n    /// Useful for advanced layouting.\n    #[inline]\n    pub fn intrinsic_size(&self) -> Vec2 {\n        // We do the rounding here instead of in `round_output_to_gui` so that rounding\n        // errors don't accumulate when concatenating multiple galleys.\n        if self.job.round_output_to_gui {\n            self.intrinsic_size.round_ui()\n        } else {\n            self.intrinsic_size\n        }\n    }\n\n    pub(crate) fn round_output_to_gui(&mut self) {\n        for placed_row in &mut self.rows {\n            // Optimization: only call `make_mut` if necessary (can cause a deep clone)\n            let rounded_size = placed_row.row.size.round_ui();\n            if placed_row.row.size != rounded_size {\n                Arc::make_mut(&mut placed_row.row).size = rounded_size;\n            }\n        }\n\n        let rect = &mut self.rect;\n\n        let did_exceed_wrap_width_by_a_lot = rect.width() > self.job.wrap.max_width + 1.0;\n\n        *rect = rect.round_ui();\n\n        if did_exceed_wrap_width_by_a_lot {\n            // If the user picked a too aggressive wrap width (e.g. more narrow than any individual glyph),\n            // we should let the user know by reporting that our width is wider than the wrap width.\n        } else {\n            // Make sure we don't report being wider than the wrap width the user picked:\n            rect.max.x = rect\n                .max\n                .x\n                .at_most(rect.min.x + self.job.wrap.max_width)\n                .floor_ui();\n        }\n    }\n\n    /// Append each galley under the previous one.\n    pub fn concat(job: Arc<LayoutJob>, galleys: &[Arc<Self>], pixels_per_point: f32) -> Self {\n        profiling::function_scope!();\n\n        let mut merged_galley = Self {\n            job,\n            rows: Vec::new(),\n            elided: false,\n            rect: Rect::ZERO,\n            mesh_bounds: Rect::NOTHING,\n            num_vertices: 0,\n            num_indices: 0,\n            pixels_per_point,\n            intrinsic_size: Vec2::ZERO,\n        };\n\n        for (i, galley) in galleys.iter().enumerate() {\n            let current_y_offset = merged_galley.rect.height();\n            let is_last_galley = i + 1 == galleys.len();\n\n            merged_galley\n                .rows\n                .extend(galley.rows.iter().enumerate().map(|(row_idx, placed_row)| {\n                    let new_pos = placed_row.pos + current_y_offset * Vec2::Y;\n                    let new_pos = new_pos.round_to_pixels(pixels_per_point);\n                    merged_galley.mesh_bounds |=\n                        placed_row.visuals.mesh_bounds.translate(new_pos.to_vec2());\n                    merged_galley.rect |= Rect::from_min_size(new_pos, placed_row.size);\n\n                    let mut ends_with_newline = placed_row.ends_with_newline;\n                    let is_last_row_in_galley = row_idx + 1 == galley.rows.len();\n                    // Since we remove the `\\n` when splitting rows, we need to add it back here\n                    ends_with_newline |= !is_last_galley && is_last_row_in_galley;\n                    super::PlacedRow {\n                        pos: new_pos,\n                        row: Arc::clone(&placed_row.row),\n                        ends_with_newline,\n                    }\n                }));\n\n            merged_galley.num_vertices += galley.num_vertices;\n            merged_galley.num_indices += galley.num_indices;\n            // Note that if `galley.elided` is true this will be the last `Galley` in\n            // the vector and the loop will end.\n            merged_galley.elided |= galley.elided;\n            merged_galley.intrinsic_size.x =\n                f32::max(merged_galley.intrinsic_size.x, galley.intrinsic_size.x);\n            merged_galley.intrinsic_size.y += galley.intrinsic_size.y;\n        }\n\n        if merged_galley.job.round_output_to_gui {\n            merged_galley.round_output_to_gui();\n        }\n\n        merged_galley\n    }\n}\n\nimpl AsRef<str> for Galley {\n    #[inline]\n    fn as_ref(&self) -> &str {\n        self.text()\n    }\n}\n\nimpl std::borrow::Borrow<str> for Galley {\n    #[inline]\n    fn borrow(&self) -> &str {\n        self.text()\n    }\n}\n\nimpl std::ops::Deref for Galley {\n    type Target = str;\n    #[inline]\n    fn deref(&self) -> &str {\n        self.text()\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// ## Physical positions\nimpl Galley {\n    /// Zero-width rect past the last character.\n    fn end_pos(&self) -> Rect {\n        if let Some(row) = self.rows.last() {\n            let x = row.rect().right();\n            Rect::from_min_max(pos2(x, row.min_y()), pos2(x, row.max_y()))\n        } else {\n            // Empty galley\n            Rect::from_min_max(pos2(0.0, 0.0), pos2(0.0, 0.0))\n        }\n    }\n\n    /// Returns a 0-width Rect.\n    pub fn pos_from_layout_cursor(&self, layout_cursor: &LayoutCursor) -> Rect {\n        let Some(row) = self.rows.get(layout_cursor.row) else {\n            return self.end_pos();\n        };\n\n        let x = row.x_offset(layout_cursor.column);\n        Rect::from_min_max(pos2(x, row.min_y()), pos2(x, row.max_y()))\n    }\n\n    /// Returns a 0-width Rect.\n    pub fn pos_from_cursor(&self, cursor: CCursor) -> Rect {\n        self.pos_from_layout_cursor(&self.layout_from_cursor(cursor))\n    }\n\n    /// Cursor at the given position within the galley.\n    ///\n    /// A cursor above the galley is considered\n    /// same as a cursor at the start,\n    /// and a cursor below the galley is considered\n    /// same as a cursor at the end.\n    /// This allows implementing text-selection by dragging above/below the galley.\n    pub fn cursor_from_pos(&self, pos: Vec2) -> CCursor {\n        // Vertical margin around galley improves text selection UX\n        const VMARGIN: f32 = 5.0;\n\n        if let Some(first_row) = self.rows.first()\n            && pos.y < first_row.min_y() - VMARGIN\n        {\n            return self.begin();\n        }\n        if let Some(last_row) = self.rows.last()\n            && last_row.max_y() + VMARGIN < pos.y\n        {\n            return self.end();\n        }\n\n        let mut best_y_dist = f32::INFINITY;\n        let mut cursor = CCursor::default();\n\n        let mut ccursor_index = 0;\n\n        for row in &self.rows {\n            let min_y = row.min_y();\n            let max_y = row.max_y();\n\n            let is_pos_within_row = min_y <= pos.y && pos.y <= max_y;\n            let y_dist = (min_y - pos.y).abs().min((max_y - pos.y).abs());\n            if is_pos_within_row || y_dist < best_y_dist {\n                best_y_dist = y_dist;\n                // char_at is `Row` not `PlacedRow` relative which means we have to subtract the pos.\n                let column = row.char_at(pos.x - row.pos.x);\n                let prefer_next_row = column < row.char_count_excluding_newline();\n                cursor = CCursor {\n                    index: ccursor_index + column,\n                    prefer_next_row,\n                };\n\n                if is_pos_within_row {\n                    return cursor;\n                }\n            }\n            ccursor_index += row.char_count_including_newline();\n        }\n\n        cursor\n    }\n}\n\n/// ## Cursor positions\nimpl Galley {\n    /// Cursor to the first character.\n    ///\n    /// This is the same as [`CCursor::default`].\n    #[inline]\n    #[expect(clippy::unused_self)]\n    pub fn begin(&self) -> CCursor {\n        CCursor::default()\n    }\n\n    /// Cursor to one-past last character.\n    pub fn end(&self) -> CCursor {\n        if self.rows.is_empty() {\n            return Default::default();\n        }\n        let mut ccursor = CCursor {\n            index: 0,\n            prefer_next_row: true,\n        };\n        for row in &self.rows {\n            let row_char_count = row.char_count_including_newline();\n            ccursor.index += row_char_count;\n        }\n        ccursor\n    }\n}\n\n/// ## Cursor conversions\nimpl Galley {\n    // The returned cursor is clamped.\n    pub fn layout_from_cursor(&self, cursor: CCursor) -> LayoutCursor {\n        let prefer_next_row = cursor.prefer_next_row;\n        let mut ccursor_it = CCursor {\n            index: 0,\n            prefer_next_row,\n        };\n\n        for (row_nr, row) in self.rows.iter().enumerate() {\n            let row_char_count = row.char_count_excluding_newline();\n\n            if ccursor_it.index <= cursor.index && cursor.index <= ccursor_it.index + row_char_count\n            {\n                let column = cursor.index - ccursor_it.index;\n\n                let select_next_row_instead = prefer_next_row\n                    && !row.ends_with_newline\n                    && column >= row.char_count_excluding_newline();\n                if !select_next_row_instead {\n                    return LayoutCursor {\n                        row: row_nr,\n                        column,\n                    };\n                }\n            }\n            ccursor_it.index += row.char_count_including_newline();\n        }\n        debug_assert!(ccursor_it == self.end(), \"Cursor out of bounds\");\n\n        if let Some(last_row) = self.rows.last() {\n            LayoutCursor {\n                row: self.rows.len() - 1,\n                column: last_row.char_count_including_newline(),\n            }\n        } else {\n            Default::default()\n        }\n    }\n\n    fn cursor_from_layout(&self, layout_cursor: LayoutCursor) -> CCursor {\n        if layout_cursor.row >= self.rows.len() {\n            return self.end();\n        }\n\n        let prefer_next_row =\n            layout_cursor.column < self.rows[layout_cursor.row].char_count_excluding_newline();\n        let mut cursor_it = CCursor {\n            index: 0,\n            prefer_next_row,\n        };\n\n        for (row_nr, row) in self.rows.iter().enumerate() {\n            if row_nr == layout_cursor.row {\n                cursor_it.index += layout_cursor\n                    .column\n                    .at_most(row.char_count_excluding_newline());\n\n                return cursor_it;\n            }\n            cursor_it.index += row.char_count_including_newline();\n        }\n        cursor_it\n    }\n}\n\n/// ## Cursor positions\nimpl Galley {\n    #[expect(clippy::unused_self)]\n    pub fn cursor_left_one_character(&self, cursor: &CCursor) -> CCursor {\n        if cursor.index == 0 {\n            Default::default()\n        } else {\n            CCursor {\n                index: cursor.index - 1,\n                prefer_next_row: true, // default to this when navigating. It is more often useful to put cursor at the beginning of a row than at the end.\n            }\n        }\n    }\n\n    pub fn cursor_right_one_character(&self, cursor: &CCursor) -> CCursor {\n        CCursor {\n            index: (cursor.index + 1).min(self.end().index),\n            prefer_next_row: true, // default to this when navigating. It is more often useful to put cursor at the beginning of a row than at the end.\n        }\n    }\n\n    pub fn clamp_cursor(&self, cursor: &CCursor) -> CCursor {\n        self.cursor_from_layout(self.layout_from_cursor(*cursor))\n    }\n\n    pub fn cursor_up_one_row(\n        &self,\n        cursor: &CCursor,\n        h_pos: Option<f32>,\n    ) -> (CCursor, Option<f32>) {\n        let layout_cursor = self.layout_from_cursor(*cursor);\n        let h_pos = h_pos.unwrap_or_else(|| self.pos_from_layout_cursor(&layout_cursor).center().x);\n        if layout_cursor.row == 0 {\n            (CCursor::default(), None)\n        } else {\n            let new_row = layout_cursor.row - 1;\n\n            let new_layout_cursor = {\n                // keep same X coord\n                let column = self.rows[new_row].char_at(h_pos);\n                LayoutCursor {\n                    row: new_row,\n                    column,\n                }\n            };\n            (self.cursor_from_layout(new_layout_cursor), Some(h_pos))\n        }\n    }\n\n    pub fn cursor_down_one_row(\n        &self,\n        cursor: &CCursor,\n        h_pos: Option<f32>,\n    ) -> (CCursor, Option<f32>) {\n        let layout_cursor = self.layout_from_cursor(*cursor);\n        let h_pos = h_pos.unwrap_or_else(|| self.pos_from_layout_cursor(&layout_cursor).center().x);\n        if layout_cursor.row + 1 < self.rows.len() {\n            let new_row = layout_cursor.row + 1;\n\n            let new_layout_cursor = {\n                // keep same X coord\n                let column = self.rows[new_row].char_at(h_pos);\n                LayoutCursor {\n                    row: new_row,\n                    column,\n                }\n            };\n\n            (self.cursor_from_layout(new_layout_cursor), Some(h_pos))\n        } else {\n            (self.end(), None)\n        }\n    }\n\n    pub fn cursor_begin_of_row(&self, cursor: &CCursor) -> CCursor {\n        let layout_cursor = self.layout_from_cursor(*cursor);\n        self.cursor_from_layout(LayoutCursor {\n            row: layout_cursor.row,\n            column: 0,\n        })\n    }\n\n    pub fn cursor_end_of_row(&self, cursor: &CCursor) -> CCursor {\n        let layout_cursor = self.layout_from_cursor(*cursor);\n        self.cursor_from_layout(LayoutCursor {\n            row: layout_cursor.row,\n            column: self.rows[layout_cursor.row].char_count_excluding_newline(),\n        })\n    }\n\n    pub fn cursor_begin_of_paragraph(&self, cursor: &CCursor) -> CCursor {\n        let mut layout_cursor = self.layout_from_cursor(*cursor);\n        layout_cursor.column = 0;\n\n        loop {\n            let prev_row = layout_cursor\n                .row\n                .checked_sub(1)\n                .and_then(|row| self.rows.get(row));\n\n            let Some(prev_row) = prev_row else {\n                // This is the first row\n                break;\n            };\n\n            if prev_row.ends_with_newline {\n                break;\n            }\n\n            layout_cursor.row -= 1;\n        }\n\n        self.cursor_from_layout(layout_cursor)\n    }\n\n    pub fn cursor_end_of_paragraph(&self, cursor: &CCursor) -> CCursor {\n        let mut layout_cursor = self.layout_from_cursor(*cursor);\n        loop {\n            let row = &self.rows[layout_cursor.row];\n            if row.ends_with_newline || layout_cursor.row == self.rows.len() - 1 {\n                layout_cursor.column = row.char_count_excluding_newline();\n                break;\n            }\n\n            layout_cursor.row += 1;\n        }\n\n        self.cursor_from_layout(layout_cursor)\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/texture_atlas.rs",
    "content": "use ecolor::Color32;\nuse emath::{Rect, remap_clamp};\n\nuse crate::{ColorImage, ImageDelta, TextOptions};\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\nstruct Rectu {\n    /// inclusive\n    min_x: usize,\n\n    /// inclusive\n    min_y: usize,\n\n    /// exclusive\n    max_x: usize,\n\n    /// exclusive\n    max_y: usize,\n}\n\nimpl Rectu {\n    const NOTHING: Self = Self {\n        min_x: usize::MAX,\n        min_y: usize::MAX,\n        max_x: 0,\n        max_y: 0,\n    };\n    const EVERYTHING: Self = Self {\n        min_x: 0,\n        min_y: 0,\n        max_x: usize::MAX,\n        max_y: usize::MAX,\n    };\n}\n\n#[derive(Copy, Clone, Debug)]\nstruct PrerasterizedDisc {\n    r: f32,\n    uv: Rectu,\n}\n\n/// A pre-rasterized disc (filled circle), somewhere in the texture atlas.\n#[derive(Copy, Clone, Debug)]\npub struct PreparedDisc {\n    /// The radius of this disc in texels.\n    pub r: f32,\n\n    /// Width in texels.\n    pub w: f32,\n\n    /// Where in the texture atlas the disc is.\n    /// Normalized in 0-1 range.\n    pub uv: Rect,\n}\n\n/// Contains font data in an atlas, where each character occupied a small rectangle.\n///\n/// More characters can be added, possibly expanding the texture.\n#[derive(Clone)]\npub struct TextureAtlas {\n    image: ColorImage,\n\n    /// What part of the image that is dirty\n    dirty: Rectu,\n\n    /// Used for when allocating new rectangles.\n    cursor: (usize, usize),\n\n    row_height: usize,\n\n    /// Set when someone requested more space than was available.\n    overflowed: bool,\n\n    /// pre-rasterized discs of radii `2^i`, where `i` is the index.\n    discs: Vec<PrerasterizedDisc>,\n\n    /// Controls how to convert glyph coverage to alpha.\n    options: TextOptions,\n}\n\nimpl TextureAtlas {\n    pub fn new(size: [usize; 2], options: TextOptions) -> Self {\n        assert!(size[0] >= 1024, \"Tiny texture atlas\");\n        let mut atlas = Self {\n            image: ColorImage::filled(size, Color32::TRANSPARENT),\n            dirty: Rectu::EVERYTHING,\n            cursor: (0, 0),\n            row_height: 0,\n            overflowed: false,\n            discs: vec![], // will be filled in below\n            options,\n        };\n\n        // Make the top left pixel fully white for `WHITE_UV`, i.e. painting something with solid color:\n        let (pos, image) = atlas.allocate((1, 1));\n        assert_eq!(\n            pos,\n            (0, 0),\n            \"Expected the first allocation to be at (0, 0), but was at {pos:?}\"\n        );\n        image[pos] = Color32::WHITE;\n\n        // Allocate a series of anti-aliased discs used to render small filled circles:\n        // TODO(emilk): these circles can be packed A LOT better.\n        // In fact, the whole texture atlas could be packed a lot better.\n        // for r in [1, 2, 4, 8, 16, 32, 64] {\n        //     let w = 2 * r + 3;\n        //     let hw = w as i32 / 2;\n        const LARGEST_CIRCLE_RADIUS: f32 = 8.0; // keep small so that the initial texture atlas is small\n        for i in 0.. {\n            let r = 2.0_f32.powf(i as f32 / 2.0 - 1.0);\n            if r > LARGEST_CIRCLE_RADIUS {\n                break;\n            }\n            let hw = (r + 0.5).ceil() as i32;\n            let w = (2 * hw + 1) as usize;\n            let ((x, y), image) = atlas.allocate((w, w));\n            for dx in -hw..=hw {\n                for dy in -hw..=hw {\n                    let distance_to_center = ((dx * dx + dy * dy) as f32).sqrt();\n                    let coverage =\n                        remap_clamp(distance_to_center, (r - 0.5)..=(r + 0.5), 1.0..=0.0);\n                    image[((x as i32 + hw + dx) as usize, (y as i32 + hw + dy) as usize)] =\n                        options.alpha_from_coverage.color_from_coverage(coverage);\n                }\n            }\n            atlas.discs.push(PrerasterizedDisc {\n                r,\n                uv: Rectu {\n                    min_x: x,\n                    min_y: y,\n                    max_x: x + w,\n                    max_y: y + w,\n                },\n            });\n        }\n\n        atlas\n    }\n\n    pub fn options(&self) -> &TextOptions {\n        &self.options\n    }\n\n    pub fn size(&self) -> [usize; 2] {\n        self.image.size\n    }\n\n    /// Returns the locations and sizes of pre-rasterized discs (filled circles) in this atlas.\n    pub fn prepared_discs(&self) -> Vec<PreparedDisc> {\n        let size = self.size();\n        let inv_w = 1.0 / size[0] as f32;\n        let inv_h = 1.0 / size[1] as f32;\n        self.discs\n            .iter()\n            .map(|disc| {\n                let r = disc.r;\n                let Rectu {\n                    min_x,\n                    min_y,\n                    max_x,\n                    max_y,\n                } = disc.uv;\n                let w = max_x - min_x;\n                let uv = Rect::from_min_max(\n                    emath::pos2(min_x as f32 * inv_w, min_y as f32 * inv_h),\n                    emath::pos2(max_x as f32 * inv_w, max_y as f32 * inv_h),\n                );\n                PreparedDisc { r, w: w as f32, uv }\n            })\n            .collect()\n    }\n\n    fn max_height(&self) -> usize {\n        // the initial width is set to the max size\n        self.image.height().max(self.image.width())\n    }\n\n    /// When this get high, it might be time to clear and start over!\n    pub fn fill_ratio(&self) -> f32 {\n        if self.overflowed {\n            1.0\n        } else {\n            (self.cursor.1 + self.row_height) as f32 / self.max_height() as f32\n        }\n    }\n\n    /// The texture options suitable for a font texture\n    #[inline]\n    pub fn texture_options() -> crate::textures::TextureOptions {\n        crate::textures::TextureOptions::LINEAR\n    }\n\n    /// The full font atlas image.\n    #[inline]\n    pub fn image(&self) -> &ColorImage {\n        &self.image\n    }\n\n    /// Call to get the change to the image since last call.\n    pub fn take_delta(&mut self) -> Option<ImageDelta> {\n        let texture_options = Self::texture_options();\n\n        let dirty = std::mem::replace(&mut self.dirty, Rectu::NOTHING);\n        if dirty == Rectu::NOTHING {\n            None\n        } else if dirty == Rectu::EVERYTHING {\n            Some(ImageDelta::full(self.image.clone(), texture_options))\n        } else {\n            let pos = [dirty.min_x, dirty.min_y];\n            let size = [dirty.max_x - dirty.min_x, dirty.max_y - dirty.min_y];\n            let region = self.image.region_by_pixels(pos, size);\n            Some(ImageDelta::partial(pos, region, texture_options))\n        }\n    }\n\n    /// Returns the coordinates of where the rect ended up,\n    /// and invalidates the region.\n    pub fn allocate(&mut self, (w, h): (usize, usize)) -> ((usize, usize), &mut ColorImage) {\n        /// On some low-precision GPUs (my old iPad) characters get muddled up\n        /// if we don't add some empty pixels between the characters.\n        /// On modern high-precision GPUs this is not needed.\n        const PADDING: usize = 1;\n\n        assert!(\n            w <= self.image.width(),\n            \"Tried to allocate a {} wide glyph in a {} wide texture atlas\",\n            w,\n            self.image.width()\n        );\n        if self.cursor.0 + w > self.image.width() {\n            // New row:\n            self.cursor.0 = 0;\n            self.cursor.1 += self.row_height + PADDING;\n            self.row_height = 0;\n        }\n\n        self.row_height = self.row_height.max(h);\n\n        let required_height = self.cursor.1 + self.row_height;\n\n        if required_height > self.max_height() {\n            // This is a bad place to be - we need to start reusing space :/\n\n            log::warn!(\"epaint texture atlas overflowed!\");\n\n            self.cursor = (0, self.image.height() / 3); // Restart a bit down - the top of the atlas has too many important things in it\n            self.overflowed = true; // this will signal the user that we need to recreate the texture atlas next frame.\n        } else if resize_to_min_height(&mut self.image, required_height) {\n            self.dirty = Rectu::EVERYTHING;\n        }\n\n        let pos = self.cursor;\n        self.cursor.0 += w + PADDING;\n\n        self.dirty.min_x = self.dirty.min_x.min(pos.0);\n        self.dirty.min_y = self.dirty.min_y.min(pos.1);\n        self.dirty.max_x = self.dirty.max_x.max(pos.0 + w);\n        self.dirty.max_y = self.dirty.max_y.max(pos.1 + h);\n\n        (pos, &mut self.image)\n    }\n}\n\nfn resize_to_min_height(image: &mut ColorImage, required_height: usize) -> bool {\n    while required_height >= image.height() {\n        image.size[1] *= 2; // double the height\n    }\n\n    if image.width() * image.height() > image.pixels.len() {\n        image\n            .pixels\n            .resize(image.width() * image.height(), Color32::TRANSPARENT);\n        true\n    } else {\n        false\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/texture_handle.rs",
    "content": "use std::sync::Arc;\n\nuse crate::{\n    ImageData, ImageDelta, TextureId, TextureManager, emath::NumExt as _, mutex::RwLock,\n    textures::TextureOptions,\n};\n\n/// Used to paint images.\n///\n/// An _image_ is pixels stored in RAM, and represented using [`ImageData`].\n/// Before you can paint it however, you need to convert it to a _texture_.\n///\n/// If you are using egui, use `egui::Context::load_texture`.\n///\n/// The [`TextureHandle`] can be cloned cheaply.\n/// When the last [`TextureHandle`] for specific texture is dropped, the texture is freed.\n///\n/// See also [`TextureManager`].\n#[must_use]\npub struct TextureHandle {\n    tex_mngr: Arc<RwLock<TextureManager>>,\n    id: TextureId,\n}\n\nimpl Drop for TextureHandle {\n    fn drop(&mut self) {\n        self.tex_mngr.write().free(self.id);\n    }\n}\n\nimpl Clone for TextureHandle {\n    fn clone(&self) -> Self {\n        self.tex_mngr.write().retain(self.id);\n        Self {\n            tex_mngr: Arc::clone(&self.tex_mngr),\n            id: self.id,\n        }\n    }\n}\n\nimpl PartialEq for TextureHandle {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.id == other.id\n    }\n}\n\nimpl Eq for TextureHandle {}\n\nimpl std::hash::Hash for TextureHandle {\n    #[inline]\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.id.hash(state);\n    }\n}\n\nimpl TextureHandle {\n    /// If you are using egui, use `egui::Context::load_texture` instead.\n    pub fn new(tex_mngr: Arc<RwLock<TextureManager>>, id: TextureId) -> Self {\n        Self { tex_mngr, id }\n    }\n\n    #[inline]\n    pub fn id(&self) -> TextureId {\n        self.id\n    }\n\n    /// Assign a new image to an existing texture.\n    #[expect(clippy::needless_pass_by_ref_mut)] // Intentionally hide interiority of mutability\n    pub fn set(&mut self, image: impl Into<ImageData>, options: TextureOptions) {\n        self.tex_mngr\n            .write()\n            .set(self.id, ImageDelta::full(image.into(), options));\n    }\n\n    /// Assign a new image to a subregion of the whole texture.\n    #[expect(clippy::needless_pass_by_ref_mut)] // Intentionally hide interiority of mutability\n    pub fn set_partial(\n        &mut self,\n        pos: [usize; 2],\n        image: impl Into<ImageData>,\n        options: TextureOptions,\n    ) {\n        self.tex_mngr\n            .write()\n            .set(self.id, ImageDelta::partial(pos, image.into(), options));\n    }\n\n    /// width x height\n    pub fn size(&self) -> [usize; 2] {\n        self.tex_mngr\n            .read()\n            .meta(self.id)\n            .map_or([0, 0], |tex| tex.size)\n    }\n\n    /// width x height\n    pub fn size_vec2(&self) -> crate::Vec2 {\n        let [w, h] = self.size();\n        crate::Vec2::new(w as f32, h as f32)\n    }\n\n    /// `width x height x bytes_per_pixel`\n    pub fn byte_size(&self) -> usize {\n        self.tex_mngr\n            .read()\n            .meta(self.id)\n            .map_or(0, |tex| tex.bytes_used())\n    }\n\n    /// width / height\n    pub fn aspect_ratio(&self) -> f32 {\n        let [w, h] = self.size();\n        w as f32 / h.at_least(1) as f32\n    }\n\n    /// Debug-name.\n    pub fn name(&self) -> String {\n        self.tex_mngr\n            .read()\n            .meta(self.id)\n            .map_or_else(|| \"<none>\".to_owned(), |tex| tex.name.clone())\n    }\n}\n\nimpl From<&TextureHandle> for TextureId {\n    #[inline(always)]\n    fn from(handle: &TextureHandle) -> Self {\n        handle.id()\n    }\n}\n\nimpl From<&mut TextureHandle> for TextureId {\n    #[inline(always)]\n    fn from(handle: &mut TextureHandle) -> Self {\n        handle.id()\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/textures.rs",
    "content": "use crate::{ImageData, ImageDelta, TextureId};\n\n// ----------------------------------------------------------------------------\n\n/// Low-level manager for allocating textures.\n///\n/// Communicates with the painting subsystem using [`Self::take_delta`].\n#[derive(Default)]\npub struct TextureManager {\n    /// We allocate texture id:s linearly.\n    next_id: u64,\n\n    /// Information about currently allocated textures.\n    metas: ahash::HashMap<TextureId, TextureMeta>,\n\n    delta: TexturesDelta,\n}\n\nimpl TextureManager {\n    /// Allocate a new texture.\n    ///\n    /// The given name can be useful for later debugging.\n    ///\n    /// The returned [`TextureId`] will be [`TextureId::Managed`], with an index\n    /// starting from zero and increasing with each call to [`Self::alloc`].\n    ///\n    /// The first texture you allocate will be `TextureId::Managed(0) == TextureId::default()` and\n    /// MUST have a white pixel at (0,0) ([`crate::WHITE_UV`]).\n    ///\n    /// The texture is given a retain-count of `1`, requiring one call to [`Self::free`] to free it.\n    pub fn alloc(&mut self, name: String, image: ImageData, options: TextureOptions) -> TextureId {\n        let id = TextureId::Managed(self.next_id);\n        self.next_id += 1;\n\n        self.metas.entry(id).or_insert_with(|| TextureMeta {\n            name,\n            size: image.size(),\n            bytes_per_pixel: image.bytes_per_pixel(),\n            retain_count: 1,\n            options,\n        });\n\n        self.delta.set.push((id, ImageDelta::full(image, options)));\n        id\n    }\n\n    /// Assign a new image to an existing texture,\n    /// or update a region of it.\n    pub fn set(&mut self, id: TextureId, delta: ImageDelta) {\n        if let Some(meta) = self.metas.get_mut(&id) {\n            if let Some(pos) = delta.pos {\n                debug_assert!(\n                    pos[0] + delta.image.width() <= meta.size[0]\n                        && pos[1] + delta.image.height() <= meta.size[1],\n                    \"Partial texture update is outside the bounds of texture {id:?}\",\n                );\n            } else {\n                // whole update\n                meta.size = delta.image.size();\n                meta.bytes_per_pixel = delta.image.bytes_per_pixel();\n                // since we update the whole image, we can discard all old enqueued deltas\n                self.delta.set.retain(|(x, _)| x != &id);\n            }\n            self.delta.set.push((id, delta));\n        } else {\n            debug_assert!(false, \"Tried setting texture {id:?} which is not allocated\");\n        }\n    }\n\n    /// Free an existing texture.\n    pub fn free(&mut self, id: TextureId) {\n        if let std::collections::hash_map::Entry::Occupied(mut entry) = self.metas.entry(id) {\n            let meta = entry.get_mut();\n            meta.retain_count -= 1;\n            if meta.retain_count == 0 {\n                entry.remove();\n                self.delta.free.push(id);\n            }\n        } else {\n            debug_assert!(false, \"Tried freeing texture {id:?} which is not allocated\");\n        }\n    }\n\n    /// Increase the retain-count of the given texture.\n    ///\n    /// For each time you call [`Self::retain`] you must call [`Self::free`] on additional time.\n    pub fn retain(&mut self, id: TextureId) {\n        if let Some(meta) = self.metas.get_mut(&id) {\n            meta.retain_count += 1;\n        } else {\n            debug_assert!(\n                false,\n                \"Tried retaining texture {id:?} which is not allocated\",\n            );\n        }\n    }\n\n    /// Take and reset changes since last frame.\n    ///\n    /// These should be applied to the painting subsystem each frame.\n    pub fn take_delta(&mut self) -> TexturesDelta {\n        std::mem::take(&mut self.delta)\n    }\n\n    /// Get meta-data about a specific texture.\n    pub fn meta(&self, id: TextureId) -> Option<&TextureMeta> {\n        self.metas.get(&id)\n    }\n\n    /// Get meta-data about all allocated textures in some arbitrary order.\n    pub fn allocated(&self) -> impl ExactSizeIterator<Item = (&TextureId, &TextureMeta)> {\n        self.metas.iter()\n    }\n\n    /// Total number of allocated textures.\n    pub fn num_allocated(&self) -> usize {\n        self.metas.len()\n    }\n}\n\n/// Meta-data about an allocated texture.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct TextureMeta {\n    /// A human-readable name useful for debugging.\n    pub name: String,\n\n    /// width x height\n    pub size: [usize; 2],\n\n    /// 4 or 1\n    pub bytes_per_pixel: usize,\n\n    /// Free when this reaches zero.\n    pub retain_count: usize,\n\n    /// The texture filtering mode to use when rendering.\n    pub options: TextureOptions,\n}\n\nimpl TextureMeta {\n    /// Size in bytes.\n    /// width x height x [`Self::bytes_per_pixel`].\n    pub fn bytes_used(&self) -> usize {\n        self.size[0] * self.size[1] * self.bytes_per_pixel\n    }\n}\n\n// ----------------------------------------------------------------------------\n\n/// How the texture texels are filtered.\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub struct TextureOptions {\n    /// How to filter when magnifying (when texels are larger than pixels).\n    pub magnification: TextureFilter,\n\n    /// How to filter when minifying (when texels are smaller than pixels).\n    pub minification: TextureFilter,\n\n    /// How to wrap the texture when the texture coordinates are outside the [0, 1] range.\n    pub wrap_mode: TextureWrapMode,\n\n    /// How to filter between texture mipmaps.\n    ///\n    /// Mipmaps ensures textures look smooth even when the texture is very small and pixels are much\n    /// larger than individual texels.\n    ///\n    /// # Notes\n    ///\n    /// - This may not be available on all backends (currently only `egui_glow`).\n    pub mipmap_mode: Option<TextureFilter>,\n}\n\nimpl TextureOptions {\n    /// Linear magnification and minification.\n    pub const LINEAR: Self = Self {\n        magnification: TextureFilter::Linear,\n        minification: TextureFilter::Linear,\n        wrap_mode: TextureWrapMode::ClampToEdge,\n        mipmap_mode: None,\n    };\n\n    /// Nearest magnification and minification.\n    pub const NEAREST: Self = Self {\n        magnification: TextureFilter::Nearest,\n        minification: TextureFilter::Nearest,\n        wrap_mode: TextureWrapMode::ClampToEdge,\n        mipmap_mode: None,\n    };\n\n    /// Linear magnification and minification, but with the texture repeated.\n    pub const LINEAR_REPEAT: Self = Self {\n        magnification: TextureFilter::Linear,\n        minification: TextureFilter::Linear,\n        wrap_mode: TextureWrapMode::Repeat,\n        mipmap_mode: None,\n    };\n\n    /// Linear magnification and minification, but with the texture mirrored and repeated.\n    pub const LINEAR_MIRRORED_REPEAT: Self = Self {\n        magnification: TextureFilter::Linear,\n        minification: TextureFilter::Linear,\n        wrap_mode: TextureWrapMode::MirroredRepeat,\n        mipmap_mode: None,\n    };\n\n    /// Nearest magnification and minification, but with the texture repeated.\n    pub const NEAREST_REPEAT: Self = Self {\n        magnification: TextureFilter::Nearest,\n        minification: TextureFilter::Nearest,\n        wrap_mode: TextureWrapMode::Repeat,\n        mipmap_mode: None,\n    };\n\n    /// Nearest magnification and minification, but with the texture mirrored and repeated.\n    pub const NEAREST_MIRRORED_REPEAT: Self = Self {\n        magnification: TextureFilter::Nearest,\n        minification: TextureFilter::Nearest,\n        wrap_mode: TextureWrapMode::MirroredRepeat,\n        mipmap_mode: None,\n    };\n\n    pub const fn with_mipmap_mode(self, mipmap_mode: Option<TextureFilter>) -> Self {\n        Self {\n            mipmap_mode,\n            ..self\n        }\n    }\n}\n\nimpl Default for TextureOptions {\n    /// The default is linear for both magnification and minification.\n    fn default() -> Self {\n        Self::LINEAR\n    }\n}\n\n/// How the texture texels are filtered.\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum TextureFilter {\n    /// Show the nearest pixel value.\n    ///\n    /// When zooming in you will get sharp, square pixels/texels.\n    /// When zooming out you will get a very crisp (and aliased) look.\n    Nearest,\n\n    /// Linearly interpolate the nearest neighbors, creating a smoother look when zooming in and out.\n    Linear,\n}\n\n/// Defines how textures are wrapped around objects when texture coordinates fall outside the [0, 1] range.\n#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\npub enum TextureWrapMode {\n    /// Stretches the edge pixels to fill beyond the texture's bounds.\n    ///\n    /// This is what you want to use for a normal image in a GUI.\n    #[default]\n    ClampToEdge,\n\n    /// Tiles the texture across the surface, repeating it horizontally and vertically.\n    Repeat,\n\n    /// Mirrors the texture with each repetition, creating symmetrical tiling.\n    MirroredRepeat,\n}\n\n// ----------------------------------------------------------------------------\n\n/// What has been allocated and freed during the last period.\n///\n/// These are commands given to the integration painter.\n#[derive(Clone, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n#[must_use = \"The painter must take care of this\"]\npub struct TexturesDelta {\n    /// New or changed textures. Apply before painting.\n    pub set: Vec<(TextureId, ImageDelta)>,\n\n    /// Textures to free after painting.\n    pub free: Vec<TextureId>,\n}\n\nimpl TexturesDelta {\n    pub fn is_empty(&self) -> bool {\n        self.set.is_empty() && self.free.is_empty()\n    }\n\n    pub fn append(&mut self, mut newer: Self) {\n        self.set.extend(newer.set);\n        self.free.append(&mut newer.free);\n    }\n\n    pub fn clear(&mut self) {\n        self.set.clear();\n        self.free.clear();\n    }\n}\n\nimpl std::fmt::Debug for TexturesDelta {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        use std::fmt::Write as _;\n\n        let mut debug_struct = f.debug_struct(\"TexturesDelta\");\n        if !self.set.is_empty() {\n            let mut string = String::new();\n            for (tex_id, delta) in &self.set {\n                let size = delta.image.size();\n                if let Some(pos) = delta.pos {\n                    write!(\n                        string,\n                        \"{:?} partial ([{} {}] - [{} {}]), \",\n                        tex_id,\n                        pos[0],\n                        pos[1],\n                        pos[0] + size[0],\n                        pos[1] + size[1]\n                    )\n                    .ok();\n                } else {\n                    write!(string, \"{:?} full {}x{}, \", tex_id, size[0], size[1]).ok();\n                }\n            }\n            debug_struct.field(\"set\", &string);\n        }\n        if !self.free.is_empty() {\n            debug_struct.field(\"free\", &self.free);\n        }\n        debug_struct.finish()\n    }\n}\n"
  },
  {
    "path": "crates/epaint/src/util/mod.rs",
    "content": "/// Hash the given value with a predictable hasher.\n#[inline]\npub fn hash(value: impl std::hash::Hash) -> u64 {\n    ahash::RandomState::with_seeds(1, 2, 3, 4).hash_one(value)\n}\n\n/// Hash the given value with the given hasher.\n#[inline]\npub fn hash_with(value: impl std::hash::Hash, mut hasher: impl std::hash::Hasher) -> u64 {\n    value.hash(&mut hasher);\n    hasher.finish()\n}\n"
  },
  {
    "path": "crates/epaint/src/viewport.rs",
    "content": "use crate::Rect;\n\n/// Size of the viewport in whole, physical pixels.\npub struct ViewportInPixels {\n    /// Physical pixel offset for left side of the viewport.\n    pub left_px: i32,\n\n    /// Physical pixel offset for top side of the viewport.\n    pub top_px: i32,\n\n    /// Physical pixel offset for bottom side of the viewport.\n    ///\n    /// This is what `glViewport`, `glScissor` etc expects for the y axis.\n    pub from_bottom_px: i32,\n\n    /// Viewport width in physical pixels.\n    pub width_px: i32,\n\n    /// Viewport height in physical pixels.\n    pub height_px: i32,\n}\n\nimpl ViewportInPixels {\n    /// Convert from ui points.\n    pub fn from_points(rect: &Rect, pixels_per_point: f32, screen_size_px: [u32; 2]) -> Self {\n        // Fractional pixel values for viewports are generally valid, but may cause sampling issues\n        // and rounding errors might cause us to get out of bounds.\n\n        // Round:\n        let left_px = (pixels_per_point * rect.min.x).round() as i32; // inclusive\n        let top_px = (pixels_per_point * rect.min.y).round() as i32; // inclusive\n        let right_px = (pixels_per_point * rect.max.x).round() as i32; // exclusive\n        let bottom_px = (pixels_per_point * rect.max.y).round() as i32; // exclusive\n\n        // Clamp to screen:\n        let screen_width = screen_size_px[0] as i32;\n        let screen_height = screen_size_px[1] as i32;\n        let left_px = left_px.clamp(0, screen_width);\n        let right_px = right_px.clamp(left_px, screen_width);\n        let top_px = top_px.clamp(0, screen_height);\n        let bottom_px = bottom_px.clamp(top_px, screen_height);\n\n        let width_px = right_px - left_px;\n        let height_px = bottom_px - top_px;\n\n        Self {\n            left_px,\n            top_px,\n            from_bottom_px: screen_height - height_px - top_px,\n            width_px,\n            height_px,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/epaint_default_fonts/CHANGELOG.md",
    "content": "# `epaint_default_fonts` changelog\nAll notable changes to the `epaint_default_fonts` crate will be documented in this file.\n\nThis file is updated upon each release.\nChanges since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.\n\n\n## 0.33.3 - 2025-12-11\nNothing new\n\n\n## 0.33.2 - 2025-11-13\nNothing new\n\n\n## 0.33.0 - 2025-10-09\nNothing new\n\n\n## 0.32.3 - 2025-09-12\nNothing new\n\n\n## 0.32.2 - 2025-09-04\nNothing new\n\n\n## 0.32.1 - 2025-08-15\nNothing new\n\n\n## 0.32.0 - 2025-07-10\nNothing new\n\n\n## 0.31.1 - 2025-03-05\nNothing new\n\n\n## 0.31.0 - 2025-02-04\n* Update `egui_default_fonts` license [#5361](https://github.com/emilk/egui/pull/5361) by [@pombredanne](https://github.com/pombredanne)\n\n\n## 0.30.0 - 2024-12-16\nNothing new\n\n\n## 0.29.1 - 2024-10-01\nNothing new\n\n\n## 0.29.0 - 2024-09-26\n* Nothing new\n\n\n## 0.28.1 - 2024-07-05\n* Move default fonts to new crate `epaint_default_fonts` [#4853](https://github.com/emilk/egui/pull/4853) by [@alex-pinkus](https://github.com/alex-pinkus)\n"
  },
  {
    "path": "crates/epaint_default_fonts/Cargo.toml",
    "content": "[package]\nname = \"epaint_default_fonts\"\nversion.workspace = true\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\ndescription = \"Default fonts for use in epaint / egui\"\nedition.workspace = true\nrust-version.workspace = true\nhomepage = \"https://github.com/emilk/egui/tree/main/crates/epaint_default_fonts\"\nlicense = \"(MIT OR Apache-2.0) AND OFL-1.1 AND Ubuntu-font-1.0\" # OFL and UFL are from the font files themselves.\nreadme = \"README.md\"\nrepository = \"https://github.com/emilk/egui/tree/main/crates/epaint_default_fonts\"\ncategories = [\"graphics\", \"gui\"]\nkeywords = [\"graphics\", \"gui\", \"egui\"]\ninclude = [\n  \"../LICENSE-APACHE\",\n  \"../LICENSE-MIT\",\n  \"**/*.rs\",\n  \"Cargo.toml\",\n  \"fonts/*.ttf\",\n  \"fonts/*.txt\",\n  \"fonts/OFL.txt\",\n  \"fonts/UFL.txt\",\n]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n"
  },
  {
    "path": "crates/epaint_default_fonts/README.md",
    "content": "# `epaint_default_fonts` -  fonts for epaint and egui\n\n[![Latest version](https://img.shields.io/crates/v/epaint_default_fonts.svg)](https://crates.io/crates/epaint_default_fonts)\n[![Documentation](https://docs.rs/epaint_default_fonts/badge.svg)](https://docs.rs/epaint_default_fonts)\n[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)\n![MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Apache](https://img.shields.io/badge/license-Apache-blue.svg)\n\nDefault fonts that are used in `epaint` and `egui`. Not intended for use as a standalone library.\n\nMade for [`egui`](https://github.com/emilk/egui/).\n"
  },
  {
    "path": "crates/epaint_default_fonts/fonts/Hack-Regular.txt",
    "content": "The work in the Hack project is Copyright 2018 Source Foundry Authors and licensed under the MIT License\n\nThe work in the DejaVu project was committed to the public domain.\n\nBitstream Vera Sans Mono Copyright 2003 Bitstream Inc. and licensed under the Bitstream Vera License with Reserved Font Names \"Bitstream\" and \"Vera\"\nMIT License\n\nCopyright (c) 2018 Source Foundry Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nBITSTREAM VERA LICENSE\n\nCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license (\"Fonts\") and associated documentation files (the \"Font Software\"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:\n\nThe above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.\n\nThe Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words \"Bitstream\" or the word \"Vera\".\n\nThis License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the \"Bitstream Vera\" names.\n\nThe Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.\n\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.\n\nExcept as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.\n"
  },
  {
    "path": "crates/epaint_default_fonts/fonts/OFL.txt",
    "content": "This Font Software is licensed under the SIL Open Font License,\nVersion 1.1.\n\nThis license is copied below, and is also available with a FAQ at:\nhttp://scripts.sil.org/OFL\n\n-----------------------------------------------------------\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\n-----------------------------------------------------------\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font\ncreation efforts of academic and linguistic communities, and to\nprovide a free and open framework in which fonts may be shared and\nimproved in partnership with others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded,\nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply to\nany document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the\ncopyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software\ncomponents as distributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to,\ndeleting, or substituting -- in part or in whole -- any of the\ncomponents of the Original Version, by changing formats or by porting\nthe Font Software to a new environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPERMISSION & CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed,\nmodify, redistribute, and sell modified and unmodified copies of the\nFont Software, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components, in\nOriginal or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the\ncorresponding Copyright Holder. This restriction only applies to the\nprimary font name as presented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5) The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created using\nthe Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE.\n"
  },
  {
    "path": "crates/epaint_default_fonts/fonts/UFL.txt",
    "content": "-------------------------------\nUBUNTU FONT LICENCE Version 1.0\n-------------------------------\n\nPREAMBLE\nThis licence allows the licensed fonts to be used, studied, modified and\nredistributed freely. The fonts, including any derivative works, can be\nbundled, embedded, and redistributed provided the terms of this licence\nare met. The fonts and derivatives, however, cannot be released under\nany other licence. The requirement for fonts to remain under this\nlicence does not require any document created using the fonts or their\nderivatives to be published under this licence, as long as the primary\npurpose of the document is not to be a vehicle for the distribution of\nthe fonts.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this licence and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Original Version\" refers to the collection of Font Software components\nas received under this licence.\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to\na new environment.\n\n\"Copyright Holder(s)\" refers to all individuals and companies who have a\ncopyright ownership of the Font Software.\n\n\"Substantially Changed\" refers to Modified Versions which can be easily\nidentified as dissimilar to the Font Software by users of the Font\nSoftware comparing the Original Version with the Modified Version.\n\nTo \"Propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy. Propagation includes copying,\ndistribution (with or without modification and with or without charging\na redistribution fee), making available to the public, and in some\ncountries other activities as well.\n\nPERMISSION & CONDITIONS\nThis licence does not grant any rights under trademark law and all such\nrights are reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of the Font Software, to propagate the Font Software, subject to\nthe below conditions:\n\n1) Each copy of the Font Software must contain the above copyright\nnotice and this licence. These can be included either as stand-alone\ntext files, human-readable headers or in the appropriate machine-\nreadable metadata fields within text or binary files as long as those\nfields can be easily viewed by the user.\n\n2) The font name complies with the following:\n(a) The Original Version must retain its name, unmodified.\n(b) Modified Versions which are Substantially Changed must be renamed to\navoid use of the name of the Original Version or similar names entirely.\n(c) Modified Versions which are not Substantially Changed must be\nrenamed to both (i) retain the name of the Original Version and (ii) add\nadditional naming elements to distinguish the Modified Version from the\nOriginal Version. The name of such Modified Versions must be the name of\nthe Original Version, with \"derivative X\" where X represents the name of\nthe new work, appended to that name.\n\n3) The name(s) of the Copyright Holder(s) and any contributor to the\nFont Software shall not be used to promote, endorse or advertise any\nModified Version, except (i) as required by this licence, (ii) to\nacknowledge the contribution(s) of the Copyright Holder(s) or (iii) with\ntheir explicit written permission.\n\n4) The Font Software, modified or unmodified, in part or in whole, must\nbe distributed entirely under this licence, and must not be distributed\nunder any other licence. The requirement for fonts to remain under this\nlicence does not affect any document created using the Font Software,\nexcept any version of the Font Software extracted from a document\ncreated using the Font Software may only be distributed under this\nlicence.\n\nTERMINATION\nThis licence becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF\nCOPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER\nDEALINGS IN THE FONT SOFTWARE.\n"
  },
  {
    "path": "crates/epaint_default_fonts/fonts/emoji-icon-font-mit-license.txt",
    "content": "MIT License\n\nCopyright (c) 2014 John Slegers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "crates/epaint_default_fonts/fonts/list_fonts.py",
    "content": "#!/usr/bin/env python\nfrom fontTools.ttLib import TTFont\nfrom fontTools.unicode import Unicode\nfrom itertools import chain\nimport sys\n\nttf = TTFont(sys.argv[1], 0, verbose=0, allowVID=0,\n             ignoreDecompileErrors=True,\n             fontNumber=-1)\n\nchars = chain.from_iterable([y + (Unicode[y[0]],)\n                             for y in x.cmap.items()] for x in ttf[\"cmap\"].tables)\n\n\nall_codepoints = {}\n\nfor entry in chars:\n    codepoint = entry[0]\n    short_name = entry[1]\n    long_name = entry[2].lower()\n    if False:\n        print(f'(0x{codepoint:02X}, \"{short_name}\", \"{long_name}\"),')\n    else:\n        name = short_name if long_name == \"????\" else long_name\n        # print(f'(0x{codepoint:02X}, \"{name}\"),')\n        all_codepoints[codepoint] = name\n\nfor codepoint in sorted(all_codepoints.keys()):\n    name = all_codepoints[codepoint]\n    print(f'(0x{codepoint:02X}, \\'{chr(codepoint)}\\', \"{name}\"),')\n\nttf.close()\n"
  },
  {
    "path": "crates/epaint_default_fonts/src/lib.rs",
    "content": "//! A library containing built-in fonts for `epaint`, embedded as bytes.\n//!\n//! This is intended to be consumed through the `epaint` crate.\n\n/// A typeface designed for source code.\n///\n/// Hack is designed to be a workhorse typeface for source code. It has deep\n/// roots in the free, open source typeface community and expands upon the\n/// contributions of the [Bitstream Vera](https://www.gnome.org/fonts/) and\n/// [DejaVu](https://dejavu-fonts.github.io/) projects.  The large x-height +\n/// wide aperture + low contrast design make it legible at commonly used source\n/// code text sizes with a sweet spot that runs in the 8 - 14 range.\n///\n/// See [the Hack repository](https://github.com/source-foundry/Hack) for more\n/// information.\npub const HACK_REGULAR: &[u8] = include_bytes!(\"../fonts/Hack-Regular.ttf\");\n\n/// A typeface containing emoji characters as designed for the Noto font family.\n///\n/// Noto is a collection of high-quality fonts with multiple weights and widths\n/// in sans, serif, mono, and other styles, in more than 1,000 languages and\n/// over 150 writing systems. Noto Emoji contains black-and-white emoji\n/// characters that match Google's emoji designs.\n///\n/// See [Google Fonts](https://fonts.google.com/noto/specimen/Noto+Emoji) for\n/// more information.\npub const NOTO_EMOJI_REGULAR: &[u8] = include_bytes!(\"../fonts/NotoEmoji-Regular.ttf\");\n\n/// A typeface designed for use by Ubuntu.\n///\n/// The Ubuntu typeface has been specially created to complement the Ubuntu tone\n/// of voice. It has a contemporary style and contains characteristics unique to\n/// the Ubuntu brand that convey a precise, reliable and free attitude.\n///\n/// See [Ubuntu design](https://design.ubuntu.com/font) for more information.\npub const UBUNTU_LIGHT: &[u8] = include_bytes!(\"../fonts/Ubuntu-Light.ttf\");\n\n/// An experimental typeface that uses standardized\n/// [UNICODE planes](http://en.wikipedia.org/wiki/Plane_(Unicode))\n/// for icon fonts.\n///\n/// The icons in this font are designed to be styled with minimal effort. Each\n/// icon is solid, which is useful for changing icon colors.\n///\n/// See [the `emoji-icon-font` repository](https://github.com/jslegers/emoji-icon-font)\n/// for more information.\npub const EMOJI_ICON: &[u8] = include_bytes!(\"../fonts/emoji-icon-font.ttf\");\n"
  },
  {
    "path": "deny.toml",
    "content": "# Copied from https://github.com/rerun-io/rerun_template\n#\n# https://github.com/EmbarkStudios/cargo-deny\n#\n# cargo-deny checks our dependency tree for copy-left licenses,\n# duplicate dependencies, and rustsec advisories (https://rustsec.org/advisories).\n#\n# Install: `cargo install cargo-deny`\n# Check: `cargo deny check`.\n\n\n# Note: running just `cargo deny check` without a `--target` can result in\n# false positives due to https://github.com/EmbarkStudios/cargo-deny/issues/324\n[graph]\ntargets = [\n  { triple = \"aarch64-apple-darwin\" },\n  { triple = \"i686-pc-windows-gnu\" },\n  { triple = \"i686-pc-windows-msvc\" },\n  { triple = \"i686-unknown-linux-gnu\" },\n  { triple = \"wasm32-unknown-unknown\" },\n  { triple = \"x86_64-apple-darwin\" },\n  { triple = \"x86_64-pc-windows-gnu\" },\n  { triple = \"x86_64-pc-windows-msvc\" },\n  { triple = \"x86_64-unknown-linux-gnu\" },\n  { triple = \"x86_64-unknown-linux-musl\" },\n  { triple = \"x86_64-unknown-redox\" },\n]\nall-features = true\n\n\n[advisories]\nversion = 2\nignore = [\n  \"RUSTSEC-2024-0320\", # unmaintained yaml-rust pulled in by syntect\n  \"RUSTSEC-2024-0436\", # unmaintained paste pulled via metal/wgpu, see https://github.com/gfx-rs/metal-rs/issues/349\n  \"RUSTSEC-2025-0141\", # https://rustsec.org/advisories/RUSTSEC-2025-0141 - bincode is unmaintained - https://git.sr.ht/~stygianentity/bincode/tree/v3.0/item/README.md\n]\n\n[bans]\nmultiple-versions = \"deny\" # Use `cargo tree --duplicates` to easily find duplicates\nwildcards = \"deny\"\ndeny = [\n  { name = \"cmake\", reason = \"It has hurt me too much\" },\n  { name = \"openssl-sys\", reason = \"Use rustls\" },\n  { name = \"openssl\", reason = \"Use rustls\" },\n]\n\nskip = [\n  { name = \"bit-set\" }, # wgpu's naga depends on 0.8, syntect's (used by egui_extras) fancy-regex depends on 0.5\n  { name = \"bit-vec\" }, # dependency of bit-set in turn, different between 0.6 and 0.5\n  { name = \"bitflags\" }, # old 1.0 version via glutin, png, spirv, …\n  { name = \"core-foundation\" }, # version conflict between winit and wgpu ecosystems\n  { name = \"core-graphics-types\" }, # version conflict between winit and wgpu ecosystems\n  { name = \"getrandom\" }, # ring / rustls (and thus ehttp) still depend on getrandom 0.2\n  { name = \"kurbo\" }, # Old version because of resvg\n  { name = \"redox_syscall\" }, # old version via winit\n  { name = \"rustc-hash\" }, # Small enough\n  { name = \"thiserror\" }, # ecosystem is in the process of migrating from 1.x to 2.x\n  { name = \"thiserror-impl\" }, # same as above\n  { name = \"toml_datetime\" }, # required while eco-system updates to toml 1.0\n]\nskip-tree = [\n  { name = \"hashbrown\" }, # wgpu's naga depends on 0.16, accesskit depends on 0.15\n  { name = \"rfd\" }, # example dependency\n  { name = \"windows\" }, # the ecosystem is currently transitioning from 0.58 to 0.61\n  { name = \"phf\" }, # mime_guess2, unicode_names2 -> 0.11.3; accesskit -> 0.13.1\n  { name = \"windows-sys\" }, # mostly hopeless to avoid\n]\n\n\n[licenses]\nversion = 2\nprivate = { ignore = true }\nconfidence-threshold = 0.93 # We want really high confidence when inferring licenses from text\nallow = [\n  \"Apache-2.0 WITH LLVM-exception\", # https://spdx.org/licenses/LLVM-exception.html\n  \"Apache-2.0\", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)\n  \"BSD-2-Clause\", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)\n  \"BSD-3-Clause\", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)\n  \"BSL-1.0\", # https://tldrlegal.com/license/boost-software-license-1.0-explained\n  \"CC0-1.0\", # https://creativecommons.org/publicdomain/zero/1.0/\n  \"ISC\", # https://www.tldrlegal.com/license/isc-license\n  \"MIT-0\", # https://choosealicense.com/licenses/mit-0/\n  \"MIT\", # https://tldrlegal.com/license/mit-license\n  \"MPL-2.0\", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11. Used by webpki-roots on Linux.\n  \"OFL-1.1\", # https://spdx.org/licenses/OFL-1.1.html\n  \"OpenSSL\", # https://www.openssl.org/source/license.html - used on Linux\n  \"Ubuntu-font-1.0\", # https://ubuntu.com/legal/font-licence\n  \"Unicode-3.0\", # https://www.unicode.org/license.txt\n  \"Unicode-DFS-2016\", # https://spdx.org/licenses/Unicode-DFS-2016.html\n  \"Zlib\", # https://tldrlegal.com/license/zlib-libpng-license-(zlib)\n]\nexceptions = []\n\n[[licenses.clarify]]\nname = \"webpki\"\nexpression = \"ISC\"\nlicense-files = [{ path = \"LICENSE\", hash = 0x001c7e6c }]\n\n[[licenses.clarify]]\nname = \"ring\"\nexpression = \"MIT AND ISC AND OpenSSL\"\nlicense-files = [{ path = \"LICENSE\", hash = 0xbd0eed23 }]\n\n\n[sources]\nunknown-registry = \"deny\"\nunknown-git = \"deny\"\n\nallow-git = [\n  \"https://github.com/rerun-io/kittest\", # TODO(lucasmerlin): remove this once the kittest crate is published\"\n]\n"
  },
  {
    "path": "examples/README.md",
    "content": "# `egui` and `eframe` examples\nAll the examples in this folder uses [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe) to set up a window for [`egui`](https://github.com/emilk/egui/). Some examples are specific to `eframe`, but many are applicable to any `egui` integration.\n\nThere are a lot more examples at <https://www.egui.rs>, and it has links to the source code of each example.\n\nAlso check out the official docs at <https://docs.rs/egui> and <https://docs.rs/eframe>.\n\nNote that all the examples on `main` are for the latest `main` version of `egui`.\n\nIf you want to look for examples for a specific version of egui, go to that tag, e.g. <https://github.com/emilk/egui/tree/latest/examples>.\n"
  },
  {
    "path": "examples/confirm_exit/Cargo.toml",
    "content": "[package]\nname = \"confirm_exit\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/confirm_exit/README.md",
    "content": "Example how to show a confirm dialog before exiting an application.\n\n```sh\ncargo run -p confirm_exit\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/confirm_exit/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"Confirm exit\",\n        options,\n        Box::new(|_cc| Ok(Box::<MyApp>::default())),\n    )\n}\n\n#[derive(Default)]\nstruct MyApp {\n    show_confirmation_dialog: bool,\n    allowed_to_close: bool,\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.heading(\"Try to close the window\");\n        });\n\n        if ui.input(|i| i.viewport().close_requested()) {\n            if self.allowed_to_close {\n                // do nothing - we will close\n            } else {\n                ui.send_viewport_cmd(egui::ViewportCommand::CancelClose);\n                self.show_confirmation_dialog = true;\n            }\n        }\n\n        if self.show_confirmation_dialog {\n            egui::Window::new(\"Do you want to quit?\")\n                .collapsible(false)\n                .resizable(false)\n                .show(ui.ctx(), |ui| {\n                    ui.horizontal(|ui| {\n                        if ui.button(\"No\").clicked() {\n                            self.show_confirmation_dialog = false;\n                            self.allowed_to_close = false;\n                        }\n\n                        if ui.button(\"Yes\").clicked() {\n                            self.show_confirmation_dialog = false;\n                            self.allowed_to_close = true;\n                            ui.send_viewport_cmd(egui::ViewportCommand::Close);\n                        }\n                    });\n                });\n        }\n    }\n}\n"
  },
  {
    "path": "examples/custom_3d_glow/Cargo.toml",
    "content": "[package]\nname = \"custom_3d_glow\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"glow\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/custom_3d_glow/README.md",
    "content": "This demo shows how to embed 3D rendering using [`glow`](https://github.com/grovesNL/glow) in `eframe`.\n\nThis is very advanced usage, and you need to be careful.\n\nIf you are content of having egui sit on top of a 3D background, take a look at:\n\n* [`bevy_egui`](https://github.com/mvlabat/bevy_egui)\n* [`three-d`](https://github.com/asny/three-d)\n\n\n```sh\ncargo run -p custom_3d_glow\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/custom_3d_glow/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n#![expect(unsafe_code)]\n#![expect(clippy::undocumented_unsafe_blocks)]\n\nuse eframe::{egui, egui_glow, glow};\n\nuse egui::mutex::Mutex;\nuse std::sync::Arc;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([350.0, 380.0]),\n        multisampling: 4,\n        renderer: eframe::Renderer::Glow,\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"Custom 3D painting in eframe using glow\",\n        options,\n        Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),\n    )\n}\n\nstruct MyApp {\n    /// Behind an `Arc<Mutex<…>>` so we can pass it to [`egui::PaintCallback`] and paint later.\n    rotating_triangle: Arc<Mutex<RotatingTriangle>>,\n    angle: f32,\n}\n\nimpl MyApp {\n    fn new(cc: &eframe::CreationContext<'_>) -> Self {\n        let gl = cc\n            .gl\n            .as_ref()\n            .expect(\"You need to run eframe with the glow backend\");\n        Self {\n            rotating_triangle: Arc::new(Mutex::new(RotatingTriangle::new(gl))),\n            angle: 0.0,\n        }\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.horizontal(|ui| {\n                ui.spacing_mut().item_spacing.x = 0.0;\n                ui.label(\"The triangle is being painted using \");\n                ui.hyperlink_to(\"glow\", \"https://github.com/grovesNL/glow\");\n                ui.label(\" (OpenGL).\");\n            });\n\n            egui::Frame::canvas(ui.style()).show(ui, |ui| {\n                self.custom_painting(ui);\n            });\n            ui.label(\"Drag to rotate!\");\n        });\n    }\n\n    fn on_exit(&mut self, gl: Option<&glow::Context>) {\n        if let Some(gl) = gl {\n            self.rotating_triangle.lock().destroy(gl);\n        }\n    }\n}\n\nimpl MyApp {\n    fn custom_painting(&mut self, ui: &mut egui::Ui) {\n        let (rect, response) =\n            ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag());\n\n        self.angle += response.drag_motion().x * 0.01;\n\n        // Clone locals so we can move them into the paint callback:\n        let angle = self.angle;\n        let rotating_triangle = Arc::clone(&self.rotating_triangle);\n\n        let callback = egui::PaintCallback {\n            rect,\n            callback: std::sync::Arc::new(egui_glow::CallbackFn::new(move |_info, painter| {\n                rotating_triangle.lock().paint(painter.gl(), angle);\n            })),\n        };\n        ui.painter().add(callback);\n    }\n}\n\nstruct RotatingTriangle {\n    program: glow::Program,\n    vertex_array: glow::VertexArray,\n}\n\nimpl RotatingTriangle {\n    fn new(gl: &glow::Context) -> Self {\n        use glow::HasContext as _;\n\n        let shader_version = if cfg!(target_arch = \"wasm32\") {\n            \"#version 300 es\"\n        } else {\n            \"#version 330\"\n        };\n\n        unsafe {\n            let program = gl.create_program().expect(\"Cannot create program\");\n\n            let (vertex_shader_source, fragment_shader_source) = (\n                r#\"\n                    const vec2 verts[3] = vec2[3](\n                        vec2(0.0, 1.0),\n                        vec2(-1.0, -1.0),\n                        vec2(1.0, -1.0)\n                    );\n                    const vec4 colors[3] = vec4[3](\n                        vec4(1.0, 0.0, 0.0, 1.0),\n                        vec4(0.0, 1.0, 0.0, 1.0),\n                        vec4(0.0, 0.0, 1.0, 1.0)\n                    );\n                    out vec4 v_color;\n                    uniform float u_angle;\n                    void main() {\n                        v_color = colors[gl_VertexID];\n                        gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0);\n                        gl_Position.x *= cos(u_angle);\n                    }\n                \"#,\n                r#\"\n                    precision mediump float;\n                    in vec4 v_color;\n                    out vec4 out_color;\n                    void main() {\n                        out_color = v_color;\n                    }\n                \"#,\n            );\n\n            let shader_sources = [\n                (glow::VERTEX_SHADER, vertex_shader_source),\n                (glow::FRAGMENT_SHADER, fragment_shader_source),\n            ];\n\n            let shaders: Vec<_> = shader_sources\n                .iter()\n                .map(|(shader_type, shader_source)| {\n                    let shader = gl\n                        .create_shader(*shader_type)\n                        .expect(\"Cannot create shader\");\n                    gl.shader_source(shader, &format!(\"{shader_version}\\n{shader_source}\"));\n                    gl.compile_shader(shader);\n                    assert!(\n                        gl.get_shader_compile_status(shader),\n                        \"Failed to compile {shader_type}: {}\",\n                        gl.get_shader_info_log(shader)\n                    );\n                    gl.attach_shader(program, shader);\n                    shader\n                })\n                .collect();\n\n            gl.link_program(program);\n            assert!(\n                gl.get_program_link_status(program),\n                \"{}\",\n                gl.get_program_info_log(program)\n            );\n\n            for shader in shaders {\n                gl.detach_shader(program, shader);\n                gl.delete_shader(shader);\n            }\n\n            let vertex_array = gl\n                .create_vertex_array()\n                .expect(\"Cannot create vertex array\");\n\n            Self {\n                program,\n                vertex_array,\n            }\n        }\n    }\n\n    fn destroy(&self, gl: &glow::Context) {\n        use glow::HasContext as _;\n        unsafe {\n            gl.delete_program(self.program);\n            gl.delete_vertex_array(self.vertex_array);\n        }\n    }\n\n    fn paint(&self, gl: &glow::Context, angle: f32) {\n        use glow::HasContext as _;\n        unsafe {\n            gl.use_program(Some(self.program));\n            gl.uniform_1_f32(\n                gl.get_uniform_location(self.program, \"u_angle\").as_ref(),\n                angle,\n            );\n            gl.bind_vertex_array(Some(self.vertex_array));\n            gl.draw_arrays(glow::TRIANGLES, 0, 3);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/custom_font/Cargo.toml",
    "content": "[package]\nname = \"custom_font\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/custom_font/README.md",
    "content": "Example of how to use custom fonts.\n\n```sh\ncargo run -p custom_font\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/custom_font/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::{\n    egui,\n    epaint::text::{FontInsert, InsertFontFamily},\n};\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"egui example: custom font\",\n        options,\n        Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),\n    )\n}\n\n// Demonstrates how to add a font to the existing ones\nfn add_font(ctx: &egui::Context) {\n    ctx.add_font(FontInsert::new(\n        \"my_font\",\n        egui::FontData::from_static(include_bytes!(\n            \"../../../crates/epaint_default_fonts/fonts/Hack-Regular.ttf\"\n        )),\n        vec![\n            InsertFontFamily {\n                family: egui::FontFamily::Proportional,\n                priority: egui::epaint::text::FontPriority::Highest,\n            },\n            InsertFontFamily {\n                family: egui::FontFamily::Monospace,\n                priority: egui::epaint::text::FontPriority::Lowest,\n            },\n        ],\n    ));\n}\n\n// Demonstrates how to replace all fonts.\nfn replace_fonts(ctx: &egui::Context) {\n    // Start with the default fonts (we will be adding to them rather than replacing them).\n    let mut fonts = egui::FontDefinitions::default();\n\n    // Install my own font (maybe supporting non-latin characters).\n    // .ttf and .otf files supported.\n    fonts.font_data.insert(\n        \"my_font\".to_owned(),\n        std::sync::Arc::new(egui::FontData::from_static(include_bytes!(\n            \"../../../crates/epaint_default_fonts/fonts/Hack-Regular.ttf\"\n        ))),\n    );\n\n    // Put my font first (highest priority) for proportional text:\n    fonts\n        .families\n        .entry(egui::FontFamily::Proportional)\n        .or_default()\n        .insert(0, \"my_font\".to_owned());\n\n    // Put my font as last fallback for monospace:\n    fonts\n        .families\n        .entry(egui::FontFamily::Monospace)\n        .or_default()\n        .push(\"my_font\".to_owned());\n\n    // Tell egui to use these fonts:\n    ctx.set_fonts(fonts);\n}\n\nstruct MyApp {\n    text: String,\n}\n\nimpl MyApp {\n    fn new(cc: &eframe::CreationContext<'_>) -> Self {\n        replace_fonts(&cc.egui_ctx);\n        add_font(&cc.egui_ctx);\n        Self {\n            text: \"Edit this text field if you want\".to_owned(),\n        }\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.heading(\"egui using custom fonts\");\n            ui.text_edit_multiline(&mut self.text);\n        });\n    }\n}\n"
  },
  {
    "path": "examples/custom_font_style/Cargo.toml",
    "content": "[package]\nname = \"custom_font_style\"\nversion = \"0.1.0\"\nauthors = [\"tami5 <kkharji@proton.me>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/custom_font_style/README.md",
    "content": "Example how to define custom test styles.\n\n```sh\ncargo run -p custom_font_style\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/custom_font_style/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui;\nuse egui::{FontFamily, FontId, RichText, TextStyle};\nuse std::collections::BTreeMap;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions::default();\n\n    eframe::run_native(\n        \"egui example: global font style\",\n        options,\n        Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),\n    )\n}\n\n#[inline]\nfn heading2() -> TextStyle {\n    TextStyle::Name(\"Heading2\".into())\n}\n\n#[inline]\nfn heading3() -> TextStyle {\n    TextStyle::Name(\"ContextHeading\".into())\n}\n\nfn configure_text_styles(ctx: &egui::Context) {\n    use FontFamily::{Monospace, Proportional};\n\n    let text_styles: BTreeMap<TextStyle, FontId> = [\n        (TextStyle::Heading, FontId::new(25.0, Proportional)),\n        (heading2(), FontId::new(22.0, Proportional)),\n        (heading3(), FontId::new(19.0, Proportional)),\n        (TextStyle::Body, FontId::new(16.0, Proportional)),\n        (TextStyle::Monospace, FontId::new(12.0, Monospace)),\n        (TextStyle::Button, FontId::new(12.0, Proportional)),\n        (TextStyle::Small, FontId::new(8.0, Proportional)),\n    ]\n    .into();\n    ctx.all_styles_mut(move |style| style.text_styles = text_styles.clone());\n}\n\nfn content(ui: &mut egui::Ui) {\n    ui.heading(\"Top Heading\");\n    ui.add_space(5.);\n    ui.label(LOREM_IPSUM);\n    ui.add_space(15.);\n    ui.label(RichText::new(\"Sub Heading\").text_style(heading2()).strong());\n    ui.monospace(LOREM_IPSUM);\n    ui.add_space(15.);\n    ui.label(RichText::new(\"Context\").text_style(heading3()).strong());\n    ui.add_space(5.);\n    ui.label(LOREM_IPSUM);\n}\n\nstruct MyApp;\n\nimpl MyApp {\n    fn new(cc: &eframe::CreationContext<'_>) -> Self {\n        configure_text_styles(&cc.egui_ctx);\n        Self\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, content);\n    }\n}\n\npub const LOREM_IPSUM: &str = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\";\n"
  },
  {
    "path": "examples/custom_keypad/Cargo.toml",
    "content": "[package]\nname = \"custom_keypad\"\nversion = \"0.1.0\"\nauthors = [\"Varphone Wong <varphone@qq.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\n\n# For image support:\negui_extras = { workspace = true, features = [\"default\", \"image\"] }\n\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/custom_keypad/README.md",
    "content": "Example showing how to implements a custom keypad.\n\n```sh\ncargo run -p custom_keypad\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/custom_keypad/src/keypad.rs",
    "content": "use eframe::egui::{self, Button, Ui, Vec2, pos2, vec2};\n\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\nenum Transition {\n    #[default]\n    None,\n    CloseOnNextFrame,\n    CloseImmediately,\n}\n\n#[derive(Clone, Debug)]\nstruct State {\n    open: bool,\n    closable: bool,\n    close_on_next_frame: bool,\n    start_pos: egui::Pos2,\n    focus: Option<egui::Id>,\n    events: Option<Vec<egui::Event>>,\n}\n\nimpl State {\n    fn new() -> Self {\n        Self {\n            open: false,\n            closable: false,\n            close_on_next_frame: false,\n            start_pos: pos2(100.0, 100.0),\n            focus: None,\n            events: None,\n        }\n    }\n\n    fn queue_char(&mut self, c: char) {\n        let events = self.events.get_or_insert(vec![]);\n        if let Some(key) = egui::Key::from_name(&c.to_string()) {\n            events.push(egui::Event::Key {\n                key,\n                physical_key: Some(key),\n                pressed: true,\n                repeat: false,\n                modifiers: Default::default(),\n            });\n        }\n        events.push(egui::Event::Text(c.to_string()));\n    }\n\n    fn queue_key(&mut self, key: egui::Key) {\n        let events = self.events.get_or_insert(vec![]);\n        events.push(egui::Event::Key {\n            key,\n            physical_key: Some(key),\n            pressed: true,\n            repeat: false,\n            modifiers: Default::default(),\n        });\n    }\n}\n\nimpl Default for State {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n/// A simple keypad widget.\npub struct Keypad {\n    id: egui::Id,\n}\n\nimpl Keypad {\n    pub fn new() -> Self {\n        Self {\n            id: egui::Id::new(\"keypad\"),\n        }\n    }\n\n    pub fn bump_events(&self, ctx: &egui::Context, raw_input: &mut egui::RawInput) {\n        let events = ctx.memory_mut(|m| {\n            m.data\n                .get_temp_mut_or_default::<State>(self.id)\n                .events\n                .take()\n        });\n        if let Some(mut events) = events {\n            events.append(&mut raw_input.events);\n            raw_input.events = events;\n        }\n    }\n\n    fn buttons(ui: &mut Ui, state: &mut State) -> Transition {\n        let mut trans = Transition::None;\n        ui.vertical(|ui| {\n            let window_margin = ui.spacing().window_margin;\n            let size_1x1 = vec2(32.0, 26.0);\n            let _size_1x2 = vec2(32.0, 52.0 + window_margin.topf());\n            let _size_2x1 = vec2(64.0 + window_margin.leftf(), 26.0);\n\n            ui.spacing_mut().item_spacing = Vec2::splat(window_margin.leftf());\n\n            ui.horizontal(|ui| {\n                if ui.add_sized(size_1x1, Button::new(\"1\")).clicked() {\n                    state.queue_char('1');\n                }\n                if ui.add_sized(size_1x1, Button::new(\"2\")).clicked() {\n                    state.queue_char('2');\n                }\n                if ui.add_sized(size_1x1, Button::new(\"3\")).clicked() {\n                    state.queue_char('3');\n                }\n                if ui.add_sized(size_1x1, Button::new(\"⏮\")).clicked() {\n                    state.queue_key(egui::Key::Home);\n                }\n                if ui.add_sized(size_1x1, Button::new(\"🔙\")).clicked() {\n                    state.queue_key(egui::Key::Backspace);\n                }\n            });\n            ui.horizontal(|ui| {\n                if ui.add_sized(size_1x1, Button::new(\"4\")).clicked() {\n                    state.queue_char('4');\n                }\n                if ui.add_sized(size_1x1, Button::new(\"5\")).clicked() {\n                    state.queue_char('5');\n                }\n                if ui.add_sized(size_1x1, Button::new(\"6\")).clicked() {\n                    state.queue_char('6');\n                }\n                if ui.add_sized(size_1x1, Button::new(\"⏭\")).clicked() {\n                    state.queue_key(egui::Key::End);\n                }\n                if ui.add_sized(size_1x1, Button::new(\"⎆\")).clicked() {\n                    state.queue_key(egui::Key::Enter);\n                    trans = Transition::CloseOnNextFrame;\n                }\n            });\n            ui.horizontal(|ui| {\n                if ui.add_sized(size_1x1, Button::new(\"7\")).clicked() {\n                    state.queue_char('7');\n                }\n                if ui.add_sized(size_1x1, Button::new(\"8\")).clicked() {\n                    state.queue_char('8');\n                }\n                if ui.add_sized(size_1x1, Button::new(\"9\")).clicked() {\n                    state.queue_char('9');\n                }\n                if ui.add_sized(size_1x1, Button::new(\"⏶\")).clicked() {\n                    state.queue_key(egui::Key::ArrowUp);\n                }\n                if ui.add_sized(size_1x1, Button::new(\"⌨\")).clicked() {\n                    trans = Transition::CloseImmediately;\n                }\n            });\n            ui.horizontal(|ui| {\n                if ui.add_sized(size_1x1, Button::new(\"0\")).clicked() {\n                    state.queue_char('0');\n                }\n                if ui.add_sized(size_1x1, Button::new(\".\")).clicked() {\n                    state.queue_char('.');\n                }\n                if ui.add_sized(size_1x1, Button::new(\"⏴\")).clicked() {\n                    state.queue_key(egui::Key::ArrowLeft);\n                }\n                if ui.add_sized(size_1x1, Button::new(\"⏷\")).clicked() {\n                    state.queue_key(egui::Key::ArrowDown);\n                }\n                if ui.add_sized(size_1x1, Button::new(\"⏵\")).clicked() {\n                    state.queue_key(egui::Key::ArrowRight);\n                }\n            });\n        });\n\n        trans\n    }\n\n    pub fn show(&self, ctx: &egui::Context) {\n        let (focus, mut state) = ctx.memory(|m| {\n            (\n                m.focused(),\n                m.data.get_temp::<State>(self.id).unwrap_or_default(),\n            )\n        });\n\n        let is_first_show = ctx.egui_wants_keyboard_input() && state.focus != focus;\n        if is_first_show {\n            let y = ctx.global_style().spacing.interact_size.y * 1.25;\n            state.open = true;\n            state.start_pos = ctx.input(|i| {\n                i.pointer\n                    .hover_pos()\n                    .map_or(pos2(100.0, 100.0), |p| p + vec2(0.0, y))\n            });\n            state.focus = focus;\n        }\n\n        if state.close_on_next_frame {\n            state.open = false;\n            state.close_on_next_frame = false;\n            state.focus = None;\n        }\n\n        let mut open = state.open;\n\n        let win = egui::Window::new(\"⌨ Keypad\");\n        let win = if is_first_show {\n            win.current_pos(state.start_pos)\n        } else {\n            win.default_pos(state.start_pos)\n        };\n        let resp = win\n            .movable(true)\n            .resizable(false)\n            .open(&mut open)\n            .show(ctx, |ui| Self::buttons(ui, &mut state));\n\n        state.open = open;\n\n        if let Some(resp) = resp {\n            match resp.inner {\n                Some(Transition::CloseOnNextFrame) => {\n                    state.close_on_next_frame = true;\n                }\n                Some(Transition::CloseImmediately) => {\n                    state.open = false;\n                    state.focus = None;\n                }\n                _ => {}\n            }\n            if !state.closable && resp.response.hovered() {\n                state.closable = true;\n            }\n            if state.closable && resp.response.clicked_elsewhere() {\n                state.open = false;\n                state.closable = false;\n                state.focus = None;\n            }\n            if is_first_show {\n                ctx.move_to_top(resp.response.layer_id);\n            }\n        }\n\n        if let (true, Some(focus)) = (state.open, state.focus) {\n            ctx.memory_mut(|m| {\n                m.request_focus(focus);\n            });\n        }\n\n        ctx.memory_mut(|m| m.data.insert_temp(self.id, state));\n    }\n}\n\nimpl Default for Keypad {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "examples/custom_keypad/src/main.rs",
    "content": "// #![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\nuse eframe::egui;\n\nmod keypad;\nuse keypad::Keypad;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([640.0, 480.0]),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"Custom Keypad App\",\n        options,\n        Box::new(|cc| {\n            // Use the dark theme\n            cc.egui_ctx.set_theme(egui::Theme::Dark);\n            // This gives us image support:\n            egui_extras::install_image_loaders(&cc.egui_ctx);\n\n            Ok(Box::<MyApp>::default())\n        }),\n    )\n}\n\nstruct MyApp {\n    name: String,\n    age: u32,\n    keypad: Keypad,\n}\n\nimpl MyApp {}\n\nimpl Default for MyApp {\n    fn default() -> Self {\n        Self {\n            name: \"Arthur\".to_owned(),\n            age: 42,\n            keypad: Keypad::new(),\n        }\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::Window::new(\"Custom Keypad\")\n            .default_pos([100.0, 100.0])\n            .title_bar(true)\n            .show(ui.ctx(), |ui| {\n                ui.horizontal(|ui| {\n                    ui.label(\"Your name: \");\n                    ui.text_edit_singleline(&mut self.name);\n                });\n                ui.add(egui::Slider::new(&mut self.age, 0..=120).text(\"age\"));\n                if ui.button(\"Increment\").clicked() {\n                    self.age += 1;\n                }\n                ui.label(format!(\"Hello '{}', age {}\", self.name, self.age));\n            });\n\n        self.keypad.show(ui.ctx());\n    }\n\n    fn raw_input_hook(&mut self, ctx: &egui::Context, raw_input: &mut egui::RawInput) {\n        self.keypad.bump_events(ctx, raw_input);\n    }\n}\n"
  },
  {
    "path": "examples/custom_style/Cargo.toml",
    "content": "[package]\nname = \"custom_style\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[package.metadata.cargo-machete]\nignored = [\"image\"] # We need the .png feature\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\negui_demo_lib.workspace = true\negui_extras = { workspace = true, features = [\"image\"] }\nimage = { workspace = true, features = [\"png\"] }\n"
  },
  {
    "path": "examples/custom_style/README.md",
    "content": "Example of how to customize the style.\n\n```sh\ncargo run -p custom_style\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/custom_style/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui::{\n    self, Color32, Stroke, Style, Theme, global_theme_preference_buttons, style::Selection,\n};\nuse egui_demo_lib::{View as _, WidgetGallery};\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([350.0, 590.0]),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"egui example: custom style\",\n        options,\n        Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),\n    )\n}\n\nfn setup_custom_style(ctx: &egui::Context) {\n    ctx.style_mut_of(Theme::Light, use_light_green_accent);\n    ctx.style_mut_of(Theme::Dark, use_dark_purple_accent);\n}\n\nfn use_light_green_accent(style: &mut Style) {\n    style.visuals.hyperlink_color = Color32::from_rgb(18, 180, 85);\n    style.visuals.text_cursor.stroke.color = Color32::from_rgb(28, 92, 48);\n    style.visuals.selection = Selection {\n        bg_fill: Color32::from_rgb(157, 218, 169),\n        stroke: Stroke::new(1.0, Color32::from_rgb(28, 92, 48)),\n    };\n}\n\nfn use_dark_purple_accent(style: &mut Style) {\n    style.visuals.hyperlink_color = Color32::from_rgb(202, 135, 227);\n    style.visuals.text_cursor.stroke.color = Color32::from_rgb(234, 208, 244);\n    style.visuals.selection = Selection {\n        bg_fill: Color32::from_rgb(105, 67, 119),\n        stroke: Stroke::new(1.0, Color32::from_rgb(234, 208, 244)),\n    };\n}\n\nstruct MyApp {\n    widget_gallery: WidgetGallery,\n}\n\nimpl MyApp {\n    fn new(cc: &eframe::CreationContext<'_>) -> Self {\n        setup_custom_style(&cc.egui_ctx);\n        egui_extras::install_image_loaders(&cc.egui_ctx); // Needed for the \"Widget Gallery\" demo\n        Self {\n            widget_gallery: WidgetGallery::default(),\n        }\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.heading(\"egui using a customized style\");\n            ui.label(\"Switch between dark and light mode to see the different styles in action.\");\n            global_theme_preference_buttons(ui);\n            ui.separator();\n            self.widget_gallery.ui(ui);\n        });\n    }\n}\n"
  },
  {
    "path": "examples/custom_window_frame/Cargo.toml",
    "content": "[package]\nname = \"custom_window_frame\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/custom_window_frame/README.md",
    "content": "Example how to show a custom window frame instead of the default OS window chrome decorations.\n\n```sh\ncargo run -p custom_window_frame\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/custom_window_frame/src/main.rs",
    "content": "//! Show a custom window frame instead of the default OS window chrome decorations.\n\n#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![allow(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui::{self, ViewportCommand};\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default()\n            .with_decorations(false) // Hide the OS-specific \"chrome\" around the window\n            .with_inner_size([400.0, 100.0])\n            .with_min_inner_size([400.0, 100.0])\n            .with_transparent(true), // To have rounded corners we need transparency\n\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"Custom window frame\", // unused title\n        options,\n        Box::new(|_cc| Ok(Box::<MyApp>::default())),\n    )\n}\n\n#[derive(Default)]\nstruct MyApp {}\n\nimpl eframe::App for MyApp {\n    fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] {\n        egui::Rgba::TRANSPARENT.to_array() // Make sure we don't paint anything behind the rounded corners\n    }\n\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        custom_window_frame(ui, \"egui with custom frame\", |ui| {\n            ui.label(\"This is just the contents of the window.\");\n            ui.horizontal(|ui| {\n                ui.label(\"egui theme:\");\n                egui::widgets::global_theme_preference_buttons(ui);\n            });\n        });\n    }\n}\n\nfn custom_window_frame(ui: &mut egui::Ui, title: &str, add_contents: impl FnOnce(&mut egui::Ui)) {\n    use egui::UiBuilder;\n\n    let panel_frame = egui::Frame::new()\n        .fill(ui.global_style().visuals.window_fill())\n        .corner_radius(10)\n        .stroke(ui.global_style().visuals.widgets.noninteractive.fg_stroke)\n        .outer_margin(1); // so the stroke is within the bounds\n\n    panel_frame.show(ui, |ui| {\n        let app_rect = ui.max_rect();\n\n        ui.expand_to_include_rect(app_rect); // Expand frame to include it all\n\n        let title_bar_height = 32.0;\n        let title_bar_rect = {\n            let mut rect = app_rect;\n            rect.max.y = rect.min.y + title_bar_height;\n            rect\n        };\n        title_bar_ui(ui, title_bar_rect, title);\n\n        // Add the contents:\n        let content_rect = {\n            let mut rect = app_rect;\n            rect.min.y = title_bar_rect.max.y;\n            rect\n        }\n        .shrink(4.0);\n        let mut content_ui = ui.new_child(UiBuilder::new().max_rect(content_rect));\n        add_contents(&mut content_ui);\n    });\n}\n\nfn title_bar_ui(ui: &mut egui::Ui, title_bar_rect: eframe::epaint::Rect, title: &str) {\n    use egui::{Align2, FontId, Id, PointerButton, Sense, UiBuilder, vec2};\n\n    let painter = ui.painter();\n\n    let title_bar_response = ui.interact(\n        title_bar_rect,\n        Id::new(\"title_bar\"),\n        Sense::click_and_drag(),\n    );\n\n    // Paint the title:\n    painter.text(\n        title_bar_rect.center(),\n        Align2::CENTER_CENTER,\n        title,\n        FontId::proportional(20.0),\n        ui.style().visuals.text_color(),\n    );\n\n    // Paint the line under the title:\n    painter.line_segment(\n        [\n            title_bar_rect.left_bottom() + vec2(1.0, 0.0),\n            title_bar_rect.right_bottom() + vec2(-1.0, 0.0),\n        ],\n        ui.visuals().widgets.noninteractive.bg_stroke,\n    );\n\n    // Interact with the title bar (drag to move window):\n    if title_bar_response.double_clicked() {\n        let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));\n        ui.send_viewport_cmd(ViewportCommand::Maximized(!is_maximized));\n    }\n\n    if title_bar_response.drag_started_by(PointerButton::Primary) {\n        ui.send_viewport_cmd(ViewportCommand::StartDrag);\n    }\n\n    ui.scope_builder(\n        UiBuilder::new()\n            .max_rect(title_bar_rect)\n            .layout(egui::Layout::right_to_left(egui::Align::Center)),\n        |ui| {\n            ui.spacing_mut().item_spacing.x = 0.0;\n            ui.visuals_mut().button_frame = false;\n            ui.add_space(8.0);\n            close_maximize_minimize(ui);\n        },\n    );\n}\n\n/// Show some close/maximize/minimize buttons for the native window.\nfn close_maximize_minimize(ui: &mut egui::Ui) {\n    use egui::{Button, RichText};\n\n    let button_height = 12.0;\n\n    let close_response = ui\n        .add(Button::new(RichText::new(\"❌\").size(button_height)))\n        .on_hover_text(\"Close the window\");\n    if close_response.clicked() {\n        ui.send_viewport_cmd(egui::ViewportCommand::Close);\n    }\n\n    let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));\n    if is_maximized {\n        let maximized_response = ui\n            .add(Button::new(RichText::new(\"🗗\").size(button_height)))\n            .on_hover_text(\"Restore window\");\n        if maximized_response.clicked() {\n            ui.send_viewport_cmd(ViewportCommand::Maximized(false));\n        }\n    } else {\n        let maximized_response = ui\n            .add(Button::new(RichText::new(\"🗗\").size(button_height)))\n            .on_hover_text(\"Maximize window\");\n        if maximized_response.clicked() {\n            ui.send_viewport_cmd(ViewportCommand::Maximized(true));\n        }\n    }\n\n    let minimized_response = ui\n        .add(Button::new(RichText::new(\"🗕\").size(button_height)))\n        .on_hover_text(\"Minimize the window\");\n    if minimized_response.clicked() {\n        ui.send_viewport_cmd(ViewportCommand::Minimized(true));\n    }\n}\n"
  },
  {
    "path": "examples/external_eventloop/Cargo.toml",
    "content": "[package]\nname = \"external_eventloop\"\nversion = \"0.1.0\"\nauthors = [\"Will Brown <opensource@rebeagle.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\n\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n\nwinit.workspace = true\n"
  },
  {
    "path": "examples/external_eventloop/README.md",
    "content": "Example running an eframe application on an external eventloop.\n\nThis allows you to run your eframe application alongside other windows and/or toolkits on the same event loop.\n\n```sh\ncargo run -p external_eventloop\n```\n"
  },
  {
    "path": "examples/external_eventloop/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs, clippy::unwrap_used)] // it's an example\n\nuse eframe::{UserEvent, egui};\nuse std::{cell::Cell, rc::Rc};\nuse winit::event_loop::{ControlFlow, EventLoop};\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n        ..Default::default()\n    };\n\n    let eventloop = EventLoop::<UserEvent>::with_user_event().build().unwrap();\n    eventloop.set_control_flow(ControlFlow::Poll);\n\n    let mut winit_app = eframe::create_native(\n        \"External Eventloop Application\",\n        options,\n        Box::new(|_| Ok(Box::<MyApp>::default())),\n        &eventloop,\n    );\n\n    eventloop.run_app(&mut winit_app)?;\n\n    Ok(())\n}\n\nstruct MyApp {\n    value: Rc<Cell<u32>>,\n    spin: bool,\n    blinky: bool,\n}\n\nimpl Default for MyApp {\n    fn default() -> Self {\n        Self {\n            value: Rc::new(Cell::new(42)),\n            spin: false,\n            blinky: false,\n        }\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.heading(\"My External Eventloop Application\");\n\n            ui.horizontal(|ui| {\n                if ui.button(\"Increment Now\").clicked() {\n                    self.value.set(self.value.get() + 1);\n                }\n            });\n            ui.label(format!(\"Value: {}\", self.value.get()));\n\n            if ui.button(\"Toggle Spinner\").clicked() {\n                self.spin = !self.spin;\n            }\n\n            if ui.button(\"Toggle Blinky\").clicked() {\n                self.blinky = !self.blinky;\n            }\n\n            if self.spin {\n                ui.spinner();\n            }\n\n            if self.blinky {\n                let now = ui.input(|i| i.time);\n                let blink = now % 1.0 < 0.5;\n                egui::Frame::new()\n                    .inner_margin(3)\n                    .corner_radius(5)\n                    .fill(if blink {\n                        egui::Color32::RED\n                    } else {\n                        egui::Color32::TRANSPARENT\n                    })\n                    .show(ui, |ui| {\n                        ui.label(\"Blinky!\");\n                    });\n\n                ui.request_repaint_after_secs((0.5 - (now % 0.5)) as f32);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "examples/external_eventloop_async/Cargo.toml",
    "content": "[package]\nname = \"external_eventloop_async\"\nversion = \"0.1.0\"\nauthors = [\"Will Brown <opensource@rebeagle.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n[features]\nlinux-example = []\n\n[[bin]]\nname = \"external_eventloop_async\"\nrequired-features = [\"linux-example\"]\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\n\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n\nlog.workspace = true\n\nwinit.workspace = true\n\ntokio = { workspace = true, features = [\"rt\", \"time\", \"net\"] }\n"
  },
  {
    "path": "examples/external_eventloop_async/README.md",
    "content": "Example running an eframe application on an external eventloop on top of a tokio executor on Linux.\n\nBy running the event loop, eframe, and tokio in the same thread, one can leverage local async tasks.\nThese tasks can share data with the UI without the need for locks or message passing.\n\nIn tokio CPU-bound async tasks can be run with `spawn_blocking` to avoid impacting the UI frame rate.\n\n```sh\ncargo run -p external_eventloop_async --features linux-example\n```\n"
  },
  {
    "path": "examples/external_eventloop_async/src/app.rs",
    "content": "#![expect(clippy::unwrap_used)] // It's an example\n\nuse std::{cell::Cell, io, os::fd::AsRawFd as _, rc::Rc, time::Duration};\n\nuse tokio::task::LocalSet;\nuse winit::event_loop::{ControlFlow, EventLoop};\n\nuse eframe::{EframePumpStatus, UserEvent, egui};\n\npub fn run() -> io::Result<()> {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n        ..Default::default()\n    };\n\n    let mut eventloop = EventLoop::<UserEvent>::with_user_event().build().unwrap();\n    eventloop.set_control_flow(ControlFlow::Poll);\n\n    let mut winit_app = eframe::create_native(\n        \"External Eventloop Application\",\n        options,\n        Box::new(|_| Ok(Box::<MyApp>::default())),\n        &eventloop,\n    );\n\n    let rt = tokio::runtime::Builder::new_current_thread()\n        .enable_all()\n        .build()\n        .unwrap();\n\n    let local = LocalSet::new();\n    local.block_on(&rt, async {\n        let eventloop_fd = tokio::io::unix::AsyncFd::new(eventloop.as_raw_fd())?;\n        let mut control_flow = ControlFlow::Poll;\n\n        loop {\n            let mut guard = match control_flow {\n                ControlFlow::Poll => None,\n                ControlFlow::Wait => Some(eventloop_fd.readable().await?),\n                ControlFlow::WaitUntil(deadline) => {\n                    tokio::time::timeout_at(deadline.into(), eventloop_fd.readable())\n                        .await\n                        .ok()\n                        .transpose()?\n                }\n            };\n\n            match winit_app.pump_eframe_app(&mut eventloop, None) {\n                EframePumpStatus::Continue(next) => control_flow = next,\n                EframePumpStatus::Exit(code) => {\n                    log::info!(\"exit code: {code}\");\n                    break;\n                }\n            }\n\n            if let Some(mut guard) = guard.take() {\n                guard.clear_ready();\n            }\n        }\n\n        Ok::<_, io::Error>(())\n    })\n}\n\nstruct MyApp {\n    value: Rc<Cell<u32>>,\n    spin: bool,\n    blinky: bool,\n}\n\nimpl Default for MyApp {\n    fn default() -> Self {\n        Self {\n            value: Rc::new(Cell::new(42)),\n            spin: false,\n            blinky: false,\n        }\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.heading(\"My External Eventloop Application\");\n\n            ui.horizontal(|ui| {\n                if ui.button(\"Increment Now\").clicked() {\n                    self.value.set(self.value.get() + 1);\n                }\n                if ui.button(\"Increment Later\").clicked() {\n                    let value = Rc::clone(&self.value);\n                    let ctx = ui.ctx().clone();\n                    tokio::task::spawn_local(async move {\n                        tokio::time::sleep(Duration::from_secs(1)).await;\n                        value.set(value.get() + 1);\n                        ctx.request_repaint();\n                    });\n                }\n            });\n            ui.label(format!(\"Value: {}\", self.value.get()));\n\n            if ui.button(\"Toggle Spinner\").clicked() {\n                self.spin = !self.spin;\n            }\n\n            if ui.button(\"Toggle Blinky\").clicked() {\n                self.blinky = !self.blinky;\n            }\n\n            if self.spin {\n                ui.spinner();\n            }\n\n            if self.blinky {\n                let now = ui.input(|i| i.time);\n                let blink = now % 1.0 < 0.5;\n                egui::Frame::new()\n                    .inner_margin(3)\n                    .corner_radius(5)\n                    .fill(if blink {\n                        egui::Color32::RED\n                    } else {\n                        egui::Color32::TRANSPARENT\n                    })\n                    .show(ui, |ui| {\n                        ui.label(\"Blinky!\");\n                    });\n\n                ui.request_repaint_after_secs((0.5 - (now % 0.5)) as f32);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "examples/external_eventloop_async/src/main.rs",
    "content": "#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\n#[cfg(target_os = \"linux\")]\nmod app;\n\n#[cfg(target_os = \"linux\")]\nfn main() -> std::io::Result<()> {\n    app::run()\n}\n\n// Do not check `app` on unsupported platforms when check \"--all-features\" is used in CI.\n#[cfg(not(target_os = \"linux\"))]\nfn main() {\n    #![expect(clippy::print_stdout)]\n    println!(\"This example only supports Linux.\");\n}\n"
  },
  {
    "path": "examples/file_dialog/Cargo.toml",
    "content": "[package]\nname = \"file_dialog\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\nrfd.workspace = true\n"
  },
  {
    "path": "examples/file_dialog/README.md",
    "content": "How to show a file dialog using [`rfd`](https://github.com/PolyMeilex/rfd).\n\n```sh\ncargo run -p file_dialog\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/file_dialog/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default()\n            .with_inner_size([640.0, 240.0]) // wide enough for the drag-drop overlay text\n            .with_drag_and_drop(true),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"Native file dialogs and drag-and-drop files\",\n        options,\n        Box::new(|_cc| Ok(Box::<MyApp>::default())),\n    )\n}\n\n#[derive(Default)]\nstruct MyApp {\n    dropped_files: Vec<egui::DroppedFile>,\n    picked_path: Option<String>,\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.label(\"Drag-and-drop files onto the window!\");\n\n            if ui.button(\"Open file…\").clicked()\n                && let Some(path) = rfd::FileDialog::new().pick_file()\n            {\n                self.picked_path = Some(path.display().to_string());\n            }\n\n            if let Some(picked_path) = &self.picked_path {\n                ui.horizontal(|ui| {\n                    ui.label(\"Picked file:\");\n                    ui.monospace(picked_path);\n                });\n            }\n\n            // Show dropped files (if any):\n            if !self.dropped_files.is_empty() {\n                ui.group(|ui| {\n                    ui.label(\"Dropped files:\");\n\n                    for file in &self.dropped_files {\n                        let mut info = if let Some(path) = &file.path {\n                            path.display().to_string()\n                        } else if !file.name.is_empty() {\n                            file.name.clone()\n                        } else {\n                            \"???\".to_owned()\n                        };\n\n                        let mut additional_info = vec![];\n                        if !file.mime.is_empty() {\n                            additional_info.push(format!(\"type: {}\", file.mime));\n                        }\n                        if let Some(bytes) = &file.bytes {\n                            additional_info.push(format!(\"{} bytes\", bytes.len()));\n                        }\n                        if !additional_info.is_empty() {\n                            info += &format!(\" ({})\", additional_info.join(\", \"));\n                        }\n\n                        ui.label(info);\n                    }\n                });\n            }\n        });\n\n        preview_files_being_dropped(ui.ctx());\n\n        // Collect dropped files:\n        ui.input(|i| {\n            if !i.raw.dropped_files.is_empty() {\n                self.dropped_files.clone_from(&i.raw.dropped_files);\n            }\n        });\n    }\n}\n\n/// Preview hovering files:\nfn preview_files_being_dropped(ctx: &egui::Context) {\n    use egui::{Align2, Color32, Id, LayerId, Order, TextStyle};\n    use std::fmt::Write as _;\n\n    if !ctx.input(|i| i.raw.hovered_files.is_empty()) {\n        let text = ctx.input(|i| {\n            let mut text = \"Dropping files:\\n\".to_owned();\n            for file in &i.raw.hovered_files {\n                if let Some(path) = &file.path {\n                    write!(text, \"\\n{}\", path.display()).ok();\n                } else if !file.mime.is_empty() {\n                    write!(text, \"\\n{}\", file.mime).ok();\n                } else {\n                    text += \"\\n???\";\n                }\n            }\n            text\n        });\n\n        let painter =\n            ctx.layer_painter(LayerId::new(Order::Foreground, Id::new(\"file_drop_target\")));\n\n        let content_rect = ctx.content_rect();\n        painter.rect_filled(content_rect, 0.0, Color32::from_black_alpha(192));\n        painter.text(\n            content_rect.center(),\n            Align2::CENTER_CENTER,\n            text,\n            TextStyle::Heading.resolve(&ctx.global_style()),\n            Color32::WHITE,\n        );\n    }\n}\n"
  },
  {
    "path": "examples/hello_android/Cargo.toml",
    "content": "[package]\nname = \"hello_android\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n# `unsafe_code` is required for `#[no_mangle]`, disable workspace lints to workaround lint error.\n# [lints]\n# workspace = true\n\n[lib]\n# cdylib is required for Android, lib is required for desktop\ncrate-type = [\"cdylib\", \"lib\"]\n\n\n[dependencies]\neframe = { workspace = true, default-features = false, features = [\n  \"default_fonts\",\n  \"glow\",\n  \"android-native-activity\",\n] }\negui_demo_lib = { workspace = true, features = [\"chrono\"] }\n\n# For image support:\negui_extras = { workspace = true, features = [\"default\", \"image\"] }\n\nandroid_logger.workspace = true\nlog.workspace = true\nwinit.workspace = true\n\n[package.metadata.android]\nbuild_targets = [\"armv7-linux-androideabi\", \"aarch64-linux-android\"]\n\n[package.metadata.android.sdk]\nmin_sdk_version = 23\ntarget_sdk_version = 35\n"
  },
  {
    "path": "examples/hello_android/README.md",
    "content": "Hello world example for Android.\n\nUse `cargo-apk` to build and run. Requires a patch to workaround [an upstream bug](https://github.com/rust-mobile/cargo-subcommand/issues/29).\n\nOne-time setup:\n\n```sh\ncargo install \\\n    --git https://github.com/parasyte/cargo-apk.git \\\n    --rev 282639508eeed7d73f2e1eaeea042da2716436d5 \\\n    cargo-apk\n```\n\nBuild and run:\n\n```sh\n# Run on android\ncargo apk run -p hello_android --lib\n\n# Run on your desktop\ncargo run -p hello_android\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/hello_android/src/lib.rs",
    "content": "#![doc = include_str!(\"../README.md\")]\n\nuse eframe::{CreationContext, egui};\n\n#[cfg(target_os = \"android\")]\n#[no_mangle]\nfn android_main(app: winit::platform::android::activity::AndroidApp) {\n    // Log to android output\n    android_logger::init_once(\n        android_logger::Config::default().with_max_level(log::LevelFilter::Info),\n    );\n\n    let options = eframe::NativeOptions {\n        android_app: Some(app),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"My egui App\",\n        options,\n        Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),\n    )\n    .unwrap()\n}\n\npub struct MyApp {\n    demo: egui_demo_lib::DemoWindows,\n}\n\nimpl MyApp {\n    pub fn new(cc: &CreationContext) -> Self {\n        egui_extras::install_image_loaders(&cc.egui_ctx);\n        Self {\n            demo: egui_demo_lib::DemoWindows::default(),\n        }\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        // Reserve some space at the top so the demo ui isn't hidden behind the android status bar\n        // TODO(lucasmerlin): This is a pretty big hack, should be fixed once safe_area implemented\n        // for android:\n        // https://github.com/rust-windowing/winit/issues/3910\n        egui::Panel::top(\"status_bar_space\").show_inside(ui, |ui| {\n            ui.set_height(32.0);\n        });\n\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            self.demo.ui(ui);\n        });\n    }\n}\n"
  },
  {
    "path": "examples/hello_android/src/main.rs",
    "content": "use hello_android::MyApp;\n\nfn main() -> eframe::Result {\n    eframe::run_native(\n        \"hello_android\",\n        Default::default(),\n        Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),\n    )\n}\n"
  },
  {
    "path": "examples/hello_world/Cargo.toml",
    "content": "[package]\nname = \"hello_world\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\n\n# For image support:\negui_extras = { workspace = true, features = [\"default\", \"image\"] }\n\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/hello_world/README.md",
    "content": "Example showing some UI controls like `Label`, `TextEdit`, `Slider`, `Button`.\n\n```sh\ncargo run -p hello_world\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/hello_world/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"My egui App\",\n        options,\n        Box::new(|cc| {\n            // This gives us image support:\n            egui_extras::install_image_loaders(&cc.egui_ctx);\n\n            Ok(Box::<MyApp>::default())\n        }),\n    )\n}\n\nstruct MyApp {\n    name: String,\n    age: u32,\n}\n\nimpl Default for MyApp {\n    fn default() -> Self {\n        Self {\n            name: \"Arthur\".to_owned(),\n            age: 42,\n        }\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.heading(\"My egui Application\");\n            ui.horizontal(|ui| {\n                let name_label = ui.label(\"Your name: \");\n                ui.text_edit_singleline(&mut self.name)\n                    .labelled_by(name_label.id);\n            });\n            ui.add(egui::Slider::new(&mut self.age, 0..=120).text(\"age\"));\n            if ui.button(\"Increment\").clicked() {\n                self.age += 1;\n            }\n            ui.label(format!(\"Hello '{}', age {}\", self.name, self.age));\n\n            ui.image(egui::include_image!(\n                \"../../../crates/egui/assets/ferris.png\"\n            ));\n        });\n    }\n}\n"
  },
  {
    "path": "examples/hello_world_par/Cargo.toml",
    "content": "[package]\nname = \"hello_world_par\"\nversion = \"0.1.0\"\nauthors = [\"Maxim Osipenko <maxim1999max@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[package.metadata.cargo-machete]\nignored = [\"winit\"] # Just enable some features of it; see below\n\n\n[dependencies]\neframe = { workspace = true, default-features = false, features = [\n  # accesskit struggles with threading\n  \"default_fonts\",\n  \"wayland\",\n  \"x11\",\n  \"wgpu\",\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n# This is normally enabled by eframe/default, which is not being used here\n# because of accesskit, as mentioned above\nwinit = { workspace = true, features = [\"default\"] }\n"
  },
  {
    "path": "examples/hello_world_par/README.md",
    "content": "This example shows that you can use egui in parallel from multiple threads.\n\n```sh\ncargo run -p hello_world_par\n```\n"
  },
  {
    "path": "examples/hello_world_par/src/main.rs",
    "content": "//! This example shows that you can use egui in parallel from multiple threads.\n\n#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(clippy::unwrap_used)] // it's an example\n\nuse std::sync::mpsc;\nuse std::thread::JoinHandle;\n\nuse eframe::egui;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([1024.0, 768.0]),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"My parallel egui App\",\n        options,\n        Box::new(|_cc| Ok(Box::new(MyApp::new()))),\n    )\n}\n\n/// State per thread.\nstruct ThreadState {\n    thread_nr: usize,\n    title: String,\n    name: String,\n    age: u32,\n}\n\nimpl ThreadState {\n    fn new(thread_nr: usize) -> Self {\n        let title = format!(\"Background thread {thread_nr}\");\n        Self {\n            thread_nr,\n            title,\n            name: \"Arthur\".into(),\n            age: 12 + thread_nr as u32 * 10,\n        }\n    }\n\n    fn show(&mut self, ctx: &egui::Context) {\n        let pos = egui::pos2(16.0, 128.0 * (self.thread_nr as f32 + 1.0));\n        egui::Window::new(&self.title)\n            .default_pos(pos)\n            .show(ctx, |ui| {\n                ui.horizontal(|ui| {\n                    ui.label(\"Your name: \");\n                    ui.text_edit_singleline(&mut self.name);\n                });\n                ui.add(egui::Slider::new(&mut self.age, 0..=120).text(\"age\"));\n                if ui.button(\"Increment\").clicked() {\n                    self.age += 1;\n                }\n                ui.label(format!(\"Hello '{}', age {}\", self.name, self.age));\n            });\n    }\n}\n\nfn new_worker(\n    thread_nr: usize,\n    on_done_tx: mpsc::SyncSender<()>,\n) -> (JoinHandle<()>, mpsc::SyncSender<egui::Context>) {\n    let (show_tx, show_rc) = mpsc::sync_channel(0);\n    let handle = std::thread::Builder::new()\n        .name(format!(\"EguiPanelWorker {thread_nr}\"))\n        .spawn(move || {\n            let mut state = ThreadState::new(thread_nr);\n            while let Ok(ctx) = show_rc.recv() {\n                state.show(&ctx);\n                let _ = on_done_tx.send(());\n            }\n        })\n        .expect(\"failed to spawn thread\");\n    (handle, show_tx)\n}\n\nstruct MyApp {\n    threads: Vec<(JoinHandle<()>, mpsc::SyncSender<egui::Context>)>,\n    on_done_tx: mpsc::SyncSender<()>,\n    on_done_rc: mpsc::Receiver<()>,\n}\n\nimpl MyApp {\n    fn new() -> Self {\n        let threads = Vec::with_capacity(3);\n        let (on_done_tx, on_done_rc) = mpsc::sync_channel(0);\n\n        let mut slf = Self {\n            threads,\n            on_done_tx,\n            on_done_rc,\n        };\n\n        slf.spawn_thread();\n        slf.spawn_thread();\n\n        slf\n    }\n\n    fn spawn_thread(&mut self) {\n        let thread_nr = self.threads.len();\n        self.threads\n            .push(new_worker(thread_nr, self.on_done_tx.clone()));\n    }\n}\n\nimpl std::ops::Drop for MyApp {\n    fn drop(&mut self) {\n        for (handle, show_tx) in self.threads.drain(..) {\n            std::mem::drop(show_tx);\n            handle.join().unwrap();\n        }\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::Window::new(\"Main thread\").show(ui.ctx(), |ui| {\n            if ui.button(\"Spawn another thread\").clicked() {\n                self.spawn_thread();\n            }\n        });\n\n        for (_handle, show_tx) in &self.threads {\n            let _ = show_tx.send(ui.ctx().clone());\n        }\n\n        for _ in 0..self.threads.len() {\n            let _ = self.on_done_rc.recv();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/hello_world_simple/Cargo.toml",
    "content": "[package]\nname = \"hello_world_simple\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/hello_world_simple/README.md",
    "content": "Example showing some UI controls like `Label`, `TextEdit`, `Slider`, `Button`.\n\n```sh\ncargo run -p hello_world_simple\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/hello_world_simple/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n        ..Default::default()\n    };\n\n    // Our application state:\n    let mut name = \"Arthur\".to_owned();\n    let mut age = 42;\n\n    eframe::run_ui_native(\"My egui App\", options, move |ui, _frame| {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.heading(\"My egui Application\");\n            ui.horizontal(|ui| {\n                let name_label = ui.label(\"Your name: \");\n                ui.text_edit_singleline(&mut name)\n                    .labelled_by(name_label.id);\n            });\n            ui.add(egui::Slider::new(&mut age, 0..=120).text(\"age\"));\n            if ui.button(\"Increment\").clicked() {\n                age += 1;\n            }\n            ui.label(format!(\"Hello '{name}', age {age}\"));\n        });\n    })\n}\n"
  },
  {
    "path": "examples/images/Cargo.toml",
    "content": "[package]\nname = \"images\"\nversion = \"0.1.0\"\nauthors = [\"Jan Procházka <github.com/jprochazk>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[package.metadata.cargo-machete]\nignored = [\"image\"] # We only use the dependency to add more features to it\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\negui_extras = { workspace = true, features = [\"default\", \"all_loaders\"] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\nimage = { workspace = true, features = [\"jpeg\", \"png\"] }\n"
  },
  {
    "path": "examples/images/README.md",
    "content": "Example showing how to show images with eframe/egui.\n\n```sh\ncargo run -p images\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/images/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 880.0]),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"Image Viewer\",\n        options,\n        Box::new(|cc| {\n            // This gives us image support:\n            egui_extras::install_image_loaders(&cc.egui_ctx);\n            Ok(Box::<MyApp>::default())\n        }),\n    )\n}\n\n#[derive(Default)]\nstruct MyApp {}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            egui::ScrollArea::both().show(ui, |ui| {\n                ui.image(egui::include_image!(\"cat.webp\"))\n                    .on_hover_text_at_pointer(\"WebP\");\n                ui.image(egui::include_image!(\"ferris.gif\"))\n                    .on_hover_text_at_pointer(\"Gif\");\n                ui.image(egui::include_image!(\"ferris.svg\"))\n                    .on_hover_text_at_pointer(\"Svg\");\n\n                let url = \"https://picsum.photos/seed/1.759706314/1024\";\n                ui.add(egui::Image::new(url).corner_radius(10))\n                    .on_hover_text_at_pointer(url);\n            });\n        });\n    }\n}\n"
  },
  {
    "path": "examples/keyboard_events/Cargo.toml",
    "content": "[package]\nname = \"keyboard_events\"\nversion = \"0.1.0\"\nauthors = [\"Jose Palazon <jose@palako.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/keyboard_events/README.md",
    "content": "```sh\ncargo run -p keyboard_events\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/keyboard_events/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui;\nuse egui::{Key, ScrollArea};\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions::default();\n    eframe::run_native(\n        \"Keyboard events\",\n        options,\n        Box::new(|_cc| Ok(Box::<Content>::default())),\n    )\n}\n\n#[derive(Default)]\nstruct Content {\n    text: String,\n}\n\nimpl eframe::App for Content {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.heading(\"Press/Hold/Release example. Press A to test.\");\n            if ui.button(\"Clear\").clicked() {\n                self.text.clear();\n            }\n            ScrollArea::vertical()\n                .auto_shrink(false)\n                .stick_to_bottom(true)\n                .show(ui, |ui| {\n                    ui.label(&self.text);\n                });\n\n            if ui.input(|i| i.key_pressed(Key::A)) {\n                self.text.push_str(\"\\nPressed\");\n            }\n            if ui.input(|i| i.key_down(Key::A)) {\n                self.text.push_str(\"\\nHeld\");\n                ui.request_repaint(); // make sure we note the holding.\n            }\n            if ui.input(|i| i.key_released(Key::A)) {\n                self.text.push_str(\"\\nReleased\");\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "examples/multiple_viewports/Cargo.toml",
    "content": "[package]\nname = \"multiple_viewports\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n[features]\nwgpu = [\"eframe/wgpu\"]\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/multiple_viewports/README.md",
    "content": "Example how to show multiple viewports (native windows) can be created in `egui` when using the `eframe` backend.\n\n```sh\ncargo run -p multiple_viewports\n```\n\nFor a more advanced example, see [../../tests/test_viewports].\n"
  },
  {
    "path": "examples/multiple_viewports/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse std::sync::{\n    Arc,\n    atomic::{AtomicBool, Ordering},\n};\n\nuse eframe::egui;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"Multiple viewports\",\n        options,\n        Box::new(|_cc| Ok(Box::<MyApp>::default())),\n    )\n}\n\n#[derive(Default)]\nstruct MyApp {\n    /// Immediate viewports are show immediately, so passing state to/from them is easy.\n    /// The downside is that their painting is linked with the parent viewport:\n    /// if either needs repainting, they are both repainted.\n    show_immediate_viewport: bool,\n\n    /// Deferred viewports run independent of the parent viewport, which can save\n    /// CPU if only some of the viewports require repainting.\n    /// However, this requires passing state with `Arc` and locks.\n    show_deferred_viewport: Arc<AtomicBool>,\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.label(\"Hello from the root viewport\");\n\n            ui.checkbox(\n                &mut self.show_immediate_viewport,\n                \"Show immediate child viewport\",\n            );\n\n            {\n                let mut show_deferred_viewport =\n                    self.show_deferred_viewport.load(Ordering::Relaxed);\n                ui.checkbox(&mut show_deferred_viewport, \"Show deferred child viewport\");\n                self.show_deferred_viewport\n                    .store(show_deferred_viewport, Ordering::Relaxed);\n            }\n\n            ui.add_space(16.0);\n            {\n                let mut embedded = ui.embed_viewports();\n                ui.checkbox(&mut embedded, \"Embed all viewports\");\n                ui.set_embed_viewports(embedded);\n            }\n        });\n\n        if self.show_immediate_viewport {\n            ui.ctx().show_viewport_immediate(\n                egui::ViewportId::from_hash_of(\"immediate_viewport\"),\n                egui::ViewportBuilder::default()\n                    .with_title(\"Immediate Viewport\")\n                    .with_inner_size([200.0, 100.0]),\n                |ui, class| {\n                    if class == egui::ViewportClass::EmbeddedWindow {\n                        ui.label(\n                            \"This viewport is embedded in the parent window, and cannot be moved outside of it.\",\n                        );\n                    } else {\n                        egui::CentralPanel::default().show_inside(ui, |ui| {\n                            ui.label(\"Hello from immediate viewport\");\n\n                            if ui.input(|i| i.viewport().close_requested()) {\n                                // Tell parent viewport that we should not show next frame:\n                                self.show_immediate_viewport = false;\n                            }\n                        });\n                    }\n                },\n            );\n        }\n\n        if self.show_deferred_viewport.load(Ordering::Relaxed) {\n            let show_deferred_viewport = Arc::clone(&self.show_deferred_viewport);\n            ui.ctx().show_viewport_deferred(\n                egui::ViewportId::from_hash_of(\"deferred_viewport\"),\n                egui::ViewportBuilder::default()\n                    .with_title(\"Deferred Viewport\")\n                    .with_inner_size([200.0, 100.0]),\n                move |ui, class| {\n                    if class == egui::ViewportClass::EmbeddedWindow {\n                        ui.label(\n                            \"This viewport is embedded in the parent window, and cannot be moved outside of it.\",\n                        );\n                    } else {\n                        egui::CentralPanel::default().show_inside(ui, |ui| {\n                            ui.label(\"Hello from deferred viewport\");\n\n                            if ui.input(|i| i.viewport().close_requested()) {\n                                // Tell parent to close us.\n                                show_deferred_viewport.store(false, Ordering::Relaxed);\n                            }\n                        });\n                    }\n                },\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "examples/popups/Cargo.toml",
    "content": "[package]\nname = \"popups\"\nedition.workspace = true\nlicense.workspace = true\nrust-version.workspace = true\nversion.workspace = true\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "examples/popups/README.md",
    "content": "Example of how to use menus, popups, context menus and tooltips.\n\n```sh\ncargo run -p popups\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/popups/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui::{CentralPanel, ComboBox, Popup, PopupCloseBehavior};\n\nfn main() -> Result<(), eframe::Error> {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions::default();\n\n    eframe::run_native(\"Popups\", options, Box::new(|_| Ok(Box::<MyApp>::default())))\n}\n\n#[derive(Default)]\nstruct MyApp {\n    checkbox: bool,\n    number: u8,\n    numbers: [bool; 10],\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut eframe::egui::Ui, _frame: &mut eframe::Frame) {\n        CentralPanel::default().show_inside(ui, |ui| {\n            ui.label(\"PopupCloseBehavior::CloseOnClick popup\");\n            ComboBox::from_label(\"ComboBox\")\n                .selected_text(format!(\"{}\", self.number))\n                .show_ui(ui, |ui| {\n                    for num in 0..10 {\n                        ui.selectable_value(&mut self.number, num, format!(\"{num}\"));\n                    }\n                });\n\n            ui.label(\"PopupCloseBehavior::CloseOnClickOutside popup\");\n            ComboBox::from_label(\"Ignore Clicks\")\n                .close_behavior(PopupCloseBehavior::CloseOnClickOutside)\n                .selected_text(\"Select Numbers\")\n                .show_ui(ui, |ui| {\n                    ui.label(\"This popup will be open even if you click the checkboxes\");\n                    for (i, num) in self.numbers.iter_mut().enumerate() {\n                        ui.checkbox(num, format!(\"Checkbox {}\", i + 1));\n                    }\n                });\n\n            ui.label(\"PopupCloseBehavior::IgnoreClicks popup\");\n            let response = ui.button(\"Open\");\n\n            Popup::menu(&response)\n                .close_behavior(PopupCloseBehavior::IgnoreClicks)\n                .show(|ui| {\n                    ui.set_min_width(310.0);\n                    ui.label(\"This popup will be open until you press the button again\");\n                    ui.checkbox(&mut self.checkbox, \"Checkbox\");\n                });\n        });\n    }\n}\n"
  },
  {
    "path": "examples/puffin_profiler/Cargo.toml",
    "content": "[package]\nname = \"puffin_profiler\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[package.metadata.cargo-machete]\nignored = [\"profiling\"]\n\n[lints]\nworkspace = true\n\n\n[features]\nwgpu = [\"eframe/wgpu\"]\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\nlog.workspace = true\npuffin.workspace = true\npuffin_http.workspace = true\nprofiling = { workspace = true, features = [\"profile-with-puffin\"] }\n"
  },
  {
    "path": "examples/puffin_profiler/README.md",
    "content": "Example how to use the [puffin profiler](https://github.com/EmbarkStudios/puffin) with an `eframe` app.\n\n\n```sh\ncargo run -p puffin_profiler &\n\ncargo install puffin_viewer\npuffin_viewer --url 127.0.0.1:8585\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/puffin_profiler/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse std::sync::{\n    Arc,\n    atomic::{AtomicBool, Ordering},\n};\n\nuse eframe::egui;\n\nfn main() -> eframe::Result {\n    let rust_log = std::env::var(\"RUST_LOG\").unwrap_or_else(|_| \"info\".to_owned());\n\n    // SAFETY: we call this from the main thread without any other threads running.\n    #[expect(unsafe_code)]\n    unsafe {\n        std::env::set_var(\"RUST_LOG\", rust_log);\n    };\n\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    start_puffin_server(); // NOTE: you may only want to call this if the users specifies some flag or clicks a button!\n\n    eframe::run_native(\n        \"My egui App\",\n        eframe::NativeOptions {\n            viewport: egui::ViewportBuilder::default(),\n\n            #[cfg(feature = \"wgpu\")]\n            renderer: eframe::Renderer::Wgpu,\n\n            ..Default::default()\n        },\n        Box::new(|_cc| Ok(Box::<MyApp>::default())),\n    )\n}\n\nstruct MyApp {\n    keep_repainting: bool,\n\n    // It is useful to be able to inspect how eframe acts with multiple viewport\n    // so we have two viewports here that we can toggle on/off.\n    show_immediate_viewport: bool,\n    show_deferred_viewport: Arc<AtomicBool>,\n}\n\nimpl Default for MyApp {\n    fn default() -> Self {\n        Self {\n            keep_repainting: true,\n            show_immediate_viewport: Default::default(),\n            show_deferred_viewport: Default::default(),\n        }\n    }\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.heading(\"Example of how to use the puffin profiler with egui\");\n            ui.separator();\n\n            let cmd = \"cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585\";\n\n            ui.label(\"To connect, run this:\");\n            ui.horizontal(|ui| {\n                ui.monospace(cmd);\n                if ui.small_button(\"📋\").clicked() {\n                    ui.copy_text(cmd.into());\n                }\n            });\n\n            ui.separator();\n\n            ui.horizontal(|ui| {\n                ui.checkbox(&mut self.keep_repainting, \"Keep repainting\");\n                if self.keep_repainting {\n                    ui.spinner();\n                    ui.request_repaint();\n                } else {\n                    ui.label(\"Repainting on events (e.g. mouse movement)\");\n                }\n            });\n\n            if ui\n                .button(\n                    \"Click to sleep a bit. That should be visible as a spike in the profiler view!\",\n                )\n                .clicked()\n            {\n                puffin::profile_scope!(\"long_sleep\");\n                std::thread::sleep(std::time::Duration::from_millis(50));\n            }\n\n            ui.checkbox(\n                &mut self.show_immediate_viewport,\n                \"Show immediate child viewport\",\n            );\n\n            let mut show_deferred_viewport = self.show_deferred_viewport.load(Ordering::Relaxed);\n            ui.checkbox(&mut show_deferred_viewport, \"Show deferred child viewport\");\n            self.show_deferred_viewport\n                .store(show_deferred_viewport, Ordering::Relaxed);\n        });\n\n        if self.show_immediate_viewport {\n            ui.ctx().show_viewport_immediate(\n                egui::ViewportId::from_hash_of(\"immediate_viewport\"),\n                egui::ViewportBuilder::default()\n                    .with_title(\"Immediate Viewport\")\n                    .with_inner_size([200.0, 100.0]),\n                |ui, class| {\n                    puffin::profile_scope!(\"immediate_viewport\");\n\n                    assert!(\n                        class == egui::ViewportClass::Immediate,\n                        \"This egui backend doesn't support multiple viewports\"\n                    );\n\n                    egui::CentralPanel::default().show_inside(ui, |ui| {\n                        ui.label(\"Hello from immediate viewport\");\n                    });\n\n                    if ui.input(|i| i.viewport().close_requested()) {\n                        // Tell parent viewport that we should not show next frame:\n                        self.show_immediate_viewport = false;\n                    }\n                },\n            );\n        }\n\n        if self.show_deferred_viewport.load(Ordering::Relaxed) {\n            let show_deferred_viewport = Arc::clone(&self.show_deferred_viewport);\n            ui.ctx().show_viewport_deferred(\n                egui::ViewportId::from_hash_of(\"deferred_viewport\"),\n                egui::ViewportBuilder::default()\n                    .with_title(\"Deferred Viewport\")\n                    .with_inner_size([200.0, 100.0]),\n                move |ui, class| {\n                    puffin::profile_scope!(\"deferred_viewport\");\n\n                    assert!(\n                        class == egui::ViewportClass::Deferred,\n                        \"This egui backend doesn't support multiple viewports\"\n                    );\n\n                    egui::CentralPanel::default().show_inside(ui, |ui| {\n                        ui.label(\"Hello from deferred viewport\");\n                    });\n                    if ui.input(|i| i.viewport().close_requested()) {\n                        // Tell parent to close us.\n                        show_deferred_viewport.store(false, Ordering::Relaxed);\n                    }\n                },\n            );\n        }\n    }\n}\n\nfn start_puffin_server() {\n    puffin::set_scopes_on(true); // tell puffin to collect data\n\n    match puffin_http::Server::new(\"127.0.0.1:8585\") {\n        Ok(puffin_server) => {\n            log::info!(\"Run:  cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585\");\n\n            std::process::Command::new(\"puffin_viewer\")\n                .arg(\"--url\")\n                .arg(\"127.0.0.1:8585\")\n                .spawn()\n                .ok();\n\n            // We can store the server if we want, but in this case we just want\n            // it to keep running. Dropping it closes the server, so let's not drop it!\n            #[expect(clippy::mem_forget)]\n            std::mem::forget(puffin_server);\n        }\n        Err(err) => {\n            log::error!(\"Failed to start puffin server: {err}\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/run_all.sh",
    "content": "#!/usr/bin/env bash\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path/\"\nset -x\n\nfor example_name in *; do\n    if [ -d \"$example_name\" ]; then\n        cargo run --quiet -p $example_name\n    fi\ndone\n"
  },
  {
    "path": "examples/screenshot/Cargo.toml",
    "content": "[package]\nname = \"screenshot\"\nversion = \"0.1.0\"\nauthors = [\"René Rössler <rene@freshx.de>\", \"Andreas Faber <andreas.mfaber@gmail.com\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n  \"wgpu\",\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\nimage = { workspace = true, features = [\"png\"] }\n"
  },
  {
    "path": "examples/screenshot/README.md",
    "content": "Example how to take screenshots and display them with eframe/egui.\n\n```sh\ncargo run -p screenshot\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/screenshot/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs, clippy::unwrap_used)] // it's an example\n\nuse std::sync::Arc;\n\nuse eframe::egui::{self, ColorImage};\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        renderer: eframe::Renderer::Wgpu,\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"Take screenshots and display with eframe/egui\",\n        options,\n        Box::new(|_cc| Ok(Box::<MyApp>::default())),\n    )\n}\n\n#[derive(Default)]\nstruct MyApp {\n    continuously_take_screenshots: bool,\n    texture: Option<egui::TextureHandle>,\n    screenshot: Option<Arc<ColorImage>>,\n    save_to_file: bool,\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            if let Some(screenshot) = self.screenshot.take() {\n                self.texture = Some(ui.ctx().load_texture(\n                    \"screenshot\",\n                    screenshot,\n                    Default::default(),\n                ));\n            }\n\n            ui.horizontal(|ui| {\n                ui.checkbox(\n                    &mut self.continuously_take_screenshots,\n                    \"continuously take screenshots\",\n                );\n\n                if ui.button(\"save to 'top_left.png'\").clicked() {\n                    self.save_to_file = true;\n                    ui.send_viewport_cmd(egui::ViewportCommand::Screenshot(Default::default()));\n                }\n\n                ui.with_layout(egui::Layout::top_down(egui::Align::RIGHT), |ui| {\n                    if self.continuously_take_screenshots {\n                        if ui\n                            .add(egui::Label::new(\"hover me!\").sense(egui::Sense::hover()))\n                            .hovered()\n                        {\n                            ui.ctx().set_theme(egui::Theme::Dark);\n                        } else {\n                            ui.ctx().set_theme(egui::Theme::Light);\n                        }\n                        ui.send_viewport_cmd(egui::ViewportCommand::Screenshot(Default::default()));\n                    } else if ui.button(\"take screenshot!\").clicked() {\n                        ui.send_viewport_cmd(egui::ViewportCommand::Screenshot(Default::default()));\n                    }\n                });\n            });\n\n            if let Some(texture) = self.texture.as_ref() {\n                ui.image((texture.id(), ui.available_size()));\n            } else {\n                ui.spinner();\n            }\n\n            // Check for returned screenshot:\n            ui.input(|i| {\n                for event in &i.raw.events {\n                    if let egui::Event::Screenshot { image, .. } = event {\n                        if self.save_to_file {\n                            let pixels_per_point = i.pixels_per_point();\n                            let region = egui::Rect::from_two_pos(\n                                egui::Pos2::ZERO,\n                                egui::Pos2 { x: 100., y: 100. },\n                            );\n                            let top_left_corner = image.region(&region, Some(pixels_per_point));\n                            image::save_buffer(\n                                \"top_left.png\",\n                                top_left_corner.as_raw(),\n                                top_left_corner.width() as u32,\n                                top_left_corner.height() as u32,\n                                image::ColorType::Rgba8,\n                            )\n                            .unwrap();\n                            self.save_to_file = false;\n                        }\n                        self.screenshot = Some(Arc::clone(image));\n                    }\n                }\n            });\n\n            ui.request_repaint();\n        });\n    }\n}\n"
  },
  {
    "path": "examples/serial_windows/Cargo.toml",
    "content": "[package]\nname = \"serial_windows\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\nlog.workspace = true\n"
  },
  {
    "path": "examples/serial_windows/README.md",
    "content": "Demonstrates how to open several windows after each other.\n\nExpected order of execution:\n\n- When the example runs a first window will be shown.\n- Once the first window is closed after a delay a second window will be shown.\n- Similarly, when the second window is closed after a delay a third will be shown.\n- Once the third is closed the program will stop.\n\nNOTE: this doesn't work on Mac. See also <https://github.com/emilk/egui/issues/1918>.\n\n```sh\ncargo run -p serial_windows\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/serial_windows/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::egui;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n\n    let options = eframe::NativeOptions {\n        run_and_return: true,\n        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n        ..Default::default()\n    };\n\n    log::info!(\"Starting first window…\");\n    eframe::run_native(\n        \"First Window\",\n        options.clone(),\n        Box::new(|_cc| Ok(Box::new(MyApp { has_next: true }))),\n    )?;\n\n    std::thread::sleep(std::time::Duration::from_secs(2));\n\n    log::info!(\"Starting second window…\");\n    eframe::run_native(\n        \"Second Window\",\n        options.clone(),\n        Box::new(|_cc| Ok(Box::new(MyApp { has_next: true }))),\n    )?;\n\n    std::thread::sleep(std::time::Duration::from_secs(2));\n\n    log::info!(\"Starting third window…\");\n    eframe::run_native(\n        \"Third Window\",\n        options,\n        Box::new(|_cc| Ok(Box::new(MyApp { has_next: false }))),\n    )\n}\n\nstruct MyApp {\n    pub(crate) has_next: bool,\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            let label_text = if self.has_next {\n                \"When this window is closed the next will be opened after a short delay\"\n            } else {\n                \"This is the last window. Program will end when closed\"\n            };\n            ui.label(label_text);\n\n            if ui.button(\"Close\").clicked() {\n                log::info!(\"Pressed Close button\");\n                ui.send_viewport_cmd(egui::ViewportCommand::Close);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "examples/user_attention/Cargo.toml",
    "content": "[package]\nname = \"user_attention\"\nversion = \"0.1.0\"\nauthors = [\"TicClick <ya@ticclick.ch>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "examples/user_attention/README.md",
    "content": "An example of requesting a user's attention to the main window, and resetting the ongoing attention animations when necessary. Only works on native platforms.\n\n```sh\ncargo run -p user_attention\n```\n\n![](screenshot.png)\n"
  },
  {
    "path": "examples/user_attention/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse eframe::{CreationContext, NativeOptions, egui};\nuse egui::{Button, CentralPanel, UserAttentionType};\n\nuse std::time::{Duration, SystemTime};\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let native_options = NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([400., 200.]),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"User attention test\",\n        native_options,\n        Box::new(|cc| Ok(Box::new(Application::new(cc)))),\n    )\n}\n\nfn repr(attention: UserAttentionType) -> String {\n    format!(\"{attention:?}\")\n}\n\nstruct Application {\n    attention: UserAttentionType,\n    request_at: Option<SystemTime>,\n\n    auto_reset: bool,\n    reset_at: Option<SystemTime>,\n}\n\nimpl Application {\n    fn new(_cc: &CreationContext<'_>) -> Self {\n        Self {\n            attention: UserAttentionType::Informational,\n            request_at: None,\n            auto_reset: false,\n            reset_at: None,\n        }\n    }\n\n    fn attention_reset_timeout() -> Duration {\n        Duration::from_secs(3)\n    }\n\n    fn attention_request_timeout() -> Duration {\n        Duration::from_secs(2)\n    }\n\n    fn repaint_max_timeout() -> Duration {\n        Duration::from_secs(1)\n    }\n}\n\nimpl eframe::App for Application {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        if let Some(request_at) = self.request_at\n            && request_at < SystemTime::now()\n        {\n            self.request_at = None;\n            ui.send_viewport_cmd(egui::ViewportCommand::RequestUserAttention(self.attention));\n            if self.auto_reset {\n                self.auto_reset = false;\n                self.reset_at = Some(SystemTime::now() + Self::attention_reset_timeout());\n            }\n        }\n\n        if let Some(reset_at) = self.reset_at\n            && reset_at < SystemTime::now()\n        {\n            self.reset_at = None;\n            ui.send_viewport_cmd(egui::ViewportCommand::RequestUserAttention(\n                UserAttentionType::Reset,\n            ));\n        }\n\n        CentralPanel::default().show_inside(ui, |ui| {\n            ui.vertical(|ui| {\n                ui.horizontal(|ui| {\n                    ui.label(\"Attention type:\");\n                    egui::ComboBox::new(\"attention\", \"\")\n                        .selected_text(repr(self.attention))\n                        .show_ui(ui, |ui| {\n                            for kind in [\n                                UserAttentionType::Informational,\n                                UserAttentionType::Critical,\n                            ] {\n                                ui.selectable_value(&mut self.attention, kind, repr(kind));\n                            }\n                        })\n                });\n\n                let button_enabled = self.request_at.is_none() && self.reset_at.is_none();\n                let button_text = if button_enabled {\n                    format!(\n                        \"Request in {} seconds\",\n                        Self::attention_request_timeout().as_secs()\n                    )\n                } else {\n                    match self.reset_at {\n                        None => \"Unfocus the window, fast!\".to_owned(),\n                        Some(t) => {\n                            if let Ok(elapsed) = t.duration_since(SystemTime::now()) {\n                                format!(\"Resetting attention in {} s…\", elapsed.as_secs())\n                            } else {\n                                \"Resetting attention…\".to_owned()\n                            }\n                        }\n                    }\n                };\n\n                let resp = ui\n                    .add_enabled(button_enabled, Button::new(button_text))\n                    .on_hover_text_at_pointer(\n                        \"After clicking, unfocus the application's window to see the effect\",\n                    );\n\n                ui.checkbox(\n                    &mut self.auto_reset,\n                    format!(\n                        \"Reset after {} seconds\",\n                        Self::attention_reset_timeout().as_secs()\n                    ),\n                );\n\n                if resp.clicked() {\n                    self.request_at = Some(SystemTime::now() + Self::attention_request_timeout());\n                }\n            });\n        });\n\n        ui.request_repaint_after(Self::repaint_max_timeout());\n    }\n}\n"
  },
  {
    "path": "kittest.toml",
    "content": "output_path = \"tests/snapshots\"\n\n# Other OSes get a higher threshold so they can still run tests locally without failures due to small rendering\n# differences.\n# To update snapshots, update them via ./scripts/update_snapshots_from_ci.sh or via kitdiff\nthreshold = 2.0\n\n[mac]\n# Since our CI runs snapshot tests on macOS, this is our source of truth.\nthreshold = 0.6\n"
  },
  {
    "path": "lychee.toml",
    "content": "################################################################################\n# Config for the link checker lychee.\n#\n# Download & learn more at:\n# https://github.com/lycheeverse/lychee\n#\n# Example config:\n# https://github.com/lycheeverse/lychee/blob/master/lychee.example.toml\n#\n# Run `lychee . --dump` to list all found links that are being checked.\n#\n# Note that by default lychee will only check markdown and html files,\n# to check any other files you have to point to them explicitly, e.g.:\n# `lychee **/*.rs`.\n#\n# This unfortunately doesn't list anything for non-glob checks.\n################################################################################\n\n# Maximum number of concurrent link checks.\n# Workaround for \"too many open files\" error on MacOS, see https://github.com/lycheeverse/lychee/issues/1248\nmax_concurrency = 32\n\n# Check links inside `<code>` and `<pre>` blocks as well as Markdown code blocks.\ninclude_verbatim = true\n\n# Proceed for server connections considered insecure (invalid TLS).\ninsecure = true\n\n# Maximum number of allowed retries before a link is declared dead.\nmax_retries = 4\n\n# Wait time between attempts in seconds.\nretry_wait_time = 2\n\n# Comma-separated list of accepted status codes for valid links.\naccept = [\n  \"100..=103\", # Informational codes.\n  \"200..=299\", # Success codes.\n  \"429\", # Too many requests. This is practically never a sign of a broken link.\n]\n\n# Exclude URLs and mail addresses from checking (supports regex).\nexclude = [\n  \"https://creativecommons.org/.*\", # They don't like bots\n  \"https://www.unicode.org/.*\",\n]\n"
  },
  {
    "path": "rust-toolchain",
    "content": "# If you see this, run \"rustup self update\" to get rustup 1.23 or newer.\n\n# NOTE: above comment is for older `rustup` (before TOML support was added),\n# which will treat the first line as the toolchain name, and therefore show it\n# to the user in the error, instead of \"error: invalid channel name '[toolchain]'\".\n\n[toolchain]\nchannel = \"1.92.0\"\ncomponents = [\"rustfmt\", \"clippy\"]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "scripts/accept_snapshots.sh",
    "content": "#!/bin/sh\n# This script moves all {name}.new.png files to {name}.png.\n# Its main use is in the update_kittest_snapshots CI job, but you can also use it locally.\n\nset -eu\n\n# rename the .new.png files to .png\nfind . -type d -path \"*/tests/snapshots*\" | while read dir; do\n    find \"$dir\" -type f -name \"*.new.png\" | while read file; do\n        mv -f \"$file\" \"${file%.new.png}.png\"\n    done\ndone\n\necho \"Done!\"\n"
  },
  {
    "path": "scripts/build_demo_web.sh",
    "content": "#!/usr/bin/env bash\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path/..\"\n\n./scripts/setup_web.sh\n\nCRATE_NAME=\"egui_demo_app\"\n\nFEATURES=\"web_app\"\n\nOPEN=false\nOPTIMIZE=false\nBUILD=debug\nBUILD_FLAGS=\"\"\nGLOW=false\nWASM_OPT_FLAGS=\"-O2 --fast-math\"\n\nwhile test $# -gt 0; do\n  case \"$1\" in\n    -h|--help)\n      echo \"build_demo_web.sh [--release] [--glow] [--open]\"\n      echo \"\"\n      echo \"  -g:        Keep debug symbols even with --release.\"\n      echo \"             These are useful profiling and size trimming.\"\n      echo \"\"\n      echo \"  --open:    Open the result in a browser.\"\n      echo \"\"\n      echo \"  --release: Build with --release, and then run wasm-opt.\"\n      echo \"             NOTE: --release also removes debug symbols, unless you also use -g.\"\n      echo \"\"\n      echo \"  --glow:    Build a binary using glow instead of wgpu.\"\n      exit 0\n      ;;\n\n    -g)\n      shift\n      WASM_OPT_FLAGS=\"${WASM_OPT_FLAGS} -g\"\n      ;;\n\n    --open)\n      shift\n      OPEN=true\n      ;;\n\n    --release)\n      shift\n      OPTIMIZE=true\n      BUILD=\"release\"\n      BUILD_FLAGS=\"--release\"\n      ;;\n\n    --glow)\n      shift\n      GLOW=true\n      ;;\n\n    *)\n      echo \"Unknown option: $1\"\n      exit 1\n      ;;\n  esac\ndone\n\nOUT_FILE_NAME=\"egui_demo_app\"\n\nif [[ \"${GLOW}\" == true ]]; then\n  FEATURES=\"${FEATURES},glow\"\nelse\n  FEATURES=\"${FEATURES},wgpu\"\nfi\n\nFINAL_WASM_PATH=web_demo/${OUT_FILE_NAME}_bg.wasm\n\n# Clear output from old stuff:\nrm -f \"${FINAL_WASM_PATH}\"\n\necho \"Building rust…\"\n\n(cd crates/$CRATE_NAME &&\n  cargo build \\\n    ${BUILD_FLAGS} \\\n    --quiet \\\n    --lib \\\n    --target wasm32-unknown-unknown \\\n    --no-default-features \\\n    --features ${FEATURES}\n)\n\n# Get the output directory (in the workspace it is in another location)\n# TARGET=`cargo metadata --format-version=1 | jq --raw-output .target_directory`\nTARGET=\"target\"\n\necho \"Generating JS bindings for wasm…\"\nTARGET_NAME=\"${CRATE_NAME}.wasm\"\nWASM_PATH=\"${TARGET}/wasm32-unknown-unknown/$BUILD/$TARGET_NAME\"\nwasm-bindgen \"${WASM_PATH}\" --out-dir web_demo --out-name ${OUT_FILE_NAME} --no-modules --no-typescript\n\n# if this fails with \"error: cannot import from modules (`env`) with `--no-modules`\", you can use:\n# wasm2wat target/wasm32-unknown-unknown/release/egui_demo_app.wasm | rg env\n# wasm2wat target/wasm32-unknown-unknown/release/egui_demo_app.wasm | rg \"call .now\\b\" -B 20 # What calls `$now` (often a culprit)\n# Or use https://rustwasm.github.io/twiggy/usage/command-line-interface/paths.html#twiggy-paths\n\n# to get wasm-strip:  apt/brew/dnf install wabt\n# wasm-strip ${FINAL_WASM_PATH}\n\nif [[ \"${OPTIMIZE}\" = true ]]; then\n  echo \"Optimizing wasm…\"\n  # to get wasm-opt:  apt/brew/dnf install binaryen\n  wasm-opt \"${FINAL_WASM_PATH}\" $WASM_OPT_FLAGS -o \"${FINAL_WASM_PATH}\"\nfi\n\necho \"Finished ${FINAL_WASM_PATH}\"\n\nif [[ \"${OPEN}\" == true ]]; then\n  if [[ \"$OSTYPE\" == \"linux-gnu\"* ]]; then\n    # Linux, ex: Fedora\n    xdg-open http://localhost:8765/index.html\n  elif [[ \"$OSTYPE\" == \"msys\" ]]; then\n    # Windows\n    start http://localhost:8765/index.html\n  else\n    # Darwin/MacOS, or something else\n    open http://localhost:8765/index.html\n  fi\nfi\n"
  },
  {
    "path": "scripts/cargo_deny.sh",
    "content": "#!/usr/bin/env bash\n\ncargo xtask deny\n"
  },
  {
    "path": "scripts/check.sh",
    "content": "#!/usr/bin/env bash\n# This scripts runs various CI-like checks in a convenient way.\n\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path/..\"\nset -x\n\n# Checks all tests, lints etc.\n# Basically does what the CI does.\n\n# cargo +1.92.0 install --quiet typos-cli\n\nexport RUSTFLAGS=\"-D warnings\"\nexport RUSTDOCFLAGS=\"-D warnings\" # https://github.com/emilk/egui/pull/1454\n\n# Fast checks first:\ntypos\n./scripts/lint.py\ncargo fmt --all -- --check\ncargo doc --quiet --lib --no-deps --all-features\ncargo doc --quiet --document-private-items --no-deps --all-features\ncargo clippy --quiet --all-targets --all-features -- -D warnings\ncargo clippy --quiet --all-targets --all-features --release -- -D warnings # we need to check release mode too\n\n./scripts/clippy_wasm.sh\n\ncargo check --quiet  --all-targets\ncargo check --quiet  --all-targets --all-features\ncargo check --quiet  -p egui_demo_app --lib --target wasm32-unknown-unknown\ncargo check --quiet  -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features\n# TODO(#5297) re-enable --all-features once the tests work with the unity feature\ncargo test  --quiet --all-targets --all-features\ncargo test  --quiet --doc # slow - checks all doc-tests\n\nif [[ \"$OSTYPE\" == \"linux-gnu\"* ]]; then\n    cargo check --quiet -p eframe --no-default-features --features \"glow\",\"x11\"\n    cargo check --quiet -p eframe --no-default-features --features \"glow\",\"wayland\"\n    cargo check --quiet -p eframe --no-default-features --features \"wgpu\",\"x11\"\n    cargo check --quiet -p eframe --no-default-features --features \"wgpu\",\"wayland\"\nelse\n    cargo check --quiet -p eframe --no-default-features --features \"glow\"\n    cargo check --quiet -p eframe --no-default-features --features \"wgpu\"\nfi\n\ncargo check --quiet -p egui --no-default-features --features \"serde\"\n\nif [[ \"$OSTYPE\" == \"linux-gnu\"* ]]; then\n    cargo check --quiet -p egui_demo_app --no-default-features --features \"glow\",\"x11\"\n    cargo check --quiet -p egui_demo_app --no-default-features --features \"glow\",\"wayland\"\n    cargo check --quiet -p egui_demo_app --no-default-features --features \"wgpu\",\"x11\"\n    cargo check --quiet -p egui_demo_app --no-default-features --features \"wgpu\",\"wayland\"\nelse\n    cargo check --quiet -p egui_demo_app --no-default-features --features \"glow\"\n    cargo check --quiet -p egui_demo_app --no-default-features --features \"wgpu\"\nfi\n\ncargo check --quiet -p egui_demo_lib --no-default-features\ncargo check --quiet -p egui_extras --no-default-features\ncargo check --quiet -p egui_glow --no-default-features\ncargo check --quiet -p egui-winit --no-default-features --features \"wayland\"\ncargo check --quiet -p egui-winit --no-default-features --features \"x11\"\ncargo check --quiet -p emath --no-default-features\ncargo check --quiet -p epaint --no-default-features --release\ncargo check --quiet -p epaint --no-default-features\n\ncargo check --quiet -p eframe --all-features\ncargo check --quiet -p egui --all-features\ncargo check --quiet -p egui_demo_app --all-features\ncargo check --quiet -p egui_extras --all-features\ncargo check --quiet -p egui_glow --all-features\ncargo check --quiet -p egui-winit --all-features\ncargo check --quiet -p emath --all-features\ncargo check --quiet -p epaint --all-features\n\n./scripts/wasm_bindgen_check.sh\n\n./scripts/cargo_deny.sh\n\n# TODO(emilk): consider using https://github.com/taiki-e/cargo-hack or https://github.com/frewsxcv/cargo-all-features\n\n# ------------------------------------------------------------\n#\n\n# For finding bloat:\n# cargo bloat --release --bin egui_demo_app -n 200 | rg egui\n# Also try https://github.com/google/bloaty\n\n# what compiles slowly?\n# cargo clean && time cargo build -p eframe --timings\n# https://fasterthanli.me/articles/why-is-my-rust-build-so-slow\n\n# what compiles slowly?\n# cargo llvm-lines --lib -p egui | head -20\n\necho \"All checks passed.\"\n"
  },
  {
    "path": "scripts/clippy_wasm/clippy.toml",
    "content": "# This is used by `scripts/clippy_wasm.sh` so we can forbid some methods that are not available in wasm.\n#\n# We cannot forbid all these methods in the main `clippy.toml` because of\n# https://github.com/rust-lang/rust-clippy/issues/10406\n\n# -----------------------------------------------------------------------------\n# Section identical to the root clippy.toml:\n\nmsrv = \"1.92\"\n\nallow-unwrap-in-tests = true\n\n# https://doc.rust-lang.org/nightly/clippy/lint_configuration.html#avoid-breaking-exported-api\n# We want suggestions, even if it changes public API.\navoid-breaking-exported-api = false\n\nmax-fn-params-bools = 2 # TODO(emilk): decrease this to 1\n\n# https://rust-lang.github.io/rust-clippy/master/index.html#/large_include_file\nmax-include-file-size = 1000000\n\n# https://rust-lang.github.io/rust-clippy/master/index.html#/type_complexity\ntype-complexity-threshold = 350\n\n# -----------------------------------------------------------------------------\n\n# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods\ndisallowed-methods = [\n  { path = \"std::time::Instant::elapsed\", reason = \"use `instant` crate instead for wasm/web compatibility\" },\n  { path = \"std::time::Instant::now\", reason = \"use `instant` crate instead for wasm/web compatibility\" },\n  { path = \"std::time::SystemTime::now\", reason = \"use `instant` or `time` crates instead for wasm/web compatibility\" },\n\n  { path = \"std::thread::spawn\", reason = \"Cannot spawn threads on wasm\" },\n]\n\n# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types\ndisallowed-types = [\n  { path = \"instant::SystemTime\", reason = \"Known bugs. Use web-time.\" },\n  { path = \"std::thread::Builder\", reason = \"Cannot spawn threads on wasm\" },\n  { path = \"std::time::Instant\", reason = \"Use web-time instead.\" },\n  # { path = \"std::path::PathBuf\", reason = \"Can't read/write files on web\" }, // TODO(emilk): consider banning Path on wasm\n]\n\n# Allow-list of words for markdown in docstrings https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown\ndoc-valid-idents = [\n  # You must also update the same list in the root `clippy.toml`!\n  \"AccessKit\",\n  \"WebGL\",\n  \"WebGL1\",\n  \"WebGL2\",\n  \"WebGPU\",\n  \"VirtualBox\",\n  \"..\",\n]\n"
  },
  {
    "path": "scripts/clippy_wasm.sh",
    "content": "#!/usr/bin/env bash\n# This scripts run clippy on the wasm32-unknown-unknown target,\n# using a special clippy.toml config file which forbids a few more things.\n\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path/..\"\nset -x\n\n# Use scripts/clippy_wasm/clippy.toml\nexport CLIPPY_CONF_DIR=\"scripts/clippy_wasm\"\n\ncargo clippy --quiet --all-features --target wasm32-unknown-unknown --target-dir target_wasm -p egui_demo_app --lib -- --deny warnings\n"
  },
  {
    "path": "scripts/docs.sh",
    "content": "#!/usr/bin/env bash\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path/..\"\n\ncargo doc -p eframe --target wasm32-unknown-unknown --lib --no-deps\ncargo doc -p emath -p epaint -p egui -p eframe -p egui-winit -p egui_extras -p egui_glow -p egui_kittest --lib --no-deps --all-features --open\n\n# cargo watch -c -x 'doc -p emath -p epaint -p egui --lib --no-deps --all-features'\n"
  },
  {
    "path": "scripts/find_bloat.sh",
    "content": "#!/usr/bin/env bash\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path/..\"\n\ncargo bloat --release --bin egui_demo_app -n 200 | rg \"egui \"\n\ncargo llvm-lines -p egui_demo_lib | rg egui | head -30\n"
  },
  {
    "path": "scripts/generate_changelog.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"\nSummarizes recent PRs based on their GitHub labels.\n\nThe result can be copy-pasted into CHANGELOG.md,\nthough it often needs some manual editing too.\n\nSetup:  pip install GitPython requests tqdm\n\"\"\"\n\nimport argparse\nimport multiprocessing\nimport os\nimport re\nimport sys\n\nfrom collections import defaultdict\nfrom datetime import date\nfrom dataclasses import dataclass\nfrom typing import Any, List, Optional\n\nimport requests\nfrom git import Repo  # pip install GitPython\nfrom tqdm import tqdm\n\nOWNER = \"emilk\"\nREPO = \"egui\"\nINCLUDE_LABELS = False  # It adds quite a bit of visual noise\n\n\n@dataclass\nclass PrInfo:\n    pr_number: int\n    gh_user_name: str\n    title: str\n    labels: List[str]\n\n\n@dataclass\nclass CommitInfo:\n    hexsha: str\n    title: str\n    pr_number: Optional[int]\n\n\ndef get_github_token() -> str:\n    import os\n\n    token = os.environ.get(\"GH_ACCESS_TOKEN\", \"\")\n    if token != \"\":\n        return token\n\n    home_dir = os.path.expanduser(\"~\")\n    token_file = os.path.join(home_dir, \".githubtoken\")\n\n    try:\n        with open(token_file, \"r\") as f:\n            token = f.read().strip()\n        return token\n    except Exception:\n        pass\n\n    print(\n        \"ERROR: expected a GitHub token in the environment variable GH_ACCESS_TOKEN or in ~/.githubtoken\"\n    )\n    sys.exit(1)\n\n\n# Slow\ndef fetch_pr_info_from_commit_info(commit_info: CommitInfo) -> Optional[PrInfo]:\n    if commit_info.pr_number is None:\n        return None\n    else:\n        return fetch_pr_info(commit_info.pr_number)\n\n\n# Slow\ndef fetch_pr_info(pr_number: int) -> Optional[PrInfo]:\n    url = f\"https://api.github.com/repos/{OWNER}/{REPO}/pulls/{pr_number}\"\n    gh_access_token = get_github_token()\n    headers = {\"Authorization\": f\"Token {gh_access_token}\"}\n    response = requests.get(url, headers=headers)\n    json = response.json()\n\n    # Check if the request was successful (status code 200)\n    if response.status_code == 200:\n        labels = [label[\"name\"] for label in json[\"labels\"]]\n        gh_user_name = json[\"user\"][\"login\"]\n        return PrInfo(\n            pr_number=pr_number,\n            gh_user_name=gh_user_name,\n            title=json[\"title\"],\n            labels=labels,\n        )\n    else:\n        print(f\"ERROR {url}: {response.status_code} - {json['message']}\")\n        return None\n\n\ndef get_commit_info(commit: Any) -> CommitInfo:\n    match = re.match(r\"(.*) \\(#(\\d+)\\)\", commit.summary)\n    if match:\n        title = str(match.group(1))\n        pr_number = int(match.group(2))\n        return CommitInfo(hexsha=commit.hexsha, title=title, pr_number=pr_number)\n    else:\n        return CommitInfo(hexsha=commit.hexsha, title=commit.summary, pr_number=None)\n\n\ndef pr_summary(pr: PrInfo, crate_name: Optional[str] = None) -> str:\n    title = pr.title\n\n    if crate_name is not None:\n        # Remove crate name prefix (common in PR titles):\n        title = remove_prefix(title, f\"[{crate_name}] \")\n        title = remove_prefix(title, f\"{crate_name}: \")\n        title = remove_prefix(title, f\"`{crate_name}`: \")\n\n    # Upper-case first letter:\n    title = title[0].upper() + title[1:]\n\n    # Remove trailing periods:\n    title = title.rstrip(\".\")\n\n    summary = f\"{title} [#{pr.pr_number}](https://github.com/{OWNER}/{REPO}/pull/{pr.pr_number})\"\n\n    if INCLUDE_LABELS and 0 < len(pr.labels):\n        summary += f\" ({', '.join(pr.labels)})\"\n\n    summary += f\" by [@{pr.gh_user_name}](https://github.com/{pr.gh_user_name})\"\n\n    return summary\n\n\ndef pr_info_section(\n    prs: List[PrInfo], *, crate_name: str, heading: Optional[str] = None\n) -> str:\n    result = \"\"\n    if 0 < len(prs):\n        if heading is not None:\n            result += f\"### {heading}\\n\"\n        for pr in prs:\n            result += f\"* {pr_summary(pr, crate_name)}\\n\"\n        result += \"\\n\"\n    return result\n\n\ndef changelog_from_prs(pr_infos: List[PrInfo], crate_name: str) -> str:\n    if len(pr_infos) == 0:\n        return \"Nothing new\"\n\n    if len(pr_infos) <= 5:\n        # For small crates, or small releases\n        return pr_info_section(pr_infos, crate_name=crate_name)\n\n    fixed = []\n    added = []\n    performance = []\n    removed = []\n    rest = []\n    for pr in pr_infos:\n        summary = pr_summary(pr, crate_name)\n        if summary.startswith(\"Fix\") or \"bug\" in pr.labels:\n            fixed.append(pr)\n        elif summary.startswith(\"Add\") or \"feature\" in pr.labels:\n            added.append(pr)\n        elif \"performance\" in pr.labels:\n            performance.append(pr)\n        elif summary.startswith(\"Remove\"):\n            removed.append(pr)\n        else:\n            rest.append(pr)\n\n    result = \"\"\n\n    result += pr_info_section(added, crate_name=crate_name, heading=\"⭐ Added\")\n    result += pr_info_section(rest, crate_name=crate_name, heading=\"🔧 Changed\")\n    result += pr_info_section(removed, crate_name=crate_name, heading=\"🔥 Removed\")\n    result += pr_info_section(fixed, crate_name=crate_name, heading=\"🐛 Fixed\")\n    result += pr_info_section(performance, crate_name=crate_name, heading=\"🚀 Performance\")\n\n    return result.rstrip()\n\n\ndef remove_prefix(text, prefix):\n    if text.startswith(prefix):\n        return text[len(prefix) :]\n    return text  # or whatever\n\n\ndef print_section(heading: str, content: str) -> None:\n    if content != \"\":\n        print(f\"## {heading}\")\n        print(content.strip())\n        print()\n        print()\n        print()\n\n\ndef changelog_filepath(crate: str) -> str:\n    scripts_dirpath = os.path.dirname(os.path.realpath(__file__))\n    if crate == \"egui\":\n        file_path = f\"{scripts_dirpath}/../CHANGELOG.md\"\n    else:\n        file_path = f\"{scripts_dirpath}/../crates/{crate}/CHANGELOG.md\"\n    return os.path.normpath(file_path)\n\n\ndef add_to_changelog_file(crate: str, content: str, version: str) -> None:\n    insert_text = f\"\\n## {version} - {date.today()}\\n\"\n    insert_text += content.strip()\n    insert_text += \"\\n\\n\"\n\n    file_path = changelog_filepath(crate)\n\n    with open(file_path, \"r\") as file:\n        content = file.read()\n\n    position = content.find(\"\\n##\")\n    assert position != -1\n\n    content = content[:position] + insert_text + content[position:]\n\n    with open(file_path, \"w\") as file:\n        file.write(content)\n\n\ndef calc_commit_range(new_version: str) -> str:\n    parts = new_version.split(\".\")\n    assert len(parts) == 3, \"Expected version to be on the format X.Y.Z\"\n    major = int(parts[0])\n    minor = int(parts[1])\n    patch = int(parts[2])\n\n    if 0 < patch:\n        # A patch release.\n        # Include changes since last patch release.\n        # This assumes we've cherry-picked stuff for this release.\n        diff_since_version = f\"0.{minor}.{patch - 1}\"\n    elif 0 < minor:\n        # A minor release\n        # The diff should span everything since the last minor release.\n        # The script later excludes duplicated automatically, so we don't include stuff that\n        # was part of intervening patch releases.\n        diff_since_version = f\"{major}.{minor - 1}.0\"\n    else:\n        # A major release\n        # The diff should span everything since the last major release.\n        # The script later excludes duplicated automatically, so we don't include stuff that\n        # was part of intervening minor/patch releases.\n        diff_since_version = f\"{major - 1}.{minor}.0\"\n\n    return f\"{diff_since_version}..HEAD\"\n\n\ndef main() -> None:\n    parser = argparse.ArgumentParser(description=\"Generate a changelog.\")\n    parser.add_argument(\"--version\", help=\"What release is this?\", required=True)\n    parser.add_argument(\n        \"--write\", help=\"Write into the different changelogs?\", action=\"store_true\"\n    )\n    args = parser.parse_args()\n    commit_range = calc_commit_range(args.version)\n\n    crate_names = [\n        \"ecolor\",\n        \"eframe\",\n        \"egui_extras\",\n        \"egui_glow\",\n        \"egui_kittest\",\n        \"egui-wgpu\",\n        \"egui-winit\",\n        \"egui\",\n        \"emath\",\n        \"epaint\",\n        \"epaint_default_fonts\",\n    ]\n\n    # We read all existing changelogs to remove duplicate entries.\n    # For instance: the PRs that were part of 0.27.2 would also show up in the diff for `0.27.0..HEAD`\n    # when its time for a 0.28 release. We can't do `0.27.2..HEAD` because we would miss PRs that were\n    # merged before in `0.27.0..0.27.2` that were not cherry-picked into `0.27.2`.\n    all_changelogs = \"\"\n    for crate in crate_names:\n        file_path = changelog_filepath(crate)\n        with open(file_path, \"r\") as file:\n            all_changelogs += file.read()\n\n    repo = Repo(\".\")\n    commits = list(repo.iter_commits(commit_range))\n    commits.reverse()  # Most recent last\n    commit_infos = list(map(get_commit_info, commits))\n\n    pool = multiprocessing.Pool()\n    pr_infos = list(\n        tqdm(\n            pool.imap(fetch_pr_info_from_commit_info, commit_infos),\n            total=len(commit_infos),\n            desc=\"Fetch PR info commits\",\n        )\n    )\n\n    ignore_labels = [\"CI\", \"dependencies\"]\n\n    crate_sections = defaultdict(list)\n    unsorted_prs = []\n    unsorted_commits = []\n\n    for commit_info, pr_info in zip(commit_infos, pr_infos):\n        hexsha = commit_info.hexsha\n        title = commit_info.title\n        title = title.rstrip(\".\").strip()  # Some PR end with an unnecessary period\n        pr_number = commit_info.pr_number\n\n        if pr_number is None:\n            # Someone committed straight to main:\n            summary = f\"{title} [{hexsha[:7]}](https://github.com/{OWNER}/{REPO}/commit/{hexsha})\"\n            unsorted_commits.append(summary)\n        else:\n            if f\"[#{pr_number}]\" in all_changelogs:\n                print(\n                    f\"* Ignoring PR that is already in the changelog: [#{pr_number}](https://github.com/{OWNER}/{REPO}/pull/{pr_number})\"\n                )\n                continue\n\n            assert pr_info is not None\n\n            if \"exclude from changelog\" in pr_info.labels:\n                continue\n            if \"typo\" in pr_info.labels:\n                # We get so many typo PRs. Let's not flood the changelog with them.\n                continue\n\n            added = False\n\n            for crate in crate_names:\n                if crate in pr_info.labels:\n                    crate_sections[crate].append(pr_info)\n                    added = True\n\n            if not added:\n                if not any(label in pr_info.labels for label in ignore_labels):\n                    unsorted_prs.append(pr_summary(pr_info))\n\n    print()\n    print(f\"Full diff at https://github.com/emilk/egui/compare/{commit_range}\")\n    print()\n    for crate in crate_names:\n        if crate in crate_sections:\n            print_section(crate, changelog_from_prs(crate_sections[crate], crate))\n    print_section(\"Unsorted PRs\", \"\\n\".join([f\"* {item}\" for item in unsorted_prs]))\n    print_section(\n        \"Unsorted commits\", \"\\n\".join([f\"* {item}\" for item in unsorted_commits])\n    )\n\n    if args.write:\n        for crate in crate_names:\n            items = changelog_from_prs(crate_sections[crate], crate)\n            add_to_changelog_file(crate, items, args.version)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/generate_example_screenshots.sh",
    "content": "#!/usr/bin/env bash\n# This script generates screenshots for all the examples in examples/\n\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path/..\"\n\ncd examples\nfor EXAMPLE_NAME in $(ls -1d */ | sed 's/\\/$//'); do\n    if [ ${EXAMPLE_NAME} != \"external_eventloop_async\" ] &&\n       [ ${EXAMPLE_NAME} != \"hello_android\" ] &&\n       [ ${EXAMPLE_NAME} != \"hello_world_par\" ] && # screenshot not implemented for wgpu backend\n       [ ${EXAMPLE_NAME} != \"multiple_viewports\" ] &&\n       [ ${EXAMPLE_NAME} != \"puffin_viewer\" ] &&\n       [ ${EXAMPLE_NAME} != \"screenshot\" ] &&\n       [ ${EXAMPLE_NAME} != \"serial_windows\" ];\n    then\n        echo \"\"\n        echo \"Running ${EXAMPLE_NAME}…\"\n        EFRAME_SCREENSHOT_TO=\"temp.png\" cargo run -p ${EXAMPLE_NAME}\n        pngcrush -rem allb -brute -reduce temp.png \"${EXAMPLE_NAME}/screenshot.png\"\n        rm temp.png\n    fi\ndone\n"
  },
  {
    "path": "scripts/lint.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nRuns custom linting on Rust code.\n\"\"\"\n\nimport argparse\nimport os\nimport re\nimport sys\n\n\ndef lint_file_path(filepath, args) -> int:\n    with open(filepath) as f:\n        lines_in = f.readlines()\n\n    errors, lines_out = lint_lines(filepath, lines_in)\n\n    for error in errors:\n        print(error)\n\n    if args.fix and lines_in != lines_out:\n        with open(filepath, \"w\") as f:\n            f.writelines(lines_out)\n        print(f\"{filepath} fixed.\")\n\n    return len(errors)\n\n\ndef lint_lines(filepath, lines_in):\n    last_line_was_empty = True\n\n    errors = []\n    lines_out = []\n    prev_line = \"\"\n\n    for line_nr, line in enumerate(lines_in):\n        line_nr = line_nr + 1\n\n        # TODO(emilk): only # and /// on lines before a keyword\n\n        pattern = (\n            r\"^\\s*((///)|((pub(\\(\\w*\\))? )?((impl|fn|struct|enum|union|trait)\\b))).*$\"\n        )\n        if re.match(pattern, line):\n            stripped = prev_line.strip()\n            last_line_was_empty = (\n                stripped == \"\"\n                or stripped.startswith(\"#\")\n                or stripped.startswith(\"//\")\n                or stripped.endswith(\"{\")\n                or stripped.endswith(\"(\")\n                or stripped.endswith(\"\\\\\")\n                or stripped.endswith('r\"')\n                or stripped.endswith(\"]\")\n            )\n            if not last_line_was_empty:\n                errors.append(\n                    f\"{filepath}:{line_nr}: for readability, add newline before `{line.strip()}`\"\n                )\n                lines_out.append(\"\\n\")\n\n        if re.search(r\"\\(mut self.*-> Self\", line) and \"pub(crate)\" not in line:\n            if prev_line.strip() != \"#[inline]\":\n                errors.append(\n                    f\"{filepath}:{line_nr}: builder methods should be marked #[inline]\"\n                )\n                lines_out.append(\"#[inline]\")\n\n\n        if re.search(r\"TODO[^(]\", line):\n            errors.append(\n                f\"{filepath}:{line_nr}: write 'TODO(username):' instead\"\n            )\n\n        if (\n            \"(target_os\" in line\n            and filepath.startswith(\"./crates/egui/\")\n            and filepath != \"./crates/egui/src/os.rs\"\n        ):\n            errors.append(\n                f\"{filepath}:{line_nr}: Don't use `target_os` - use ctx.os() instead.\"\n            )\n\n        lines_out.append(line)\n\n        prev_line = line\n\n    return errors, lines_out\n\n\ndef test_lint():\n    should_pass = [\n        \"hello world\",\n        \"\"\"\n        /// docstring\n        foo\n\n        /// docstring\n        bar\n        \"\"\",\n        \"\"\"\n        #[inline]\n        pub fn with_color(mut self, color: Color32) -> Self {\n            self.color = color;\n            self\n        }\n        \"\"\",\n    ]\n\n    should_fail = [\n        \"\"\"\n        /// docstring\n        foo\n        /// docstring\n        bar\n        \"\"\",\n        \"\"\"\n        // not inlined\n        pub fn with_color(mut self, color: Color32) -> Self {\n            self.color = color;\n            self\n        }\n        \"\"\",\n    ]\n\n    for code in should_pass:\n        errors, _ = lint_lines(\"test.py\", code.split(\"\\n\"))\n        assert len(errors) == 0, f\"expected this to pass:\\n{code}\\ngot: {errors}\"\n\n    for code in should_fail:\n        errors, _ = lint_lines(\"test.py\", code.split(\"\\n\"))\n        assert len(errors) > 0, f\"expected this to fail:\\n{code}\"\n\n    pass\n\n\ndef main():\n    test_lint()  # Make sure we are bug free before we run!\n\n    parser = argparse.ArgumentParser(description=\"Lint Rust code with custom linter.\")\n    parser.add_argument(\n        \"files\",\n        metavar=\"file\",\n        type=str,\n        nargs=\"*\",\n        help=\"File paths. Empty = all files, recursively.\",\n    )\n    parser.add_argument(\n        \"--fix\", dest=\"fix\", action=\"store_true\", help=\"Automatically fix the files\"\n    )\n\n    args = parser.parse_args()\n\n    num_errors = 0\n\n    if args.files:\n        for filepath in args.files:\n            num_errors += lint_file_path(filepath, args)\n    else:\n        script_dirpath = os.path.dirname(os.path.realpath(__file__))\n        root_dirpath = os.path.abspath(f\"{script_dirpath}/..\")\n        os.chdir(root_dirpath)\n\n        exclude = set([\"target\", \"target_ra\", \"target_wasm\"])\n        for root, dirs, files in os.walk(\".\", topdown=True):\n            dirs[:] = [d for d in dirs if d not in exclude]\n            for filename in files:\n                if filename.endswith(\".rs\"):\n                    filepath = os.path.join(root, filename)\n                    num_errors += lint_file_path(filepath, args)\n\n    if num_errors == 0:\n        print(f\"{sys.argv[0]} finished without error\")\n        sys.exit(0)\n    else:\n        print(f\"{sys.argv[0]} found {num_errors} errors.\")\n        sys.exit(1)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/publish_crates.sh",
    "content": "#!/usr/bin/env bash\n\n(cd crates/emath                && cargo publish --quiet)  &&  echo \"✅ emath\"\n(cd crates/ecolor               && cargo publish --quiet)  &&  echo \"✅ ecolor\"\n(cd crates/epaint_default_fonts && cargo publish --quiet)  &&  echo \"✅ epaint_default_fonts\"\n(cd crates/epaint               && cargo publish --quiet)  &&  echo \"✅ epaint\"\n(cd crates/egui                 && cargo publish --quiet)  &&  echo \"✅ egui\"\n(cd crates/egui-winit           && cargo publish --quiet)  &&  echo \"✅ egui-winit\"\n(cd crates/egui_glow            && cargo publish --quiet)  &&  echo \"✅ egui_glow\"\n(cd crates/egui-wgpu            && cargo publish --quiet)  &&  echo \"✅ egui-wgpu\"\n(cd crates/eframe               && cargo publish --quiet)  &&  echo \"✅ eframe\"\n(cd crates/egui_extras          && cargo publish --quiet)  &&  echo \"✅ egui_extras\"\n(cd crates/egui_kittest         && cargo publish --quiet)  &&  echo \"✅ egui_kittest\"\n(cd crates/egui_demo_lib        && cargo publish --quiet)  &&  echo \"✅ egui_demo_lib\"\n"
  },
  {
    "path": "scripts/setup_web.sh",
    "content": "#!/usr/bin/env bash\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path/..\"\n\nset -x\n\n# Pre-requisites:\nrustup target add wasm32-unknown-unknown\n\n# For generating JS bindings:\n# Keep wasm-bindgen version in sync in: setup_web.sh, Cargo.toml, Cargo.lock, rust.yml\nif ! cargo install --list | grep -q 'wasm-bindgen-cli v0.2.100'; then\n    cargo install --force --quiet wasm-bindgen-cli --version 0.2.100\nfi\n"
  },
  {
    "path": "scripts/start_server.sh",
    "content": "#!/usr/bin/env bash\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path/..\"\n\n# Starts a local web-server that serves the contents of the `doc/` folder,\n# i.e. the web-version of `egui_demo_app`.\n\nPORT=8765\n\necho \"ensuring basic-http-server is installed…\"\ncargo install basic-http-server\n\necho \"starting server…\"\necho \"serving at http://localhost:${PORT}\"\n\n(cd web_demo && basic-http-server --addr 0.0.0.0:${PORT} .)\n# (cd web_demo && python3 -m http.server ${PORT} --bind 0.0.0.0)\n"
  },
  {
    "path": "scripts/update_snapshots_from_ci.sh",
    "content": "#!/usr/bin/env bash\n# This script searches for the last CI run with your branch name, downloads the test_results artifact\n# and replaces your existing snapshots with the new ones.\n# Make sure you have the gh cli installed and authenticated before running this script.\n# If prompted to select a default repo, choose the emilk/egui one\n\nset -eu\n\nBRANCH=$(git rev-parse --abbrev-ref HEAD)\n\nif [ -z \"${RUN_ID:-}\" ]; then\n    RUN_ID=$(gh run list --branch \"$BRANCH\" --workflow \"Rust\" --json databaseId -q '.[0].databaseId')\n    echo \"Downloading test results from run $RUN_ID from branch $BRANCH\"\nelse\n    echo \"Using provided RUN_ID: $RUN_ID\"\nfi\n\n# remove any existing .new.png that might have been left behind\nfind . -type d -path \"*/tests/snapshots*\" | while read dir; do\n    find \"$dir\" -type f -name \"*.new.png\" | while read file; do\n        rm \"$file\"\n    done\ndone\n\n\ngh run download \"$RUN_ID\" --name \"test-results\" --dir tmp_artefacts\n\n# move the snapshots to the correct location, overwriting the existing ones\nrsync -a tmp_artefacts/ .\n\nrm -r tmp_artefacts\n\n./scripts/accept_snapshots.sh\n"
  },
  {
    "path": "scripts/wasm_bindgen_check.sh",
    "content": "#!/usr/bin/env bash\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path/..\"\n\nif [[ $* == --skip-setup ]]\nthen\n  echo \"Skipping setup_web.sh\"\nelse\n  echo \"Running setup_web.sh\"\n  ./scripts/setup_web.sh\nfi\n\nCRATE_NAME=\"egui_demo_app\"\nFEATURES=\"wgpu,http,persistence\"\n\necho \"Building rust…\"\nBUILD=debug # debug builds are faster\n\n(cd crates/$CRATE_NAME &&\n  cargo build \\\n    --quiet \\\n    --lib \\\n    --target wasm32-unknown-unknown \\\n    --no-default-features \\\n    --features ${FEATURES}\n)\n\nTARGET=\"target\"\n\necho \"Generating JS bindings for wasm…\"\n\nrm -f \"${CRATE_NAME}_bg.wasm\" # Remove old output (if any)\n\nTARGET_NAME=\"${CRATE_NAME}.wasm\"\nwasm-bindgen \"${TARGET}/wasm32-unknown-unknown/$BUILD/$TARGET_NAME\" \\\n  --out-dir . --no-modules --no-typescript\n\n# Remove output:\nrm -f \"${CRATE_NAME}_bg.wasm\"\nrm -f \"${CRATE_NAME}.js\"\n"
  },
  {
    "path": "scripts/wasm_size.sh",
    "content": "#!/usr/bin/env bash\nset -eu\nscript_path=$( cd \"$(dirname \"${BASH_SOURCE[0]}\")\" ; pwd -P )\ncd \"$script_path\"\n\n./build_demo_web.sh && ls -lh ../web_demo/*.wasm\n"
  },
  {
    "path": "taplo.toml",
    "content": "# https://github.com/tamasfe/taplo\n\ninclude = [\n  \"*.toml\",\n  \"crates/**/*.toml\",\n  \"examples/**/*.toml\",\n  \"scripts/**/*.toml\",\n  \"tests/**/*.toml\",\n]\n\n[formatting]\nalign_comments = false # causes unnecessary churn\ncolumn_width = 100\n"
  },
  {
    "path": "tests/README.md",
    "content": "## Test apps\nSome application to tests various parts of egui and eframe.\n\nAt some point it would be nice to have automatic screenshot regression tests for these.\n"
  },
  {
    "path": "tests/egui_tests/Cargo.toml",
    "content": "[package]\nname = \"egui_tests\"\nedition.workspace = true\nlicense.workspace = true\nrust-version.workspace = true\nversion.workspace = true\n\n[dev-dependencies]\negui = { workspace = true, default-features = true }\negui_kittest = { workspace = true, features = [\"snapshot\", \"wgpu\"] }\negui_extras = { workspace = true, features = [\"image\"] }\nimage = { workspace = true, features = [\"png\"] }\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "tests/egui_tests/tests/regression_tests.rs",
    "content": "use egui::accesskit::Role;\nuse egui::epaint::Shape;\nuse egui::{Align, Color32, Image, Label, Layout, RichText, Sense, TextWrapMode, include_image};\nuse egui_kittest::Harness;\nuse egui_kittest::kittest::Queryable as _;\n\n#[test]\nfn image_button_should_have_alt_text() {\n    let harness = Harness::new_ui(|ui| {\n        _ = ui.button(\n            Image::new(include_image!(\"../../../crates/eframe/data/icon.png\")).alt_text(\"Egui\"),\n        );\n    });\n\n    harness.get_by_label(\"Egui\");\n}\n\n#[test]\nfn hovering_should_preserve_text_format() {\n    let mut harness = Harness::builder().with_size((200.0, 70.0)).build_ui(|ui| {\n        ui.add(\n            Label::new(\n                RichText::new(\"Long text that should be elided and has lots of styling and is long enough to have multiple lines.\")\n                    .italics()\n                    .underline()\n                    .color(Color32::LIGHT_BLUE),\n            )\n            .wrap_mode(TextWrapMode::Truncate),\n        );\n    });\n\n    harness.get_by_label_contains(\"Long text\").hover();\n\n    harness.run_steps(5);\n\n    harness.snapshot(\"hovering_should_preserve_text_format\");\n}\n\n#[test]\nfn text_edit_rtl() {\n    let mut text = \"hello \".to_owned();\n    let mut harness = Harness::builder().with_size((200.0, 50.0)).build_ui(|ui| {\n        ui.with_layout(Layout::right_to_left(Align::Min), |ui| {\n            _ = ui.button(\"right\");\n            ui.add(\n                egui::TextEdit::singleline(&mut text)\n                    .desired_width(10.0)\n                    .clip_text(false),\n            );\n            _ = ui.button(\"left\");\n        });\n    });\n\n    harness.get_by_role(Role::TextInput).focus();\n    harness.step();\n    harness.snapshot(\"text_edit_rtl_0\");\n\n    harness.get_by_role(Role::TextInput).type_text(\"world\");\n\n    for i in 1..3 {\n        harness.step();\n        harness.snapshot(format!(\"text_edit_rtl_{i}\"));\n    }\n}\n\n#[test]\nfn combobox_should_have_value() {\n    let harness = Harness::new_ui(|ui| {\n        egui::ComboBox::from_label(\"Select an option\")\n            .selected_text(\"Option 1\")\n            .show_ui(ui, |_ui| {});\n    });\n\n    assert_eq!(\n        harness.get_by_label(\"Select an option\").value().as_deref(),\n        Some(\"Option 1\")\n    );\n}\n\n/// This test ensures that `ui.response().interact(...)` works correctly.\n///\n/// This was broken, because there was an optimization in [`egui::Response::interact`]\n/// which caused the [`Sense`] of the original response to flip-flop between `click` and `hover`\n/// between frames.\n///\n/// See <https://github.com/emilk/egui/pull/7713> for more details.\n#[test]\nfn interact_on_ui_response_should_be_stable() {\n    let mut first_frame = true;\n    let mut click_count = 0;\n    let mut harness = Harness::new_ui(|ui| {\n        let ui_response = ui.response();\n        if !first_frame {\n            assert!(\n                ui_response.sense.contains(Sense::click()),\n                \"ui.response() didn't have click sense even though we called interact(Sense::click()) last frame\"\n            );\n        }\n\n        // Add a label so we have something to click with kittest\n        ui.add(\n            Label::new(\"senseless label\")\n                .sense(Sense::hover())\n                .selectable(false),\n        );\n\n        let click_response = ui_response.interact(Sense::click());\n        if click_response.clicked() {\n            click_count += 1;\n        }\n        first_frame = false;\n    });\n\n    for i in 0..=10 {\n        harness.run_steps(i);\n        harness.get_by_label(\"senseless label\").click();\n    }\n\n    drop(harness);\n    assert_eq!(click_count, 10, \"We missed some clicks!\");\n}\n\nfn has_red_warning_rect(output: &egui::FullOutput) -> bool {\n    output.shapes.iter().any(|clipped| {\n        matches!(\n            &clipped.shape,\n            Shape::Rect(rect_shape)\n                if rect_shape.stroke.color == Color32::RED\n        )\n    })\n}\n\n/// A button that changes its text on hover, with the Id derived from the text.\n/// This is a plausible bug: the widget keeps the same rect, but its Id changes\n/// between frames because the label (and thus the Id salt) changes on hover.\n/// The `warn_if_rect_changes_id` debug check should catch this.\n#[test]\nfn warn_if_rect_changes_id() {\n    let button_rect = egui::Rect::from_min_size(egui::pos2(10.0, 10.0), egui::vec2(100.0, 30.0));\n\n    let mut harness = Harness::builder().with_size((200.0, 50.0)).build_ui(|ui| {\n        // Simulate a buggy widget whose Id depends on its label text,\n        // and the label changes on hover:\n        let is_hovered = ui.rect_contains_pointer(button_rect);\n        let label = if is_hovered { \"Hovering!\" } else { \"Click me\" };\n        let id = ui.id().with(label);\n        let _response = ui.interact(button_rect, id, Sense::click());\n    });\n\n    // no hover — establishes stable prev_pass\n    harness.step();\n    assert!(\n        !has_red_warning_rect(harness.output()),\n        \"Should not warn without hover\"\n    );\n\n    // Move the pointer over the button\n    harness.hover_at(button_rect.center());\n\n    harness.step();\n    assert!(\n        has_red_warning_rect(harness.output()),\n        \"Should warn when a widget rect changes Id between passes\"\n    );\n}\n"
  },
  {
    "path": "tests/egui_tests/tests/test_atoms.rs",
    "content": "use egui::{Align, AtomExt as _, Button, Layout, TextWrapMode, Ui, Vec2};\nuse egui_kittest::{HarnessBuilder, SnapshotResult, SnapshotResults};\n\n#[test]\nfn test_atoms() {\n    let mut results = SnapshotResults::new();\n\n    results.add(single_test(\"max_width\", |ui| {\n        ui.add(Button::new((\n            \"max width not grow\".atom_max_width(30.0),\n            \"other text\",\n        )));\n    }));\n    results.add(single_test(\"max_width_and_grow\", |ui| {\n        ui.add(Button::new((\n            \"max width and grow\".atom_max_width(30.0).atom_grow(true),\n            \"other text\",\n        )));\n    }));\n    results.add(single_test(\"shrink_first_text\", |ui| {\n        ui.style_mut().wrap_mode = Some(TextWrapMode::Truncate);\n        ui.add(Button::new((\"this should shrink\", \"this shouldn't\")));\n    }));\n    results.add(single_test(\"shrink_last_text\", |ui| {\n        ui.style_mut().wrap_mode = Some(TextWrapMode::Truncate);\n        ui.add(Button::new((\n            \"this shouldn't shrink\",\n            \"this should\".atom_shrink(true),\n        )));\n    }));\n    results.add(single_test(\"grow_all\", |ui| {\n        ui.style_mut().wrap_mode = Some(TextWrapMode::Truncate);\n        ui.add(Button::new((\n            \"I grow\".atom_grow(true),\n            \"I also grow\".atom_grow(true),\n            \"I grow as well\".atom_grow(true),\n        )));\n    }));\n    results.add(single_test(\"size_max_size\", |ui| {\n        ui.style_mut().wrap_mode = Some(TextWrapMode::Truncate);\n        ui.add(Button::new((\n            \"size and max size\"\n                .atom_size(Vec2::new(80.0, 80.0))\n                .atom_max_size(Vec2::new(20.0, 20.0)),\n            \"other text\".atom_grow(true),\n        )));\n    }));\n}\n\nfn single_test(name: &str, mut f: impl FnMut(&mut Ui)) -> SnapshotResult {\n    let mut harness = HarnessBuilder::default()\n        .with_size(Vec2::new(400.0, 200.0))\n        .build_ui(move |ui| {\n            ui.label(\"Normal\");\n            let normal_width = ui.horizontal(&mut f).response.rect.width();\n\n            ui.label(\"Justified\");\n            ui.with_layout(\n                Layout::left_to_right(Align::Min).with_main_justify(true),\n                &mut f,\n            );\n\n            ui.label(\"Shrunk\");\n            ui.scope(|ui| {\n                ui.set_max_width(normal_width / 2.0);\n                f(ui);\n            });\n        });\n\n    harness.try_snapshot(name)\n}\n\n#[test]\nfn test_intrinsic_size() {\n    let widgets = [Ui::button, Ui::label];\n\n    for widget in widgets {\n        let mut intrinsic_size = None;\n        for wrapping in [\n            TextWrapMode::Extend,\n            TextWrapMode::Wrap,\n            TextWrapMode::Truncate,\n        ] {\n            _ = HarnessBuilder::default()\n                .with_size(Vec2::new(100.0, 100.0))\n                .build_ui(|ui| {\n                    ui.style_mut().wrap_mode = Some(wrapping);\n                    let response = widget(\n                        ui,\n                        \"Hello world this is a long text that should be wrapped.\",\n                    );\n                    if let Some(current_intrinsic_size) = intrinsic_size {\n                        assert_eq!(\n                            Some(current_intrinsic_size),\n                            response.intrinsic_size,\n                            \"For wrapping: {wrapping:?}\"\n                        );\n                    }\n                    assert!(\n                        response.intrinsic_size.is_some(),\n                        \"intrinsic_size should be set for `Button`\"\n                    );\n                    intrinsic_size = response.intrinsic_size;\n                    if wrapping == TextWrapMode::Extend {\n                        assert_eq!(Some(response.rect.size()), response.intrinsic_size);\n                    }\n                });\n        }\n    }\n}\n\n#[test]\nfn test_button_shortcut_text() {\n    let mut harness = HarnessBuilder::default().build_ui(|ui| {\n        ui.add(egui::Button::new(\"Click me\").shortcut_text((\"1\", \"2\", \"3\")));\n    });\n    harness.run();\n    harness.fit_contents();\n\n    harness.snapshot(\"button_shortcut\");\n}\n\n/// Tests the spacing between galleys.\n/// All of these should look the same.\n#[test]\nfn test_atom_letter_spacing() {\n    use egui::AtomLayout;\n\n    let mut harness = HarnessBuilder::default().build_ui(|ui| {\n        ui.add(AtomLayout::new(\"1.00x\").gap(0.0));\n        ui.add(AtomLayout::new((\"1.00\", \"x\")).gap(0.0));\n        ui.horizontal(|ui| {\n            ui.spacing_mut().item_spacing.x = 0.0;\n            ui.label(\"1.00\");\n            ui.label(\"x\");\n        });\n    });\n    harness.run();\n    harness.fit_contents();\n\n    harness.snapshot(\"atom_letter_spacing\");\n}\n"
  },
  {
    "path": "tests/egui_tests/tests/test_sides.rs",
    "content": "use egui::{TextWrapMode, Vec2, containers::Sides};\nuse egui_kittest::{Harness, SnapshotResults};\n\n#[test]\nfn sides_container_tests() {\n    let mut results = SnapshotResults::new();\n\n    test_variants(\"default\", |sides| sides, &mut results);\n\n    test_variants(\n        \"shrink_left\",\n        |sides| sides.shrink_left().truncate(),\n        &mut results,\n    );\n\n    test_variants(\n        \"shrink_right\",\n        |sides| sides.shrink_right().truncate(),\n        &mut results,\n    );\n\n    test_variants(\n        \"wrap_left\",\n        |sides| sides.shrink_left().wrap_mode(TextWrapMode::Wrap),\n        &mut results,\n    );\n\n    test_variants(\n        \"wrap_right\",\n        |sides| sides.shrink_right().wrap_mode(TextWrapMode::Wrap),\n        &mut results,\n    );\n}\n\nfn test_variants(\n    name: &str,\n    mut create_sides: impl FnMut(Sides) -> Sides,\n    results: &mut SnapshotResults,\n) {\n    for (variant_name, left_text, right_text, fit_contents) in [\n        (\"short\", \"Left\", \"Right\", false),\n        (\n            \"long\",\n            \"Very long left content that should not fit.\",\n            \"Very long right text that should also not fit.\",\n            false,\n        ),\n        (\"short_fit_contents\", \"Left\", \"Right\", true),\n        (\n            \"long_fit_contents\",\n            \"Very long left content that should not fit.\",\n            \"Very long right text that should also not fit.\",\n            true,\n        ),\n    ] {\n        let mut harness = Harness::builder()\n            .with_size(Vec2::new(400.0, 50.0))\n            .build_ui(|ui| {\n                create_sides(Sides::new()).show(\n                    ui,\n                    |left| {\n                        left.label(left_text);\n                    },\n                    |right| {\n                        right.label(right_text);\n                    },\n                );\n            });\n\n        if fit_contents {\n            harness.fit_contents();\n        }\n\n        results.add(harness.try_snapshot(format!(\"sides/{name}_{variant_name}\")));\n    }\n}\n"
  },
  {
    "path": "tests/egui_tests/tests/test_widgets.rs",
    "content": "#![expect(clippy::unwrap_used)] // it's a test\n\nuse egui::accesskit::Role;\nuse egui::load::SizedTexture;\nuse egui::{\n    Align, AtomExt as _, AtomLayout, Button, Color32, ColorImage, Direction, DragValue, Event,\n    Grid, IntoAtoms as _, Layout, PointerButton, Response, RichText, Slider, Stroke, StrokeKind,\n    TextEdit, TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _,\n    include_image,\n};\nuse egui_kittest::kittest::{Queryable as _, by};\nuse egui_kittest::{Harness, Node, SnapshotResult, SnapshotResults};\n\n#[test]\nfn widget_tests() {\n    let mut results = SnapshotResults::new();\n\n    test_widget(\"button\", |ui| ui.button(\"Button\"), &mut results);\n    test_widget(\n        \"button_image\",\n        |ui| {\n            Button::image_and_text(\n                include_image!(\"../../../crates/eframe/data/icon.png\"),\n                \"Button\",\n            )\n            .ui(ui)\n        },\n        &mut results,\n    );\n    test_widget(\n        \"button_image_shortcut\",\n        |ui| {\n            Button::image_and_text(\n                include_image!(\"../../../crates/eframe/data/icon.png\"),\n                \"Open\",\n            )\n            .shortcut_text(\"⌘O\")\n            .ui(ui)\n        },\n        &mut results,\n    );\n    results.add(VisualTests::test(\"button_image_shortcut_selected\", |ui| {\n        Button::image_and_text(\n            include_image!(\"../../../crates/eframe/data/icon.png\"),\n            \"Open\",\n        )\n        .shortcut_text(\"⌘O\")\n        .selected(true)\n        .ui(ui)\n    }));\n\n    test_widget(\n        \"selectable_value\",\n        |ui| ui.selectable_label(false, \"Selectable\"),\n        &mut results,\n    );\n    test_widget(\n        \"selectable_value_selected\",\n        |ui| ui.selectable_label(true, \"Selectable\"),\n        &mut results,\n    );\n\n    test_widget(\n        \"checkbox\",\n        |ui| ui.checkbox(&mut false, \"Checkbox\"),\n        &mut results,\n    );\n    test_widget(\n        \"checkbox_checked\",\n        |ui| ui.checkbox(&mut true, \"Checkbox\"),\n        &mut results,\n    );\n    test_widget(\"radio\", |ui| ui.radio(false, \"Radio\"), &mut results);\n    test_widget(\"radio_checked\", |ui| ui.radio(true, \"Radio\"), &mut results);\n\n    test_widget(\n        \"drag_value\",\n        |ui| {\n            DragValue::new(&mut 12.0)\n                .suffix(RichText::new(\" px\").weak().small())\n                .ui(ui)\n        },\n        &mut results,\n    );\n\n    test_widget(\n        \"text_edit\",\n        |ui| {\n            ui.spacing_mut().text_edit_width = 45.0;\n            ui.text_edit_singleline(&mut \"Hi!\".to_owned())\n        },\n        &mut results,\n    );\n    test_widget(\n        \"text_edit_clip\",\n        |ui| {\n            ui.spacing_mut().text_edit_width = 45.0;\n            TextEdit::singleline(&mut \"This is a very very long text\".to_owned())\n                .clip_text(true)\n                .ui(ui)\n        },\n        &mut results,\n    );\n    test_widget(\n        \"text_edit_no_clip\",\n        |ui| {\n            ui.spacing_mut().text_edit_width = 45.0;\n            TextEdit::singleline(&mut \"This is a very very long text\".to_owned())\n                .clip_text(false)\n                .ui(ui)\n        },\n        &mut results,\n    );\n    test_widget(\n        \"text_edit_placeholder_clip\",\n        |ui| {\n            ui.spacing_mut().text_edit_width = 45.0;\n            TextEdit::singleline(&mut String::new())\n                .hint_text(\"This is a very very long placeholder\")\n                .clip_text(true)\n                .ui(ui)\n        },\n        &mut results,\n    );\n\n    test_widget(\n        \"slider\",\n        |ui| {\n            ui.spacing_mut().slider_width = 45.0;\n            Slider::new(&mut 12.0, 0.0..=100.0).ui(ui)\n        },\n        &mut results,\n    );\n\n    let source = include_image!(\"../../../crates/eframe/data/icon.png\");\n    let interesting_atoms = vec![\n        (\"minimal\", (\"Hello World!\").into_atoms()),\n        (\n            \"image\",\n            (source.clone().atom_max_height(12.0), \"With Image\").into_atoms(),\n        ),\n        (\n            \"multi_grow\",\n            (\"g\".atom_grow(true), \"2\", \"g\".atom_grow(true), \"4\").into_atoms(),\n        ),\n    ];\n\n    for atoms in interesting_atoms {\n        results.add(test_widget_layout(&format!(\"atoms_{}\", atoms.0), |ui| {\n            AtomLayout::new(atoms.1.clone()).ui(ui)\n        }));\n    }\n}\n\nfn test_widget(name: &str, mut w: impl FnMut(&mut Ui) -> Response, results: &mut SnapshotResults) {\n    results.add(test_widget_layout(name, &mut w));\n    results.add(VisualTests::test(name, &mut w));\n}\n\nfn test_widget_layout(name: &str, mut w: impl FnMut(&mut Ui) -> Response) -> SnapshotResult {\n    let test_size = Vec2::new(110.0, 45.0);\n\n    struct Row {\n        main_dir: Direction,\n        main_align: Align,\n        main_justify: bool,\n    }\n\n    struct Col {\n        cross_align: Align,\n        cross_justify: bool,\n    }\n\n    let mut rows = Vec::new();\n    let mut cols = Vec::new();\n\n    for main_justify in [false, true] {\n        for main_dir in [\n            Direction::LeftToRight,\n            Direction::TopDown,\n            Direction::RightToLeft,\n            Direction::BottomUp,\n        ] {\n            for main_align in [Align::Min, Align::Center, Align::Max] {\n                rows.push(Row {\n                    main_dir,\n                    main_align,\n                    main_justify,\n                });\n            }\n        }\n    }\n\n    for cross_justify in [false, true] {\n        for cross_align in [Align::Min, Align::Center, Align::Max] {\n            cols.push(Col {\n                cross_align,\n                cross_justify,\n            });\n        }\n    }\n\n    let mut harness = Harness::builder().build_ui(|ui| {\n        egui_extras::install_image_loaders(ui.ctx());\n\n        {\n            let mut wrap_test_size = test_size;\n            wrap_test_size.x /= 3.0;\n            ui.heading(\"Wrapping\");\n\n            let modes = [\n                TextWrapMode::Extend,\n                TextWrapMode::Truncate,\n                TextWrapMode::Wrap,\n            ];\n            Grid::new(\"wrapping\")\n                .spacing(Vec2::new(test_size.x / 2.0, 4.0))\n                .show(ui, |ui| {\n                    for mode in &modes {\n                        ui.label(format!(\"{mode:?}\"));\n                    }\n                    ui.end_row();\n\n                    for mode in &modes {\n                        let (_, rect) = ui.allocate_space(wrap_test_size);\n\n                        let mut child_ui = ui.new_child(UiBuilder::new().max_rect(rect));\n                        child_ui.style_mut().wrap_mode = Some(*mode);\n                        w(&mut child_ui);\n\n                        ui.painter().rect_stroke(\n                            rect,\n                            0.0,\n                            Stroke::new(1.0, Color32::WHITE),\n                            StrokeKind::Outside,\n                        );\n                    }\n                });\n        }\n\n        ui.heading(\"Layout\");\n        Grid::new(\"layout\").striped(true).show(ui, |ui| {\n            ui.label(\"\");\n            for col in &cols {\n                ui.label(format!(\n                    \"cross_align: {:?}\\ncross_justify:{:?}\",\n                    col.cross_align, col.cross_justify\n                ));\n            }\n            ui.end_row();\n\n            for row in &rows {\n                ui.label(format!(\n                    \"main_dir: {:?}\\nmain_align: {:?}\\nmain_justify: {:?}\",\n                    row.main_dir, row.main_align, row.main_justify\n                ));\n                for col in &cols {\n                    let layout = Layout {\n                        main_dir: row.main_dir,\n                        main_align: row.main_align,\n                        main_justify: row.main_justify,\n                        cross_align: col.cross_align,\n                        cross_justify: col.cross_justify,\n                        main_wrap: false,\n                    };\n\n                    let (_, rect) = ui.allocate_space(test_size);\n\n                    let mut child_ui = ui.new_child(UiBuilder::new().layout(layout).max_rect(rect));\n                    w(&mut child_ui);\n\n                    ui.painter().rect_stroke(\n                        rect,\n                        0.0,\n                        Stroke::new(1.0, Color32::WHITE),\n                        StrokeKind::Outside,\n                    );\n                }\n\n                ui.end_row();\n            }\n        });\n    });\n\n    harness.fit_contents();\n    harness.try_snapshot(format!(\"layout/{name}\"))\n}\n\n/// Utility to create a snapshot test of the different states of a egui widget.\n/// This renders each state to a texture to work around the fact only a single widget can be\n/// hovered / pressed / focused at a time.\nstruct VisualTests<'a> {\n    name: String,\n    w: &'a mut dyn FnMut(&mut Ui) -> Response,\n    results: Vec<(String, ColorImage)>,\n}\n\nimpl<'a> VisualTests<'a> {\n    pub fn test(name: &str, mut w: impl FnMut(&mut Ui) -> Response) -> SnapshotResult {\n        let mut vis = VisualTests::new(name, &mut w);\n        vis.add_default_states();\n        vis.render()\n    }\n\n    pub fn new(name: &str, w: &'a mut dyn FnMut(&mut Ui) -> Response) -> Self {\n        Self {\n            name: name.to_owned(),\n            w,\n            results: Vec::new(),\n        }\n    }\n\n    fn add_default_states(&mut self) {\n        self.add(\"idle\", |_| {});\n        self.add_node(\"hover\", |node| {\n            node.hover();\n        });\n        self.add(\"pressed\", |harness| {\n            harness.get_next_widget().hover();\n            let rect = harness.get_next_widget().rect();\n            harness.input_mut().events.push(Event::PointerButton {\n                button: PointerButton::Primary,\n                pos: rect.center(),\n                pressed: true,\n                modifiers: Default::default(),\n            });\n        });\n        self.add_node(\"focussed\", |node| {\n            node.focus();\n        });\n        self.add_disabled();\n    }\n\n    fn single_test(&mut self, f: impl FnOnce(&mut Harness<'_>), enabled: bool) -> ColorImage {\n        let mut harness = Harness::builder().with_step_dt(0.05).build_ui(|ui| {\n            egui_extras::install_image_loaders(ui.ctx());\n            ui.add_enabled_ui(enabled, |ui| {\n                (self.w)(ui);\n            });\n        });\n\n        harness.fit_contents();\n\n        // Wait for images to load\n        harness.try_run_realtime().ok();\n\n        f(&mut harness);\n\n        harness.run();\n\n        let image = harness.render().expect(\"Failed to render harness\");\n\n        ColorImage::from_rgba_unmultiplied(\n            [image.width() as usize, image.height() as usize],\n            image.as_ref(),\n        )\n    }\n\n    pub fn add(&mut self, name: &str, test: impl FnOnce(&mut Harness<'_>)) {\n        let image = self.single_test(test, true);\n        self.results.push((name.to_owned(), image));\n    }\n\n    pub fn add_disabled(&mut self) {\n        let image = self.single_test(|_| {}, false);\n        self.results.push((\"disabled\".to_owned(), image));\n    }\n\n    pub fn add_node(&mut self, name: &str, test: impl FnOnce(&Node<'_>)) {\n        self.add(name, |harness| {\n            let node = harness.get_next_widget();\n            test(&node);\n        });\n    }\n\n    pub fn render(self) -> SnapshotResult {\n        let mut results = Some(self.results);\n        let mut images: Option<Vec<(String, TextureHandle, SizedTexture)>> = None;\n\n        let mut harness = Harness::new_ui(|ui| {\n            let results = images.get_or_insert_with(|| {\n                results\n                    .take()\n                    .unwrap()\n                    .into_iter()\n                    .map(|(name, image)| {\n                        let size = Vec2::new(image.width() as f32, image.height() as f32);\n                        let texture_handle =\n                            ui.ctx()\n                                .load_texture(name.clone(), image, TextureOptions::default());\n                        let texture = SizedTexture::new(texture_handle.id(), size);\n                        (name.clone(), texture_handle, texture)\n                    })\n                    .collect()\n            });\n\n            Grid::new(\"results\").show(ui, |ui| {\n                for (name, _, image) in results {\n                    ui.label(&*name);\n\n                    ui.scope(|ui| {\n                        ui.image(*image);\n                    });\n\n                    ui.end_row();\n                }\n            });\n        });\n\n        harness.fit_contents();\n\n        harness.try_snapshot(format!(\"visuals/{}\", self.name))\n    }\n}\n\ntrait HarnessExt {\n    fn get_next_widget(&self) -> Node<'_>;\n}\n\nimpl HarnessExt for Harness<'_> {\n    fn get_next_widget(&self) -> Node<'_> {\n        self.get_all(by().predicate(|node| node.role() != Role::GenericContainer))\n            .next()\n            .unwrap()\n    }\n}\n"
  },
  {
    "path": "tests/test_background_logic/Cargo.toml",
    "content": "[package]\nname = \"test_background_logic\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n[dependencies]\neframe = { workspace = true, features = [\"default\"] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "tests/test_background_logic/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")]\n#![expect(rustdoc::missing_crate_level_docs)]\n#![allow(clippy::print_stderr)]\n\nuse std::time::Duration;\n\nuse eframe::egui::{self, ViewportInfo};\n\nfn main() {\n    env_logger::init();\n\n    let _ = eframe::run_native(\n        \"Background Logic Test\",\n        eframe::NativeOptions {\n            viewport: egui::ViewportBuilder::default().with_inner_size([400.0, 200.0]),\n            ..Default::default()\n        },\n        Box::new(|_cc| Ok(Box::new(App))),\n    );\n}\n\nstruct App;\n\nimpl eframe::App for App {\n    fn logic(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {\n        eprintln!(\"App::logic called {}\", viewport_info(ctx));\n        ctx.request_repaint_after(Duration::from_secs(1));\n    }\n\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        eprintln!(\"App::ui called {}\", viewport_info(ui.ctx()));\n        ui.centered_and_justified(|ui| {\n            ui.heading(\"Minimize this window\");\n        });\n    }\n}\n\nfn viewport_info(ctx: &egui::Context) -> String {\n    ctx.input(|i| {\n        let ViewportInfo {\n            minimized,\n            focused,\n            occluded,\n            ..\n        } = i.viewport();\n\n        let visible = i.viewport().visible();\n\n        let mut s = String::new();\n\n        let flags = [\n            (\"focused\", focused),\n            (\"occluded\", occluded),\n            (\"minimized\", minimized),\n            (\"visible\", &visible),\n        ];\n        for (name, value) in flags {\n            if let Some(value) = value {\n                s += &format!(\" {name}={value}\");\n            }\n        }\n        s\n    })\n}\n"
  },
  {
    "path": "tests/test_egui_extras_compilation/Cargo.toml",
    "content": "[package]\nname = \"test_egui_extras_compilation\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n[package.metadata.cargo-machete]\nignored = [\"eframe\", \"egui_extras\"] # We don't use them, just check that things compile\n\n[dependencies]\neframe = { workspace = true, features = [\"default\", \"persistence\"] }\negui_extras.workspace = true\n"
  },
  {
    "path": "tests/test_egui_extras_compilation/README.md",
    "content": "Regression test for <https://github.com/emilk/egui/issues/4771>\n"
  },
  {
    "path": "tests/test_egui_extras_compilation/src/main.rs",
    "content": "//! Regression test for <https://github.com/emilk/egui/issues/4771>\n\nfn main() {}\n"
  },
  {
    "path": "tests/test_inline_glow_paint/Cargo.toml",
    "content": "[package]\nname = \"test_inline_glow_paint\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "tests/test_inline_glow_paint/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(\n    // it's a test:\n    clippy::undocumented_unsafe_blocks,\n    clippy::unwrap_used,\n    rustdoc::missing_crate_level_docs\n)]\n\n// Test that we can paint to the screen using glow directly.\n\nuse eframe::egui;\nuse eframe::glow;\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        renderer: eframe::Renderer::Glow,\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"My test app\",\n        options,\n        Box::new(|_cc| Ok(Box::<MyTestApp>::default())),\n    )?;\n    Ok(())\n}\n\n#[derive(Default)]\nstruct MyTestApp {}\n\nimpl eframe::App for MyTestApp {\n    fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {\n        use glow::HasContext as _;\n        let gl = frame.gl().unwrap();\n\n        #[expect(unsafe_code)]\n        unsafe {\n            gl.disable(glow::SCISSOR_TEST);\n            gl.viewport(0, 0, 100, 100);\n            gl.clear_color(1.0, 0.0, 1.0, 1.0); // purple\n            gl.clear(glow::COLOR_BUFFER_BIT);\n        }\n\n        egui::Window::new(\"Floating Window\").show(ui.ctx(), |ui| {\n            ui.label(\"The background should be purple.\");\n        });\n    }\n}\n"
  },
  {
    "path": "tests/test_size_pass/Cargo.toml",
    "content": "[package]\nname = \"test_size_pass\"\nversion = \"0.1.0\"\nauthors = [\"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n[features]\nwgpu = [\"eframe/wgpu\"]\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "tests/test_size_pass/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's a test\n\nuse eframe::egui;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Use `RUST_LOG=debug` to see logs.\n\n    let options = eframe::NativeOptions::default();\n    eframe::run_ui_native(\"My egui App\", options, move |ui, _frame| {\n        // A bottom panel to force the tooltips to consider if the fit below or under the widget:\n        egui::Panel::bottom(\"bottom\").show_inside(ui, |ui| {\n            ui.horizontal(|ui| {\n                ui.vertical(|ui| {\n                    ui.label(\"Single tooltips:\");\n                    for i in 0..3 {\n                        ui.label(format!(\"Hover label {i} for a tooltip\"))\n                            .on_hover_text(\"There is some text here\");\n                    }\n                });\n                ui.vertical(|ui| {\n                    ui.label(\"Double tooltips:\");\n                    for i in 0..3 {\n                        ui.label(format!(\"Hover label {i} for two tooltips\"))\n                            .on_hover_text(\"First tooltip\")\n                            .on_hover_text(\"Second tooltip\");\n                    }\n                });\n            });\n            ui.with_layout(egui::Layout::right_to_left(egui::Align::BOTTOM), |ui| {\n                ui.label(\"Hover for tooltip\")\n                    .on_hover_text(\"This is a rather long tooltip that needs careful positioning.\");\n            });\n        });\n\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.horizontal(|ui| {\n                if ui.button(\"Reset egui memory\").clicked() {\n                    ui.memory_mut(|mem| *mem = Default::default());\n                }\n\n                ui.with_layout(egui::Layout::right_to_left(egui::Align::BOTTOM), |ui| {\n                    ui.label(\"Hover for tooltip\").on_hover_text(\n                        \"This is a rather long tooltip that needs careful positioning.\",\n                    );\n                    ui.label(\"Hover for interactive tooltip\").on_hover_ui(|ui| {\n                        ui.label(\"This tooltip has a button:\");\n                        let _ = ui.button(\"Clicking me does nothing\");\n                    });\n                });\n            });\n\n            let has_tooltip = ui\n                .label(\"This label has a tooltip at the mouse cursor\")\n                .on_hover_text_at_pointer(\"Told you!\")\n                .is_tooltip_open();\n\n            let response = ui.label(\"This label gets a tooltip when the previous label is hovered\");\n            if has_tooltip {\n                response.show_tooltip_text(\"The ever-present tooltip!\");\n            }\n\n            ui.separator();\n\n            ui.label(\"The menu should be as wide as the widest button\");\n            ui.menu_button(\"Click for menu\", |ui| {\n                let _ = ui.button(\"Narrow\").clicked();\n                let _ = ui.button(\"Very wide text\").clicked();\n                let _ = ui.button(\"Narrow\").clicked();\n            });\n\n            ui.label(\"Hover for tooltip\").on_hover_ui(|ui| {\n                ui.label(\"A separator:\");\n                ui.separator();\n            });\n\n            ui.separator();\n\n            let alternatives = [\n                \"Short\",\n                \"Min\",\n                \"Very very long text that will extend\",\n                \"Short\",\n            ];\n            let mut selected = 1;\n\n            egui::ComboBox::from_label(\"ComboBox\").show_index(\n                ui,\n                &mut selected,\n                alternatives.len(),\n                |i| alternatives[i],\n            );\n\n            egui::ComboBox::from_id_salt(\"combo\")\n                .selected_text(\"ComboBox\")\n                .width(100.0)\n                .show_ui(ui, |ui| {\n                    ui.debug_painter()\n                        .debug_rect(ui.max_rect(), egui::Color32::RED, \"\");\n\n                    ui.label(\"Hello\");\n                    ui.label(\"World\");\n                    ui.label(\"Hellooooooooooooooooooooooooo\");\n                });\n\n            ui.separator();\n\n            let time = ui.input(|i| i.time);\n            ui.label(\"Hover for a tooltip with changing content\")\n                .on_hover_text(format!(\"A number: {}\", time % 10.0));\n        });\n    })\n}\n"
  },
  {
    "path": "tests/test_ui_stack/Cargo.toml",
    "content": "[package]\nname = \"test_ui_stack\"\nversion = \"0.1.0\"\nauthors = [\"Antoine Beyeler <abeyeler@gmail.com>\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"persistence\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\n\n# For image support:\negui_extras = { workspace = true, features = [\"default\", \"image\", \"serde\"] }\n\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "tests/test_ui_stack/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(rustdoc::missing_crate_level_docs)] // it's an example\n\nuse std::sync::Arc;\n\nuse eframe::egui;\nuse eframe::egui::{Rangef, Shape, UiKind};\nuse egui_extras::Column;\n\nfn main() -> eframe::Result {\n    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).\n    let options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n        ..Default::default()\n    };\n    eframe::run_native(\n        \"Stack Frame Demo\",\n        options,\n        Box::new(|cc| {\n            // This gives us image support:\n            egui_extras::install_image_loaders(&cc.egui_ctx);\n\n            Ok(Box::<MyApp>::default())\n        }),\n    )\n}\n\n#[derive(Default)]\nstruct MyApp {\n    show_settings: bool,\n    show_inspection: bool,\n    show_memory: bool,\n}\n\nimpl eframe::App for MyApp {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        ui.all_styles_mut(|style| style.interaction.tooltip_delay = 0.0);\n\n        egui::Panel::left(\"side_panel_left\").show_inside(ui, |ui| {\n            ui.heading(\"Information\");\n            ui.label(\n                \"This is a demo/test environment of the `UiStack` feature. The tables display \\\n                the UI stack in various contexts. You can hover on the IDs to display the \\\n                corresponding origin/`max_rect`.\\n\\n\\\n                The \\\"Full span test\\\" labels showcase an implementation of full-span \\\n                highlighting. Hover to see them in action!\",\n            );\n            ui.add_space(10.0);\n            ui.checkbox(&mut self.show_settings, \"🔧 Settings\");\n            ui.checkbox(&mut self.show_inspection, \"🔍 Inspection\");\n            ui.checkbox(&mut self.show_memory, \"📝 Memory\");\n            ui.add_space(10.0);\n            if ui.button(\"Reset egui memory\").clicked() {\n                ui.memory_mut(|mem| *mem = Default::default());\n            }\n            ui.add_space(20.0);\n\n            egui::ScrollArea::both().auto_shrink(false).show(ui, |ui| {\n                stack_ui(ui);\n\n                // full span test\n                ui.add_space(20.0);\n                full_span_widget(ui, false);\n\n                // nested frames test\n                ui.add_space(20.0);\n                egui::Frame::new()\n                    .stroke(ui.visuals().noninteractive().bg_stroke)\n                    .inner_margin(4)\n                    .outer_margin(4)\n                    .show(ui, |ui| {\n                        full_span_widget(ui, false);\n                        stack_ui(ui);\n\n                        egui::Frame::new()\n                            .stroke(ui.visuals().noninteractive().bg_stroke)\n                            .inner_margin(8)\n                            .outer_margin(6)\n                            .show(ui, |ui| {\n                                full_span_widget(ui, false);\n                                stack_ui(ui);\n                            });\n                    });\n            });\n        });\n\n        egui::Panel::right(\"side_panel_right\").show_inside(ui, |ui| {\n            egui::ScrollArea::both().auto_shrink(false).show(ui, |ui| {\n                stack_ui(ui);\n\n                // full span test\n                ui.add_space(20.0);\n                full_span_widget(ui, false);\n            });\n        });\n\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            egui::ScrollArea::both().auto_shrink(false).show(ui, |ui| {\n                ui.label(\"stack here:\");\n                stack_ui(ui);\n\n                // full span test\n                ui.add_space(20.0);\n                full_span_widget(ui, false);\n\n                // tooltip test\n                ui.add_space(20.0);\n                ui.label(\"Hover me\").on_hover_ui(|ui| {\n                    full_span_widget(ui, true);\n                    ui.add_space(20.0);\n                    stack_ui(ui);\n                });\n\n                // combobox test\n                ui.add_space(20.0);\n                egui::ComboBox::from_id_salt(\"combo_box\")\n                    .selected_text(\"click me\")\n                    .show_ui(ui, |ui| {\n                        full_span_widget(ui, true);\n                        ui.add_space(20.0);\n                        stack_ui(ui);\n                    });\n\n                // Ui nesting test\n                ui.add_space(20.0);\n                ui.label(\"UI nesting test:\");\n                egui::Frame::new()\n                    .stroke(ui.visuals().noninteractive().bg_stroke)\n                    .inner_margin(4)\n                    .show(ui, |ui| {\n                        ui.horizontal(|ui| {\n                            ui.vertical(|ui| {\n                                ui.scope(stack_ui);\n                            });\n                        });\n                    });\n\n                // table test\n                let mut cell_stack = None;\n                ui.add_space(20.0);\n                ui.label(\"Table test:\");\n\n                egui_extras::TableBuilder::new(ui)\n                    .vscroll(false)\n                    .column(Column::auto())\n                    .column(Column::auto())\n                    .header(20.0, |mut header| {\n                        header.col(|ui| {\n                            ui.strong(\"column 1\");\n                        });\n                        header.col(|ui| {\n                            ui.strong(\"column 2\");\n                        });\n                    })\n                    .body(|mut body| {\n                        body.row(20.0, |mut row| {\n                            row.col(|ui| {\n                                full_span_widget(ui, false);\n                            });\n                            row.col(|ui| {\n                                ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);\n                                ui.label(\"See stack below\");\n                                cell_stack = Some(Arc::clone(ui.stack()));\n                            });\n                        });\n                    });\n\n                if let Some(cell_stack) = cell_stack {\n                    ui.label(\"Cell's stack:\");\n                    stack_ui_impl(ui, &cell_stack);\n                }\n            });\n        });\n\n        egui::Panel::bottom(\"bottom_panel\")\n            .resizable(true)\n            .show_inside(ui, |ui| {\n                egui::ScrollArea::vertical()\n                    .auto_shrink(false)\n                    .show(ui, |ui| {\n                        stack_ui(ui);\n\n                        // full span test\n                        ui.add_space(20.0);\n                        full_span_widget(ui, false);\n                    });\n            });\n\n        egui::Window::new(\"Window\")\n            .pivot(egui::Align2::RIGHT_TOP)\n            .show(ui.ctx(), |ui| {\n                full_span_widget(ui, false);\n                ui.add_space(20.0);\n                stack_ui(ui);\n            });\n\n        let ctx = ui.ctx().clone();\n        egui::Window::new(\"🔧 Settings\")\n            .open(&mut self.show_settings)\n            .vscroll(true)\n            .show(&ctx, |ui| {\n                ctx.settings_ui(ui);\n            });\n\n        egui::Window::new(\"🔍 Inspection\")\n            .open(&mut self.show_inspection)\n            .vscroll(true)\n            .show(&ctx, |ui| {\n                ctx.inspection_ui(ui);\n            });\n\n        egui::Window::new(\"📝 Memory\")\n            .open(&mut self.show_memory)\n            .resizable(false)\n            .show(&ctx, |ui| {\n                ctx.memory_ui(ui);\n            });\n    }\n}\n\n/// Demo of a widget that highlights its background all the way to the edge of its container when\n/// hovered.\nfn full_span_widget(ui: &mut egui::Ui, permanent: bool) {\n    let bg_shape_idx = ui.painter().add(Shape::Noop);\n    let response = ui.label(\"Full span test\");\n    let ui_stack = ui.stack();\n\n    let rect = egui::Rect::from_x_y_ranges(\n        full_span_horizontal_range(ui_stack),\n        response.rect.y_range(),\n    );\n\n    if permanent || response.hovered() {\n        ui.painter().set(\n            bg_shape_idx,\n            Shape::rect_filled(rect, 0.0, ui.visuals().selection.bg_fill),\n        );\n    }\n}\n\n/// Find the horizontal range of the enclosing container.\nfn full_span_horizontal_range(ui_stack: &egui::UiStack) -> Rangef {\n    for node in ui_stack.iter() {\n        if node.has_visible_frame()\n            || node.is_panel_ui()\n            || node.is_root_ui()\n            || node.kind() == Some(UiKind::TableCell)\n        {\n            return (node.max_rect + node.frame().inner_margin).x_range();\n        }\n    }\n\n    // should never happen\n    Rangef::EVERYTHING\n}\n\nfn stack_ui(ui: &mut egui::Ui) {\n    let ui_stack = Arc::clone(ui.stack());\n    ui.scope(|ui| {\n        stack_ui_impl(ui, &ui_stack);\n    });\n}\n\nfn stack_ui_impl(ui: &mut egui::Ui, stack: &egui::UiStack) {\n    egui::Frame::new()\n        .stroke(ui.style().noninteractive().fg_stroke)\n        .inner_margin(4)\n        .show(ui, |ui| {\n            ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);\n\n            egui_extras::TableBuilder::new(ui)\n                .column(Column::auto())\n                .column(Column::auto())\n                .column(Column::auto())\n                .column(Column::auto())\n                .column(Column::auto())\n                .column(Column::auto())\n                .header(20.0, |mut header| {\n                    header.col(|ui| {\n                        ui.strong(\"id\");\n                    });\n                    header.col(|ui| {\n                        ui.strong(\"kind\");\n                    });\n                    header.col(|ui| {\n                        ui.strong(\"stroke\");\n                    });\n                    header.col(|ui| {\n                        ui.strong(\"inner\");\n                    });\n                    header.col(|ui| {\n                        ui.strong(\"outer\");\n                    });\n                    header.col(|ui| {\n                        ui.strong(\"direction\");\n                    });\n                })\n                .body(|mut body| {\n                    for node in stack.iter() {\n                        body.row(20.0, |mut row| {\n                            row.col(|ui| {\n                                if ui.label(format!(\"{:?}\", node.id)).hovered() {\n                                    ui.debug_painter().debug_rect(\n                                        node.max_rect,\n                                        egui::Color32::GREEN,\n                                        \"max\",\n                                    );\n                                    ui.debug_painter().circle_filled(\n                                        node.min_rect.min,\n                                        2.0,\n                                        egui::Color32::RED,\n                                    );\n                                }\n                            });\n                            row.col(|ui| {\n                                let s = if let Some(kind) = node.kind() {\n                                    format!(\"{kind:?}\")\n                                } else {\n                                    \"-\".to_owned()\n                                };\n\n                                ui.label(s);\n                            });\n                            row.col(|ui| {\n                                let frame = node.frame();\n                                if frame.stroke == egui::Stroke::NONE {\n                                    ui.label(\"-\");\n                                } else {\n                                    let mut layout_job = egui::text::LayoutJob::default();\n                                    layout_job.append(\n                                        \"⬛ \",\n                                        0.0,\n                                        egui::TextFormat::simple(\n                                            egui::TextStyle::Body.resolve(ui.style()),\n                                            frame.stroke.color,\n                                        ),\n                                    );\n                                    layout_job.append(\n                                        format!(\"{}px\", frame.stroke.width).as_str(),\n                                        0.0,\n                                        egui::TextFormat::simple(\n                                            egui::TextStyle::Body.resolve(ui.style()),\n                                            ui.style().visuals.text_color(),\n                                        ),\n                                    );\n                                    ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);\n                                    ui.label(layout_job);\n                                }\n                            });\n                            row.col(|ui| {\n                                ui.label(print_margin(&node.frame().inner_margin));\n                            });\n                            row.col(|ui| {\n                                ui.label(print_margin(&node.frame().outer_margin));\n                            });\n                            row.col(|ui| {\n                                ui.label(format!(\"{:?}\", node.layout_direction));\n                            });\n                        });\n                    }\n                });\n        });\n}\n\nfn print_margin(margin: &egui::Margin) -> String {\n    if margin.is_same() {\n        format!(\"{}px\", margin.left)\n    } else {\n        let s1 = if margin.left == margin.right {\n            format!(\"H: {}px\", margin.left)\n        } else {\n            format!(\"L: {}px R: {}px\", margin.left, margin.right)\n        };\n        let s2 = if margin.top == margin.bottom {\n            format!(\"V: {}px\", margin.top)\n        } else {\n            format!(\"T: {}px B: {}px\", margin.top, margin.bottom)\n        };\n        format!(\"{s1} / {s2}\")\n    }\n}\n"
  },
  {
    "path": "tests/test_viewports/Cargo.toml",
    "content": "[package]\nname = \"test_viewports\"\nversion = \"0.1.0\"\nauthors = [\"konkitoman\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrust-version = \"1.92\"\npublish = false\n\n[lints]\nworkspace = true\n\n[features]\nwgpu = [\"eframe/wgpu\"]\n\n[dependencies]\neframe = { workspace = true, features = [\n  \"default\",\n  \"__screenshot\", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO\n] }\nenv_logger = { workspace = true, features = [\"auto-color\", \"humantime\"] }\n"
  },
  {
    "path": "tests/test_viewports/README.md",
    "content": "This is a test of the viewports feature of eframe and egui, where we show off using multiple windows.\n\nFor a simple example, see [`multiple_viewports`](../../examples/multiple_viewports).\n"
  },
  {
    "path": "tests/test_viewports/src/main.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n#![expect(clippy::unwrap_used, rustdoc::missing_crate_level_docs)] // it's a test\n\nuse std::sync::Arc;\n\nuse eframe::egui;\nuse egui::{Id, InnerResponse, UiBuilder, ViewportBuilder, ViewportId, mutex::RwLock};\n\n// Drag-and-drop between windows is not yet implemented, but if you wanna work on it, enable this:\npub const DRAG_AND_DROP_TEST: bool = false;\n\nfn main() {\n    env_logger::init(); // Use `RUST_LOG=debug` to see logs.\n\n    let _ = eframe::run_native(\n        \"Viewports\",\n        eframe::NativeOptions {\n            viewport: egui::ViewportBuilder::default().with_inner_size([450.0, 400.0]),\n\n            #[cfg(feature = \"wgpu\")]\n            renderer: eframe::Renderer::Wgpu,\n\n            ..Default::default()\n        },\n        Box::new(|_cc| Ok(Box::<App>::default())),\n    );\n}\n\npub struct ViewportState {\n    pub id: ViewportId,\n    pub visible: bool,\n    pub immediate: bool,\n    pub title: String,\n    pub children: Vec<Arc<RwLock<Self>>>,\n}\n\nimpl ViewportState {\n    pub fn new_deferred(\n        title: &'static str,\n        children: Vec<Arc<RwLock<Self>>>,\n    ) -> Arc<RwLock<Self>> {\n        Arc::new(RwLock::new(Self {\n            id: ViewportId::from_hash_of(title),\n            visible: false,\n            immediate: false,\n            title: title.into(),\n            children,\n        }))\n    }\n\n    pub fn new_immediate(\n        title: &'static str,\n        children: Vec<Arc<RwLock<Self>>>,\n    ) -> Arc<RwLock<Self>> {\n        Arc::new(RwLock::new(Self {\n            id: ViewportId::from_hash_of(title),\n            visible: false,\n            immediate: true,\n            title: title.into(),\n            children,\n        }))\n    }\n\n    pub fn show(vp_state: Arc<RwLock<Self>>, ctx: &egui::Context, close_button: bool) {\n        if !vp_state.read().visible {\n            return;\n        }\n        let vp_id = vp_state.read().id;\n        let immediate = vp_state.read().immediate;\n        let title = vp_state.read().title.clone();\n\n        let viewport = ViewportBuilder::default()\n            .with_title(&title)\n            .with_close_button(close_button)\n            .with_inner_size([500.0, 500.0]);\n\n        if immediate {\n            let mut vp_state = vp_state.write();\n            ctx.show_viewport_immediate(vp_id, viewport, move |ui, class| {\n                if ui.input(|i| i.viewport().close_requested()) {\n                    vp_state.visible = false;\n                }\n                show_as_popup(ui, class, |ui: &mut egui::Ui| {\n                    generic_child_ui(ui, &mut vp_state, close_button);\n                });\n            });\n        } else {\n            let count = Arc::new(RwLock::new(0));\n            ctx.show_viewport_deferred(vp_id, viewport, move |ui, class| {\n                let mut vp_state = vp_state.write();\n                if ui.input(|i| i.viewport().close_requested()) {\n                    vp_state.visible = false;\n                }\n                let count = Arc::clone(&count);\n                show_as_popup(ui, class, move |ui: &mut egui::Ui| {\n                    let current_count = *count.read();\n                    ui.label(format!(\"Callback has been reused {current_count} times\"));\n                    *count.write() += 1;\n\n                    generic_child_ui(ui, &mut vp_state, close_button);\n                });\n            });\n        }\n    }\n\n    pub fn set_visible_recursive(&mut self, visible: bool) {\n        self.visible = visible;\n        for child in &self.children {\n            child.write().set_visible_recursive(true);\n        }\n    }\n}\n\npub struct App {\n    top: Vec<Arc<RwLock<ViewportState>>>,\n    close_button: bool,\n}\n\nimpl Default for App {\n    fn default() -> Self {\n        Self {\n            top: vec![\n                ViewportState::new_deferred(\n                    \"Top Deferred Viewport\",\n                    vec![\n                        ViewportState::new_deferred(\n                            \"DD: Deferred Viewport in Deferred Viewport\",\n                            vec![],\n                        ),\n                        ViewportState::new_immediate(\n                            \"DS: Immediate Viewport in Deferred Viewport\",\n                            vec![],\n                        ),\n                    ],\n                ),\n                ViewportState::new_immediate(\n                    \"Top Immediate Viewport\",\n                    vec![\n                        ViewportState::new_deferred(\n                            \"SD: Deferred Viewport in Immediate Viewport\",\n                            vec![],\n                        ),\n                        ViewportState::new_immediate(\n                            \"SS: Immediate Viewport in Immediate Viewport\",\n                            vec![],\n                        ),\n                    ],\n                ),\n            ],\n            close_button: true,\n        }\n    }\n}\n\nimpl eframe::App for App {\n    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {\n        egui::CentralPanel::default().show_inside(ui, |ui| {\n            ui.heading(\"Root viewport\");\n            {\n                let mut embed_viewports = ui.embed_viewports();\n                ui.checkbox(&mut embed_viewports, \"Embed all viewports\");\n                if ui.button(\"Open all viewports\").clicked() {\n                    for viewport in &self.top {\n                        viewport.write().set_visible_recursive(true);\n                    }\n                }\n                ui.set_embed_viewports(embed_viewports);\n            }\n            ui.checkbox(&mut self.close_button, \"with close button\");\n            generic_ui(ui, &self.top, self.close_button);\n        });\n    }\n}\n\n/// This will make the content as a popup if cannot has his own native window\nfn show_as_popup(\n    ui: &mut egui::Ui,\n    class: egui::ViewportClass,\n    content: impl FnOnce(&mut egui::Ui),\n) {\n    if class == egui::ViewportClass::EmbeddedWindow {\n        // Not a real viewport - already has a frame\n        content(ui);\n    } else {\n        egui::CentralPanel::default().show_inside(ui, content);\n    }\n}\n\nfn generic_child_ui(ui: &mut egui::Ui, vp_state: &mut ViewportState, close_button: bool) {\n    ui.horizontal(|ui| {\n        ui.label(\"Title:\");\n        if ui.text_edit_singleline(&mut vp_state.title).changed() {\n            // Title changes\n            ui.send_viewport_cmd_to(\n                vp_state.id,\n                egui::ViewportCommand::Title(vp_state.title.clone()),\n            );\n        }\n    });\n\n    generic_ui(ui, &vp_state.children, close_button);\n}\n\nfn generic_ui(ui: &mut egui::Ui, children: &[Arc<RwLock<ViewportState>>], close_button: bool) {\n    let container_id = ui.id();\n\n    let ctx = ui.ctx().clone();\n    ui.label(format!(\n        \"Frame nr: {} (this increases when this viewport is being rendered)\",\n        ctx.cumulative_pass_nr()\n    ));\n    ui.horizontal(|ui| {\n        let mut show_spinner =\n            ui.data_mut(|data| *data.get_temp_mut_or(container_id.with(\"show_spinner\"), false));\n        ui.checkbox(&mut show_spinner, \"Show Spinner (forces repaint)\");\n        if show_spinner {\n            ui.spinner();\n        }\n        ui.data_mut(|data| data.insert_temp(container_id.with(\"show_spinner\"), show_spinner));\n    });\n\n    ui.add_space(8.0);\n\n    ui.label(format!(\"Viewport Id: {:?}\", ctx.viewport_id()));\n    ui.label(format!(\n        \"Parent Viewport Id: {:?}\",\n        ctx.parent_viewport_id()\n    ));\n\n    ui.collapsing(\"Info\", |ui| {\n        ui.label(format!(\"zoom_factor: {}\", ctx.zoom_factor()));\n        ui.label(format!(\"pixels_per_point: {}\", ctx.pixels_per_point()));\n\n        if let Some(native_pixels_per_point) = ctx.input(|i| i.viewport().native_pixels_per_point) {\n            ui.label(format!(\n                \"native_pixels_per_point: {native_pixels_per_point:?}\"\n            ));\n        }\n        if let Some(monitor_size) = ctx.input(|i| i.viewport().monitor_size) {\n            ui.label(format!(\"monitor_size: {monitor_size:?} (points)\"));\n        }\n        if let Some(viewport_rect) = ui.input(|i| i.raw.screen_rect) {\n            ui.label(format!(\n                \"Viewport Rect: Pos: {:?}, Size: {:?} (points)\",\n                viewport_rect.min,\n                viewport_rect.size()\n            ));\n        }\n        if let Some(inner_rect) = ctx.input(|i| i.viewport().inner_rect) {\n            ui.label(format!(\n                \"Inner Rect: Pos: {:?}, Size: {:?} (points)\",\n                inner_rect.min,\n                inner_rect.size()\n            ));\n        }\n        if let Some(outer_rect) = ctx.input(|i| i.viewport().outer_rect) {\n            ui.label(format!(\n                \"Outer Rect: Pos: {:?}, Size: {:?} (points)\",\n                outer_rect.min,\n                outer_rect.size()\n            ));\n        }\n    });\n\n    if ctx.viewport_id() != ctx.parent_viewport_id() {\n        let parent = ctx.parent_viewport_id();\n        if ui.button(\"Set parent pos 0,0\").clicked() {\n            ctx.send_viewport_cmd_to(\n                parent,\n                egui::ViewportCommand::OuterPosition(egui::pos2(0.0, 0.0)),\n            );\n        }\n    }\n\n    if DRAG_AND_DROP_TEST {\n        drag_and_drop_test(ui);\n    }\n\n    if !children.is_empty() {\n        ui.separator();\n\n        ui.heading(\"Children:\");\n\n        for child in children {\n            let visible = {\n                let mut child_lock = child.write();\n                let ViewportState { visible, title, .. } = &mut *child_lock;\n                ui.checkbox(visible, title.as_str());\n                *visible\n            };\n            if visible {\n                ViewportState::show(Arc::clone(child), &ctx, close_button);\n            }\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n// Drag-and-drop between windows is not yet implemented, but there is some test code for it here:\n\nfn drag_and_drop_test(ui: &mut egui::Ui) {\n    use std::collections::HashMap;\n    use std::sync::OnceLock;\n\n    let container_id = ui.id();\n\n    const COLS: usize = 2;\n    static DATA: OnceLock<RwLock<DragAndDrop>> = OnceLock::new();\n    let data = DATA.get_or_init(Default::default);\n    data.write().init(container_id);\n\n    #[derive(Default)]\n    struct DragAndDrop {\n        containers_data: HashMap<Id, Vec<Vec<Id>>>,\n        data: HashMap<Id, String>,\n        counter: usize,\n        is_dragged: Option<Id>,\n    }\n\n    impl DragAndDrop {\n        fn init(&mut self, container: Id) {\n            if !self.containers_data.contains_key(&container) {\n                for i in 0..COLS {\n                    self.insert(\n                        container,\n                        i,\n                        format!(\"From: {container:?}, and is: {}\", self.counter),\n                    );\n                }\n            }\n        }\n\n        fn insert(&mut self, container: Id, col: usize, value: impl Into<String>) {\n            assert!(col < COLS, \"The coll should be less than: {COLS}\");\n\n            let value: String = value.into();\n            let id = Id::new(format!(\"%{}% {}\", self.counter, &value));\n            self.data.insert(id, value);\n            let viewport_data = self.containers_data.entry(container).or_insert_with(|| {\n                let mut res = Vec::new();\n                res.resize_with(COLS, Default::default);\n                res\n            });\n            self.counter += 1;\n\n            viewport_data[col].push(id);\n        }\n\n        fn cols(&self, container: Id, col: usize) -> Vec<(Id, String)> {\n            assert!(col < COLS, \"The col should be less than: {COLS}\");\n            let container_data = &self.containers_data[&container];\n            container_data[col]\n                .iter()\n                .map(|id| (*id, self.data[id].clone()))\n                .collect()\n        }\n\n        /// Move element ID to Viewport and col\n        fn mov(&mut self, to: Id, col: usize) {\n            let Some(id) = self.is_dragged.take() else {\n                return;\n            };\n            assert!(col < COLS, \"The col should be less than: {COLS}\");\n\n            // Should be a better way to do this!\n            #[expect(clippy::iter_over_hash_type)]\n            for container_data in self.containers_data.values_mut() {\n                for ids in container_data {\n                    ids.retain(|i| *i != id);\n                }\n            }\n\n            if let Some(container_data) = self.containers_data.get_mut(&to) {\n                container_data[col].push(id);\n            }\n        }\n\n        fn dragging(&mut self, id: Id) {\n            self.is_dragged = Some(id);\n        }\n    }\n\n    ui.separator();\n    ui.label(\"Drag and drop:\");\n    ui.columns(COLS, |ui| {\n        for col in 0..COLS {\n            let data = DATA.get().unwrap();\n            let ui = &mut ui[col];\n            let mut is_dragged = None;\n            let res = drop_target(ui, |ui| {\n                ui.set_min_height(60.0);\n                for (id, value) in data.read().cols(container_id, col) {\n                    drag_source(ui, id, |ui| {\n                        ui.add(egui::Label::new(value).sense(egui::Sense::click()));\n                        if ui.ctx().is_being_dragged(id) {\n                            is_dragged = Some(id);\n                        }\n                    });\n                }\n            });\n            if let Some(id) = is_dragged {\n                data.write().dragging(id);\n            }\n            if res.response.hovered() && ui.input(|i| i.pointer.any_released()) {\n                data.write().mov(container_id, col);\n            }\n        }\n    });\n}\n\n// This is taken from crates/egui_demo_lib/src/debo/drag_and_drop.rs\nfn drag_source<R>(\n    ui: &mut egui::Ui,\n    id: egui::Id,\n    body: impl FnOnce(&mut egui::Ui) -> R,\n) -> InnerResponse<R> {\n    let is_being_dragged = ui.ctx().is_being_dragged(id);\n\n    if !is_being_dragged {\n        let res = ui.scope(body);\n\n        // Check for drags:\n        let response = ui.interact(res.response.rect, id, egui::Sense::drag());\n        if response.hovered() {\n            ui.set_cursor_icon(egui::CursorIcon::Grab);\n        }\n        res\n    } else {\n        ui.set_cursor_icon(egui::CursorIcon::Grabbing);\n\n        // Paint the body to a new layer:\n        let layer_id = egui::LayerId::new(egui::Order::Tooltip, id);\n        let res = ui.scope_builder(UiBuilder::new().layer_id(layer_id), body);\n\n        if let Some(pointer_pos) = ui.ctx().pointer_interact_pos() {\n            let delta = pointer_pos - res.response.rect.center();\n            ui.ctx().set_transform_layer(\n                layer_id,\n                eframe::emath::TSTransform::from_translation(delta),\n            );\n        }\n\n        res\n    }\n}\n\n// TODO(emilk): Update to be more like `crates/egui_demo_lib/src/debo/drag_and_drop.rs`\nfn drop_target<R>(\n    ui: &mut egui::Ui,\n    body: impl FnOnce(&mut egui::Ui) -> R,\n) -> egui::InnerResponse<R> {\n    let is_being_dragged = ui.ctx().dragged_id().is_some();\n\n    let margin = egui::Vec2::splat(ui.visuals().clip_rect_margin); // 3.0\n\n    let background_id = ui.painter().add(egui::Shape::Noop);\n\n    let available_rect = ui.available_rect_before_wrap();\n    let inner_rect = available_rect.shrink2(margin);\n    let mut content_ui = ui.new_child(UiBuilder::new().max_rect(inner_rect));\n    let ret = body(&mut content_ui);\n\n    let outer_rect =\n        egui::Rect::from_min_max(available_rect.min, content_ui.min_rect().max + margin);\n    let (rect, response) = ui.allocate_at_least(outer_rect.size(), egui::Sense::hover());\n\n    let style = if is_being_dragged && response.hovered() {\n        ui.visuals().widgets.active\n    } else {\n        ui.visuals().widgets.inactive\n    };\n\n    let fill = style.bg_fill;\n    let stroke = style.bg_stroke;\n\n    ui.painter().set(\n        background_id,\n        egui::epaint::RectShape::new(\n            rect,\n            style.corner_radius,\n            fill,\n            stroke,\n            egui::StrokeKind::Inside,\n        ),\n    );\n\n    egui::InnerResponse::new(ret, response)\n}\n"
  },
  {
    "path": "web_demo/.gitignore",
    "content": "egui_demo_app_bg.wasm\negui_demo_app.js\n"
  },
  {
    "path": "web_demo/CNAME",
    "content": "www.egui.rs"
  },
  {
    "path": "web_demo/README.md",
    "content": "This folder contains the files required for the egui web demo hosted at <https://www.egui.rs/>.\n\n## Testing locally\n* Build with `scripts/build_demo_web.sh`\n* Host with `scripts/start_server.sh`\n* Open <http://localhost:8765/index.html>\n\n## Deploying egui.rs\nEach merge into `main` will trigger a new deploy\n"
  },
  {
    "path": "web_demo/example.html",
    "content": "<!DOCTYPE html>\n<html>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n\n<head>\n    <meta http-equiv=\"refresh\" content=\"0; url = https://www.egui.rs/#http\" />\n</head>\n\n<body>\n</body>\n\n</html>\n"
  },
  {
    "path": "web_demo/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n\n<!-- Disable zooming: -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n\n<head>\n    <title>egui – An immediate mode GUI written in Rust</title>\n    <style>\n        html {\n            /* Remove touch delay: */\n            touch-action: manipulation;\n        }\n\n        body {\n            /* Light mode background color for what is not covered by the egui canvas,\n            or where the egui canvas is translucent. */\n            background: #909090;\n        }\n\n        @media (prefers-color-scheme: dark) {\n            body {\n                /* Dark mode background color for what is not covered by the egui canvas,\n                or where the egui canvas is translucent. */\n                background: #404040;\n            }\n        }\n\n        /* Allow canvas to fill entire web page: */\n        html,\n        body {\n            overflow: hidden;\n            margin: 0 !important;\n            padding: 0 !important;\n            height: 100%;\n            width: 100%;\n        }\n\n        /* Make canvas fill entire document: */\n        canvas {\n            margin-right: auto;\n            margin-left: auto;\n            display: block;\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n        }\n\n        .centered {\n            margin-right: auto;\n            margin-left: auto;\n            display: block;\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            color: #f0f0f0;\n            font-size: 24px;\n            font-family: Ubuntu-Light, Helvetica, sans-serif;\n            text-align: center;\n        }\n\n        /* ---------------------------------------------- */\n        /* Loading animation from https://loading.io/css/ */\n        .lds-dual-ring {\n            display: inline-block;\n            width: 24px;\n            height: 24px;\n        }\n\n        .lds-dual-ring:after {\n            content: \" \";\n            display: block;\n            width: 24px;\n            height: 24px;\n            margin: 0px;\n            border-radius: 50%;\n            border: 3px solid #fff;\n            border-color: #fff transparent #fff transparent;\n            animation: lds-dual-ring 1.2s linear infinite;\n        }\n\n        @keyframes lds-dual-ring {\n            0% {\n                transform: rotate(0deg);\n            }\n\n            100% {\n                transform: rotate(360deg);\n            }\n        }\n    </style>\n</head>\n\n<body>\n    <canvas id=\"the_canvas_id\"></canvas>\n\n    <div class=\"centered\" id=\"center_text\">\n        <p style=\"font-size:16px\">\n            Loading…\n        </p>\n        <div class=\"lds-dual-ring\"></div>\n    </div>\n\n    <script>\n        // The `--no-modules`-generated JS from `wasm-bindgen` attempts to use\n        // `WebAssembly.instantiateStreaming` to instantiate the wasm module,\n        // but this doesn't work with `file://` urls. This example is frequently\n        // viewed by simply opening `index.html` in a browser (with a `file://`\n        // url), so it would fail if we were to call this function!\n        //\n        // Work around this for now by deleting the function to ensure that the\n        // `no_modules.js` script doesn't have access to it. You won't need this\n        // hack when deploying over HTTP.\n        delete WebAssembly.instantiateStreaming;\n    </script>\n\n    <!-- this is the JS generated by the `wasm-bindgen` CLI tool -->\n    <script src=\"./egui_demo_app.js\"></script>\n\n    <script>\n        // We'll defer our execution until the wasm is ready to go.\n        // Here we tell bindgen the path to the wasm file so it can start\n        // initialization and return to us a promise when it's done.\n        console.debug(\"Loading wasm…\");\n        wasm_bindgen(\"./egui_demo_app_bg.wasm\")\n            .then(on_wasm_loaded)\n            .catch(on_error);\n\n        function on_wasm_loaded() {\n            console.debug(\"Wasm loaded. Starting app…\");\n\n            let handle = new wasm_bindgen.WebHandle();\n\n            function check_for_panic() {\n                if (handle.has_panicked()) {\n                    console.error(\"The egui app has crashed\");\n\n                    // The demo app already logs the panic message and callstack, but you\n                    // can access them like this if you want to show them in the html:\n                    // console.error(`${handle.panic_message()}`);\n                    // console.error(`${handle.panic_callstack()}`);\n\n                    document.getElementById(\"the_canvas_id\").remove();\n                    document.getElementById(\"center_text\").innerHTML = `\n                        <p>\n                            The egui app has crashed.\n                        </p>\n                        <p style=\"font-size:10px\" align=\"left\">\n                            ${handle.panic_message()}\n                        </p>\n                        <p style=\"font-size:14px\">\n                            See the console for details.\n                        </p>\n                        <p style=\"font-size:14px\">\n                            Reload the page to try again.\n                        </p>`;\n                } else {\n                    let delay_ms = 1000;\n                    setTimeout(check_for_panic, delay_ms);\n                }\n            }\n\n            check_for_panic();\n\n            handle.start(document.getElementById(\"the_canvas_id\")).then(on_app_started).catch(on_error);\n        }\n\n        function on_app_started(handle) {\n            // Call `handle.destroy()` to stop. Uncomment to quick result:\n            // setTimeout(() => { handle.destroy(); handle.free()) }, 2000)\n\n            console.debug(\"App started.\");\n            document.getElementById(\"center_text\").innerHTML = '';\n\n            // Make sure the canvas is focused so it can receive keyboard events right away:\n            document.getElementById(\"the_canvas_id\").focus();\n        }\n\n        function on_error(error) {\n            console.error(\"Failed to start: \" + error);\n            document.getElementById(\"the_canvas_id\").remove();\n            document.getElementById(\"center_text\").innerHTML = `\n                <p>\n                    An error occurred during loading:\n                </p>\n                <p style=\"font-family:Courier New\">\n                    ${error}\n                </p>\n                <p style=\"font-size:14px\">\n                    Make sure you use a modern browser with WebGL and WASM enabled.\n                </p>`;\n        }\n    </script>\n</body>\n\n</html>\n\n<!-- Powered by egui: https://github.com/emilk/egui/ -->\n"
  },
  {
    "path": "web_demo/multiple_apps.html",
    "content": "<!DOCTYPE html>\n<html>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n\n<!-- Disable zooming: -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n\n<head>\n    <title>egui – An immediate mode GUI written in Rust</title>\n    <style>\n        html {\n            /* Remove touch delay: */\n            touch-action: manipulation;\n        }\n\n        body {\n            /* Light mode background color for what is not covered by the egui canvas,\n            or where the egui canvas is translucent. */\n            background: #909090;\n            display: flex;\n        }\n\n        @media (prefers-color-scheme: dark) {\n            body {\n                /* Dark mode background color for what is not covered by the egui canvas,\n                or where the egui canvas is translucent. */\n                background: #404040;\n            }\n        }\n\n        /* Allow canvas to fill entire web page: */\n        html,\n        body {\n            height: 100%;\n        }\n\n        canvas {\n            margin: 8px;\n            padding: 8px;\n            width: 45%;\n            height: 110%;\n        }\n\n        .centered {\n            margin-right: auto;\n            margin-left: auto;\n            display: block;\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            color: #f0f0f0;\n            font-size: 24px;\n            font-family: Ubuntu-Light, Helvetica, sans-serif;\n            text-align: center;\n        }\n\n        /* ---------------------------------------------- */\n        /* Loading animation from https://loading.io/css/ */\n        .lds-dual-ring {\n            display: inline-block;\n            width: 24px;\n            height: 24px;\n        }\n\n        .lds-dual-ring:after {\n            content: \" \";\n            display: block;\n            width: 24px;\n            height: 24px;\n            margin: 0px;\n            border-radius: 50%;\n            border: 3px solid #fff;\n            border-color: #fff transparent #fff transparent;\n            animation: lds-dual-ring 1.2s linear infinite;\n        }\n\n        @keyframes lds-dual-ring {\n            0% {\n                transform: rotate(0deg);\n            }\n\n            100% {\n                transform: rotate(360deg);\n            }\n        }\n    </style>\n</head>\n\n<body>\n    <!-- The WASM code will resize the canvas dynamically -->\n\n    <button class=\"stop_one\">\n        Stop one app\n    </button>\n\n    <canvas id=\"canvas_id_one\"></canvas>\n\n    <canvas id=\"canvas_id_two\"></canvas>\n\n    <div class=\"centered\" id=\"center_text\">\n        <p style=\"font-size:16px\">\n            Loading…\n        </p>\n        <div class=\"lds-dual-ring\"></div>\n    </div>\n\n    <script>\n        // The `--no-modules`-generated JS from `wasm-bindgen` attempts to use\n        // `WebAssembly.instantiateStreaming` to instantiate the wasm module,\n        // but this doesn't work with `file://` urls. This example is frequently\n        // viewed by simply opening `index.html` in a browser (with a `file://`\n        // url), so it would fail if we were to call this function!\n        //\n        // Work around this for now by deleting the function to ensure that the\n        // `no_modules.js` script doesn't have access to it. You won't need this\n        // hack when deploying over HTTP.\n        delete WebAssembly.instantiateStreaming;\n    </script>\n\n    <!-- this is the JS generated by the `wasm-bindgen` CLI tool -->\n    <script src=\"egui_demo_app.js\"></script>\n\n    <script>\n        // We'll defer our execution until the wasm is ready to go.\n        // Here we tell bindgen the path to the wasm file so it can start\n        // initialization and return to us a promise when it's done.\n        console.debug(\"Loading wasm…\");\n        wasm_bindgen(\"./egui_demo_app_bg.wasm\")\n            .then(on_wasm_loaded)\n            .catch(on_error);\n\n        function on_wasm_loaded() {\n            console.debug(\"Wasm loaded. Starting apps…\");\n\n            // This call installs a bunch of callbacks and then returns:\n\n            const handle_one = new wasm_bindgen.WebHandle();\n            const handle_two = new wasm_bindgen.WebHandle();\n\n            Promise.all([\n                handle_one.start(document.getElementById(\"canvas_id_one\")),\n                handle_two.start(document.getElementById(\"canvas_id_two\")),\n            ]).then((handles) => {\n                on_apps_started(handle_one, handle_two)\n            }).catch(on_error);\n        }\n\n        function on_apps_started(handle_one, handle_two) {\n            const button = document.getElementsByClassName(\"stop_one\")[0]\n\n            button.addEventListener(\"click\", () => {\n                document.getElementById(\"canvas_id_one\").remove()\n                handle_one.destroy()\n                handle_one.free()\n            });\n\n            // Call `handle.destroy()` to stop. Uncomment to quick result:\n            // setTimeout(() => { handle.destroy() }, 2000)\n\n            console.debug(\"Apps started.\");\n            document.getElementById(\"center_text\").remove();\n        }\n\n        function on_error(error) {\n            console.error(\"Failed to start: \" + error);\n            document.getElementById(\"center_text\").innerHTML = `\n                <p>\n                    An error occurred during loading:\n                </p>\n                <p style=\"font-family:Courier New\">\n                    ${error}\n                </p>\n                <p style=\"font-size:14px\">\n                    Make sure you use a modern browser with WebGL and WASM enabled.\n                </p>`;\n        }\n    </script>\n</body>\n\n</html>\n\n<!-- Powered by egui: https://github.com/emilk/egui/ -->\n"
  },
  {
    "path": "xtask/Cargo.toml",
    "content": "[package]\nname = \"xtask\"\nedition.workspace = true\nlicense.workspace = true\nrust-version.workspace = true\nversion.workspace = true\npublish = false\n\n[lints]\nworkspace = true\n\n[dependencies]\n"
  },
  {
    "path": "xtask/README.md",
    "content": "## xtask - Task automation\n\nThis crate is meant to automate common tasks on the repository. It serves as a\nreplacement for shell scripts that is more portable across host operating\nsystems (namely Windows) and hopefully also easier to work with for\ncontributors who are already familiar with Rust (and not necessarily with shell\nscripting).\n\nThe executable can be invoked via the subcommand `cargo xtask`, thanks to an\nalias defined in `.cargo/config.toml`.\n\nFor more information, see <https://github.com/matklad/cargo-xtask>.\n"
  },
  {
    "path": "xtask/src/deny.rs",
    "content": "//! Run `cargo deny`\n//!\n//! Also installs the subcommand if it is not already installed.\n\nuse std::process::Command;\n\nuse super::DynError;\n\npub fn deny(args: &[&str]) -> Result<(), DynError> {\n    if !args.is_empty() {\n        return Err(format!(\"Invalid arguments: {args:?}\").into());\n    }\n    install_cargo_deny()?;\n    let targets = [\n        \"aarch64-apple-darwin\",\n        \"aarch64-linux-android\",\n        \"i686-pc-windows-gnu\",\n        \"i686-pc-windows-msvc\",\n        \"i686-unknown-linux-gnu\",\n        \"wasm32-unknown-unknown\",\n        \"x86_64-apple-darwin\",\n        \"x86_64-pc-windows-gnu\",\n        \"x86_64-pc-windows-msvc\",\n        \"x86_64-unknown-linux-gnu\",\n        \"x86_64-unknown-linux-musl\",\n        \"x86_64-unknown-redox\",\n    ];\n    for target in targets {\n        let mut cmd = Command::new(\"cargo\");\n        cmd.args([\n            \"deny\",\n            \"--all-features\",\n            \"--log-level\",\n            \"error\",\n            \"--target\",\n            target,\n            \"check\",\n        ]);\n        super::utils::print_cmd(&cmd);\n        let status = cmd.status()?;\n        if !status.success() {\n            return Err(status.to_string().into());\n        }\n    }\n    Ok(())\n}\n\nfn install_cargo_deny() -> Result<(), DynError> {\n    let already_installed = Command::new(\"cargo\")\n        .args([\"deny\", \"--version\"])\n        .output()\n        .is_ok_and(|out| out.status.success());\n    if already_installed {\n        return Ok(());\n    }\n    let mut cmd = Command::new(\"cargo\");\n    cmd.args([\"+stable\", \"install\", \"--quiet\", \"--locked\", \"cargo-deny\"]);\n    let reason = \"install cargo-deny\";\n    super::utils::ask_to_run(cmd, true, reason)\n}\n"
  },
  {
    "path": "xtask/src/main.rs",
    "content": "//! Helper crate for running scripts within the `egui` repo\n\n#![expect(clippy::print_stderr, clippy::print_stdout)]\n#![allow(clippy::exit)]\n\nmod deny;\npub(crate) mod utils;\n\ntype DynError = Box<dyn std::error::Error>;\n\nfn main() {\n    if let Err(e) = try_main() {\n        eprintln!(\"{e}\");\n        std::process::exit(-1);\n    }\n}\n\nfn try_main() -> Result<(), DynError> {\n    let arg_strings: Vec<_> = std::env::args().skip(1).collect();\n    let args: Vec<_> = arg_strings.iter().map(String::as_str).collect();\n\n    match args.as_slice() {\n        &[] | &[\"-h\"] | &[\"--help\"] => print_help(),\n        &[\"deny\", ..] => deny::deny(&args[1..])?,\n        c => Err(format!(\"Invalid arguments {c:?}\"))?,\n    }\n    Ok(())\n}\n\nfn print_help() {\n    let help = \"\n    xtask help\n\n    Subcommands\n    deny: Run cargo-deny for all targets\n\n    Options\n    -h, --help: print help and exit\n        \";\n    println!(\"{help}\");\n}\n"
  },
  {
    "path": "xtask/src/utils.rs",
    "content": "#![expect(clippy::unwrap_used)]\n\nuse std::{\n    env,\n    io::{self, Write as _},\n    process::Command,\n};\n\nuse super::DynError;\n\n/// Print the command and its arguments as if the user had typed them\npub fn print_cmd(cmd: &Command) {\n    print!(\"{} \", cmd.get_program().to_string_lossy());\n    for arg in cmd.get_args() {\n        print!(\"{} \", arg.to_string_lossy());\n    }\n    println!();\n}\n\n/// Prompt user before running a command\n///\n/// Adapted from [miri](https://github.com/rust-lang/miri/blob/dba35d2be72f4b78343d1a0f0b4737306f310672/cargo-miri/src/util.rs#L181-L204)\npub fn ask_to_run(mut cmd: Command, ask: bool, reason: &str) -> Result<(), DynError> {\n    // Disable interactive prompts in CI (GitHub Actions, Travis, AppVeyor, etc).\n    // Azure doesn't set `CI` though (nothing to see here, just Microsoft being Microsoft),\n    // so we also check their `TF_BUILD`.\n    let is_ci = env::var_os(\"CI\").is_some() || env::var_os(\"TF_BUILD\").is_some();\n    if ask && !is_ci {\n        let mut buf = String::new();\n        print!(\"The script is going to run: \\n\\n`{cmd:?}`\\n\\n To {reason}.\\nProceed? [Y/n] \",);\n        io::stdout().flush().unwrap();\n        io::stdin().read_line(&mut buf).unwrap();\n        match buf.trim().to_lowercase().as_ref() {\n            \"\" | \"y\" | \"yes\" => {}\n            \"n\" | \"no\" => return Err(\"Aborting as per your request\".into()),\n            a => return Err(format!(\"Invalid answer `{a}`\").into()),\n        }\n    } else {\n        println!(\"Running `{cmd:?}` to {reason}.\");\n    }\n\n    let status = cmd.status()?;\n    if !status.success() {\n        return Err(format!(\"failed to {reason}: {status}\").into());\n    }\n    Ok(())\n}\n"
  }
]