From cb688770998709ab4a50560d352c4817fcd50fb0 Mon Sep 17 00:00:00 2001 From: Tsuki Date: Wed, 24 Jan 2024 20:26:29 +0800 Subject: [PATCH] timeline scroll --- Cargo.lock | 61 ++++++++------- src/components/control_panel/control_panel.rs | 23 +++++- src/main.rs | 10 +-- src/render/mod.rs | 6 +- src/timeline/imp.rs | 61 ++++++++++++++- src/timeline/mod.rs | 77 +++++++++++++------ src/utils.rs | 16 ++-- 7 files changed, 174 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53f6991..069d8f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1075,9 +1075,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1090,9 +1090,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1100,15 +1100,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1117,15 +1117,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2 1.0.76", "quote 1.0.35", @@ -1134,21 +1134,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -2960,9 +2960,9 @@ checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "relm4" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "813306b9bc23e2f1e719801eb9aa59e4c2f96830be951a6168ad0c53363c6196" +checksum = "0c16f3fad883034773b7f5af4d7e865532b8f3641e5a8bab2a34561a8d960d81" dependencies = [ "async-trait", "flume", @@ -2978,9 +2978,9 @@ dependencies = [ [[package]] name = "relm4-components" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd02dd4c31b952cedb6f3ef200438963b1c4b7889f2ce760c607a1dc7f513e3" +checksum = "5485d72dc94c12a59c571d80cf9a545e5b9a2f0ebc90ea5fd234929a9376f66d" dependencies = [ "once_cell", "relm4", @@ -2999,9 +2999,9 @@ dependencies = [ [[package]] name = "relm4-macros" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad8d946cf33654b1df3e2e7d1c4952a154f008368cdda03a99c1840a6fcab3b" +checksum = "9340e2553c0a184a80a0bfa1dcf73c47f3d48933aa6be90724b202f9fbd24735" dependencies = [ "proc-macro2 1.0.76", "quote 1.0.35", @@ -3591,11 +3591,10 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3603,9 +3602,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2 1.0.76", "quote 1.0.35", @@ -3614,9 +3613,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] diff --git a/src/components/control_panel/control_panel.rs b/src/components/control_panel/control_panel.rs index 1626278..98ab52c 100644 --- a/src/components/control_panel/control_panel.rs +++ b/src/components/control_panel/control_panel.rs @@ -1,6 +1,6 @@ use crate::timeline::TimeLine; use adw::prelude::*; -use chrono::{DateTime, Duration, Utc}; +use chrono::{prelude::*, DateTime, Duration, TimeZone, Utc}; use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, ToggleButtonExt}; use relm4::typed_list_view::{RelmListItem, TypedListView}; use relm4::*; @@ -12,6 +12,7 @@ use std::path::PathBuf; #[derive(Debug)] pub struct ControlPanelModel { timeline_start: DateTime, + selection: Option>, #[tracker::no_eq] open_button: Controller, #[tracker::no_eq] @@ -83,6 +84,7 @@ pub enum TimelineMsg { pub enum AppMsg { Open(PathBuf), TimeLine(TimelineMsg), + Selection(Option>), } #[relm4::component(pub)] @@ -168,6 +170,17 @@ impl SimpleComponent for ControlPanelModel { set_width_request: 400, #[track = "model.changed(ControlPanelModel::timeline_start())"] set_time_start: model.timeline_start, + connect_start_time_notify[sender] => move |time| { + let time = time.start_time(); + sender.input( + AppMsg::Selection( + if time > i64::MIN { + Some(Utc.timestamp_opt(time, 0).unwrap()) + } else{ + None + })); + + }, }, gtk::Button{ set_icon_name: "fast-forward-filled", @@ -178,6 +191,7 @@ impl SimpleComponent for ControlPanelModel { }, } }, + gtk::ScrolledWindow{ set_height_request: 75, #[local_ref] @@ -188,6 +202,9 @@ impl SimpleComponent for ControlPanelModel { }, } }, + gtk::Separator{ + set_orientation: gtk::Orientation::Vertical, + }, gtk::ScrolledWindow{ set_hexpand: true, gtk::Box{ @@ -228,6 +245,7 @@ impl SimpleComponent for ControlPanelModel { let timeline_start = Utc::now(); let model = ControlPanelModel { + selection: None, timeline_start, open_button, list_img_wrapper, @@ -249,6 +267,9 @@ impl SimpleComponent for ControlPanelModel { self.set_timeline_start(current + c); } }, + AppMsg::Selection(selection) => { + self.set_selection(selection); + } } } } diff --git a/src/main.rs b/src/main.rs index 77f215c..49b85ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,7 @@ mod utils; -use gtk::prelude::*; -use gtk::{gio, glib, Application, ApplicationWindow}; -use pipeline::offscreen_renderer::OffscreenRenderer; -use relm4::menu; -use relm4::RelmApp; +use gtk::gio; use std::ptr; use tokio::runtime::Runtime; -use utils::creator; mod chart; mod components; mod coords; @@ -59,14 +54,11 @@ fn main() { fn initialize_custom_css() { gio::resources_register_include!("css.gresource").unwrap(); - use gtk::gdk; // Load the CSS file and add it to the provider let provider = gtk::CssProvider::new(); // provider.load_from_string(); provider.load_from_resource("/org/tsuki/radar_g/css/main.css"); - use gtk::gdk::Display; - // Add the provider to the default screen gtk::style_context_add_provider_for_display( &Display::default().expect("Could not connect to a display."), diff --git a/src/render/mod.rs b/src/render/mod.rs index 080cccb..269b202 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -7,7 +7,6 @@ mod renders; pub use self::cms::CMS; pub use self::imp::{RenderConfig, RenderMotion, RenderStatus}; use crate::coords::Mapper; -use crate::RUNTIME; use adw::prelude::{GLAreaExt, GestureDragExt}; use geo_types::LineString; use glib::clone; @@ -15,10 +14,7 @@ pub use glib::subclass::prelude::*; use gtk::traits::WidgetExt; use gtk::{EventControllerScrollFlags, Inhibit}; pub use interior::*; -use relm4::once_cell::sync::Lazy; -use std::cell::{Ref, RefCell, RefMut}; -use std::sync::{Arc, Mutex}; -use tokio::runtime::Runtime; +use std::cell::{Ref, RefMut}; pub(super) type WindowCoord = (f32, f32); diff --git a/src/timeline/imp.rs b/src/timeline/imp.rs index e1800e6..ef796d3 100644 --- a/src/timeline/imp.rs +++ b/src/timeline/imp.rs @@ -1,5 +1,5 @@ use chrono::{prelude::*, Duration}; -use glib::Properties; +use gtk::glib::{self, prelude::*, ParamSpec, Properties, Property, Value}; use gtk::prelude::*; use gtk::subclass::prelude::*; use std::cell::{Cell, RefCell}; @@ -11,8 +11,8 @@ pub enum Selection { Point(DateTime), } -// #[derive(Properties)] -// #[properties(wrapper_type = super::TimeLine)] +#[derive(Properties)] +#[properties(wrapper_type = super::TimeLine)] pub struct TimeLine { pub(super) drawing_area: RefCell>, pub(super) height: Cell, @@ -26,6 +26,11 @@ pub struct TimeLine { pub(super) border_radius: Cell, pub(super) tick_selection: Rc>, pub(super) start_time: Cell>, + #[property(name = "start-time", get, set)] + pub(super) start_time_stamp: Cell, + #[property(name = "end-time", get, set)] + pub(super) end_time_stamp: Cell, + pub(super) dpi: Cell, } impl Default for TimeLine { @@ -43,6 +48,9 @@ impl Default for TimeLine { border_radius: Cell::new(8.0), tick_selection: Rc::new(Cell::new(0)), start_time: Cell::new(Utc::now()), + start_time_stamp: Cell::new(i64::MIN), + end_time_stamp: Cell::new(i64::MIN), + dpi: Cell::new(3600f64/30.0), } } } @@ -58,7 +66,43 @@ impl ObjectSubclass for TimeLine { } } -impl ObjectImpl for TimeLine {} +impl ObjectImpl for TimeLine { + fn properties() -> &'static [ParamSpec] { + Self::derived_properties() + } + + fn set_property(&self, id: usize, value: &Value, pspec: &ParamSpec) { + match pspec.name() { + "start-time" => { + self.selection.set(Some(Selection::Point( + Utc.timestamp_opt(value.get().unwrap(), 0).unwrap(), + ))); + } + "end-time" => { + self.end_time_stamp.set(value.get().unwrap()); + } + _ => { + self.derived_set_property(id, value, pspec); + } + } + } + + fn property(&self, id: usize, pspec: &ParamSpec) -> Value { + match pspec.name() { + "start-time" => match self.selection.get() { + Some(Selection::Slice((start, _))) => start.timestamp().to_value(), + Some(Selection::Point(time)) => time.timestamp().to_value(), + None => self.start_time_stamp.get().to_value(), + }, + "end-time" => match self.selection.get() { + Some(Selection::Slice((_, end))) => end.timestamp().to_value(), + Some(Selection::Point(time)) => time.timestamp().to_value(), + None => self.start_time_stamp.get().to_value(), + }, + _ => self.derived_property(id, pspec), + } + } +} impl WidgetImpl for TimeLine {} @@ -137,3 +181,12 @@ pub(super) fn draw_cursor(cr: >k::cairo::Context, x: f64, y: f64, width: f64, // 绘制三角形部分 cr.fill().expect("Failed to fill the triangle"); } + +impl TimeLine { + fn get_selection_stamp(&self) -> Option { + self.selection.get().map(|selection| match selection { + Selection::Slice((start, _)) => start.timestamp(), + Selection::Point(time) => time.timestamp(), + }) + } +} diff --git a/src/timeline/mod.rs b/src/timeline/mod.rs index b2822e7..7ea5f25 100644 --- a/src/timeline/mod.rs +++ b/src/timeline/mod.rs @@ -7,6 +7,8 @@ use gtk::traits::WidgetExt; use gtk::EventControllerMotion; pub use imp::Selection; use imp::{draw_cursor, draw_rounded_rectangle, round_to_nearest}; +use std::cell::Cell; +use std::rc::Rc; glib::wrapper! { pub struct TimeLine(ObjectSubclass) @@ -115,7 +117,7 @@ impl TimeLine { let d = Utc.timestamp_opt(time_stamp, 0).unwrap(); if d.hour() % 3 == 0 { - let d_format = d.format("%H:%M").to_string(); + let d_format = if d.hour() ==0 {d.format("%Y/%m/%d %H:%M").to_string()} else { d.format("%H:%M").to_string()}; let extents = cr.text_extents(&d_format).unwrap(); let x = time_cursor - (extents.width() / 2.0 + extents.x_bearing()); cr.move_to(x + margin_horizontal, h / 2.0 + 15.0); @@ -164,34 +166,61 @@ impl TimeLine { ); let gesture_click = gtk::GestureClick::new(); + let click_position = Rc::new(Cell::new(0f64)); gesture_click.set_button(1); - gesture_click.connect_pressed(clone!(@weak drawing_area, @weak this =>move |gesture, _, x, y| { - let self_ = this.imp(); - let mut gesture_click_selection = self_.selection.clone(); - - let margin_horizontal = self_.margin_horizontal.get() as f64; - let major_tick_step = self_.major_tick_step.get(); - let minor_tick_step = self_.minor_tick_step.get(); - let major_tick_interval = self_.major_tick_interval.get() as f64; - let minor_tick_interval = major_tick_interval / (major_tick_step / minor_tick_step) as f64; - let height = self_.height.get(); - let width = self_.width.get(); - - let start = (&self_.start_time).get(); - let x = x.clamp(margin_horizontal, width as f64 - margin_horizontal); - let secs = (x - margin_horizontal) / (major_tick_interval / major_tick_step as f64); - - gesture_click_selection.replace(Some(Selection::Point( - start + Duration::seconds(secs as i64), - ))); - - cursor_pos_clicker_clone.replace(None); - drawing_area.queue_draw(); - + gesture_click.connect_pressed(clone!(@strong click_position => move |_, _, x, _| { + click_position.set(x); })); + gesture_click.connect_released( + clone!(@strong click_position,@weak drawing_area, @weak this =>move |_, _, x, _| { + if click_position.get() != x { + return; + } + let self_ = this.imp(); + let margin_horizontal = self_.margin_horizontal.get() as f64; + let major_tick_step = self_.major_tick_step.get(); + let major_tick_interval = self_.major_tick_interval.get() as f64; + let width = self_.width.get(); + + let start = (&self_.start_time).get(); + let x = x.clamp(margin_horizontal, width as f64 - margin_horizontal); + let secs = (x - margin_horizontal) / (major_tick_interval / major_tick_step as f64); + + let selection_start = start + Duration::seconds(secs as i64); + + self_.selection.set(Some(Selection::Point( + selection_start + ))); + this.set_start_time(selection_start.timestamp()); + cursor_pos_clicker_clone.replace(None); + drawing_area.queue_draw(); + + }), + ); + + let temp_start = Rc::new(Cell::new(self_.start_time.get())); + let drag_detecture = gtk::GestureDrag::new(); + drag_detecture.connect_drag_update( + clone!(@strong temp_start,@weak this as canvas => move |this, _, _| { + let self_ = canvas.imp(); + let dpi = self_.dpi.get(); + let (ox, _) = this.offset().unwrap_or((0.0,0.0)); + + canvas.set_time_start(temp_start.get() - Duration::seconds((ox * dpi) as i64)); + canvas.imp().drawing_area.borrow().as_ref().map(|d| d.queue_draw()); + }), + ); + + drag_detecture.connect_drag_end(clone!( + @strong temp_start,@weak this => move |_,_,_|{ + temp_start.set(this.imp().start_time.get()); + } + )); + drawing_area.add_controller(gesture_click); drawing_area.add_controller(motion_controller); + drawing_area.add_controller(drag_detecture); drawing_area.set_hexpand(true); drawing_area.set_vexpand(true); drawing_area.set_parent(&this); diff --git a/src/utils.rs b/src/utils.rs index aeb1c04..314719f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,11 +1,13 @@ +use chrono::{DateTime, Utc}; +use gtk::glib::{HasParamSpec, ParamSpecInt64, ParamSpecInt64Builder, ToValue}; use std::{borrow::BorrowMut, num::NonZeroU32}; use euclid::Size2D; use femtovg::Canvas; use ndarray::{Array2, ArrayView1}; use surfman::{ - Connection, Context, ContextAttributeFlags, ContextAttributes, ContextDescriptor, - NativeConnection, NativeContext, SurfaceAccess, SurfaceType, Device, GLVersion, + Connection, Context, ContextAttributeFlags, ContextAttributes, ContextDescriptor, Device, + GLVersion, NativeConnection, NativeContext, SurfaceAccess, SurfaceType, }; pub fn meshgrid(x: ArrayView1, y: ArrayView1) -> (Array2, Array2) @@ -24,10 +26,12 @@ pub fn creator() { let adapter = connection.create_adapter().unwrap(); let mut device = connection.create_device(&adapter).unwrap(); let api = device.gl_api(); - let descriptor = device.create_context_descriptor(&ContextAttributes { - version: GLVersion::new(3, 3), - flags: ContextAttributeFlags::empty(), - }).unwrap(); + let descriptor = device + .create_context_descriptor(&ContextAttributes { + version: GLVersion::new(3, 3), + flags: ContextAttributeFlags::empty(), + }) + .unwrap(); let mut context = device.create_context(&descriptor, None).unwrap(); let mut surface = device