Several bugfixes

- Fixed event invisibility when offset is after event's half width
- Fixed event being shown outside timeline component
- When timer reaches 24:00:00, new day is automatically started
This commit is contained in:
Alexey 2025-09-12 13:59:12 +03:00
commit b5e9a4115a
7 changed files with 79 additions and 27 deletions

2
Cargo.lock generated
View file

@ -147,7 +147,7 @@ dependencies = [
[[package]] [[package]]
name = "aliveline" name = "aliveline"
version = "0.1.0" version = "0.1.1"
dependencies = [ dependencies = [
"chrono", "chrono",
"serde", "serde",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "aliveline" name = "aliveline"
version = "0.1.0" version = "0.1.1"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -5,7 +5,7 @@ use std::{error::Error, rc::Rc, sync::{Arc, Mutex}};
use aliveline::{config::Config, load_config, log::{Event, Log}}; use aliveline::{config::Config, load_config, log::{Event, Log}};
use chrono::{Datelike, Timelike}; use chrono::{Datelike, Timelike};
use slint::{Model, ModelRc, SharedString, ToSharedString, VecModel}; use slint::{Model, ModelRc, SharedString, ToSharedString, VecModel, Weak};
use toml::value::{Date as TomlDate, Time}; use toml::value::{Date as TomlDate, Time};
slint::include_modules!(); slint::include_modules!();
@ -46,6 +46,20 @@ impl From<TimelineEvent> for Event {
} }
} }
fn load_log(ui_weak: Weak<AppWindow>, log: Arc<Mutex<Log>>) {
let ui = ui_weak.unwrap();
let log_guard = log.lock().expect("Log shouldn't be used twice");
let events: Vec<TimelineEvent> = (*log_guard)
.events
.iter()
.map(|event| TimelineEvent::from((*event).clone()))
.collect();
let in_progress = events.iter().any(|event| !event.finished);
let model: ModelRc<TimelineEvent> = Rc::new(VecModel::from(events)).into();
ui.set_record_events(model);
ui.set_in_progress(in_progress);
}
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let ui = AppWindow::new()?; let ui = AppWindow::new()?;
@ -61,23 +75,9 @@ fn main() -> Result<(), Box<dyn Error>> {
let config: Arc<Config> = Arc::new(load_config()); let config: Arc<Config> = Arc::new(load_config());
let writing_log: Arc<Mutex<Log>> = Arc::new(Mutex::new(Log::load_from(&config, date))); let writing_log: Arc<Mutex<Log>> = Arc::new(Mutex::new(Log::load_from(&config, date)));
{
let ui_weak = ui.as_weak(); let ui_weak = ui.as_weak();
let log = writing_log.clone(); let log = writing_log.clone();
(move || { load_log(ui_weak, log);
let ui = ui_weak.unwrap();
let log_guard = log.lock().expect("Log shouldn't be used twice");
let events: Vec<TimelineEvent> = (*log_guard)
.events
.iter()
.map(|event| TimelineEvent::from((*event).clone()))
.collect();
let in_progress = events.iter().any(|event| !event.finished);
let model: ModelRc<TimelineEvent> = Rc::new(VecModel::from(events)).into();
ui.set_record_events(model);
ui.set_in_progress(in_progress);
})()
}
ui.invoke_update_record_offset(offset as i32); ui.invoke_update_record_offset(offset as i32);
@ -165,7 +165,8 @@ fn main() -> Result<(), Box<dyn Error>> {
let ui = ui_weak.unwrap(); let ui = ui_weak.unwrap();
let events_rc = ui.get_record_events(); let events_rc = ui.get_record_events();
let events = events_rc.as_any() let events = events_rc.as_any()
.downcast_ref::<VecModel<TimelineEvent>>().unwrap(); .downcast_ref::<VecModel<TimelineEvent>>()
.unwrap();
let offset = ui.get_record_offset(); let offset = ui.get_record_offset();
let event_id = events.iter() let event_id = events.iter()
@ -182,9 +183,9 @@ fn main() -> Result<(), Box<dyn Error>> {
{ {
let mut log_guard = log.lock().expect("Log shouldn't be used twice"); let mut log_guard = log.lock().expect("Log shouldn't be used twice");
(*log_guard).events.push(Event::from(new_event.clone())); log_guard.events.push(Event::from(new_event.clone()));
let index = (*log_guard).events.iter().position(|data| !data.finished).unwrap(); let index = log_guard.events.iter().position(|data| !data.finished).unwrap();
(*log_guard).events.swap_remove(index); log_guard.events.swap_remove(index);
} }
ui.invoke_save_log(); ui.invoke_save_log();
@ -202,6 +203,47 @@ fn main() -> Result<(), Box<dyn Error>> {
} }
}); });
ui.on_new_day_started({
let ui_weak = ui.as_weak();
let log = writing_log.clone();
move || {
let ui = ui_weak.unwrap();
let new_event: Option<Event> = {
let log_guard = log.lock().expect("Log shouldn't be used twice");
let maybe_unfinished_event = log_guard.events.iter().find(|event| !event.finished);
match maybe_unfinished_event {
Some(unfinished_event) => Some(Event::new(unfinished_event.name.clone(), 0, 0, false)),
None => None
}
};
ui.invoke_stop_event();
{
let mut log_guard = log.lock().expect("Log shouldn't be used twice");
log_guard.events.clear();
let now = chrono::Local::now();
let date = TomlDate {
year: now.year() as u16,
month: now.month() as u8,
day: now.day() as u8
};
log_guard.date = date;
log_guard.events.clear();
if let Some(event) = new_event {
log_guard.events.push(event);
}
}
load_log(ui.as_weak(), log.clone());
ui.invoke_save_log();
}
});
ui.run()?; ui.run()?;

View file

@ -7,6 +7,7 @@ export component AppWindow inherits Window {
callback start-new-event <=> record.start-new-event; callback start-new-event <=> record.start-new-event;
callback stop-event <=> record.stop-event; callback stop-event <=> record.stop-event;
callback chain-event <=> record.chain-event; callback chain-event <=> record.chain-event;
callback new-day-started <=> record.new-day-started;
callback update-record-offset(int); callback update-record-offset(int);
callback save-log; callback save-log;

View file

@ -2,6 +2,7 @@ import { VerticalBox, LineEdit, Button, ComboBox } from "std-widgets.slint";
import { Timeline } from "timeline.slint"; import { Timeline } from "timeline.slint";
export component RecordWidget inherits VerticalBox { export component RecordWidget inherits VerticalBox {
callback new-day-started <=> tl.new-day-started;
callback update-visible-time(string); callback update-visible-time(string);
callback start-new-event(string); callback start-new-event(string);
callback chain-event(string); callback chain-event(string);

View file

@ -22,7 +22,7 @@ export component ReviewWidget inherits VerticalBox {
spacing-horizontal: 16px; spacing-horizontal: 16px;
Slider { Slider {
minimum: visible-time; minimum: visible-time;
maximum: 24 * 3600; maximum: tl.max-offset;
value: offset; value: offset;
row: 0; row: 0;
colspan: 2; colspan: 2;

View file

@ -24,16 +24,24 @@ global TimeString {
} }
export component Timeline inherits Rectangle { export component Timeline inherits Rectangle {
callback new-day-started;
in-out property<bool> updating: true; in-out property<bool> updating: true;
in-out property<[TimelineEvent]> events: []; in-out property<[TimelineEvent]> events: [];
in-out property<int> visible-time: 3600; in-out property<int> visible-time: 3600;
property<int> visible-offset: max(offset, visible-time); property<int> visible-offset: max(offset, visible-time);
in-out property<int> offset: 0; in-out property<int> offset: 0;
out property<int> max-offset: 24 * 3600 - 1;
timer := Timer { timer := Timer {
interval: 1s; interval: 1s;
running: updating; running: updating;
triggered => { triggered => {
if (offset >= max-offset) {
root.new-day-started();
offset = 0;
return;
}
offset += 1; offset += 1;
} }
} }
@ -70,10 +78,10 @@ export component Timeline inherits Rectangle {
y: parent.height / 4; y: parent.height / 4;
z: 1; z: 1;
width: event.finished ? width: event.finished ?
(event.duration) / visible-time * parent.width + min(real-x, 0): min(parent.width - self.x, event.duration / visible-time * parent.width + min(real-x, 0)):
parent.width - self.x; parent.width - self.x;
height: parent.height / 2; height: parent.height / 2;
visible: self.real-x + self.width > 0 && self.real-x < parent.width; visible: self.width > 0 && self.real-x < parent.width;
border-color: black; border-color: black;
border-width: 1px; border-width: 1px;
background: red; background: red;