Compare commits

...

4 Commits

7 changed files with 135 additions and 23 deletions

41
Cargo.lock generated
View File

@ -140,7 +140,7 @@ version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
dependencies = [ dependencies = [
"heck", "heck 0.4.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.37", "syn 2.0.37",
@ -379,7 +379,7 @@ version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72793962ceece3863c2965d7f10c8786323b17c7adea75a515809fa20ab799a5" checksum = "72793962ceece3863c2965d7f10c8786323b17c7adea75a515809fa20ab799a5"
dependencies = [ dependencies = [
"heck", "heck 0.4.1",
"proc-macro-crate 2.0.0", "proc-macro-crate 2.0.0",
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
@ -528,6 +528,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.3" version = "0.3.3"
@ -608,6 +614,7 @@ dependencies = [
"gtk4", "gtk4",
"log", "log",
"poppler-rs", "poppler-rs",
"strum",
] ]
[[package]] [[package]]
@ -798,6 +805,12 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "rustversion"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.19" version = "1.0.19"
@ -854,6 +867,28 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.37",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -883,7 +918,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3"
dependencies = [ dependencies = [
"cfg-expr", "cfg-expr",
"heck", "heck 0.4.1",
"pkg-config", "pkg-config",
"toml", "toml",
"version-compare", "version-compare",

View File

@ -14,3 +14,4 @@ gtk = { version = "0.7.3", package = "gtk4", features = ["v4_8"] }
anyhow = "1.0.75" anyhow = "1.0.75"
log = "0.4.20" log = "0.4.20"
env_logger = "0.10.1" env_logger = "0.10.1"
strum = { version = "0.26.3", features = ["derive"] }

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- GTK settings schema / configuration. Deploy this e.g. to $HOME/.local/share/glib-2.0/schemas -->
<schemalist>
<schema id="de.frajul.music-reader" path="/de/frajul/music-reader/">
<key name="fullscreen" type="b">
<default>false</default>
<summary>Whether the application is in fullscreen mode</summary>
</key>
<key name="color-mode" type="s">
<default>'Normal'</default>
<summary>The color mode used to render the pdf pages</summary>
</key>
</schema>
</schemalist>

View File

@ -42,6 +42,10 @@
mkdir -p $out/share/icons mkdir -p $out/share/icons
cp ${./music-reader.png} $out/share/icons/music-reader.png cp ${./music-reader.png} $out/share/icons/music-reader.png
mkdir -p $out/share/glib-2.0/schemas
cp ${./de.frajul.music-reader.gschema.xml} $out/share/glib-2.0/schemas/de.frajul.music-reader.gschema.xml
glib-compile-schemas $out/share/glib-2.0/schemas/
''; '';
}; };
devShell = devShell =
@ -53,6 +57,13 @@
rustfmt rustfmt
pre-commit pre-commit
rustPackages.clippy rustPackages.clippy
gtk4
cairo
glib
pkg-config
poppler
wrapGAppsHook
]; ];
# Without inheriting nativeBuildinputs, cargo build will fail but that is good since we want to use only nix build # Without inheriting nativeBuildinputs, cargo build will fail but that is good since we want to use only nix build
# inherit nativeBuildInputs; # inherit nativeBuildInputs;

View File

@ -1,12 +1,27 @@
use cairo::ImageSurface; use core::fmt;
#[derive(Clone)] use cairo::ImageSurface;
use strum::EnumString;
#[derive(Clone, EnumString, Debug)]
pub enum ColorMode { pub enum ColorMode {
Normal, Normal,
Dark, Dark,
Sepia, Sepia,
} }
impl fmt::Display for ColorMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl Default for ColorMode {
fn default() -> Self {
ColorMode::Normal
}
}
impl ColorMode { impl ColorMode {
fn transform_color(&self, r: u8, g: u8, b: u8) -> (u8, u8, u8) { fn transform_color(&self, r: u8, g: u8, b: u8) -> (u8, u8, u8) {
// l of black is 0.13 (approx 0x22) // l of black is 0.13 (approx 0x22)

View File

@ -5,9 +5,9 @@ mod ui;
use clap::Parser; use clap::Parser;
use env_logger::Env; use env_logger::Env;
use gio::Settings;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::Application; use gtk::Application;
use log::debug;
use std::cell::RefCell; use std::cell::RefCell;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
@ -27,7 +27,8 @@ fn main() {
let app = Application::builder().application_id(APP_ID).build(); let app = Application::builder().application_id(APP_ID).build();
app.connect_activate(move |app| { app.connect_activate(move |app| {
let ui = build_ui(app); let settings = Settings::new(APP_ID);
let ui = build_ui(app, settings);
if let Some(file) = cli.file.as_ref() { if let Some(file) = cli.file.as_ref() {
ui::load_document(file, Rc::clone(&ui), 0); ui::load_document(file, Rc::clone(&ui), 0);
} }
@ -36,6 +37,6 @@ fn main() {
app.run_with_args(&[] as &[&str]); app.run_with_args(&[] as &[&str]);
} }
fn build_ui(app: &Application) -> Rc<RefCell<Ui>> { fn build_ui(app: &Application, settings: Settings) -> Rc<RefCell<Ui>> {
Ui::build(app) Ui::build(app, settings)
} }

View File

@ -2,9 +2,11 @@ use std::{
cell::RefCell, cell::RefCell,
path::{Path, PathBuf}, path::{Path, PathBuf},
rc::Rc, rc::Rc,
str::FromStr,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use gio::Settings;
use gtk::{ use gtk::{
glib, Application, ApplicationWindow, Box, Button, FileChooserAction, FileChooserDialog, glib, Application, ApplicationWindow, Box, Button, FileChooserAction, FileChooserDialog,
HeaderBar, Label, Overlay, Picture, ResponseType, ToggleButton, HeaderBar, Label, Overlay, Picture, ResponseType, ToggleButton,
@ -19,6 +21,7 @@ use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
pub struct Ui { pub struct Ui {
settings: Settings,
window: ApplicationWindow, window: ApplicationWindow,
bottom_bar: gtk::Box, bottom_bar: gtk::Box,
header_bar: gtk::HeaderBar, header_bar: gtk::HeaderBar,
@ -114,22 +117,24 @@ impl DocumentCanvas {
} }
} }
pub fn toggle_fullscreen(ui: &Ui) { pub fn set_fullscreen(ui: &Ui, fullscreen: bool) {
match !ui.window.is_fullscreen() { match fullscreen {
true => { true => {
ui.header_bar.hide(); ui.header_bar.hide();
ui.bottom_bar.hide(); ui.bottom_bar.hide();
ui.window.fullscreen(); ui.window.fullscreen();
let new_area_height = ui.image_container.height() + ui.header_bar.height(); let new_area_height = ui.image_container.height() + ui.header_bar.height();
ui.document_canvas if ui.document_canvas.is_some() {
.as_ref() ui.document_canvas
.unwrap() .as_ref()
.priority_cache_current_pages(new_area_height); .unwrap()
ui.document_canvas .priority_cache_current_pages(new_area_height);
.as_ref() ui.document_canvas
.unwrap() .as_ref()
.cache_surrounding_pages(new_area_height); .unwrap()
.cache_surrounding_pages(new_area_height);
}
} }
false => { false => {
ui.header_bar.show(); ui.header_bar.show();
@ -137,6 +142,11 @@ pub fn toggle_fullscreen(ui: &Ui) {
ui.window.unfullscreen(); ui.window.unfullscreen();
} }
} }
ui.settings.set_boolean("fullscreen", fullscreen).unwrap();
}
pub fn toggle_fullscreen(ui: &Ui) {
set_fullscreen(ui, !ui.window.is_fullscreen());
} }
fn update_page_status(ui: &Ui) { fn update_page_status(ui: &Ui) {
@ -213,10 +223,11 @@ fn process_left_click(ui: &mut Ui, x: f64, y: f64) {
} }
impl Ui { impl Ui {
pub fn build(app: &Application) -> Rc<RefCell<Ui>> { pub fn build(app: &Application, settings: Settings) -> Rc<RefCell<Ui>> {
debug!("building ui"); debug!("building ui");
let open_file_button = Button::from_icon_name("document-open"); let open_file_button = Button::from_icon_name("document-open");
let normal_color_mode_button = ToggleButton::builder().label("Std").active(true).build(); let fullscreen_button = Button::from_icon_name("view-fullscreen");
let normal_color_mode_button = ToggleButton::builder().label("Std").build();
let dark_color_mode_button = ToggleButton::builder() let dark_color_mode_button = ToggleButton::builder()
.label("Dark") .label("Dark")
.group(&normal_color_mode_button) .group(&normal_color_mode_button)
@ -227,7 +238,7 @@ impl Ui {
.build(); .build();
let button_container = Box::builder() let button_container = Box::builder()
.spacing(5) .spacing(10)
.hexpand(true) .hexpand(true)
.orientation(gtk::Orientation::Horizontal) .orientation(gtk::Orientation::Horizontal)
.build(); .build();
@ -240,6 +251,7 @@ impl Ui {
color_mode_button_container.append(&dark_color_mode_button); color_mode_button_container.append(&dark_color_mode_button);
color_mode_button_container.append(&sepia_color_mode_button); color_mode_button_container.append(&sepia_color_mode_button);
button_container.append(&open_file_button); button_container.append(&open_file_button);
button_container.append(&fullscreen_button);
button_container.append(&color_mode_button_container); button_container.append(&color_mode_button_container);
let image_container = Box::builder() let image_container = Box::builder()
@ -281,7 +293,15 @@ impl Ui {
.build(); .build();
window.present(); window.present();
let color_mode = ColorMode::from_str(&settings.string("color-mode")).unwrap_or_default();
match color_mode {
ColorMode::Normal => normal_color_mode_button.set_active(true),
ColorMode::Dark => dark_color_mode_button.set_active(true),
ColorMode::Sepia => sepia_color_mode_button.set_active(true),
};
let ui = Ui { let ui = Ui {
settings,
window, window,
app_wrapper, app_wrapper,
bottom_bar: Box::builder() bottom_bar: Box::builder()
@ -295,7 +315,7 @@ impl Ui {
image_right, image_right,
document_canvas: None, document_canvas: None,
last_touch_time: None, last_touch_time: None,
color_mode: ColorMode::Normal, color_mode,
}; };
let ui = Rc::new(RefCell::new(ui)); let ui = Rc::new(RefCell::new(ui));
@ -327,6 +347,11 @@ impl Ui {
choose_file(Rc::clone(&ui), &ui.borrow().window); choose_file(Rc::clone(&ui), &ui.borrow().window);
}), }),
); );
fullscreen_button.connect_clicked(
glib::clone!(@strong ui => @default-panic, move |_button| {
toggle_fullscreen(&ui.borrow());
}),
);
normal_color_mode_button.connect_clicked( normal_color_mode_button.connect_clicked(
glib::clone!(@strong ui => @default-panic, move |_button| { glib::clone!(@strong ui => @default-panic, move |_button| {
switch_color_mode(Rc::clone(&ui), ColorMode::Normal); switch_color_mode(Rc::clone(&ui), ColorMode::Normal);
@ -344,11 +369,21 @@ impl Ui {
); );
ui.borrow().window.present(); ui.borrow().window.present();
if ui.borrow().settings.boolean("fullscreen") {
set_fullscreen(&ui.borrow(), true);
}
ui ui
} }
} }
fn switch_color_mode(ui: Rc<RefCell<Ui>>, color_mode: ColorMode) { fn switch_color_mode(ui: Rc<RefCell<Ui>>, color_mode: ColorMode) {
ui.borrow()
.settings
.set_string("color-mode", &color_mode.to_string())
.unwrap();
ui.borrow_mut().color_mode = color_mode; ui.borrow_mut().color_mode = color_mode;
if ui.borrow().document_canvas.is_none() { if ui.borrow().document_canvas.is_none() {