Prevent `ScrollArea` contents from exceeding the container size (#5006)
When a `ScrollArea` is added to a `Ui` or its contents change
dynamically, the contents will briefly escape the container. This occurs
because `ScrollArea` internally maintains `content_is_too_large` flags,
from which it determines when to clip. The `content_is_too_large` flags
are calculated after painting, so they always lag one frame behind. This
can lead to flickering.
To fix this, I have changed the `ScrollArea` so that it always clips
scrollable content. I believe that this should fix things without
negatively impacting other behavior. To see this, consider how
`ScrollArea` calculates the `content_is_too_large` flag:
```rust
// This calculates a new inner rect, after painting, from the initial clip rect
let inner_rect = {
// At this point this is the available size for the inner rect.
let mut inner_size = inner_rect.size();
for d in 0..2 {
inner_size[d] = match (scroll_enabled[d], auto_shrink[d]) {
(true, true) => inner_size[d].min(content_size[d]), // shrink scroll area if content is small
(true, false) => inner_size[d], // let scroll area be larger than content; fill with blank space
(false, true) => content_size[d], // Follow the content (expand/contract to fit it).
(false, false) => inner_size[d].max(content_size[d]), // Expand to fit content
};
}
Rect::from_min_size(inner_rect.min, inner_size)
};
let outer_rect = Rect::from_min_size(inner_rect.min, inner_rect.size() + current_bar_use);
let content_is_too_large = Vec2b::new(
scroll_enabled[0] && inner_rect.width() < content_size.x,
scroll_enabled[1] && inner_rect.height() < content_size.y,
);
```
If `scroll_enabled[d] == true`, then the actual `inner_rect` (which is
calculated after painting contents) will always be smaller than the
original `inner_rect`. Hence, it is safe to unconditionally clip the
contents to `inner_rect` whenever `scroll_enabled[d] == true`.
<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUTING.md)
before opening a Pull Request!
* Keep your PR:s small and focused.
* The PR title is what ends up in the changelog, so make it descriptive!
* If applicable, add a screenshot or gif.
* If it is a non-trivial addition, consider adding a demo for it to
`egui_demo_lib`, or a new example.
* Do NOT open PR:s from your `master` branch, as that makes it hard for
maintainers to test and add commits to your PR.
* Remember to run `cargo fmt` and `cargo clippy`.
* Open the PR as a draft until you have self-reviewed it and run
`./scripts/check.sh`.
* When you have addressed a PR comment, mark it as resolved.
Please be patient! I will review your PR, but my time is limited!
-->
* Closes <https://github.com/emilk/egui/issues/4742>
* [x] I have followed the instructions in the PR template
This commit is contained in:
parent
0f8614d69e
commit
73bb4cedb4
|
|
@ -572,10 +572,8 @@ impl ScrollArea {
|
|||
let mut content_clip_rect = ui.clip_rect();
|
||||
for d in 0..2 {
|
||||
if scroll_enabled[d] {
|
||||
if state.content_is_too_large[d] {
|
||||
content_clip_rect.min[d] = inner_rect.min[d] - clip_rect_margin;
|
||||
content_clip_rect.max[d] = inner_rect.max[d] + clip_rect_margin;
|
||||
}
|
||||
content_clip_rect.min[d] = inner_rect.min[d] - clip_rect_margin;
|
||||
content_clip_rect.max[d] = inner_rect.max[d] + clip_rect_margin;
|
||||
} else {
|
||||
// Nice handling of forced resizing beyond the possible:
|
||||
content_clip_rect.max[d] = ui.clip_rect().max[d] - current_bar_use[d];
|
||||
|
|
|
|||
Loading…
Reference in New Issue