175 lines
5.3 KiB
Plaintext
175 lines
5.3 KiB
Plaintext
import { ScrollView, Palette } from "std-widgets.slint";
|
|
import { Global } from "global.slint";
|
|
import { ImageBucket, Visibility, ImagePreview } from "types.slint";
|
|
|
|
export component ImagePreview inherits Rectangle {
|
|
in property <ImagePreview> preview;
|
|
in property <length> size: 32px;
|
|
callback clicked <=> touch.clicked;
|
|
|
|
width: size;
|
|
height: size;
|
|
clip: true;
|
|
|
|
Image {
|
|
width: preview.ratio < 1.0 ? size : size * preview.ratio;
|
|
height: preview.ratio > 1.0 ? size : size / preview.ratio;
|
|
source: preview.image;
|
|
}
|
|
|
|
touch := TouchArea {}
|
|
}
|
|
|
|
component TimelineBlock inherits VerticalLayout {
|
|
in property <int> index: -1;
|
|
in-out property <ImageBucket> bucket;
|
|
|
|
property <length> min-image-size: Global.min-image-size;
|
|
property <length> min-size-with-margin: min-image-size + Global.image-margin;
|
|
property <int> count-x: Math.floor(self.width / min-size-with-margin); // TODO: or is it ceil?
|
|
property <int> count-y: Math.ceil(bucket.count / count-x);
|
|
|
|
function calc-image-size() -> length {
|
|
let remaining-length = Math.mod(self.width, min-size-with-margin);
|
|
min-image-size + remaining-length / count-x
|
|
}
|
|
|
|
property <length> image-size: calc-image-size();
|
|
property <length> image-size-with-margin: image-size + Global.image-margin;
|
|
|
|
property <length> title-box-height: 44px;
|
|
height: title-box-height + count-y * image-size-with-margin;
|
|
|
|
y: bucket.y;
|
|
min-width: min-image-size;
|
|
alignment: start;
|
|
|
|
title-box := Rectangle {
|
|
property <bool> checked: false;
|
|
|
|
HorizontalLayout {
|
|
alignment: space-between;
|
|
height: title-box-height;
|
|
padding: 8px;
|
|
|
|
title := Text {
|
|
text: bucket.title;
|
|
font-size: 20px;
|
|
}
|
|
|
|
if !checked : Image {
|
|
source: @image-url("../assets/unchecked.svg");
|
|
colorize: Palette.foreground;
|
|
opacity: 0.8;
|
|
height: title.height;
|
|
width: self.height;
|
|
}
|
|
|
|
if checked : Image {
|
|
source: @image-url("../assets/checked.svg");
|
|
colorize: Palette.accent-background;
|
|
height: title.height;
|
|
width: self.height;
|
|
}
|
|
}
|
|
|
|
title-touch := TouchArea {
|
|
clicked => {
|
|
parent.checked = !parent.checked;
|
|
}
|
|
}
|
|
}
|
|
|
|
image-box := Rectangle {
|
|
width: 100%;
|
|
height: count-y * image-size-with-margin;
|
|
|
|
for preview[i] in bucket.previews : ImagePreview {
|
|
preview: preview;
|
|
size: image-size;
|
|
x: Global.image-margin / 2 + Math.mod(i, count-x) * (Global.image-margin + image-size);
|
|
y: Math.floor(i / count-x) * (image-size + Global.image-margin);
|
|
clicked => {
|
|
Global.viewed-image = preview;
|
|
Global.view-image(preview.asset-id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export component ScrollHandle {
|
|
out property<float> maximum: 1;
|
|
out property<float> minimum: 0;
|
|
in-out property<float> value;
|
|
callback dragged(float);
|
|
|
|
width: handle.width * 0.66;
|
|
horizontal-stretch: 0;
|
|
vertical-stretch: 1;
|
|
height: 100%;
|
|
|
|
handle := Rectangle {
|
|
x: 0;
|
|
width: 64px;
|
|
height: self.width;
|
|
border-width: 3px;
|
|
border-radius: self.height / 2;
|
|
background: touch.pressed ? Palette.accent-background : Palette.alternate-background;
|
|
border-color: Palette.accent-foreground;
|
|
y: (root.height - handle.height) * (root.value - root.minimum)/(root.maximum - root.minimum);
|
|
|
|
touch := TouchArea {
|
|
moved => {
|
|
if (self.enabled && self.pressed) {
|
|
root.value = max(root.minimum, min(root.maximum,
|
|
root.value + (self.mouse-y - self.pressed-y) * (root.maximum - root.minimum) / root.height));
|
|
dragged(root.value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export component Timeline {
|
|
scroll-view := ScrollView {
|
|
mouse-drag-pan-enabled: true;
|
|
viewport-height: rect.height;
|
|
vertical-scrollbar-policy: always-off;
|
|
horizontal-scrollbar-policy: always-off;
|
|
|
|
scrolled => {
|
|
// sync ScrollHandle with ScrollView
|
|
scroll-handle.value = (-scroll-view.viewport-y) / scroll-view.viewport-height;
|
|
}
|
|
|
|
changed viewport-y => {
|
|
Global.timeline-scrolled(-self.viewport-y);
|
|
}
|
|
|
|
rect := Rectangle {
|
|
y: 0;
|
|
x: 0;
|
|
width: root.width;
|
|
height: Global.timeline-height;
|
|
preferred-width: self.width;
|
|
preferred-height: self.height;
|
|
for bucket[i] in Global.image-buckets : Rectangle {
|
|
if bucket.visibility == Visibility.InView : TimelineBlock {
|
|
width: root.width;
|
|
index: i;
|
|
bucket: bucket;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
scroll-handle := ScrollHandle {
|
|
x: parent.x + parent.width - self.width;
|
|
height: root.height;
|
|
dragged(value) => {
|
|
// sync ScrollView with ScrollHandle
|
|
scroll-view.viewport-y = -(value * scroll-view.viewport-height);
|
|
}
|
|
}
|
|
}
|