diff --git a/flake.nix b/flake.nix index ddcb079..8172cfe 100644 --- a/flake.nix +++ b/flake.nix @@ -54,7 +54,10 @@ buildInputs = with pkgs; - [ gtk4 ] + [ + gtk4 + xournalpp # not needed for building + ] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ # Additional darwin specific inputs can be set here pkgs.libiconv diff --git a/icons.toml b/icons.toml index 0a49695..8b80cff 100644 --- a/icons.toml +++ b/icons.toml @@ -3,7 +3,7 @@ base_resource_path = "/org/gtkrs/" # List of icon names you found (shipped with this crate) # Note: the file ending `-symbolic.svg` isn't part of the icon name. -icons = ["refresh", "edit", "arrow-sort-regular", "playlist-shuffle", "user-trash"] +icons = ["refresh", "edit", "arrow-sort-regular", "playlist-shuffle", "user-trash", "open-filled", "document-settings-filled"] # Optional: Specify a folder containing your own SVG icons # icon_folder = "my_svg_icons" diff --git a/src/sheet.rs b/src/sheet.rs index 15c420b..204d787 100644 --- a/src/sheet.rs +++ b/src/sheet.rs @@ -51,18 +51,26 @@ impl Ord for Sheet { } impl Sheet { - pub fn open_file_or_annotated_version_if_exists(&self) { - let path = &self.pdf.path; + pub fn construct_xopp_file_path(&self) -> PathBuf { + let mut xopp_path = self.pdf.path.with_extension("").into_os_string(); + xopp_path.push(".xopp"); + PathBuf::from(xopp_path) + } - let mut annotated_path = path.with_extension("").into_os_string(); + pub fn construct_annotated_file_path(&self) -> PathBuf { + let mut annotated_path = self.pdf.path.with_extension("").into_os_string(); annotated_path.push("_annotated.pdf"); - let annotated_version = Path::new(&annotated_path); + PathBuf::from(annotated_path) + } + + pub fn open_file_or_annotated_version_if_exists(&self) { + let annotated_version = self.construct_annotated_file_path(); if annotated_version.exists() { // TODO: open on first_page opener::open(annotated_version).unwrap(); } else { // TODO: open on first_page - opener::open(path).unwrap(); + opener::open(&self.pdf.path).unwrap(); } } diff --git a/src/ui/app.rs b/src/ui/app.rs index 441655e..fa7ca61 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, sync::Arc}; +use std::{path::PathBuf, process::Command, sync::Arc}; use chrono::Utc; use gtk::prelude::*; @@ -11,7 +11,7 @@ use relm4::{ use relm4_icons::icon_names; use crate::{ - database::Database, + database::{self, Database}, sheet::{I64DateTime, Sheet}, sheet_dao, sheet_validation, ui::mcdu::McduOutput, @@ -28,11 +28,18 @@ pub struct AppModel { directory: Arc, mcdu: Controller, sheets_listing: Controller, - edit_mode: bool, + click_mode: ClickMode, scroll_adjustment: Adjustment, sheet_edit_dialog: Option>, } +#[derive(Debug)] +pub enum ClickMode { + Open, + Edit, + Annotate, +} + #[derive(Debug)] pub enum AppInput { SearchStarted(String), @@ -40,7 +47,7 @@ pub enum AppInput { Refresh, Sort, Shuffle, - SetEditMode(bool), + SetClickMode(ClickMode), SheetListingContentsChanged, } @@ -78,11 +85,6 @@ impl AsyncComponent for AppModel { set_margin_end: 10, connect_clicked[sender] => move |_| sender.input(AppInput::Refresh), }, - gtk::ToggleButton { - set_icon_name: icon_names::EDIT, - set_margin_end: 10, - connect_clicked[sender] => move |button| sender.input(AppInput::SetEditMode(button.is_active())), - }, #[name = "button_sort"] gtk::ToggleButton { set_icon_name: icon_names::ARROW_SORT_REGULAR, @@ -92,8 +94,25 @@ impl AsyncComponent for AppModel { gtk::ToggleButton { set_icon_name: icon_names::PLAYLIST_SHUFFLE, set_group: Some(&button_sort), + set_margin_end: 10, connect_clicked[sender] => move |_| sender.input(AppInput::Shuffle), }, + #[name = "button_open"] + gtk::ToggleButton { + set_icon_name: icon_names::OPEN_FILLED, + set_active: true, + connect_clicked[sender] => move |button| if button.is_active() { sender.input(AppInput::SetClickMode(ClickMode::Open)) }, + }, + gtk::ToggleButton { + set_icon_name: icon_names::DOCUMENT_SETTINGS_FILLED, + set_group: Some(&button_open), + connect_clicked[sender] => move |button| if button.is_active() { sender.input(AppInput::SetClickMode(ClickMode::Edit)) }, + }, + gtk::ToggleButton { + set_icon_name: icon_names::EDIT, + set_group: Some(&button_open), + connect_clicked[sender] => move |button| if button.is_active() { sender.input(AppInput::SetClickMode(ClickMode::Annotate)) }, + }, }, gtk::ScrolledWindow { model.sheets_listing.widget(), @@ -115,10 +134,12 @@ impl AsyncComponent for AppModel { sender: AsyncComponentSender, ) -> AsyncComponentParts { relm4_icons::initialize_icons(); + gtk::init().unwrap(); let display = gdk::Display::default().unwrap(); let theme = gtk::IconTheme::for_display(&display); + theme.add_resource_path("/org/gtkrs/icons/"); - theme.add_resource_path("/org/gtkrs/icons/scalable/actions/"); + // theme.add_resource_path("/org/gtkrs/icons/scalable/actions/"); let mcdu = McduModel::builder() .launch(()) @@ -142,7 +163,7 @@ impl AsyncComponent for AppModel { directory: Arc::new(init_data.directory), mcdu, sheets_listing, - edit_mode: false, + click_mode: ClickMode::Open, scroll_adjustment: Adjustment::builder().build(), sheet_edit_dialog: None, }; @@ -164,24 +185,21 @@ impl AsyncComponent for AppModel { .emit(SheetListingInput::Query(query.clone())); } AppInput::SheetPressed(sheet) => { - if self.edit_mode { - self.sheet_edit_dialog = Some( - SheetEditDialogModel::builder() - .transient_for(root) - .launch(SheetEditDialogInit { - sheet, - database: Arc::clone(&self.database), - }) - .forward(sender.input_sender(), |_| todo!()), - ); - } else { - sheet.open_file_or_annotated_version_if_exists(); - let mut sheet = sheet; - sheet.last_opened = I64DateTime(Utc::now()); - sheet_dao::update_sheet_last_opened(&self.database, &sheet) - .await - .unwrap(); - } + match self.click_mode { + ClickMode::Open => open_sheet(&sheet, &self.database).await, + ClickMode::Edit => { + self.sheet_edit_dialog = Some( + SheetEditDialogModel::builder() + .transient_for(root) + .launch(SheetEditDialogInit { + sheet, + database: Arc::clone(&self.database), + }) + .forward(sender.input_sender(), |_| todo!()), + ); + } + ClickMode::Annotate => annotate_sheet(&sheet).await, + }; } AppInput::Refresh => { let db = Arc::clone(&self.database); @@ -192,7 +210,7 @@ impl AsyncComponent for AppModel { } AppInput::Sort => self.sheets_listing.emit(SheetListingInput::Sort), AppInput::Shuffle => self.sheets_listing.emit(SheetListingInput::Shuffle), - AppInput::SetEditMode(edit_mode) => self.edit_mode = edit_mode, + AppInput::SetClickMode(click_mode) => self.click_mode = click_mode, AppInput::SheetListingContentsChanged => self.scroll_adjustment.set_value(0.0), } } @@ -210,3 +228,19 @@ impl AsyncComponent for AppModel { .emit(SheetListingInput::ReloadSheets(sheets)); } } + +async fn open_sheet(sheet: &Sheet, database: &Database) { + sheet.open_file_or_annotated_version_if_exists(); + let mut sheet = sheet.to_owned(); + sheet.last_opened = I64DateTime(Utc::now()); + sheet_dao::update_sheet_last_opened(database, &sheet) + .await + .unwrap(); +} + +async fn annotate_sheet(sheet: &Sheet) { + Command::new("xournalpp") + .arg(&sheet.pdf.path) + .spawn() + .expect("failed to execute process"); +}