From 84d3273768138261fb93e46e498f91ed6cb79395 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:12:40 +0200 Subject: [PATCH] page indicator for fixed layouts --- crates/rnote-engine/src/document/mod.rs | 2 +- crates/rnote-engine/src/engine/mod.rs | 11 ++++++ crates/rnote-ui/data/ui/mainheader.ui | 6 ++++ crates/rnote-ui/src/appwindow/mod.rs | 37 +++++++++++++++++-- crates/rnote-ui/src/canvas/canvaslayout.rs | 5 +++ crates/rnote-ui/src/canvas/input.rs | 1 + crates/rnote-ui/src/canvas/mod.rs | 41 ++++++++++++++++++++++ crates/rnote-ui/src/canvaswrapper.rs | 1 - crates/rnote-ui/src/mainheader.rs | 22 ++++++++++-- crates/rnote-ui/src/settingspanel/mod.rs | 1 + 10 files changed, 121 insertions(+), 6 deletions(-) diff --git a/crates/rnote-engine/src/document/mod.rs b/crates/rnote-engine/src/document/mod.rs index 53ccee1a58..20739f3a6d 100644 --- a/crates/rnote-engine/src/document/mod.rs +++ b/crates/rnote-engine/src/document/mod.rs @@ -165,7 +165,7 @@ impl Document { } #[allow(unused)] - pub(crate) fn calc_n_pages(&self) -> u32 { + pub fn calc_n_pages(&self) -> u32 { // Avoid div by 0 if self.format.height() > 0.0 && self.format.width() > 0.0 { (self.width / self.format.width()).ceil() as u32 diff --git a/crates/rnote-engine/src/engine/mod.rs b/crates/rnote-engine/src/engine/mod.rs index 0d4e4ff653..914b6b7a3f 100644 --- a/crates/rnote-engine/src/engine/mod.rs +++ b/crates/rnote-engine/src/engine/mod.rs @@ -680,6 +680,17 @@ impl Engine { self.camera_set_offset_expand(new_offset) } + /// go to the page number for fixed layouts + pub fn go_to_page(&mut self, page_number: f64) -> WidgetFlags { + let zoom = self.camera.zoom(); + let previous_offset = self.camera.offset(); + let new_offset = na::vector![ + previous_offset.x, + (page_number - 1.0) * self.document.format.height() * zoom + ]; + self.camera_set_offset_expand(new_offset) + } + /// Resize the doc when in autoexpanding layouts. called e.g. when finishing a new stroke. /// /// Background rendering then needs to be updated. diff --git a/crates/rnote-ui/data/ui/mainheader.ui b/crates/rnote-ui/data/ui/mainheader.ui index 4b58415ebd..3cf7ee680d 100644 --- a/crates/rnote-ui/data/ui/mainheader.ui +++ b/crates/rnote-ui/data/ui/mainheader.ui @@ -61,6 +61,12 @@ 3 + + + horizontal + true + + Save Document diff --git a/crates/rnote-ui/src/appwindow/mod.rs b/crates/rnote-ui/src/appwindow/mod.rs index df7a104d54..1e24c2da71 100644 --- a/crates/rnote-ui/src/appwindow/mod.rs +++ b/crates/rnote-ui/src/appwindow/mod.rs @@ -12,6 +12,7 @@ use adw::{prelude::*, subclass::prelude::*}; use gettextrs::gettext; use gtk4::{gdk, gio, glib, Application, IconTheme}; use rnote_compose::Color; +use rnote_engine::document::Layout; use rnote_engine::ext::GdkRGBAExt; use rnote_engine::pens::pensconfig::brushconfig::BrushStyle; use rnote_engine::pens::pensconfig::shaperconfig::ShaperStyle; @@ -200,13 +201,12 @@ impl RnAppWindow { // Returns true if the flags indicate that any loop that handles the flags should be quit. (usually an async event loop) pub(crate) fn handle_widget_flags(&self, widget_flags: WidgetFlags, canvas: &RnCanvas) { - //tracing::debug!("handling widget flags: '{widget_flags:?}'"); - if widget_flags.redraw { canvas.queue_draw(); } if widget_flags.resize { canvas.queue_resize(); + self.refresh_pages(canvas); } if widget_flags.refresh_ui { self.refresh_ui_from_engine(&self.active_tab_wrapper()); @@ -492,6 +492,38 @@ impl RnAppWindow { self.main_header().main_title().set_subtitle(&subtitle); } + pub(crate) fn refresh_pages(&self, canvas: &RnCanvas) { + let layout = canvas.engine_ref().document.layout; + match layout { + Layout::FixedSize | Layout::ContinuousVertical => { + self.main_header().page_spin().set_visible(true); + let n_pages = canvas.engine_ref().document.calc_n_pages(); + self.main_header() + .page_spin() + .set_range(1.0, n_pages as f64); + + // zoom need to be taken into account + + // slight offset : for now, because going to the page will + // give the previous page when the go_to_page function is called + // hence a cascade of calls are made until the first page is shown + // maybe we should set the page number based on the page visible + // at the center of the viewport instead + let page_number = ((canvas.engine_ref().camera.offset().y + 0.01) + / (canvas.engine_ref().document.format.height() + * canvas.engine_ref().camera.zoom())) + .ceil() + .max(1.0); + self.main_header().page_spin().set_value(page_number); + } + _ => { + // for now, not really working for infinite modes + self.main_header().page_spin().set_visible(false) + } + } + canvas.set_refresh_pages(false); // reset this property after dealing with the update + } + /// Open the file, with import dialogs when appropriate. /// /// When the file is a rnote save file, `rnote_file_new_tab` determines if a new tab is opened, @@ -873,6 +905,7 @@ impl RnAppWindow { .refresh_ui(active_tab); self.sidebar().settings_panel().refresh_ui(active_tab); self.refresh_titles(active_tab); + self.refresh_pages(&active_tab.canvas()); } /// Sync the state from the previous active tab and the current one. Used when the selected tab changes. diff --git a/crates/rnote-ui/src/canvas/canvaslayout.rs b/crates/rnote-ui/src/canvas/canvaslayout.rs index e1b927d789..4f1aa3019c 100644 --- a/crates/rnote-ui/src/canvas/canvaslayout.rs +++ b/crates/rnote-ui/src/canvas/canvaslayout.rs @@ -105,6 +105,11 @@ mod imp { .engine_mut() .update_content_rendering_current_viewport(); } + + if old_viewport != new_viewport { + // update the page number + canvas.set_refresh_pages(true); + } } } } diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index a0c96c9060..557fe625cc 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -171,6 +171,7 @@ pub(crate) fn handle_pointer_controller_event( match pen_state { PenState::Up => { canvas.enable_drawing_cursor(false); + // refresh the page number let (ep, wf) = canvas.engine_mut().handle_pen_event( PenEvent::Up { diff --git a/crates/rnote-ui/src/canvas/mod.rs b/crates/rnote-ui/src/canvas/mod.rs index eb929a57d1..485bf95f04 100644 --- a/crates/rnote-ui/src/canvas/mod.rs +++ b/crates/rnote-ui/src/canvas/mod.rs @@ -40,6 +40,7 @@ struct Connections { appwindow_scalefactor: Option, appwindow_save_in_progress: Option, appwindow_unsaved_changes: Option, + appwindow_refresh_pages: Option, appwindow_touch_drawing: Option, appwindow_show_drawing_cursor: Option, appwindow_regular_cursor: Option, @@ -78,6 +79,7 @@ mod imp { pub(crate) output_file_expect_write: Cell, pub(crate) save_in_progress: Cell, pub(crate) unsaved_changes: Cell, + pub(crate) refresh_pages: Cell, pub(crate) empty: Cell, pub(crate) touch_drawing: Cell, pub(crate) show_drawing_cursor: Cell, @@ -172,6 +174,7 @@ mod imp { output_file_expect_write: Cell::new(false), save_in_progress: Cell::new(false), unsaved_changes: Cell::new(false), + refresh_pages: Cell::new(false), empty: Cell::new(true), touch_drawing: Cell::new(false), show_drawing_cursor: Cell::new(false), @@ -260,6 +263,9 @@ mod imp { glib::ParamSpecBoolean::builder("unsaved-changes") .default_value(false) .build(), + glib::ParamSpecBoolean::builder("refresh-pages") + .default_value(false) + .build(), glib::ParamSpecBoolean::builder("empty") .default_value(true) .build(), @@ -290,6 +296,7 @@ mod imp { "output-file" => self.output_file.borrow().to_value(), "save-in-progress" => self.save_in_progress.get().to_value(), "unsaved-changes" => self.unsaved_changes.get().to_value(), + "refresh-pages" => self.refresh_pages.get().to_value(), "empty" => self.empty.get().to_value(), "hadjustment" => self.hadjustment.borrow().to_value(), "vadjustment" => self.vadjustment.borrow().to_value(), @@ -323,6 +330,11 @@ mod imp { value.get().expect("The value needs to be of type `bool`"); self.unsaved_changes.replace(unsaved_changes); } + "refresh-pages" => { + let refresh_pages: bool = + value.get().expect("The value needs to be of type `bool`"); + self.refresh_pages.replace(refresh_pages); + } "empty" => { let empty: bool = value.get().expect("The value needs to be of type `bool`"); self.empty.replace(empty); @@ -665,6 +677,18 @@ impl RnCanvas { } } + #[allow(unused)] + pub(crate) fn refresh_pages(&self) -> bool { + self.property::("refresh-pages") + } + + #[allow(unused)] + pub(crate) fn set_refresh_pages(&self, refresh_pages: bool) { + if self.imp().refresh_pages.get() != refresh_pages { + self.set_property("refresh-pages", refresh_pages.to_value()); + } + } + #[allow(unused)] pub(crate) fn empty(&self) -> bool { self.property::("empty") @@ -1078,6 +1102,7 @@ impl RnCanvas { } appwindow.refresh_titles(&appwindow.active_tab_wrapper()); + appwindow.refresh_pages(&canvas); }), ); @@ -1114,6 +1139,13 @@ impl RnCanvas { }), ); + let appwindow_refresh_pages = self.connect_notify_local( + Some("refresh-pages"), + clone!(@weak appwindow => move |_,_| { + appwindow.refresh_pages(&appwindow.active_tab_wrapper().canvas()); + }), + ); + // one per-appwindow property for touch-drawing let appwindow_touch_drawing = appwindow .bind_property("touch-drawing", self, "touch-drawing") @@ -1224,6 +1256,12 @@ impl RnCanvas { { self.disconnect(old); } + if let Some(old) = connections + .appwindow_refresh_pages + .replace(appwindow_refresh_pages) + { + self.disconnect(old) + } if let Some(old) = connections .appwindow_touch_drawing .replace(appwindow_touch_drawing) @@ -1281,6 +1319,9 @@ impl RnCanvas { if let Some(old) = connections.appwindow_unsaved_changes.take() { self.disconnect(old); } + if let Some(old) = connections.appwindow_refresh_pages.take() { + self.disconnect(old); + } if let Some(old) = connections.appwindow_touch_drawing.take() { old.unbind(); } diff --git a/crates/rnote-ui/src/canvaswrapper.rs b/crates/rnote-ui/src/canvaswrapper.rs index a2fcddfaa3..e96695cff2 100644 --- a/crates/rnote-ui/src/canvaswrapper.rs +++ b/crates/rnote-ui/src/canvaswrapper.rs @@ -351,7 +351,6 @@ mod imp { widget_flags |= canvas.engine_mut().camera_set_offset_expand(new_camera_offset); canvas.emit_handle_widget_flags(widget_flags); } - glib::Propagation::Stop })); } diff --git a/crates/rnote-ui/src/mainheader.rs b/crates/rnote-ui/src/mainheader.rs index dc8d3757f0..6b1f3adfa4 100644 --- a/crates/rnote-ui/src/mainheader.rs +++ b/crates/rnote-ui/src/mainheader.rs @@ -1,8 +1,8 @@ // Imports use crate::{appmenu::RnAppMenu, appwindow::RnAppWindow, canvasmenu::RnCanvasMenu}; use gtk4::{ - glib, prelude::*, subclass::prelude::*, Box, CompositeTemplate, EventControllerLegacy, Label, - ToggleButton, Widget, + glib, glib::clone, prelude::*, subclass::prelude::*, Box, CompositeTemplate, + EventControllerLegacy, Label, SpinButton, ToggleButton, Widget, }; mod imp { @@ -29,6 +29,8 @@ mod imp { pub(crate) quickactions_box: TemplateChild, #[template_child] pub(crate) right_buttons_box: TemplateChild, + #[template_child] + pub(crate) page_spin: TemplateChild, } #[glib::object_subclass] @@ -105,6 +107,10 @@ impl RnMainHeader { self.imp().appmenu.get() } + pub(crate) fn page_spin(&self) -> SpinButton { + self.imp().page_spin.get() + } + pub(crate) fn init(&self, appwindow: &RnAppWindow) { let imp = self.imp(); @@ -128,5 +134,17 @@ impl RnMainHeader { capture_right.connect_event(|_, _| glib::Propagation::Stop); imp.right_buttons_box.add_controller(capture_right); + + imp.page_spin.set_increments(1.0, 1.0); + imp.page_spin + .connect_value_changed(clone!(@weak appwindow => move |spinb| { + let page_number = spinb.value(); + let canvas = appwindow.active_tab_wrapper().canvas(); + if !canvas.property::("refresh-pages") { + // go to the page indicated + let widget_flags = canvas.engine_mut().go_to_page(page_number); + appwindow.handle_widget_flags(widget_flags, &canvas); + } + })); } } diff --git a/crates/rnote-ui/src/settingspanel/mod.rs b/crates/rnote-ui/src/settingspanel/mod.rs index f2a3a0923c..5efeba6204 100644 --- a/crates/rnote-ui/src/settingspanel/mod.rs +++ b/crates/rnote-ui/src/settingspanel/mod.rs @@ -641,6 +641,7 @@ impl RnSettingsPanel { .canvasmenu() .fixedsize_quickactions_box() .set_sensitive(document_layout == Layout::FixedSize); + appwindow.refresh_pages(&canvas); if canvas.engine_ref().document.layout != document_layout { let mut widget_flags = canvas.engine_mut().set_doc_layout(document_layout);