From 24d91b628e7f22e543dfbd52ea16b326cfa10c21 Mon Sep 17 00:00:00 2001 From: sleptworld Date: Tue, 23 Jan 2024 01:41:22 +0800 Subject: [PATCH] time line --- data/css/main.css | 6 +- src/components/control_panel/control_panel.rs | 88 ++++++++++++++- src/main.rs | 2 +- src/timeline/imp.rs | 103 ++++++++++++++++-- 4 files changed, 184 insertions(+), 15 deletions(-) diff --git a/data/css/main.css b/data/css/main.css index 68d4ae7..3afcf6f 100644 --- a/data/css/main.css +++ b/data/css/main.css @@ -27,4 +27,8 @@ paned>separator { .content { font-size: 14px; -} \ No newline at end of file +} + +.lv { + background: transparent; +} diff --git a/src/components/control_panel/control_panel.rs b/src/components/control_panel/control_panel.rs index d0b966b..a873d8e 100644 --- a/src/components/control_panel/control_panel.rs +++ b/src/components/control_panel/control_panel.rs @@ -1,6 +1,7 @@ use crate::timeline::TimeLine; use adw::prelude::*; use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, ToggleButtonExt}; +use relm4::typed_list_view::{RelmListItem, TypedListView}; use relm4::*; use relm4_components::open_button::{OpenButton, OpenButtonSettings}; use relm4_components::open_dialog::OpenDialogSettings; @@ -8,6 +9,55 @@ use std::path::PathBuf; pub struct ControlPanelModel { open_button: Controller, + list_img_wrapper: TypedListView, +} + +#[derive(Debug, PartialEq, PartialOrd, Ord, Eq)] +struct ImgItem { + time: String, + img: Option, + visiable: bool, +} + +impl ImgItem { + fn new(time: String, img: Option, visiable: bool) -> Self { + Self { + time, + img, + visiable, + } + } +} + +struct Widgets { + img: gtk::Image, +} + +impl RelmListItem for ImgItem { + type Root = gtk::Frame; + type Widgets = Widgets; + + fn setup(_item: >k::ListItem) -> (gtk::Frame, Widgets) { + relm4::view! { + my_box = gtk::Frame { + set_height_request: 70, + set_width_request: 100, + gtk::Box{ + set_margin_all:2, + #[name = "img"] + gtk::Image{} + } + } + } + + let widgets = Widgets { img }; + + (my_box, widgets) + } + + fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) { + let Widgets { img } = widgets; + } } #[derive(Debug)] @@ -79,10 +129,24 @@ impl SimpleComponent for ControlPanelModel { } }, gtk::Frame{ - set_width_request: 100, - TimeLine{ - set_hexpand: true, - set_vexpand: true, + set_width_request: 500, + gtk::Box{ + set_orientation:gtk::Orientation::Vertical, + set_spacing: 10, + gtk::Box{ + TimeLine{ + set_width_request: 500, + } + }, + gtk::ScrolledWindow{ + set_height_request: 70, + #[local_ref] + my_view -> gtk::ListView{ + add_css_class: "lv", + set_orientation: gtk::Orientation::Horizontal, + } + }, + } }, } @@ -106,7 +170,21 @@ impl SimpleComponent for ControlPanelModel { gtk::StringList::new(&["Option 1", "Option 2", "Option 3", "Editable..."]); let step_selector = gtk::DropDown::from_strings(&["Option 1", "Option 2", "Option 3"]); - let model = ControlPanelModel { open_button }; + let mut list_img_wrapper: TypedListView = + TypedListView::with_sorting(); + + list_img_wrapper.append(ImgItem::new( + "00:00:00".to_string(), + None, + true, + )); + + let model = ControlPanelModel { + open_button, + list_img_wrapper, + }; + + let my_view = &model.list_img_wrapper.view; let widgets = view_output!(); ComponentParts { model, widgets } diff --git a/src/main.rs b/src/main.rs index dbc7ee4..77f215c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,7 +44,7 @@ fn main() { .or_else(|_| libloading::os::windows::Library::open_already_loaded("epoxy-0.dll")) .unwrap(); - gio::resources_register_include!("p.gresource").expect("Failed to register resources."); + // gio::resources_register_include!("p.gresource").expect("Failed to register resources."); epoxy::load_with(|name| { unsafe { library.get(name.as_bytes()) } diff --git a/src/timeline/imp.rs b/src/timeline/imp.rs index 6131d62..4ee5aa9 100644 --- a/src/timeline/imp.rs +++ b/src/timeline/imp.rs @@ -1,10 +1,11 @@ -use gtk::cairo; use gtk::glib::clone; -use gtk::prelude::DrawingAreaExtManual; +use gtk::prelude::{DrawingAreaExtManual, StyleContextExt}; use gtk::subclass::prelude::*; use gtk::traits::{GLAreaExt, WidgetExt}; +use gtk::{cairo, EventControllerMotion}; use std::cell::RefCell; use std::num::NonZeroU32; +use svg::parser::Event; use crate::render::Render; @@ -34,19 +35,96 @@ impl ObjectSubclass for TimeLine { impl ObjectImpl for TimeLine { fn constructed(&self) { let drawing_area = gtk::DrawingArea::new(); + let settings = gtk::Settings::default().unwrap(); + let prefers_dark_theme = settings.is_gtk_application_prefer_dark_theme(); + + let obj = self.obj().clone(); + + let (r, g, b) = if prefers_dark_theme { + (1.0, 1.0, 1.0) + } else { + (0.0, 0.0, 0.0) + }; + + let container = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .height_request(70) + .hexpand(true) + .margin_start(10) + .margin_end(10) + .build(); + + let cursor_pos = std::rc::Rc::new(std::cell::RefCell::new(50.0)); + let cursor_pos_clone = cursor_pos.clone(); + + let motion_controller = EventControllerMotion::new(); + let cursor_pos_clone = cursor_pos.clone(); + motion_controller.connect_motion(clone!(@weak drawing_area => + move |_, x, _| { + *cursor_pos.borrow_mut() = x; + drawing_area.queue_draw(); // 重绘时间线和游标 + })); + drawing_area.set_draw_func( (move |_, cr, w, h| { - cr.set_source_rgb(1.0, 0.0, 0.0); - Self::draw_rounded_rectangle(cr, 0f64, 50f64, w as f64, 10f64, 5f64); - cr.fill(); + cr.set_source_rgba(0.89, 0.89, 0.89, 1.0); + + Self::draw_rounded_rectangle(cr, 0.0, h as f64 / 2.0 - 20.0, w as f64, 45.0, 5.0); + + let w = w - 20; + cr.fill().unwrap(); + cr.set_source_rgb(r, g, b); + cr.set_line_width(1.5); + + let major_tick_interval = 30; // 每小时的像素宽度 + let minor_tick_interval = major_tick_interval / 10; + + let y_pos = h as f64 / 2.0; // 时间轴位于中央 + cr.move_to(10.0, y_pos); + cr.line_to(w as f64, y_pos); + cr.stroke().unwrap(); + + // 绘制大刻度(一小时) + for i in 0..(w / major_tick_interval + 1) { + let x_pos = i * major_tick_interval; + cr.set_line_width(1.0); + cr.move_to(x_pos as f64 + 10.0, h as f64 / 2.0); + cr.line_to(x_pos as f64 + 10.0, h as f64 / 2.0 - 8.0); + cr.stroke().unwrap(); + let time_text = format!("{}", i); // 假设时间轴从0点开始 + cr.move_to(x_pos as f64 + 10.0, y_pos + 15.0); // 文本位置 + cr.show_text(&time_text).unwrap(); + } + + // 绘制小刻度(一分钟) + for i in 0..(w / minor_tick_interval + 1) { + let x_pos = i * minor_tick_interval; + cr.set_line_width(0.5); + cr.move_to(x_pos as f64 + 10.0, h as f64 / 2.0); + cr.line_to(x_pos as f64 + 10.0, h as f64 / 2.0 - 5.0); + cr.stroke().unwrap(); + } + + cr.set_source_rgb(0.98, 0.26, 0.24); // 红色 + + Self::draw_cursor( + cr, + *cursor_pos_clone.borrow(), + h as f64 / 2.0 - 15.0, + 2f64, + 35f64, + ); }), ); - let obj = self.obj().clone(); - drawing_area.set_parent(&obj); - drawing_area.set_hexpand(true); + drawing_area.add_controller(motion_controller); + + drawing_area.set_parent(&container); drawing_area.set_vexpand(true); + drawing_area.set_hexpand(true); self.drawing_area.borrow_mut().replace(drawing_area); + + container.set_parent(&obj); self.parent_constructed(); } } @@ -93,4 +171,13 @@ impl TimeLine { ); cr.close_path(); } + + fn draw_cursor(cr: >k::cairo::Context, x: f64, y: f64, width: f64, height: f64) { + // 绘制矩形部分 + cr.rectangle(x - width / 2.0, y, width, height); + cr.fill().expect("Failed to fill the rectangle"); + + // 绘制三角形部分 + cr.fill().expect("Failed to fill the triangle"); + } }