export struct TimelineEvent { start: int, duration: int, finished: bool, label: string } global TimeString { pure function pad-mh(seconds: int, param: int) -> string { if seconds / param < 10 { return "0\{floor(seconds / param)}"; } return "\{floor(seconds / param)}"; } pure function pad-s(seconds: int) -> string { if mod(seconds, 60) < 10 { return "0\{mod(seconds, 60)}"; } return "\{mod(seconds, 60)}"; } public pure function from(seconds: int) -> string { return "\{pad-mh(seconds, 3600)}:\{pad-mh(mod(seconds, 3600), 60)}:\{pad-s(seconds)}"; } } export component Timeline inherits Rectangle { in-out property updating: true; in-out property<[TimelineEvent]> events: []; in-out property visible-time: 3600; property visible-offset: max(offset, visible-time); in-out property offset: 0; timer := Timer { interval: 1s; running: updating; triggered => { offset += 1; } } background: gray; border-width: 1px; border-color: black; Rectangle { x: 0; y: parent.height / 4; z: 0; width: parent.width; height: parent.height / 2; border-color: black; border-width: 1px; background: purple; } Text { x: 0; y: parent.height - self.height; text: TimeString.from(visible-offset - visible-time); } Text { x: parent.width - self.width; y: parent.height - self.height; text: TimeString.from(visible-offset); } for event in events: timeline-event := Rectangle { property real-x: ((visible-time - (visible-offset - event.start)) / visible-time) * parent.width; x: max(real-x, 0); y: parent.height / 4; z: 1; width: event.finished ? (event.duration) / visible-time * parent.width + min(real-x, 0): parent.width - self.x; height: parent.height / 2; visible: self.real-x + self.width > 0 && self.real-x < parent.width; border-color: black; border-width: 1px; background: red; Text { x: 0; y: -self.height; text: event.label; visible: timeline-event.visible; } } @children }