Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added masonry/screenshots/scrollbar_collapsed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions masonry/src/properties/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod line_breaking;
mod object_fit;
mod placeholder_color;
mod progress_bar;
mod scroll_bar;
mod selection;
mod slider;
mod step_input;
Expand All @@ -25,6 +26,7 @@ pub use line_breaking::*;
pub use object_fit::*;
pub use placeholder_color::*;
pub use progress_bar::*;
pub use scroll_bar::*;
pub use selection::*;
pub use slider::*;
pub use step_input::*;
Expand Down
15 changes: 15 additions & 0 deletions masonry/src/properties/scroll_bar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2026 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0

use crate::core::Property;

/// Declares if the scroll bar collapses when not being hovered.
#[derive(Default, Clone, Copy, Debug, PartialEq)]
pub struct Collapsible(pub bool);

impl Property for Collapsible {
fn static_default() -> &'static Self {
static DEFAULT: Collapsible = Collapsible(false);
&DEFAULT
}
}
44 changes: 34 additions & 10 deletions masonry/src/widgets/scroll_bar.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright 2022 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0

use std::any::TypeId;

use accesskit::{Node, Role};
use include_doc_path::include_doc_path;
use tracing::{Span, trace_span};
Expand All @@ -9,11 +11,12 @@ use crate::core::keyboard::{Key, KeyState, NamedKey};
use crate::core::{
AccessCtx, AccessEvent, AllowRawMut, ChildrenIds, EventCtx, LayoutCtx, MeasureCtx, NoAction,
PaintCtx, PointerButtonEvent, PointerEvent, PointerUpdate, PropertiesMut, PropertiesRef,
RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetId, WidgetMut,
Property, RegisterCtx, TextEvent, UpdateCtx, UsesProperty, Widget, WidgetId, WidgetMut,
};
use crate::imaging::Painter;
use crate::kurbo::{Axis, Point, Rect, Size, Stroke};
use crate::layout::LenReq;
use crate::properties::Collapsible;
use crate::theme;

// TODO
Expand Down Expand Up @@ -167,6 +170,8 @@ impl ScrollBar {
}
}

impl UsesProperty<Collapsible> for ScrollBar {}

// --- MARK: IMPL WIDGET
impl Widget for ScrollBar {
type Action = NoAction;
Expand Down Expand Up @@ -338,12 +343,10 @@ impl Widget for ScrollBar {

fn register_children(&mut self, _ctx: &mut RegisterCtx<'_>) {}

fn update(
&mut self,
_ctx: &mut UpdateCtx<'_>,
_props: &mut PropertiesMut<'_>,
_event: &Update,
) {
fn property_changed(&mut self, ctx: &mut UpdateCtx<'_>, property_type: TypeId) {
if Collapsible::matches(property_type) {
ctx.request_paint_only();
}
}

fn measure(
Expand Down Expand Up @@ -377,19 +380,29 @@ impl Widget for ScrollBar {
fn paint(
&mut self,
ctx: &mut PaintCtx<'_>,
_props: &PropertiesRef<'_>,
props: &PropertiesRef<'_>,
painter: &mut Painter<'_>,
) {
let cache = ctx.property_cache();
let collapsible = props.get::<Collapsible>(cache).0;

let radius = theme::SCROLLBAR_RADIUS;
let edge_width = theme::SCROLLBAR_EDGE_WIDTH;
let cursor_padding = theme::SCROLLBAR_PAD;
let cursor_min_length = theme::SCROLLBAR_MIN_SIZE;
let scrollbar_width = theme::SCROLLBAR_WIDTH;

let size = ctx.content_box_size();
let (inset_x, inset_y) = self.axis.pack_xy(0.0, cursor_padding);
let inset_start = if !collapsible || ctx.is_hovered() || self.grab_anchor.is_some() {
cursor_padding
} else {
cursor_padding + scrollbar_width / 2.
};
let (inset_x0, inset_y0) = self.axis.pack_xy(0.0, inset_start);
let (inset_x1, inset_y1) = self.axis.pack_xy(0.0, cursor_padding);
let cursor_rect = self
.cursor_rect(size, cursor_min_length)
.inset((-inset_x, -inset_y))
.inset((-inset_x0, -inset_y0, -inset_x1, -inset_y1))
.to_rounded_rect(radius);

painter.fill(cursor_rect, theme::SCROLLBAR_COLOR).draw();
Expand Down Expand Up @@ -498,6 +511,17 @@ mod tests {
assert_render_snapshot!(harness, "scrollbar_bottom");
}

#[test]
fn collapsible_scrollbar() {
let widget = NewWidget::new(ScrollBar::new(Axis::Vertical, 200.0, 600.0))
.with_props(Collapsible(true))
.with_props(Dimensions::FIT);

let mut harness = TestHarness::create_with_size(test_property_set(), widget, (20, 200));

assert_render_snapshot!(harness, "scrollbar_collapsed");
}

#[test]
fn horizontal_scrollbar() {
let widget = NewWidget::new(ScrollBar::new(Axis::Horizontal, 200.0, 600.0))
Expand Down
Loading