Skip to content

Conversation

@PPakalns
Copy link
Contributor

@PPakalns PPakalns commented Oct 24, 2025

Objective

Add UI nodes that can be used as sticky row, column headers.

Solution

Add ui nodes that ignore parent node scroll position. This approach only allows for row, column headers that are located at the top and at the left side of the parent node. Multiple rows, columns can be used as headers.

Testing

Extended scroll example.

Prior art

Used the same approach to implement sticky elements in egui taffy (sticky)


Showcase

sticky-2025-10-24_23.22.28.mov

@PPakalns PPakalns changed the title Added sticky ui nodes and ScrollSticky component Add sticky ui nodes Oct 24, 2025
@alice-i-cecile alice-i-cecile added A-UI Graphical user interfaces, styles, layouts, and widgets M-Release-Note Work that should be called out in the blog due to impact labels Oct 24, 2025
@github-actions
Copy link
Contributor

It looks like your PR has been selected for a highlight in the next release blog post, but you didn't provide a release note.

Please review the instructions for writing release notes, then expand or revise the content in the release notes directory to showcase your changes.

Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

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

Can you add or extend an example so then we can easily test this?

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Needs-Review Needs reviewer attention (from anyone!) to move forward and removed M-Release-Note Work that should be called out in the blog due to impact labels Oct 24, 2025
Copy link
Contributor

@ickshonpe ickshonpe left a comment

Choose a reason for hiding this comment

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

It looks like it should work fine.

I'm not sure about the API and implementation though, but I've been working too much on text and forgotten everything I knew about CSS. I'd like an example, like alice already asked for. It's quite limited compared to position: sticky, we should at least support bottom and right alignment I think.

Probably this is okay for an initial step though and it's simple enough that we can easily replace it completely if it's not possible to extend the implementation to support more features.

Comment on lines 350 to 362
/// Determines which axes of a node are *sticky* during scrolling.
///
/// A **sticky** node maintains its position along the specified axes
/// instead of moving with its scrolled parent content when [`ScrollPosition`] is applied.
#[derive(Component, Debug, Clone, Default, Deref, DerefMut, Reflect)]
#[reflect(Component, Default, Clone)]
pub struct ScrollSticky(pub BVec2);

impl From<BVec2> for ScrollSticky {
fn from(value: BVec2) -> Self {
Self(value)
}
}
Copy link
Contributor

@ickshonpe ickshonpe Oct 25, 2025

Choose a reason for hiding this comment

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

I don't like the plain BVec2 here, I'd prefer named fields or some helpers like:

impl ScrollSticky {
    fn left() -> Self { 
        Self(BVec2::new(true, false))
    }
    
    // etc..
}

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 would like to avoid implying in API that ScrollSticky has method left. What does it mean?

Sticky headers can be achieved with this feature only by combining multiple features:
ZIndex, BackgroundColor, ScrollSticky, parent with display Grid.

Users need to carefully mix multiple features to get the final behavior working. So I would like to avoid implying any specific use case in this level of API.

See example: 5ab4c9a

Copy link
Contributor

Choose a reason for hiding this comment

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

It works fine with Flex as well. You don't have to be especially careful.
left does carry incorrect implications though, you are right. I was overthinking things, this isn't an implementation of the css sticky positions API, it's just an override that ignores the inherited scroll positions when it's set.

@PPakalns PPakalns marked this pull request as ready for review October 26, 2025 16:45
@alice-i-cecile alice-i-cecile added the M-Release-Note Work that should be called out in the blog due to impact label Oct 26, 2025
@alice-i-cecile alice-i-cecile added this to the 0.18 milestone Oct 26, 2025
@PPakalns PPakalns requested a review from ickshonpe October 30, 2025 17:21
Comment on lines 350 to 362
/// Determines which axes of a node are *sticky* during scrolling.
///
/// A **sticky** node maintains its position along the specified axes
/// instead of moving with its scrolled parent content when [`ScrollPosition`] is applied.
#[derive(Component, Debug, Clone, Default, Deref, DerefMut, Reflect)]
#[reflect(Component, Default, Clone)]
pub struct ScrollSticky(pub BVec2);

impl From<BVec2> for ScrollSticky {
fn from(value: BVec2) -> Self {
Self(value)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It works fine with Flex as well. You don't have to be especially careful.
left does carry incorrect implications though, you are right. I was overthinking things, this isn't an implementation of the css sticky positions API, it's just an override that ignores the inherited scroll positions when it's set.

/// instead of moving with its scrolled parent content when [`ScrollPosition`] is applied.
#[derive(Component, Debug, Clone, Default, Deref, DerefMut, Reflect)]
#[reflect(Component, Default, Clone)]
pub struct ScrollSticky(pub BVec2);
Copy link
Contributor

@ickshonpe ickshonpe Oct 31, 2025

Choose a reason for hiding this comment

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

I think the name ScrollSticky carries the wrong implications. It should be called IgnoreScroll or something. And the description is misleading too, the node isn't sticky, it's fixed in place.

Copy link
Contributor Author

@PPakalns PPakalns Oct 31, 2025

Choose a reason for hiding this comment

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

True, I was focusing on sticky headers, but IgnoreScroll would be a lot better.

I will update PR.

Copy link
Contributor Author

@PPakalns PPakalns Nov 3, 2025

Choose a reason for hiding this comment

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

Renamed to IgnoreScroll . PR updated.

Copy link
Contributor

@ickshonpe ickshonpe left a comment

Choose a reason for hiding this comment

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

I've no more reservations about this, will approve once the naming + doc fixes are made.

@PPakalns PPakalns requested a review from ickshonpe November 3, 2025 16:38
@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Nov 3, 2025
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Nov 3, 2025
Merged via the queue into bevyengine:main with commit 55bb1b4 Nov 3, 2025
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-UI Graphical user interfaces, styles, layouts, and widgets C-Feature A new feature, making something new possible D-Straightforward Simple bug fixes and API improvements, docs, test and examples M-Release-Note Work that should be called out in the blog due to impact S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants