From c83a9e6d8aa7eae2723121ec08f06446a7304633 Mon Sep 17 00:00:00 2001 From: Tsuki Date: Thu, 22 Feb 2024 11:41:28 +0800 Subject: [PATCH] sync --- Cargo.lock | 7 ++++ Cargo.toml | 3 ++ build.rs | 8 ++++ data/css.gresource.xml | 6 +++ data/css/main.css | 34 ++++++++++++++++ src/components/app.rs | 36 +++++++++++++---- src/components/new_project.rs | 56 ++++++++++++++++++++----- src/components/setting_item/mod.rs | 65 +++++++++++++++++++++--------- src/config.rs | 44 +++++++++----------- src/main.rs | 33 +++++++-------- 10 files changed, 214 insertions(+), 78 deletions(-) create mode 100644 build.rs create mode 100644 data/css.gresource.xml create mode 100644 data/css/main.css diff --git a/Cargo.lock b/Cargo.lock index 4b20a72..026fdcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -433,6 +433,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "glib-build-tools" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a65d79efe318ef2cbbbb37032b125866fd82c34ea44c816132621bbc552e716" + [[package]] name = "glib-macros" version = "0.17.10" @@ -971,6 +977,7 @@ name = "rsproject" version = "0.1.0" dependencies = [ "chrono", + "glib-build-tools", "gtk4", "libadwaita", "relm4", diff --git a/Cargo.toml b/Cargo.toml index eb589c2..486c0bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ relm4 = { version = "0.6.2", features = ["libadwaita"]} relm4-icons = { version = "0.6.0", features = ["add-filled"] } chrono = "0.4.34" tracker = "0.2.1" + +[build-dependencies] +glib-build-tools = "0.17.0" \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..ac6db38 --- /dev/null +++ b/build.rs @@ -0,0 +1,8 @@ +fn main() { + glib_build_tools::compile_resources( + &["data"], + // "src/resources/resources.gresource.xml", + "data/css.gresource.xml", + "css.gresource", + ); +} diff --git a/data/css.gresource.xml b/data/css.gresource.xml new file mode 100644 index 0000000..fcf0d5d --- /dev/null +++ b/data/css.gresource.xml @@ -0,0 +1,6 @@ + + + + css/main.css + + diff --git a/data/css/main.css b/data/css/main.css new file mode 100644 index 0000000..ba92bd8 --- /dev/null +++ b/data/css/main.css @@ -0,0 +1,34 @@ +.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; +} + +.lv { + background: transparent; +} \ No newline at end of file diff --git a/src/components/app.rs b/src/components/app.rs index b91bd49..ef44994 100644 --- a/src/components/app.rs +++ b/src/components/app.rs @@ -12,6 +12,7 @@ use std::{ rc::Rc, sync::{Arc, Mutex}, }; +use gtk::Widget; relm4::new_action_group!(FileActionGroup, "file"); relm4::new_stateless_action!(OpenAction, FileActionGroup, "open"); @@ -45,27 +46,33 @@ impl Component for AppModel { set_default_height: 900, set_focus_on_click:true, connect_close_request[sender] => move |_| { + let app = relm4::main_application(); + app.quit(); gtk::Inhibit(true) }, gtk::Box{ set_orientation: gtk::Orientation::Vertical, set_valign:gtk::Align::Fill, set_spacing:2, - gtk::HeaderBar{}, + // gtk::HeaderBar{}, gtk::Box{ set_hexpand: true, set_vexpand: true, #[name="stack"] - gtk::Stack{ - set_transition_type:gtk::StackTransitionType::SlideUp, - set_transition_duration:300, + adw::NavigationView{ set_hexpand:true, set_vexpand:true, }, + } } }, - home_page = adw::Clamp{ + home_page = adw::NavigationPage{ + #[wrap(Some)] + set_child=&adw::ToolbarView{ + add_top_bar=&adw::HeaderBar{}, + #[wrap(Some)] + set_content=&adw::Clamp{ set_maximum_size: 1000, gtk::Box{ set_orientation: gtk::Orientation::Vertical, @@ -84,9 +91,21 @@ impl Component for AppModel { }, }, } + } + } + }, + new_page = adw::NavigationPage{ + set_title: "New Project", + set_tag: Some("new_page"), + #[wrap(Some)] + set_child=&adw::ToolbarView{ + add_top_bar=&adw::HeaderBar{}, + #[wrap(Some)] + set_content=model.new_page_model.widget() -> &adw::NavigationSplitView {} + } }, - home_stack_page = stack.add_titled(&home_page, Some("home"), "Home") -> gtk::StackPage{}, - new_project_page = stack.add_titled(model.new_page_model.widget(),Some("new_page"), "New") -> gtk::StackPage{}, + stack.add(&home_page), + stack.add(&new_page), } menu! { @@ -137,7 +156,8 @@ impl Component for AppModel { ) { match msg { AppMsg::NewProject => { - widgets.stack.set_visible_child_name("new_page"); + // widgets.stack.set_visible_child_name("new_page"); + widgets.stack.push_by_tag("new_page"); } } } diff --git a/src/components/new_project.rs b/src/components/new_project.rs index 0d9fa94..0a8cc64 100644 --- a/src/components/new_project.rs +++ b/src/components/new_project.rs @@ -1,25 +1,33 @@ +use crate::components::setting_item::{SettingItem, SettingType}; +use crate::config::CommonConfig; use adw::prelude::*; use gtk::glib::clone; use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, ToggleButtonExt}; -use relm4::{prelude::*, view}; -use std::path::PathBuf; -use std::rc::Rc; use gtk::ResponseType::No; use gtk::Widget; use relm4::factory::FactoryVecDeque; -use crate::components::setting_item::{SettingItem, SettingType}; -use crate::config::CommonConfig; +use relm4::{prelude::*, view}; +use std::cell::RefCell; +use std::path::PathBuf; +use std::rc::Rc; +#[tracker::track] #[derive(Debug)] pub struct NewPageModel { - common_setting: CommonConfig + project_name: String, + common_setting: CommonConfig, +} + +#[derive(Debug)] +pub enum NewPageMsg{ + ChangeName(String) } #[relm4::component(pub)] impl SimpleComponent for NewPageModel { type Init = (); type Output = (); - type Input = (); + type Input = NewPageMsg; view! { #[root] @@ -46,11 +54,28 @@ impl SimpleComponent for NewPageModel { common_setting = gtk::Box{ set_orientation: gtk::Orientation::Vertical, + set_valign: gtk::Align::Center, + adw::PreferencesPage{ set_title: "Common", add=&adw::PreferencesGroup{ + add=>k::Label { + set_halign: gtk::Align::Start, + #[track = "model.changed(NewPageModel::project_name())"] + set_text: &model.project_name, + add_css_class: "h1" + } + }, + add=&adw::PreferencesGroup{ + set_title: "Common", + add=&adw::EntryRow{ + set_title: "Project Name", + connect_text_notify[sender] => move |s| { + sender.input(NewPageMsg::ChangeName(s.text().to_string())); + } + }, #[iterate] - add: model.common_setting.to_settings().iter_mut().map(|v| v.to_widget()).collect::>().iter() + add: model.common_setting.to_settings().iter_mut().map(|v| v.widget()).collect::>().iter() } } }, @@ -64,10 +89,21 @@ impl SimpleComponent for NewPageModel { sender: relm4::ComponentSender, ) -> relm4::ComponentParts { let config = CommonConfig::default(); - let model = NewPageModel { common_setting: config }; + let mut model = NewPageModel { + project_name: format!("Project {}", 0), + common_setting: config, + tracker: 0, + }; let widgets = view_output!(); ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _sender: ComponentSender) {} + fn update(&mut self, msg: Self::Input, _sender: ComponentSender) { + self.reset(); + match msg { + NewPageMsg::ChangeName(name) => { + self.set_project_name(name); + } + } + } } diff --git a/src/components/setting_item/mod.rs b/src/components/setting_item/mod.rs index 96e71c7..0665cdf 100644 --- a/src/components/setting_item/mod.rs +++ b/src/components/setting_item/mod.rs @@ -1,7 +1,8 @@ +use std::cell::{Cell, RefCell}; use adw::prelude::*; use gtk::prelude::*; use gtk::{StringList, Widget}; -use relm4::{factory::FactoryView, gtk, prelude::*, FactorySender}; +use relm4::{factory::FactoryView, gtk, prelude::*, FactorySender, RelmObjectExt}; use std::rc::Rc; #[derive(Debug)] @@ -11,11 +12,11 @@ pub enum Msg {} pub enum OutputMsg {} pub enum SettingType { - Select(Vec), + Select(Vec, Option>>), Action, - Entry(Option, Option bool>>), - Switch(bool), - Spin(f64, f64, f64, f64), + Entry(Option, Option bool>>, Option>>), + Switch(bool, Option>>), + Spin(f64, f64, f64, f64, Option>>), } pub struct SettingItem { @@ -27,10 +28,17 @@ impl SettingItem { pub fn new(title: String, _type: SettingType) -> Self { Self { title, _type } } - pub fn to_widget(&mut self) -> Widget { + pub fn widget(&mut self) -> Widget { (match &mut self._type { - SettingType::Select(selects) => { + SettingType::Select(selects, selected) => { let w = adw::ComboRow::builder().title(&self.title).build(); + if let Some(selected) = selected { + let selected = selected.clone(); + w.connect_selected_item_notify(|s| { + // println!("{}", text); + // selected.set(text.to_string()); + }); + } let model = StringList::new( selects .iter() @@ -45,37 +53,54 @@ impl SettingItem { .title(&self.title) .build() .upcast::(), - SettingType::Entry(text, f) => { + SettingType::Entry(text, f, buffer) => { let w = adw::EntryRow::new(); w.set_title(&self.title); - let f = f.take(); - if let Some(f) = f { - println!("test"); - w.connect_text_notify(move |s| { - let text = s.text(); - if !f(&text) { - s.set_text(""); - } - }); - } if let Some(text) = text { w.set_text(text); } + + let f = f.take(); + let buffer = buffer.clone(); + if let Some(buffer) = buffer { + w.connect_text_notify(move |s| { + let text = s.text(); + if let Some(f) = f.as_ref(){ + if !f(&text) { + s.set_text(""); + return; + } + } + *buffer.borrow_mut() = text.to_string(); + }); + } w.upcast::() } - SettingType::Switch(t) => { + SettingType::Switch(t, v) => { let w = adw::SwitchRow::new(); w.set_title(&self.title); w.set_active(*t); + if let Some(v) = v { + let v = v.clone(); + w.connect_active_notify(move |s| { + v.set(s.is_active()); + }); + } w.upcast::() } - SettingType::Spin(min, max, clamb, value) => { + SettingType::Spin(min, max, clamb, value, v) => { let w = adw::SpinRow::new( Some(>k::Adjustment::new(*value, *min, *max, *clamb, 0.0, 0.0)), *clamb, 0, ); w.set_numeric(true); + if let Some(v) = v { + let v = v.clone(); + w.connect_value_notify(move |s| { + v.set(s.value()); + }); + } // w.set_range(*min, *max); w.set_value(*value); w.set_title(&self.title); diff --git a/src/config.rs b/src/config.rs index 2590d5e..43a33ab 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,15 +1,18 @@ +use std::cell::{Cell, Ref, RefCell}; use std::collections::HashMap; +use std::rc::Rc; use crate::components::{SettingItem, SettingType}; -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, PartialOrd, PartialEq)] pub struct CommonConfig{ - pub name: String, - pub version: String, + pub name: Rc>, + pub version: Rc>, + radar_lens: Rc>, pub radars: Vec, pub algorithms: Vec, } -#[derive( Clone, Debug)] +#[derive( Clone, Debug, PartialOrd, PartialEq)] pub struct RadarConfig{ pub name: String, pub _type: String, @@ -21,35 +24,28 @@ pub struct RadarConfig{ r_limit: f64, } -#[derive( Clone, Debug)] +#[derive( Clone, Debug, PartialOrd, PartialEq)] pub struct AlgorithmConfig { name: String, version: String, - info: HashMap + // info: HashMap } impl CommonConfig{ pub fn to_settings(&self) -> Vec{ let mut settings = Vec::new(); - settings.push(SettingItem::new("Name".to_string(), SettingType::Entry(Some(self.name.clone()), None))); - settings.push(SettingItem::new("Version".to_string(), SettingType::Entry(Some(self.version.clone()), None))); - settings.push(SettingItem::new("Radar Num".to_string(), SettingType::Spin(0.0, 0.0, 100.0, 1.0))); + // settings.push(SettingItem::new("Name".to_string(), SettingType::Entry(Some(self.name.borrow().clone()), None, Some(self.name.clone())))); + settings.push(SettingItem::new("Version".to_string(), SettingType::Entry(Some(self.version.borrow().clone()), None, Some(self.version.clone())))); + settings.push(SettingItem::new("Radar Num".to_string(), SettingType::Spin(0.0, 0.0, 100.0, 1.0, Some(self.radar_lens.clone())))); for radar in self.radars.iter(){ - settings.push(SettingItem::new("Name".to_string(), SettingType::Entry(Some(radar.name.clone()), None))); - settings.push(SettingItem::new("Type".to_string(), SettingType::Entry(Some(radar._type.clone()), None))); - settings.push(SettingItem::new("Location".to_string(), SettingType::Entry(Some(format!("{},{},{}", radar.loc[0], radar.loc[1], radar.loc[2])), None))); - settings.push(SettingItem::new("Azimuth Beam Width".to_string(), SettingType::Entry(Some(radar.az_beam_width.to_string()), None))); - settings.push(SettingItem::new("Elevation Beam Width".to_string(), SettingType::Entry(Some(radar.el_beam_width.to_string()), None))); - settings.push(SettingItem::new("Azimuth Method".to_string(), SettingType::Select(vec!["1".to_string(), "2".to_string(), "3".to_string()]))); - settings.push(SettingItem::new("Elevation Method".to_string(), SettingType::Select(vec!["1".to_string(), "2".to_string(), "3".to_string()]))); - settings.push(SettingItem::new("Range Limit".to_string(), SettingType::Entry(Some(radar.r_limit.to_string()), None))); - } - for algorithm in self.algorithms.iter(){ - settings.push(SettingItem::new("Name".to_string(), SettingType::Entry(Some(algorithm.name.clone()), None))); - settings.push(SettingItem::new("Version".to_string(), SettingType::Entry(Some(algorithm.version.clone()), None))); - for (k, v) in algorithm.info.iter(){ - settings.push(SettingItem::new(k.clone(), SettingType::Entry(Some(v.clone()), None))); - } + settings.push(SettingItem::new("Name".to_string(), SettingType::Entry(Some(radar.name.clone()), None, None))); + settings.push(SettingItem::new("Type".to_string(), SettingType::Entry(Some(radar._type.clone()), None, None))); + settings.push(SettingItem::new("Location".to_string(), SettingType::Entry(Some(format!("{},{},{}", radar.loc[0], radar.loc[1], radar.loc[2])), None, None))); + settings.push(SettingItem::new("Azimuth Beam Width".to_string(), SettingType::Entry(Some(radar.az_beam_width.to_string()), None, None))); + settings.push(SettingItem::new("Elevation Beam Width".to_string(), SettingType::Entry(Some(radar.el_beam_width.to_string()), None, None))); + settings.push(SettingItem::new("Azimuth Method".to_string(), SettingType::Select(vec!["1".to_string(), "2".to_string(), "3".to_string()], None))); + settings.push(SettingItem::new("Elevation Method".to_string(), SettingType::Select(vec!["1".to_string(), "2".to_string(), "3".to_string()], None))); + settings.push(SettingItem::new("Range Limit".to_string(), SettingType::Entry(Some(radar.r_limit.to_string()), None, None))); } settings } diff --git a/src/main.rs b/src/main.rs index 7cb4c14..1f2d629 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,26 +9,27 @@ mod components; mod config; use components::AppModel; -const APP_ID: &str = "org.tsuki.radar_g"; +const APP_ID: &str = "org.tsuki.rsproject"; fn main() { // Load GL pointers from epoxy (GL context management library used by GTK). let relm = relm4::RelmApp::new(APP_ID); + + initialize_custom_css(); relm.run::(()); - // initialize_custom_css(); } -// fn initialize_custom_css() { -// gio::resources_register_include!("css.gresource").unwrap(); -// // 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, -// ); -// } +fn initialize_custom_css() { + gio::resources_register_include!("css.gresource").unwrap(); + // 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/rsproject/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, + ); +}