Due to "Recursion detected" bugs in slint, we need to calculate the layout of the timeline in Rust. This is ugly, but works.
181 lines
4.4 KiB
Plaintext
181 lines
4.4 KiB
Plaintext
import { AboutSlint, Button, Palette, HorizontalBox, ScrollView } from "std-widgets.slint";
|
|
|
|
enum PreviewKind {
|
|
None,
|
|
Thumbhash,
|
|
Thumbnail,
|
|
}
|
|
|
|
struct ImagePreview {
|
|
asset_id: string,
|
|
image: image,
|
|
kind: PreviewKind,
|
|
}
|
|
|
|
enum Visibility {
|
|
Hidden,
|
|
NearView,
|
|
InView,
|
|
}
|
|
|
|
struct ImageBucket {
|
|
key: string,
|
|
title: string,
|
|
count: int,
|
|
previews: [ImagePreview],
|
|
y: length,
|
|
height: length,
|
|
visibility: Visibility,
|
|
}
|
|
|
|
export global Global {
|
|
in-out property <length> min-image-size: 100px;
|
|
in-out property <length> image-margin: 2px;
|
|
in-out property <[ImageBucket]> image-buckets: [
|
|
{ key: "2026-02-01", title: "Feb 1, 2026", count: 12 },
|
|
{ key: "2026-02-02", title: "Feb 2, 2026", count: 12 },
|
|
{ key: "2026-02-03", title: "Feb 3, 2026", count: 12 },
|
|
];
|
|
in property <length> timeline-height;
|
|
in property <length> timeline-width;
|
|
in property <length> timeline-scroll;
|
|
callback set-timeline-width(length);
|
|
callback timeline-scrolled(length);
|
|
}
|
|
|
|
|
|
component Header inherits Rectangle {
|
|
width: 100%;
|
|
height: 48px;
|
|
background: Palette.alternate-background;
|
|
|
|
HorizontalBox {
|
|
height: parent.height;
|
|
Text {
|
|
text: "immich";
|
|
}
|
|
}
|
|
}
|
|
|
|
component ImagePreview inherits Rectangle {
|
|
in property <image> preview;
|
|
in property <length> size: 32px;
|
|
width: size;
|
|
height: size;
|
|
|
|
Image {
|
|
width: 100%;
|
|
height: 100%;
|
|
source: preview;
|
|
}
|
|
|
|
touch := TouchArea {
|
|
clicked => {
|
|
}
|
|
}
|
|
}
|
|
|
|
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: 36px;
|
|
height: title-box.height + count-y * image-size-with-margin;
|
|
|
|
y: bucket.y;
|
|
min-width: min-image-size;
|
|
alignment: start;
|
|
|
|
title-box := HorizontalBox {
|
|
alignment: space-between;
|
|
height: title-box-height;
|
|
|
|
Text {
|
|
text: bucket.title;
|
|
}
|
|
|
|
// TODO: checkbox thingy
|
|
Text {
|
|
text: "O";
|
|
}
|
|
}
|
|
|
|
image-box := Rectangle {
|
|
width: 100%;
|
|
height: count-y * image-size-with-margin;
|
|
|
|
for preview[i] in bucket.previews : ImagePreview {
|
|
preview: preview.image;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// component ImageViewer inherits Rectangle {
|
|
// in property <image> image;
|
|
|
|
// width: 100%;
|
|
// height: 100%;
|
|
|
|
// background: black;
|
|
// }
|
|
|
|
export component AppWindow inherits Window {
|
|
out property <length> window-height: self.height;
|
|
|
|
// Do not base preferred-width on children
|
|
preferred-width: 480px;
|
|
|
|
changed width => {
|
|
Global.set-timeline-width(self.width);
|
|
}
|
|
|
|
VerticalLayout {
|
|
padding: 0px;
|
|
width: 100%;
|
|
|
|
Header {}
|
|
|
|
ScrollView {
|
|
mouse-drag-pan-enabled: true;
|
|
viewport-height: rect.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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|