// SPDX-FileCopyrightText: 2024 vivi developers <vivi-ui@tuta.io>
// SPDX-License-Identifier: MIT
import { Border, BorderStyle, ScrollBarStyle, ScrollViewBase } from "../foundation.slint";
import { MagicPalette, MagicSizeSettings, MagicLayoutSettings, MagicAnimationSettings, MagicBorderSettings } from "./styling.slint";
import { StateLayer } from "state_layer.slint";
component ScrollBar {
in property <bool> enabled <=> touch_area.enabled;
in_out property <length> value;
in property <length> maximum;
in property <length> content_size;
in property <ScrollBarStyle> style;
in property <bool> horizontal;
out property <bool> has_hover: touch_area.has_hover;
property <length> offset: MagicLayoutSettings.control_spacing / 2;
property <length> handle_padding: MagicLayoutSettings.control_spacing / 2;
property <length> available_size: root.horizontal ? root.width - 2 * root.offset : root.height - 2 * offset;
touch_area := TouchArea {
property <length> pressed_value;
pointer_event(event) => {
if event.button == PointerEventButton.left && event.kind == PointerEventKind.down {
self.pressed_value = -root.value;
}
}
moved => {
if !self.enabled || !self.pressed {
return;
}
root.value = -max(0px, min(root.maximum, self.pressed_value + (
root.horizontal ? (self.mouse_x - self.pressed_x) * (root.maximum / (root.available_size - handle.width))
: (self.mouse_y - self.pressed_y) * (root.maximum / (root.available_size - handle.height))
)));
}
scroll_event(event) => {
if root.horizontal && event.delta_x != 0 {
root.value = max(-root.maximum, min(0px, root.value + event.delta_x));
return accept;
} else if !root.horizontal && event.delta_y != 0 {
root.value = max(-root.maximum, min(0px, root.value + event.delta_y));
return accept;
}
reject
}
}
background_layer := Rectangle {
background: transparent;
border_width: 0;
animate background { duration: MagicAnimationSettings.color_duration; }
}
border := Rectangle {
x: 0;
y: 0;
width: root.horizontal ? root.width : root.style.border_style.border_width;
height: root.horizontal ? root.style.border_style.border_width : root.height;
background: root.style.border_style.border_brush;
opacity: 0;
animate background { duration: MagicAnimationSettings.color_duration; }
}
handle := Border {
x: !root.horizontal ? (parent.width - self.width) / 2 : root.offset + (root.available_size - self.width) * (-root.value / root.maximum);
y: root.horizontal ? (parent.height - self.height) / 2 : root.offset + (root.available_size - self.height) * (-root.value / root.maximum);
width: !root.horizontal ? parent.width - 2 * root.handle_padding : root.maximum <= 0 ? 0 : max(MagicSizeSettings.control_height, root.available_size * (root.content_size / (root.maximum + root.content_size)));
height: root.horizontal ? parent.height - 2 * root.handle_padding : root.maximum <= 0 ? 0 : max(MagicSizeSettings.control_height, root.available_size * (root.content_size / (root.maximum + root.content_size)));
style: root.style.handle_style;
border_radius: max(root.style.handle_style.border_radius, root.horizontal ? self.height / 2 : self.width / 2);
opacity: 0.6;
animate width, height { duration: MagicAnimationSettings.fast_resize_duration; }
}
state_layer := StateLayer {
x: handle.x;
y: handle.y;
width: handle.width;
height: handle.height;
border_radius: handle.border_radius;
pressed: touch_area.pressed;
has_hover: touch_area.has_hover;
}
states [
hover when touch_area.has_hover : {
background_layer.background: root.style.border_style.background;
handle_padding: MagicLayoutSettings.control_spacing;
border.opacity: 1;
}
]
animate width, height, handle_padding { duration: MagicAnimationSettings.fast_resize_duration; }
}
export component ScrollView inherits ScrollViewBase {
out property <length> visible_width <=> flickable.width;
out property <length> visible_height <=> flickable.height;
in_out property <bool> has_focus;
min_width: 2 * MagicSizeSettings.control_height;
min_height: self.min_width;
preferred_width: 100%;
preferred_height: 100%;
viewport_x <=> flickable.viewport_x;
viewport_y <=> flickable.viewport_y;
viewport_width <=> flickable.viewport_width;
viewport_height <=> flickable.viewport_height;
style: {
border_style: {
background: MagicPalette.transparent,
},
scroll_bar_style: {
border_style: {
background: MagicPalette.foreground.with_alpha(0.2),
border_width: MagicBorderSettings.control_border_width,
border_brush: MagicPalette.border
},
handle_style: {
background: MagicPalette.control_background,
border_width: 0,
border_brush: MagicPalette.transparent
}
}
};
flickable := Flickable {
viewport_x <=> horizontal_scroll_bar.value;
viewport_y <=> vertical_scroll_bar.value;
@children
}
vertical_scroll_bar := ScrollBar {
x: parent.width - self.width;
y: 0;
width: self.has_hover ? MagicSizeSettings.box_height : MagicSizeSettings.box_height / 2;
height: horizontal_scroll_bar.visible ? parent.height - horizontal_scroll_bar.height : parent.height;
style: root.style.scroll_bar_style;
enabled: root.enabled;
visible: flickable.viewport_height > flickable.height;
maximum: flickable.viewport_height - flickable.height;
content_size: flickable.height;
}
horizontal_scroll_bar := ScrollBar {
x: 0;
y: parent.height - self.height;
width: vertical_scroll_bar.visible ? parent.width - vertical_scroll_bar.width : parent.width;
height: self.has_hover ? MagicSizeSettings.box_height : MagicSizeSettings.box_height / 2;
style: root.style.scroll_bar_style;
enabled: root.enabled;
visible: flickable.viewport_width > flickable.width;
horizontal: true;
maximum: flickable.viewport_width - flickable.width;
content_size: flickable.width;
}
}