Implement ScrollBarPolicy property for ScrollView (#6442)

ChangeLog: ScrollView: added  vertical-bar-policy and horizontal-bar-policy

Fixes: #3552
Fixes: #5578
This commit is contained in:
Renato Araujo Oliveira Filho 2024-10-08 12:02:17 -03:00 committed by GitHub
parent 3784780e22
commit 1e4de3fe0c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 125 additions and 42 deletions

View file

@ -17,6 +17,7 @@ using for loops may be added in the future and is tracked in issue #407.
- **`viewport-width`** and **`viewport-height`** (_in-out_ _length_): The `width` and `length` properties of the viewport
- **`viewport-x`** and **`viewport-y`** (_in-out_ _length_): The `x` and `y` properties of the viewport. Usually these are negative
- **`visible-width`** and **`visible-height`** (_out_ _length_): The size of the visible area of the ScrollView (not including the scrollbar)
- **`vertical-bar-policy`** and **`horizontal-bar-policy`** (_in_ _ScrollBarPolicy_): The vertical and horizontal scroll bar visibility policy. The default value is `ScrollBarPolicy.as-needed`.
### Callbacks

View file

@ -1,7 +1,7 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT
import { HorizontalBox, VerticalBox, ListView, StandardListView, GroupBox } from "std-widgets.slint";
import { HorizontalBox, VerticalBox, ListView, StandardListView, GroupBox, ComboBox } from "std-widgets.slint";
import { GallerySettings } from "../gallery_settings.slint";
import { Page } from "page.slint";
@ -10,12 +10,51 @@ export component ListViewPage inherits Page {
show-enable-switch: false;
description: @tr("ListViews can be used to display a list of elements. The StandardListBox is like the default ListView just with a default text based definition of the visual items. Both can be imported from \"std-widgets.slint\"");
GroupBox {
title: @tr("Scroll Bar Policy:");
function policy-from-index(index: int) -> ScrollBarPolicy
{
if (index == 1) {
return ScrollBarPolicy.always-on;
}
if (index == 2) {
return ScrollBarPolicy.always-off;
}
return ScrollBarPolicy.as-needed;
}
HorizontalBox {
HorizontalBox {
Text {
text: @tr("Vertical:");
}
vertical-bar-policy := ComboBox {
in-out property<ScrollBarPolicy> current-policy: policy-from-index(self.current-index);
model: [@tr("As Needed"), @tr("Always On"), @tr("Always Off")];
}
}
HorizontalBox {
Text {
text: @tr("Horizontal:");
}
horizontal-bar-policy := ComboBox {
in-out property<ScrollBarPolicy> current-policy: policy-from-index(self.current-index);
model: [@tr("As Needed"), @tr("Always On"), @tr("Always Off")];
}
}
}
}
HorizontalBox {
vertical-stretch: 1;
GroupBox {
title: @tr("ListView");
ListView {
vertical-bar-policy: vertical-bar-policy.current-policy;
horizontal-bar-policy: horizontal-bar-policy.current-policy;
vertical-stretch: 0;
for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] : HorizontalBox {
Image {
@ -34,6 +73,8 @@ export component ListViewPage inherits Page {
vertical-stretch: 0;
StandardListView {
vertical-bar-policy: vertical-bar-policy.current-policy;
horizontal-bar-policy: horizontal-bar-policy.current-policy;
model: [
{text: @tr("Lorem")}, {text: @tr("ipsum")},{text: @tr("dolor")},{text: @tr("sit")},{text: @tr("amet")},{text: @tr("consetetur")},
{text: @tr("Lorem")}, {text: @tr("ipsum")},{text: @tr("dolor")},{text: @tr("sit")},{text: @tr("amet")},{text: @tr("consetetur")},

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
use i_slint_core::input::FocusEventResult;
use i_slint_core::items::ScrollBarPolicy;
use super::*;
@ -22,6 +23,8 @@ pub struct NativeScrollView {
pub native_padding_bottom: Property<LogicalLength>,
pub enabled: Property<bool>,
pub has_focus: Property<bool>,
pub vertical_bar_policy: Property<ScrollBarPolicy>,
pub horizontal_bar_policy: Property<ScrollBarPolicy>,
data: Property<NativeSliderData>,
widget_ptr: std::cell::Cell<SlintTypeErasedWidgetPtr>,
animation_tracker: Property<i32>,
@ -292,6 +295,9 @@ impl Item for NativeScrollView {
};
let enabled: bool = this.enabled();
let has_focus: bool = this.has_focus();
let vertical_bar_visible = (this.vertical_bar_policy() == ScrollBarPolicy::AlwaysOn) || ((this.vertical_bar_policy() == ScrollBarPolicy::AsNeeded) && (this.vertical_max().get() > 0.0));
let horizontal_bar_visible = (this.horizontal_bar_policy() == ScrollBarPolicy::AlwaysOn) || ((this.horizontal_bar_policy() == ScrollBarPolicy::AsNeeded) && (this.horizontal_max().get() > 0.0));
let scrollbar_bar_visible = vertical_bar_visible || horizontal_bar_visible;
let frame_around_contents = cpp!(unsafe [
painter as "QPainterPtr*",
widget as "QWidget*",
@ -300,7 +306,8 @@ impl Item for NativeScrollView {
margins as "QMargins",
enabled as "bool",
has_focus as "bool",
initial_state as "int"
initial_state as "int",
scrollbar_bar_visible as "bool"
] -> bool as "bool" {
ensure_initialized();
QStyleOptionFrame frameOption;
@ -327,11 +334,15 @@ impl Item for NativeScrollView {
frameOption.rect = QRect(QPoint(), (size / dpr) - corner_size);
qApp->style()->drawControl(QStyle::CE_ShapedFrame, &frameOption, painter->get(), widget);
frameOption.rect = QRect(frameOption.rect.bottomRight() + QPoint(1, 1), corner_size);
qApp->style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &frameOption, painter->get(), widget);
if (scrollbar_bar_visible) {
qApp->style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &frameOption, painter->get(), widget);
}
} else {
qApp->style()->drawControl(QStyle::CE_ShapedFrame, &frameOption, painter->get(), widget);
frameOption.rect = QRect(frameOption.rect.bottomRight() + QPoint(1, 1) - QPoint(margins.right(), margins.bottom()), corner_size);
qApp->style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &frameOption, painter->get(), widget);
if (scrollbar_bar_visible) {
qApp->style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &frameOption, painter->get(), widget);
}
}
return foac;
});
@ -399,36 +410,40 @@ impl Item for NativeScrollView {
let scrollbars_width = (margins.right - margins.left) as f32;
let scrollbars_height = (margins.bottom - margins.top) as f32;
draw_scrollbar(
false,
qttypes::QRectF {
x: ((size.width as f32 / dpr) - if frame_around_contents { scrollbars_width } else { margins.right as _ }) as _,
y: (if frame_around_contents { 0 } else { margins.top }) as _,
width: scrollbars_width as _,
height: ((size.height as f32 / dpr) - if frame_around_contents { scrollbars_height } else { (margins.bottom + margins.top) as f32 }) as _,
},
this.vertical_value().get() as i32,
this.vertical_page_size().get() as i32,
this.vertical_max().get() as i32,
data.active_controls,
data.pressed == 2,
initial_state
);
draw_scrollbar(
true,
qttypes::QRectF {
x: (if frame_around_contents { 0 } else { margins.left }) as _,
y: ((size.height as f32 / dpr) - if frame_around_contents { scrollbars_height } else { margins.bottom as _ }) as _,
width: ((size.width as f32 / dpr) - if frame_around_contents { scrollbars_width } else { (margins.left + margins.right) as _ }) as _,
height: (scrollbars_height) as _,
},
this.horizontal_value().get() as i32,
this.horizontal_page_size().get() as i32,
this.horizontal_max().get() as i32,
data.active_controls,
data.pressed == 1,
initial_state
);
if vertical_bar_visible {
draw_scrollbar(
false,
qttypes::QRectF {
x: ((size.width as f32 / dpr) - if frame_around_contents { scrollbars_width } else { margins.right as _ }) as _,
y: (if frame_around_contents { 0 } else { margins.top }) as _,
width: scrollbars_width as _,
height: ((size.height as f32 / dpr) - if frame_around_contents { scrollbars_height } else { (margins.bottom + margins.top) as f32 }) as _,
},
this.vertical_value().get() as i32,
this.vertical_page_size().get() as i32,
this.vertical_max().get() as i32,
data.active_controls,
data.pressed == 2,
initial_state
);
}
if horizontal_bar_visible {
draw_scrollbar(
true,
qttypes::QRectF {
x: (if frame_around_contents { 0 } else { margins.left }) as _,
y: ((size.height as f32 / dpr) - if frame_around_contents { scrollbars_height } else { margins.bottom as _ }) as _,
width: ((size.width as f32 / dpr) - if frame_around_contents { scrollbars_width } else { (margins.left + margins.right) as _ }) as _,
height: (scrollbars_height) as _,
},
this.horizontal_value().get() as i32,
this.horizontal_page_size().get() as i32,
this.horizontal_max().get() as i32,
data.active_controls,
data.pressed == 1,
initial_state
);
}
}
}

View file

@ -421,6 +421,16 @@ macro_rules! for_each_enums {
/// The ["alternate reverse" direction as defined in CSS](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-direction#alternate-reverse).
AlternateReverse,
}
/// This enum describes the scrollbar visibility
enum ScrollBarPolicy {
/// Scrolbar will be visible only when needed
AsNeeded,
/// Scrollbar never shown
AlwaysOff,
/// Scrollbar always visible
AlwaysOn,
}
];
};
}

View file

@ -536,6 +536,8 @@ export component NativeScrollView {
out property <length> native-padding-top;
out property <length> native-padding-bottom;
in property <bool> has_focus;
in property <ScrollBarPolicy> vertical-bar-policy;
in property <ScrollBarPolicy> horizontal-bar-policy;
in property <bool> enabled: true;
//-default_size_binding:expands_to_parent_geometry
//-is_internal

View file

@ -11,12 +11,14 @@ export component ScrollBar {
in-out property <length> maximum;
in-out property <length> page-size;
in-out property <length> value;
in property <ScrollBarPolicy> policy: ScrollBarPolicy.as-needed;
private property <length> track-size: root.horizontal ? root.width - 2 * root.offset : root.height - 2 * offset;
private property <length> step-size: 10px;
private property <length> offset: 2px;
opacity: 0.7;
visible: (self.policy == ScrollBarPolicy.always-on) || (self.policy == ScrollBarPolicy.as-needed && self.maximum > 0);
Rectangle {
border-radius: thumb.border-radius;
@ -84,6 +86,8 @@ export component ScrollView {
in-out property <length> viewport-height <=> flickable.viewport-height;
in-out property <length> viewport-x <=> flickable.viewport-x;
in-out property <length> viewport-y <=> flickable.viewport-y;
in property <ScrollBarPolicy> vertical-bar-policy <=> vertical-bar.policy;
in property <ScrollBarPolicy> horizontal-bar-policy <=> horizontal-bar.policy;
// FIXME: remove. This property is currently set by the ListView and is used by the native style to draw the scrollbar differently when it has focus
in-out property <bool> has-focus;
@ -115,7 +119,6 @@ export component ScrollView {
horizontal: false;
maximum: flickable.viewport-height - flickable.height;
page-size: flickable.height;
visible: flickable.viewport-height > flickable.height;
}
horizontal-bar := ScrollBar {
@ -127,6 +130,5 @@ export component ScrollView {
horizontal: true;
maximum: flickable.viewport-width - flickable.width;
page-size: flickable.width;
visible: flickable.viewport-width > flickable.width;
}
}

View file

@ -10,6 +10,7 @@ export component ScrollBar inherits Rectangle {
in-out property <length> maximum;
in-out property <length> page-size;
in-out property <length> value;
in property <ScrollBarPolicy> policy: ScrollBarPolicy.as-needed;
private property <length> track-size: root.horizontal ? root.width - 2 * root.offset : root.height - 2 * offset;
private property <length> step-size: 10px;
@ -17,6 +18,7 @@ export component ScrollBar inherits Rectangle {
private property <length> pad: 2px;
background: transparent;
visible: (self.policy == ScrollBarPolicy.always-on) || (self.policy == ScrollBarPolicy.as-needed && self.maximum > 0);
states [
hover when touch-area.has-hover : {
@ -92,6 +94,8 @@ export component ScrollView {
in-out property <length> viewport-height <=> flickable.viewport-height;
in-out property <length> viewport-x <=> flickable.viewport-x;
in-out property <length> viewport-y <=> flickable.viewport-y;
in property <ScrollBarPolicy> vertical-bar-policy <=> vertical-bar.policy;
in property <ScrollBarPolicy> horizontal-bar-policy <=> horizontal-bar.policy;
// FIXME: remove. This property is currently set by the ListView and is used by the native style to draw the scrollbar differently when it has focus
in-out property <bool> has-focus;
@ -125,7 +129,6 @@ export component ScrollView {
horizontal: false;
maximum: flickable.viewport-height - flickable.height;
page-size: flickable.height;
visible: flickable.viewport-height > flickable.height;
}
horizontal-bar := ScrollBar {
@ -137,6 +140,5 @@ export component ScrollView {
horizontal: true;
maximum: flickable.viewport-width - flickable.width;
page-size: flickable.width;
visible: flickable.viewport-width > flickable.width;
}
}

View file

@ -37,6 +37,7 @@ component ScrollBar inherits Rectangle {
in-out property <length> maximum;
in-out property <length> page-size;
in-out property <length> value;
in property <ScrollBarPolicy> policy: ScrollBarPolicy.as-needed;
in property <bool> enabled;
private property <length> offset: 16px;
@ -46,6 +47,7 @@ component ScrollBar inherits Rectangle {
border-width: 1px;
border-radius: 7px;
visible: (self.policy == ScrollBarPolicy.always-on) || (self.policy == ScrollBarPolicy.as-needed && self.maximum > 0);
states [
hover when touch-area.has-hover || down-scroll-button.has-hover || up-scroll-button.has-hover : {
@ -133,6 +135,9 @@ export component ScrollView {
in-out property <length> viewport-height <=> flickable.viewport-height;
in-out property <length> viewport-x <=> flickable.viewport-x;
in-out property <length> viewport-y <=> flickable.viewport-y;
in property <ScrollBarPolicy> vertical-bar-policy <=> vertical-bar.policy;
in property <ScrollBarPolicy> horizontal-bar-policy <=> horizontal-bar.policy;
// FIXME: remove. This property is currently set by the ListView and is used by the native style to draw the scrollbar differently when it has focus
in-out property <bool> has-focus;
@ -164,7 +169,6 @@ export component ScrollView {
horizontal: false;
maximum: flickable.viewport-height - flickable.height;
page-size: flickable.height;
visible: flickable.viewport-height > flickable.height;
}
horizontal-bar := ScrollBar {
@ -176,6 +180,5 @@ export component ScrollView {
horizontal: true;
maximum: flickable.viewport-width - flickable.width;
page-size: flickable.width;
visible: flickable.viewport-width > flickable.width;
}
}

View file

@ -11,6 +11,7 @@ component ScrollBar inherits Rectangle {
// this is always negative and bigger than -maximum
in-out property <length> value;
in-out property <bool> enabled <=> touch-area.enabled;
in property <ScrollBarPolicy> policy: ScrollBarPolicy.as-needed;
states [
disabled when !touch-area.enabled : {
@ -25,6 +26,8 @@ component ScrollBar inherits Rectangle {
}
]
visible: (self.policy == ScrollBarPolicy.always-on) || (self.policy == ScrollBarPolicy.as-needed && self.maximum > 0);
state-layer := Rectangle {
width: 100%;
height: 100%;
@ -95,6 +98,8 @@ export component ScrollView {
in-out property <length> viewport-height <=> flickable.viewport-height;
in-out property <length> viewport-x <=> flickable.viewport-x;
in-out property <length> viewport-y <=> flickable.viewport-y;
in property <ScrollBarPolicy> vertical-bar-policy <=> vertical-bar.policy;
in property <ScrollBarPolicy> horizontal-bar-policy <=> horizontal-bar.policy;
callback scrolled <=> flickable.flicked;
@ -125,7 +130,6 @@ export component ScrollView {
maximum: flickable.viewport-height - flickable.height;
page-size: flickable.height;
enabled: root.enabled;
visible: flickable.viewport-height > flickable.height;
}
horizontal-bar := ScrollBar {
@ -137,6 +141,5 @@ export component ScrollView {
maximum: flickable.viewport-width - flickable.width;
page-size: flickable.width;
enabled: root.enabled;
visible: flickable.viewport-width > flickable.width;
}
}

View file

@ -15,6 +15,8 @@ export component InternalScrollView {
out property <length> visible-height <=> fli.height;
in-out property <bool> has-focus <=> native.has-focus;
in property <bool> enabled <=> native.enabled;
in property <ScrollBarPolicy> vertical-bar-policy <=> native.vertical-bar-policy;
in property <ScrollBarPolicy> horizontal-bar-policy <=> native.horizontal-bar-policy;
// Used by the StandardTableView
out property <length> native-padding-left: native.native-padding-left;

View file

@ -12,6 +12,8 @@ export component ScrollView {
in-out property <length> viewport-height <=> internal.viewport-height;
in-out property <length> viewport-x <=> internal.viewport-x;
in-out property <length> viewport-y <=> internal.viewport-y;
in property <ScrollBarPolicy> vertical-bar-policy <=> internal.vertical-bar-policy;
in property <ScrollBarPolicy> horizontal-bar-policy <=> internal.horizontal-bar-policy;
callback scrolled <=> internal.scrolled;