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 paint and accessibility passes #522

Merged
merged 21 commits into from
Aug 23, 2024

Conversation

PoignardAzur
Copy link
Contributor

No description provided.

@PoignardAzur
Copy link
Contributor Author

PoignardAzur commented Aug 17, 2024

(This is very much a draft, more so than usual. Accessibility isn't ported yet, and there's a bunch of changes I deliberately delayed to avoid creating huge diffs.)

@PoignardAzur PoignardAzur force-pushed the implement_pass_spec_paint_access branch from df31c3e to ca13a0d Compare August 17, 2024 13:09
@PoignardAzur PoignardAzur force-pushed the implement_pass_spec_paint_access branch 3 times, most recently from 8481254 to cbb10c0 Compare August 20, 2024 10:10
@PoignardAzur PoignardAzur marked this pull request as ready for review August 20, 2024 11:14
@PoignardAzur
Copy link
Contributor Author

I think this is ready for review now. I left some things for later (notably removing WidgetPod::paint and WidgetPod::accessibility and using a more general shape for the clip path) because the diff is large enough as it is.

I don't intend to add a default implementation for paint and accessibility, because I think widgets that don't need to do anything with them will be rare (even eg Flex will get properties like a background color); for accessibility in particular, I think we should encourage people to look at accessibility tree values before decide they don't need any, and having a default impl goes against that.

Copy link
Contributor

@jaredoconnell jaredoconnell left a comment

Choose a reason for hiding this comment

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

Looks good. I just have one minor comment.

Comment on lines +891 to +892
self.widget_state.request_accessibility = true;
self.widget_state.needs_accessibility = true;
self.widget_state.needs_paint = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

I've seen this repeated at least three times. It may make sense to create a function just for marking the state change for needing paint and accessibility.

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'd like to keep these kinds of refactor for last, especially if we're only factoring three lines or less. I think there's a benefit to being able to see directly what the code does instead of jumping to another function.

@@ -165,7 +165,7 @@ pub fn root_on_text_event(
handled
}

pub fn root_on_access_event(
pub(crate) fn root_on_access_event(
Copy link
Contributor

Choose a reason for hiding this comment

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

(Had commented directly on the commit, but then that doesn't show up here ...)

I use this in my own event loop integration. I need some way to pump an access event into the system ...

Copy link
Contributor

Choose a reason for hiding this comment

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

My mistake. I'm calling this on RenderRoot, not this one here.

@DJMcNab
Copy link
Member

DJMcNab commented Aug 21, 2024

First thought when reviewing. The only thing which renders in mason is the textbox, which I don't think is the intention

@PoignardAzur
Copy link
Contributor Author

The only thing which renders in mason is the textbox, which I don't think is the intention

This is also the case on main. Bisecting to find the culprit...

@PoignardAzur
Copy link
Contributor Author

PoignardAzur commented Aug 21, 2024

Apparently #527 introduced the regression.

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.

This is looking really good! I don't have any blocking concerns, but there are a few bits of clarification I'd like to see.

Comment on lines +67 to +68
state.item.request_accessibility = false;
state.item.needs_accessibility = false;
Copy link
Member

Choose a reason for hiding this comment

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

I suppose that since AccessCtx doesn't have these methods, it doesn't matter when these values are set, so long as it's after they are read?

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 assume that by "these methods" you mean "the methods that gave me problems in the layout PR"?

To be honest, I haven't decided on (or documented) a principled solution for that class of problem. I'm mostly deferring it until all passes are implemented.

But yeah, your reasoning makes sense.

Copy link
Member

Choose a reason for hiding this comment

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

In this case, I meant request_accessibility. I hadn't checked if get_raw_mut were available in AccessCtx

// This may have knock-on effects we'd need to document.
if state.item.is_stashed {
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
}
}
// TODO: If child is completely outside of parent's clip path, don't paint it
// The naïve solution breaks if the widget overflows its layout box; ideally, widgets
// wouldn't do that, but they are currently allowed to do so.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's also the fact that, if we adopt compositor layers, then you'll want to display some offscreen stuff so you don't get caught flatfooted when the user scrolls.

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've added a comment here.

widget_state: state.item,
widget_state_children: state.children.reborrow_mut(),
widget_children: widget.children.reborrow_mut(),
depth,
Copy link
Member

Choose a reason for hiding this comment

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

What is depth expected to be used for?

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 think it was used in Druid for z-order stuff? I guess it's obsolete now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed it.

@@ -87,6 +93,20 @@ impl WidgetArena {
.expect("get_state_mut: widget state not in widget tree")
}

#[track_caller]
pub(crate) fn get_scene(&mut self, widget_id: WidgetId) -> &Scene {
Copy link
Member

Choose a reason for hiding this comment

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

These methods are never used...

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 removed them.

@@ -65,7 +65,9 @@ pub struct WidgetState {
// TODO - Document
pub(crate) is_portal: bool,

pub(crate) request_compose: bool,
// TODO - Use general Shape
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// TODO - Use general Shape
// The clip is a rectangle to maintain compatibility with system compositor APIs, which only know about rectangles.
// Note that we don't currently integrate with the system compositor.
// A future extension to use an arbitrary shape may be possible if there is a significant need for it.

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'd rather support arbitrary shapes. Or at the very least, rectangles with rounded corners. The reason I'm doing rectangles only is that Kurbo doesn't provide you with a type to store an arbitrary shape.

Copy link
Member

Choose a reason for hiding this comment

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

Right - I guess a solution for rounded rectangles and similar is to draw the parent surface "above" the child surfaces, and have a transparent cutout for the composited view. If compositors can implement that performantly, that would work well

My mistake, I think arbitrary paths are probably fine then, although it probably requires some careful use of blend modes to punch through a transparent region.

pub(crate) widget_states: TreeArena<WidgetState>,
pub(crate) scenes: HashMap<WidgetId, Scene>,
Copy link
Member

Choose a reason for hiding this comment

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

It's not obvious why this isn't a field on the WidgetState.

Copy link
Contributor Author

@PoignardAzur PoignardAzur Aug 21, 2024

Choose a reason for hiding this comment

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

There are cases where you want to separately borrow both.

Wait, no. I think it's because Scene doesn't implement Debug and I didn't want to manually implement it in WidgetState.

pub(crate) widget_states: TreeArena<WidgetState>,
pub(crate) scenes: HashMap<WidgetId, Scene>,
Copy link
Member

Choose a reason for hiding this comment

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

Do these ever get cleaned up?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch.

@PoignardAzur
Copy link
Contributor Author

I think I've addressed all review comments.

This is a big one, so I'll wait until tomorrow to merge it, in case there's any concerns left.

@PoignardAzur PoignardAzur force-pushed the implement_pass_spec_paint_access branch from e248536 to def14c0 Compare August 21, 2024 14:38
PoignardAzur and others added 8 commits August 21, 2024 16:51
Fix stashed child handling.
Handle scrolling in compose pass.
Fix set_child_translation method.
Co-authored-by: Daniel McNab <36049421+DJMcNab@users.noreply.github.com>
@PoignardAzur PoignardAzur force-pushed the implement_pass_spec_paint_access branch from def14c0 to 61cfb07 Compare August 21, 2024 14:51
@PoignardAzur PoignardAzur added this pull request to the merge queue Aug 23, 2024
Merged via the queue into main with commit ff7635e Aug 23, 2024
17 checks passed
@PoignardAzur PoignardAzur deleted the implement_pass_spec_paint_access branch August 23, 2024 08:51
github-merge-queue bot pushed a commit that referenced this pull request Aug 28, 2024
Recursing is done inside the paint and accessibility passes since
ff7635e. I believe this is the correct
continuation of #522, with the removal of these methods "left for later"
as mentioned in
#522 (comment).

One note is that Flex now debug-paints its baseline under its children,
rather than over them.
github-merge-queue bot pushed a commit that referenced this pull request Sep 2, 2024
Fixes scrolling `Portal` on scrollbar drag by recomposing instead of
relayouting.

Regression probably caused in 59ee615
(#488) or
ff7635e
(#522).
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.

4 participants