add timeline

This commit is contained in:
Tsuki 2024-01-22 18:43:55 +08:00
parent ce23512f35
commit d63048d1b2
10 changed files with 271 additions and 30 deletions

View File

@ -32,7 +32,7 @@ proj = "0.27.2"
image = "0.24.7" image = "0.24.7"
anyhow = "1.0.72" anyhow = "1.0.72"
proj5 = { version = "0.1.7", features = ["multithreading"] } proj5 = { version = "0.1.7", features = ["multithreading"] }
relm4 = { version = "0.6.1",features=["libadwaita"]} relm4 = { version = "0.6.1", features = ["libadwaita"] }
relm4-components = "0.6.1" relm4-components = "0.6.1"
rstar = "*" rstar = "*"
geo = "0.26.0" geo = "0.26.0"
@ -40,11 +40,27 @@ topojson = "0.5.1"
geojson = "0.24.1" geojson = "0.24.1"
plotters = "0.3.5" plotters = "0.3.5"
plotters-backend = "0.3.5" plotters-backend = "0.3.5"
tokio = { version = "1.35.1", features = ["time", "fs", "io-std", "macros", "num_cpus", "bytes", "io-util"] } tokio = { version = "1.35.1", features = [
"time",
"fs",
"io-std",
"macros",
"num_cpus",
"bytes",
"io-util",
] }
async-trait = "0.1.77" async-trait = "0.1.77"
lazy_static = "1.4.0" lazy_static = "1.4.0"
once_cell = "1.19.0" once_cell = "1.19.0"
relm4-icons = {version="0.6.0",features=["add-filled","delete-filled","chevron-up-filled","chevron-down-filled"]} relm4-icons = { version = "0.6.0", features = [
"add-filled",
"delete-filled",
"chevron-up-filled",
"chevron-down-filled",
"play-filled",
"rewind-filled",
"fast-forward-filled",
] }
surfman = "0.8.1" surfman = "0.8.1"
euclid = "0.22.9" euclid = "0.22.9"
gl = "0.14.0" gl = "0.14.0"
@ -52,7 +68,6 @@ crossbeam = "0.8.4"
# plotters-cairo = "0.5.0" # plotters-cairo = "0.5.0"
[build-dependencies] [build-dependencies]
glib-build-tools = "0.17.0" glib-build-tools = "0.17.0"
[dependencies.geo-macros] [dependencies.geo-macros]
@ -61,4 +76,4 @@ path = "geo-macros"
[dependencies.adw] [dependencies.adw]
package = "libadwaita" package = "libadwaita"
version = "*" version = "*"
features= ["v1_4"] features = ["v1_4"]

View File

@ -1,7 +1,8 @@
fn main() { fn main() {
glib_build_tools::compile_resources( glib_build_tools::compile_resources(
&["src/resources"], &["data"],
"src/resources/resources.gresource.xml", // "src/resources/resources.gresource.xml",
"p.gresource", "data/css.gresource.xml",
"css.gresource",
); );
} }

6
data/css.gresource.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="org/tsuki/radar_g/css/">
<file alias="main.css">css/main.css</file>
</gresource>
</gresources>

30
data/css/main.css Normal file
View File

@ -0,0 +1,30 @@
.rb {
border-radius: 10px;
}
paned>separator {
color: transparent;
}
.h1 {
font-size: 25px;
font-weight: bold;
line-height: 1.25;
}
.h2 {
font-size: 20px;
font-weight: bold;
line-height: 1.25;
}
.h3 {
font-size: 15px;
font-weight: bold;
line-height: 1.25;
color: #666;
}
.content {
font-size: 14px;
}

View File

@ -1,10 +1,10 @@
use std::path::PathBuf; use crate::timeline::TimeLine;
use adw::prelude::*;
use adw::prelude::WidgetExt;
use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, ToggleButtonExt}; use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, ToggleButtonExt};
use relm4::*; use relm4::*;
use relm4_components::open_button::{OpenButton, OpenButtonSettings}; use relm4_components::open_button::{OpenButton, OpenButtonSettings};
use relm4_components::open_dialog::OpenDialogSettings; use relm4_components::open_dialog::OpenDialogSettings;
use std::path::PathBuf;
pub struct ControlPanelModel { pub struct ControlPanelModel {
open_button: Controller<OpenButton>, open_button: Controller<OpenButton>,
@ -34,12 +34,56 @@ impl SimpleComponent for ControlPanelModel {
set_orientation: gtk::Orientation::Horizontal, set_orientation: gtk::Orientation::Horizontal,
set_spacing: 10, set_spacing: 10,
set_size_request: (100, 150), set_size_request: (100, 150),
model.open_button.widget(), set_margin_horizontal:5,
gtk::Button { set_margin_top: 5,
set_label: "Edit", gtk::Frame{
set_width_request: 100,
gtk::Box{
set_orientation: gtk::Orientation::Vertical,
set_margin_all:10,
set_spacing: 10,
gtk::Box{
set_orientation: gtk::Orientation::Vertical,
gtk::Label{
set_label: "Header",
add_css_class:"h2",
set_halign: gtk::Align::Start,
}, },
gtk::Button { gtk::Label{
set_label: "Export", set_label: "Subheader",
add_css_class:"h3",
set_halign: gtk::Align::Start,
}
},
#[local]
step_selector -> gtk::DropDown{
set_expression:None::<&gtk::Expression>,
connect_selected_notify[sender] => move |step_selector| {
println!("Selected: {}", step_selector.selected());
},
},
gtk::Box{
set_orientation: gtk::Orientation::Horizontal,
set_spacing:10,
gtk::Button{
set_icon_name: "rewind-filled"
},
gtk::Button{
set_icon_name: "play-filled"
},
gtk::Button{
set_icon_name: "fast-forward-filled"
}
}
}
},
gtk::Frame{
set_width_request: 100,
TimeLine{
set_hexpand: true,
set_vexpand: true,
}
}, },
} }
} }
@ -58,6 +102,10 @@ impl SimpleComponent for ControlPanelModel {
}) })
.forward(sender.input_sender(), AppMsg::Open); .forward(sender.input_sender(), AppMsg::Open);
let select_model =
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 model = ControlPanelModel { open_button };
let widgets = view_output!(); let widgets = view_output!();

View File

@ -66,12 +66,22 @@ impl Component for MonitorModel {
set_start_child=&gtk::Frame{ set_start_child=&gtk::Frame{
add_css_class: "rb", add_css_class: "rb",
set_margin_all: 5, set_margin_all: 5,
Render{ gtk::Overlay{
#[wrap(Some)]
set_child = &Render{
add_css_class: "rb", add_css_class: "rb",
#[watch] #[watch]
set_interior_layers: model.layers.clone(), set_interior_layers: model.layers.clone(),
},
add_overlay=&gtk::Button{
set_label:"Add",
set_margin_all:10,
set_valign: gtk::Align::Start,
set_halign: gtk::Align::End,
} }
}, },
},
#[wrap(Some)] #[wrap(Some)]
set_end_child=model.sidebar.widget(), set_end_child=model.sidebar.widget(),
} }

View File

@ -16,11 +16,12 @@ mod dynamic_col;
mod errors; mod errors;
mod pipeline; mod pipeline;
mod render; mod render;
mod timeline;
mod window; mod window;
use components::app::{AppMode, AppModel}; use components::app::{AppMode, AppModel};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
const APP_ID: &str = "org.gtk_rs.HelloWorld2"; const APP_ID: &str = "org.tsuki.radar_g";
static RUNTIME: Lazy<Runtime> = static RUNTIME: Lazy<Runtime> =
Lazy::new(|| Runtime::new().expect("Setting up tokio runtime needs to succeed.")); Lazy::new(|| Runtime::new().expect("Setting up tokio runtime needs to succeed."));
@ -51,14 +52,25 @@ fn main() {
.unwrap_or(ptr::null()) .unwrap_or(ptr::null())
}); });
} }
// gio::resources_register_include!("monitor.gresource").expect("Failed to register resources");
// Create a new application
// let app = Application::builder().application_id(APP_ID).build();
// // Connect to "activate" signal of `app`
// app.connect_activate(build_ui);
// // Run the application
// app.run()
let relm = relm4::RelmApp::new(APP_ID); let relm = relm4::RelmApp::new(APP_ID);
initialize_custom_css();
relm.run::<AppModel>(AppMode::Edit); relm.run::<AppModel>(AppMode::Edit);
} }
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."),
&provider,
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
}

96
src/timeline/imp.rs Normal file
View File

@ -0,0 +1,96 @@
use gtk::cairo;
use gtk::glib::clone;
use gtk::prelude::DrawingAreaExtManual;
use gtk::subclass::prelude::*;
use gtk::traits::{GLAreaExt, WidgetExt};
use std::cell::RefCell;
use std::num::NonZeroU32;
use crate::render::Render;
pub struct TimeLine {
drawing_area: RefCell<Option<gtk::DrawingArea>>,
}
impl Default for TimeLine {
fn default() -> Self {
Self {
drawing_area: RefCell::new(None),
}
}
}
#[glib::object_subclass]
impl ObjectSubclass for TimeLine {
const NAME: &'static str = "TimeLine";
type Type = super::TimeLine;
type ParentType = gtk::Widget;
fn class_init(klass: &mut Self::Class) {
klass.set_layout_manager_type::<gtk::BoxLayout>();
}
}
impl ObjectImpl for TimeLine {
fn constructed(&self) {
let drawing_area = gtk::DrawingArea::new();
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();
}),
);
let obj = self.obj().clone();
drawing_area.set_parent(&obj);
drawing_area.set_hexpand(true);
drawing_area.set_vexpand(true);
self.drawing_area.borrow_mut().replace(drawing_area);
self.parent_constructed();
}
}
impl WidgetImpl for TimeLine {}
impl TimeLine {
fn draw_rounded_rectangle(
cr: &gtk::cairo::Context,
x: f64,
y: f64,
width: f64,
height: f64,
radius: f64,
) {
cr.new_sub_path();
cr.arc(
x + width - radius,
y + radius,
radius,
-90f64.to_radians(),
0f64.to_radians(),
);
cr.arc(
x + width - radius,
y + height - radius,
radius,
0f64.to_radians(),
90f64.to_radians(),
);
cr.arc(
x + radius,
y + height - radius,
radius,
90f64.to_radians(),
180f64.to_radians(),
);
cr.arc(
x + radius,
y + radius,
radius,
180f64.to_radians(),
270f64.to_radians(),
);
cr.close_path();
}
}

23
src/timeline/mod.rs Normal file
View File

@ -0,0 +1,23 @@
mod imp;
pub use glib::subclass::prelude::*;
use glib::{clone, Time};
use gtk::traits::WidgetExt;
use gtk::{EventControllerScrollFlags, Inhibit};
glib::wrapper! {
pub struct TimeLine(ObjectSubclass<imp::TimeLine>)
@extends gtk::Widget;
}
impl Default for TimeLine {
fn default() -> Self {
Self::new()
}
}
impl TimeLine {
pub fn new() -> Self {
let this: Self = glib::Object::new();
this
}
}