time line
This commit is contained in:
parent
d63048d1b2
commit
24d91b628e
@ -28,3 +28,7 @@ paned>separator {
|
||||
.content {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.lv {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@ -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<OpenButton>,
|
||||
list_img_wrapper: TypedListView<ImgItem, gtk::SingleSelection>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq)]
|
||||
struct ImgItem {
|
||||
time: String,
|
||||
img: Option<gtk::gdk::Texture>,
|
||||
visiable: bool,
|
||||
}
|
||||
|
||||
impl ImgItem {
|
||||
fn new(time: String, img: Option<gtk::gdk::Texture>, 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<ImgItem, gtk::SingleSelection> =
|
||||
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 }
|
||||
|
||||
@ -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()) }
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user