// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.0 OR LicenseRef-Slint-commercial import { Palette, Icons } from "styling.slint"; component ScrollViewButton inherits TouchArea { in property icon; width: 8px; height: 8px; i-icon := Image { x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: parent.width; source: root.icon; colorize: Palette.control-stroke; animate colorize, opacity { duration: 150ms; } animate width, height { duration: 150ms; easing: ease-out; } } states [ pressed when root.pressed : { opacity: 1; i-icon.width: 6px; } hover when root.has-hover : { opacity: 1; i-icon.colorize: Palette.text-secondary; } ] } component ScrollBar inherits Rectangle { private property offset: 16px; private property size: 2px; private property track-size: root.horizontal ? root.width - 2 * root.offset : root.height - 2 * offset; private property step-size: 10px; in-out property horizontal; in-out property maximum; in-out property page-size; in-out property value; in property enabled; border-width: 1px; border-radius: 7px; i-thumb := Rectangle { width: !root.horizontal ? root.size : root.maximum <= 0phx ? 0phx : max(32px, root.track-size * root.page-size / (root.maximum + root.page-size)); height: root.horizontal ? root.size : root.maximum <= 0phx ? 0phx : max(32px, root.track-size * (root.page-size / (root.maximum + root.page-size))); x: !root.horizontal ? parent.width - 4px - self.width : root.offset + (root.track-size - i-thumb.width) * (-root.value / root.maximum); y: root.horizontal ? parent.height - 4px - self.height : root.offset + (root.track-size - i-thumb.height) * (-root.value / root.maximum); border-radius: (root.horizontal ? self.height : self.width) / 2; background: Palette.control-stroke; animate width, height { duration: 150ms; easing: ease-out; } } i-touch-area := TouchArea { property pressed-value; width: parent.width; height: parent.height; pointer-event(event) => { if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) { self.pressed-value = -root.value; } } moved => { if (self.enabled && self.pressed) { root.value = -max(0px, min(root.maximum, self.pressed-value + ( root.horizontal ? (i-touch-area.mouse-x - i-touch-area.pressed-x) * (root.maximum / (root.track-size - i-thumb.width)) : (i-touch-area.mouse-y - i-touch-area.pressed-y) * (root.maximum / (root.track-size - i-thumb.height)) ))); } } } i-up-scroll-button := ScrollViewButton { opacity: 0; x: !root.horizontal ? (parent.width - self.width) / 2 : 4px; y: root.horizontal ? (parent.height - self.height) / 2 : 4px; icon: root.horizontal ? Icons.left : Icons.up; clicked => { root.value = min(0px, root.value + root.step-size); } } i-down-scroll-button := ScrollViewButton { opacity: 0; x: !root.horizontal ? (parent.width - self.width) / 2 : root.width - self.width - 4px; y: root.horizontal ? (parent.height - self.height) / 2 : root.height - self.height - 4px; icon: root.horizontal ? Icons.right : Icons.down; clicked => { root.value = max(-root.maximum, root.value - root.step-size); } } states [ hover when i-touch-area.has-hover || i-down-scroll-button.has-hover || i-up-scroll-button.has-hover : { root.background: Palette.acrylic-background; root.size: 6px; i-up-scroll-button.opacity: 1; i-down-scroll-button.opacity: 1; } ] animate size { duration: 150ms; easing: ease-out; } } export component ScrollView { in property enabled: true; out property visible-width <=> i-flickable.width; out property visible-height <=> i-flickable.height; in-out property viewport-width <=> i-flickable.viewport-width; in-out property viewport-height <=> i-flickable.viewport-height; in-out property viewport-x <=> i-flickable.viewport-x; in-out property viewport-y <=> i-flickable.viewport-y; // 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 has-focus; min-height: 50px; min-width: 50px; horizontal-stretch: 1; vertical-stretch: 1; preferred-height: 100%; preferred-width: 100%; i-flickable := Flickable { x: 2px; y: 2px; interactive: false; viewport-y <=> i-vertical-bar.value; viewport-x <=> i-horizontal-bar.value; width: parent.width - i-vertical-bar.width; height: parent.height - i-horizontal-bar.height; @children } i-vertical-bar := ScrollBar { enabled: root.enabled; width: 14px; x: i-flickable.width + i-flickable.x; y: i-flickable.y; height: i-flickable.height; horizontal: false; maximum: i-flickable.viewport-height - i-flickable.height; page-size: i-flickable.height; visible: i-flickable.viewport-height > i-flickable.height; } i-horizontal-bar := ScrollBar { enabled: root.enabled; width: i-flickable.width; height: 14px; y: i-flickable.height + i-flickable.y; x: i-flickable.x; horizontal: true; maximum: i-flickable.viewport-width - i-flickable.width; page-size: i-flickable.width; visible: i-flickable.viewport-width > i-flickable.width; } }