Skip to content
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

Implement update_scrolls pass #550

Merged
merged 4 commits into from
Sep 9, 2024
Merged

Conversation

PoignardAzur
Copy link
Contributor

@PoignardAzur PoignardAzur commented Aug 25, 2024

This is part of the Pass Specification RFC: linebender/rfcs#7

Note: This PRs comes with a lot of new TODO items. Addressing most of these items is difficult without major refactors, because Portal code deals with accessing values across multiple widgets, which is still hard to do elegantly.

@PoignardAzur
Copy link
Contributor Author

And that's the last pass implemented!

Once we've merged all of them, the next step will be to replace RenderRoot::post_event_processing with something like RenderRoot::run_rewrite_passes, following the logic outlined in the pass spec RFC.

DJMcNab
DJMcNab previously approved these changes Aug 26, 2024
Copy link
Member

@DJMcNab DJMcNab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way in the current examples to test this?

Actual code changes look reasonable, presuming that the panning maths is correct

run_targeted_update_pass(root, Some(target), |widget, ctx| {
let event = LifeCycle::RequestPanToChild(rect);
widget.lifecycle(ctx, &event);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will smooth scroll would be handled?

The best logic I can see if to "get where the widget will be", and all the parents scroll to that.

Copy link
Contributor Author

@PoignardAzur PoignardAzur Aug 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends what you mean by smooth scroll. Do you mean on a touchscreen?

I think the most straightforward solution is to have CSS-style animatable properties, and have scroll progress be one of them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mean on a touchscreen, I mean scrolling to a position over a certain amount of time.

Right, yeah. This logic would have to change at that point, but it's also deliberately unaddressed at the moment.

let _span = info_span!("update_scroll").entered();

let scroll_request_targets = std::mem::take(&mut root.state.scroll_request_targets);
for (target, rect) in scroll_request_targets {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the order matter here? Is it meaningful to document that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order doesn't matter. scroll_request_target was initially an option, and I turned it to a Vec just to cover hypothetical cases where someone would want to pan several scroll areas at once.

I guess several targets could interfere if they're in the same scroll region. I think this will be rare enough that we won't need to document it.

// Note - Rect is in child coordinates
// TODO - Merge with pan_viewport_to
// Right now these functions are just different enough to be a pain to merge.
pub fn pan_viewport_to_raw(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pub, so needs a doc comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not supposed to be pub. Fixed.

viewport.min_y()..viewport.max_y(),
target.min_y()..target.max_y(),
)
.start;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we prefer the top-left of the rectangle to go into view. That seems reasonable, but should probably be documented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added in a comment on the pass function. I'll do a more thorough documentation pass later.

@PoignardAzur
Copy link
Contributor Author

Is there any way in the current examples to test this?

The to_do_list example.

@DJMcNab DJMcNab dismissed their stale review August 26, 2024 13:27

The scroll functionality in the suggested example is completely broken by this change

@DJMcNab
Copy link
Member

DJMcNab commented Aug 26, 2024

The to_do_list example.

Thanks, checked it out and I can see that it is now impossible to scroll in that example.

@PoignardAzur
Copy link
Contributor Author

Thanks, checked it out and I can see that it is now impossible to scroll in that example.

That doesn't sound right, it worked when I tested it. Maybe I forgot to push some changes? Will look into it.

@DJMcNab
Copy link
Member

DJMcNab commented Aug 27, 2024

Ah, this is #424 . Indeed, applying

diff --git a/masonry/src/widget/textbox.rs b/masonry/src/widget/textbox.rs
index 8a85f2d8e..7ab850e26 100644
--- a/masonry/src/widget/textbox.rs
+++ b/masonry/src/widget/textbox.rs
@@ -13,6 +13,7 @@ use vello::{
     peniko::{BlendMode, Color},
     Scene,
 };
+use winit::event::Ime;
 
 use crate::widget::{LineBreaking, WidgetMut};
 use crate::{
@@ -208,7 +209,9 @@ impl Widget for Textbox {
         // If focused on a link and enter pressed, follow it?
         if result.is_handled() {
             // TODO - Use request_scroll_to with cursor rect
-            ctx.request_scroll_to_this();
+            if !matches!(event, TextEvent::Ime(Ime::Preedit(..))) {
+                ctx.request_scroll_to_this();
+            }
             ctx.set_handled();
             // TODO: only some handlers need this repaint
             ctx.request_layout();

makes this work

Copy link
Member

@DJMcNab DJMcNab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the workaround mentioned above, this now works well.

Some remaining concerns are documenting the preferred part of the rectangle which is shown, and fixing the scrollbars properly.

let portal_size = ctx.size();
let content_size = ctx.get_raw_ref(&mut self.child).ctx().layout_rect().size();

// FIXME - Update scrollbars
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to do this in this PR, or explain why it's hard. The behaviour without that is quite bad.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -207,6 +207,8 @@ impl Widget for Textbox {
let result = self.editor.text_event(ctx, event);
// If focused on a link and enter pressed, follow it?
if result.is_handled() {
// TODO - Use request_scroll_to with cursor rect
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine this will happen in #515 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably.

Note: This PRs comes with a lot of new TODO items.
Addressing most of these items is difficult without major refactors.
This pass is still mostly functional.
@PoignardAzur PoignardAzur force-pushed the implement_pass_spec_update_scrolls branch from 54b607b to 7f359c8 Compare August 30, 2024 10:29
Copy link
Member

@DJMcNab DJMcNab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small remaining proposed tweak.

if result.is_handled() {
// Some platforms will send a lot of spurious Preedit events.
// We only want to request a scroll on user input.
if !matches!(event, TextEvent::Ime(Ime::Preedit(..))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh, apparently we can do

Suggested change
if !matches!(event, TextEvent::Ime(Ime::Preedit(..))) {
if !matches!(event, TextEvent::Ime(Ime::Preedit(preedit, ..)) if preedit.is_empty()) {

which in my testing has better behaviour (but is not the easiest to reason about in its current form)

@PoignardAzur PoignardAzur added this pull request to the merge queue Sep 9, 2024
Merged via the queue into main with commit 2ae7326 Sep 9, 2024
17 checks passed
@PoignardAzur PoignardAzur deleted the implement_pass_spec_update_scrolls branch September 9, 2024 11:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants