timeline scroll

This commit is contained in:
Tsuki 2024-01-24 20:26:29 +08:00
parent 7a1b9adb61
commit cb68877099
7 changed files with 174 additions and 80 deletions

61
Cargo.lock generated
View File

@ -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",
]

View File

@ -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<Utc>,
selection: Option<DateTime<Utc>>,
#[tracker::no_eq]
open_button: Controller<OpenButton>,
#[tracker::no_eq]
@ -83,6 +84,7 @@ pub enum TimelineMsg {
pub enum AppMsg {
Open(PathBuf),
TimeLine(TimelineMsg),
Selection(Option<DateTime<Utc>>),
}
#[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);
}
}
}
}

View File

@ -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."),

View File

@ -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);

View File

@ -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<Utc>),
}
// #[derive(Properties)]
// #[properties(wrapper_type = super::TimeLine)]
#[derive(Properties)]
#[properties(wrapper_type = super::TimeLine)]
pub struct TimeLine {
pub(super) drawing_area: RefCell<Option<gtk::DrawingArea>>,
pub(super) height: Cell<u32>,
@ -26,6 +26,11 @@ pub struct TimeLine {
pub(super) border_radius: Cell<f64>,
pub(super) tick_selection: Rc<Cell<usize>>,
pub(super) start_time: Cell<DateTime<Utc>>,
#[property(name = "start-time", get, set)]
pub(super) start_time_stamp: Cell<i64>,
#[property(name = "end-time", get, set)]
pub(super) end_time_stamp: Cell<i64>,
pub(super) dpi: Cell<f64>,
}
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: &gtk::cairo::Context, x: f64, y: f64, width: f64,
// 绘制三角形部分
cr.fill().expect("Failed to fill the triangle");
}
impl TimeLine {
fn get_selection_stamp(&self) -> Option<i64> {
self.selection.get().map(|selection| match selection {
Selection::Slice((start, _)) => start.timestamp(),
Selection::Point(time) => time.timestamp(),
})
}
}

View File

@ -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<imp::TimeLine>)
@ -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);

View File

@ -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<T>(x: ArrayView1<T>, y: ArrayView1<T>) -> (Array2<T>, Array2<T>)
@ -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