-
-
Notifications
You must be signed in to change notification settings - Fork 345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
update panels and their scrollbars when window resizes #358
update panels and their scrollbars when window resizes #358
Conversation
@@ -630,7 +630,7 @@ impl Widget { | |||
w.restore(ctx, prev); | |||
} | |||
} else if self.widget.can_restore() { | |||
if let Some(ref other) = prev.top_level.find(self.id.as_ref().unwrap()) { | |||
if let Some(ref other) = prev.maybe_find(self.id.as_ref().unwrap()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to make top_level private so I could confidently mess with it within Panel, this was the only external usage.
widgetry/src/widgets/panel.rs
Outdated
// Wrap the main widget in scrollable containers if necessary. | ||
if self.scrollable_x && !old_scrollable_x { | ||
let mut place_holder = Widget::nothing(); | ||
std::mem::swap(&mut place_holder, &mut self.top_level); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My Rust-fu is weak. Is there a better way to do this shuffling?
The old self.top_level
is owned by the new self.top_level
so there's kind of an ownership circle without temporarily introducing this Widget::nothing()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do let placeholder = std::mem::replace(&mut self.top_level, Widget::nothing())
. That's a slightly shorter form, equivalent to what you have here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a little better - updated!
@@ -180,10 +262,9 @@ impl Panel { | |||
Polygon::rectangle(self.container_dims.width, self.container_dims.height) | |||
.translate(top_left.x, top_left.y), | |||
); | |||
g.unfork(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not related to my change. And didn't seem to be causing problems as is... but it seemed wrong where it was. I assumed all unfork
should have a corresponding fork
.
I'm only like 1/2 sure I understand what fork
ing is even about - I think it's about nesting coordinate systems, akin to UIKit's#bounds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assumed all unfork should have a corresponding fork.
Correct, they should. Here must've been missing by mistake. unfork
isn't strictly needed if there's a later explicit fork
, which must've wound up being true here.
I think it's about nesting coordinate systems
fork
is about making the GPU do coordinate transformations. At first, it was just "screen-space" and "map-space". We want to do map_to_screen
inside the vertex shader for things that can pan and zoom, but not for screen-space UI things. Later I started using it also to shift/compress map-space, like for the minimap. It's possible some of those uses could instead do the work on the CPU, calling translate
and scale
on the GeomBatch
. Haven't tried that. I'd assume it'd be much slower initially for things like the minimap, which has a large amount of geometry.
@@ -146,6 +227,7 @@ impl Panel { | |||
} | |||
|
|||
if ctx.input.is_window_resized() { | |||
self.update_container_dims_for_canvas_dims(ctx.canvas.get_window_dims()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is another key change - don't miss it. 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a nice fix, thanks! I found some weird behavior if I launch something like the jump-to-time dialog and then resize:
But the behavior before this PR is also broken, just in a different way. Is this expected, or is it something you'd expect to be fixed?
Upon launch the window goes through a series of rapid size changes, starting small and growing to full size (this might just be a macos thing, I'm not sure?).
I think Linux does the same, maybe it's just Windows that initially gives a window with the full requested size. I don't remember exactly, but one of the bugs people reported right after alpha launch demonstrated the different window sizing behavior on diff platforms.
widgetry/src/widgets/panel.rs
Outdated
// Wrap the main widget in scrollable containers if necessary. | ||
if self.scrollable_x && !old_scrollable_x { | ||
let mut place_holder = Widget::nothing(); | ||
std::mem::swap(&mut place_holder, &mut self.top_level); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do let placeholder = std::mem::replace(&mut self.top_level, Widget::nothing())
. That's a slightly shorter form, equivalent to what you have here
@@ -180,10 +262,9 @@ impl Panel { | |||
Polygon::rectangle(self.container_dims.width, self.container_dims.height) | |||
.translate(top_left.x, top_left.y), | |||
); | |||
g.unfork(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assumed all unfork should have a corresponding fork.
Correct, they should. Here must've been missing by mistake. unfork
isn't strictly needed if there's a later explicit fork
, which must've wound up being true here.
I think it's about nesting coordinate systems
fork
is about making the GPU do coordinate transformations. At first, it was just "screen-space" and "map-space". We want to do map_to_screen
inside the vertex shader for things that can pan and zoom, but not for screen-space UI things. Later I started using it also to shift/compress map-space, like for the minimap. It's possible some of those uses could instead do the work on the CPU, calling translate
and scale
on the GeomBatch
. Haven't tried that. I'd assume it'd be much slower initially for things like the minimap, which has a large amount of geometry.
Hmmm, nope that's not expected. I'll see if I can figure out what's going on. |
So the issue is that, similar to what this PR fixes for Panels, scrollbar (Slider) size and position is hardcoded at creation time. As an optimization, I only created the scrollbars when they first become necessary, so their size and position was staying at whatever it was when the scrollbar is first drawn. I'm now re-creating the sliders whenever a Panel re-computes it's layout, so it can now incorporate it's new size and location. I spent maybe an hour fixing this problem. Then, wanting to make sure the perf wasn't terrible, spent another 3 hours digging into The perf is pretty inconsequential even when regenerating the scrollbars all the time. If we did want to improve perf, I left a TODO about being smarter about when we actually need to recompute layout. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I spent maybe an hour fixing this problem. Then, wanting to make sure the perf wasn't terrible, spent another 3 hours digging into cargo flamegraph bugs on macos. disappointed
Oh no, yak shaving :(
If we did want to improve perf
My attitude so far has been, don't optimize it until it's noticeable. There's tons of low-hanging fruit to be more incremental in widgetry. No need yet, thankfully.
So your change makes sense, but I think it breaks things:
- The scrollbars in the timeseries demo go away by default (they worked before this PR)
- upon resizing the window, now the scrollbars show up in the right spot (fixed with this PR), but now all the scrollbars can't be moved (a regression)
I'll take a look at some of these this weekend
let (bg_texture, fg_texture) = self.controls.dropdown_value("texture dropdown"); | ||
"apply" => { | ||
let (v_align, h_align) = self.controls.dropdown_value("alignment"); | ||
self.controls.align(v_align, h_align); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice addition to the demo! I was a bit confused at first, forgot to hit "Apply". Dunno how much UX design we need to do for the demo -- I like demonstrating dropdown_value
here.
8b30015
to
ab8ed68
Compare
TBH I don't know of problem currently being caused by this, but it seemed wrong to unfork without a matching fork.
"c" was holdover from when panels were called composites
ab8ed68
to
90de843
Compare
90de843
to
ffc5728
Compare
Ah, previously we were laying out twice when building the panel. I thought we could avoid it, but I was wrong. I've added it back and some comments explaining why here: 07117ab
Shoot. I broke this while fixing this:
This fixes the inability to scroll by restoring old scroll offset to the new scrollbars: ffc5728 I'd like to do more to make panels responsive in the future - in particular, I think the big missing piece is allowing them to resize their content size without going through the "rebuild the panel and restore dance". |
This is ready for re-review PTAL @dabreegster |
All my resizing tests work as expected; it's quite intuitive how a scrollbar grows/shrinks on a partly-scrolled panel. Nice!
SGTM. The other two related feature requests from Yuwen to keep in mind, that may relate to this implementation:
|
exciting! these seem aligned with a longer term goal of mine to start leveraging relative position/size rather than only absolute sizes. I've only barely scratched the surface on the |
Panels calculate their container size and whether or not to show scroll bars based on the canvas size. They do this only once when the panel is first built.
But canvas size changes as window size changes.
Window size can change manually as the user manually adjusts it, or plugs in an external monitor, etc. But one place this is particularly annoying for me personally is upon first launch. Upon launch the window goes through a series of rapid size changes, starting small and growing to full size (this might just be a macos thing, I'm not sure?).
But this means the panels are all drawn WRT to the initial small size.
before
e.g. look how the top center panel looks broken here:
But even putting the initial launch thing aside, there are a bunch of expected scenarios where the use can change their window size mid-run, and we should try to handle them.
This PR moves the scrollbar manipulation into the Panel, rather than the PanelBuilder, so that it can be recomputed as necessary.
after