From 2f736e2138f738145898827761be3e3cc7bb9ff4 Mon Sep 17 00:00:00 2001 From: Daniel Langbein Date: Sat, 1 Mar 2025 12:20:05 +0100 Subject: [PATCH] WIP --- Cargo.lock | 16 ++-- Cargo.toml | 2 +- README.md | 4 +- src/clipboard.rs | 172 --------------------------------------- src/clock.rs | 45 ---------- src/counter.rs | 78 ------------------ src/custom_app/imp.rs | 22 +++++ src/custom_app/mod.rs | 44 ++++++++++ src/custom_button/imp.rs | 24 ++++++ src/custom_button/mod.rs | 27 ++++++ src/main.rs | 104 ++--------------------- src/notification.rs | 29 ------- 12 files changed, 134 insertions(+), 433 deletions(-) delete mode 100644 src/clipboard.rs delete mode 100644 src/clock.rs delete mode 100644 src/counter.rs create mode 100644 src/custom_app/imp.rs create mode 100644 src/custom_app/mod.rs create mode 100644 src/custom_button/imp.rs create mode 100644 src/custom_button/mod.rs delete mode 100644 src/notification.rs diff --git a/Cargo.lock b/Cargo.lock index eafb420..c0a0905 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,6 +103,14 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "dndbuster" +version = "0.1.0" +dependencies = [ + "chrono", + "gtk4", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -551,14 +559,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "pdt" -version = "0.1.0" -dependencies = [ - "chrono", - "gtk4", -] - [[package]] name = "pin-project-lite" version = "0.2.16" diff --git a/Cargo.toml b/Cargo.toml index b0a244c..ce1eb8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pdt" +name = "dndbuster" version = "0.1.0" edition = "2021" diff --git a/README.md b/README.md index 2955e91..bdf53cd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ -# pdt - PleaseDisturbTimer +# DnDBuster - Do not Disturb Buster A timer that notifies you - even if "do not disturb" is enabled. +Note: notification doesn't work as .desktop entry is missing. See dndbuster-python. + ## Alternatives https://man.archlinux.org/man/notify-send.1.en diff --git a/src/clipboard.rs b/src/clipboard.rs deleted file mode 100644 index f11213f..0000000 --- a/src/clipboard.rs +++ /dev/null @@ -1,172 +0,0 @@ -// Copied from https://github.com/gtk-rs/gtk4-rs/blob/main/examples/clipboard/main.rs - -use gtk4 as gtk; -use gtk::{ - gdk, gio, - glib::{self, clone}, - prelude::*, -}; - -fn main() -> glib::ExitCode { - let application = gtk::Application::builder() - .application_id("com.github.gtk-rs.examples.clipboard") - .build(); - application.connect_activate(build_ui); - application.run() -} - -fn build_ui(application: >k::Application) { - let window = gtk::ApplicationWindow::builder() - .application(application) - .title("Clipboard") - .default_width(660) - .default_height(420) - .build(); - - let display = gdk::Display::default().unwrap(); - let clipboard = display.clipboard(); - - let container = gtk::Box::builder() - .orientation(gtk::Orientation::Vertical) - .margin_top(24) - .margin_bottom(24) - .margin_start(24) - .margin_end(24) - .halign(gtk::Align::Center) - .valign(gtk::Align::Center) - .spacing(24) - .build(); - - // The text copy/paste part - let title = gtk::Label::builder() - .label("Text") - .halign(gtk::Align::Start) - .build(); - title.add_css_class("title-2"); - container.append(&title); - - let text_container = gtk::Box::builder() - .halign(gtk::Align::Center) - .orientation(gtk::Orientation::Horizontal) - .spacing(24) - .build(); - - let from_entry = gtk::Entry::builder() - .placeholder_text("Type text to copy") - .build(); - text_container.append(&from_entry); - - let copy_btn = gtk::Button::with_label("Copy"); - copy_btn.connect_clicked(clone!( - #[weak] - clipboard, - #[weak] - from_entry, - move |_btn| { - let text = from_entry.text(); - clipboard.set_text(&text); - } - )); - text_container.append(©_btn); - - let into_entry = gtk::Entry::new(); - text_container.append(&into_entry); - - let paste_btn = gtk::Button::with_label("Paste"); - paste_btn.connect_clicked(clone!( - #[weak] - clipboard, - #[weak] - into_entry, - move |_btn| { - clipboard.read_text_async( - gio::Cancellable::NONE, - clone!( - #[weak] - into_entry, - move |res| { - if let Ok(Some(text)) = res { - into_entry.set_text(&text); - } - } - ), - ); - } - )); - text_container.append(&paste_btn); - container.append(&text_container); - - // The texture copy/paste part - let title = gtk::Label::builder() - .label("Texture") - .halign(gtk::Align::Start) - .build(); - title.add_css_class("title-2"); - container.append(&title); - - let texture_container = gtk::Box::builder() - .orientation(gtk::Orientation::Horizontal) - .halign(gtk::Align::Center) - .spacing(24) - .build(); - - let file = gio::File::for_path("./examples/clipboard/asset.png"); - let asset_paintable = gdk::Texture::from_file(&file).unwrap(); - - let image_from = gtk::Image::builder() - .pixel_size(96) - .paintable(&asset_paintable) - .build(); - texture_container.append(&image_from); - let copy_texture_btn = gtk::Button::builder() - .label("Copy") - .valign(gtk::Align::Center) - .build(); - copy_texture_btn.connect_clicked(clone!( - #[weak] - clipboard, - #[weak] - image_from, - move |_btn| { - let texture = image_from - .paintable() - .and_downcast::() - .unwrap(); - clipboard.set_texture(&texture); - } - )); - texture_container.append(©_texture_btn); - - let image_into = gtk::Image::builder() - .pixel_size(96) - .icon_name("image-missing") - .build(); - texture_container.append(&image_into); - let paste_texture_btn = gtk::Button::builder() - .label("Paste") - .valign(gtk::Align::Center) - .build(); - paste_texture_btn.connect_clicked(clone!( - #[weak] - clipboard, - move |_btn| { - clipboard.read_texture_async( - gio::Cancellable::NONE, - clone!( - #[weak] - image_into, - move |res| { - if let Ok(Some(texture)) = res { - image_into.set_paintable(Some(&texture)); - } - } - ), - ); - } - )); - texture_container.append(&paste_texture_btn); - container.append(&texture_container); - - window.set_child(Some(&container)); - window.present(); -} \ No newline at end of file diff --git a/src/clock.rs b/src/clock.rs deleted file mode 100644 index ef0aaea..0000000 --- a/src/clock.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Example copied from https://github.com/gtk-rs/gtk4-rs/blob/main/examples/clock/main.rs - -use chrono::Local; - -use gtk4 as gtk; -use gtk::{glib, prelude::*}; - -fn main() -> glib::ExitCode { - let application = gtk::Application::builder() - .application_id("com.github.gtk-rs.examples.clock") - .build(); - application.connect_activate(build_ui); - application.run() -} - -fn build_ui(application: >k::Application) { - let window = gtk::ApplicationWindow::new(application); - - window.set_title(Some("Clock Example")); - window.set_default_size(260, 40); - - let time = current_time(); - let label = gtk::Label::default(); - label.set_text(&time); - - window.set_child(Some(&label)); - - window.present(); - - // we are using a closure to capture the label (else we could also use a normal - // function) - let tick = move || { - let time = current_time(); - label.set_text(&time); - // we could return glib::ControlFlow::Break to stop our clock after this tick - glib::ControlFlow::Continue - }; - - // executes the closure once every second - glib::timeout_add_seconds_local(1, tick); -} - -fn current_time() -> String { - format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")) -} diff --git a/src/counter.rs b/src/counter.rs deleted file mode 100644 index b361923..0000000 --- a/src/counter.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Example copied from https://github.com/kashifsoofi/blog-code-samples/blob/gtk4-c-counter-app/gtk4-rust-counter-app/src/main.rs - -use std::cell::Cell; -use std::rc::Rc; - -use gtk4 as gtk; -use gtk::glib::clone; -use gtk::prelude::*; -use gtk::{glib, Application, ApplicationWindow, Label, Box, Button, Orientation}; - -const APP_ID: &str = "org.gtk_rs.GTK4Counter"; - -fn main() -> glib::ExitCode { - // 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() -} - -fn build_ui(app: &Application) { - let counter = Rc::new(Cell::new(0)); - let label_counter = Label::builder() - .label(&counter.get().to_string()) - .margin_top(12) - .margin_bottom(12) - .margin_start(12) - .margin_end(12) - .build(); - - let button_increase = Button::builder() - .label("Increase") - .margin_top(12) - .margin_bottom(12) - .margin_start(12) - .margin_end(12) - .build(); - - let button_decrease = Button::builder() - .label("Decrease") - .margin_top(12) - .margin_bottom(12) - .margin_start(12) - .margin_end(12) - .build(); - - button_increase.connect_clicked(clone!(@weak counter, @weak label_counter => - move |_| { - counter.set(counter.get() + 1); - label_counter.set_label(&counter.get().to_string()); - })); - - button_decrease.connect_clicked(clone!(@weak label_counter => - move |_| { - counter.set(counter.get() - 1); - label_counter.set_label(&counter.get().to_string()); - })); - - let gtk_box = Box::builder() - .orientation(Orientation::Vertical) - .build(); - gtk_box.append(&label_counter); - gtk_box.append(&button_increase); - gtk_box.append(&button_decrease); - - // Create a window and set the title - let window = ApplicationWindow::builder() - .application(app) - .title("GTK Counter App") - .child(>k_box) - .build(); - - // Present window - window.present(); -} \ No newline at end of file diff --git a/src/custom_app/imp.rs b/src/custom_app/imp.rs new file mode 100644 index 0000000..07a05a3 --- /dev/null +++ b/src/custom_app/imp.rs @@ -0,0 +1,22 @@ +use gtk::glib; +use gtk::subclass::prelude::*; +use gtk4 as gtk; + +// Object holding the state +#[derive(Default)] +pub struct CustomApp; + +// The central trait for subclassing a GObject +#[glib::object_subclass] +impl ObjectSubclass for CustomApp { + const NAME: &'static str = "MyGtkAppCustomApp"; + type Type = super::CustomApp; + type ParentType = gtk::Application; +} + +impl ObjectImpl for CustomApp {} + +impl ApplicationImpl for CustomApp {} + +impl GtkApplicationImpl for CustomApp {} + diff --git a/src/custom_app/mod.rs b/src/custom_app/mod.rs new file mode 100644 index 0000000..a62533e --- /dev/null +++ b/src/custom_app/mod.rs @@ -0,0 +1,44 @@ +mod imp; + +use gtk::gio; +use gtk::glib; +use gtk::prelude::ApplicationExtManual; +use gtk4 as gtk; +use gtk4::prelude::ApplicationExt; + +const APP_ID: &str = "de.privacy1st.dndbuster"; + +glib::wrapper! { + pub struct CustomApp(ObjectSubclass) + @extends gio::Application, gtk::Application, + @implements gio::ActionMap, gio::ActionGroup; +} + +impl CustomApp { + pub fn new() -> Self { + glib::Object::builder() + .property("application-id", APP_ID) + .property("resource-base-path", "/io/github/seadve/Kooha/") + .build() + } + + pub fn run(&self) -> glib::ExitCode { + eprintln!("Kooha ({})", APP_ID); + eprintln!("Version: {} ({})", 0.1, "dev"); + eprintln!("Datadir: {}", "."); + + ApplicationExtManual::run(self) + } + + pub async fn send_record_success_notification(&self) { + let notification = gio::Notification::new("Screencast recorded"); + notification.set_body(Some("Yeeeeha!")); + self.send_notification(Some("record-success"), ¬ification); + } +} + +impl Default for CustomApp { + fn default() -> Self { + Self::new() + } +} diff --git a/src/custom_button/imp.rs b/src/custom_button/imp.rs new file mode 100644 index 0000000..483b0d4 --- /dev/null +++ b/src/custom_button/imp.rs @@ -0,0 +1,24 @@ +use gtk::glib; +use gtk::subclass::prelude::*; +use gtk4 as gtk; + +// Object holding the state +#[derive(Default)] +pub struct CustomButton; + +// The central trait for subclassing a GObject +#[glib::object_subclass] +impl ObjectSubclass for CustomButton { + const NAME: &'static str = "MyGtkAppCustomButton"; + type Type = super::CustomButton; + type ParentType = gtk::Button; +} + +// Trait shared by all GObjects +impl ObjectImpl for CustomButton {} + +// Trait shared by all widgets +impl WidgetImpl for CustomButton {} + +// Trait shared by all buttons +impl ButtonImpl for CustomButton {} \ No newline at end of file diff --git a/src/custom_button/mod.rs b/src/custom_button/mod.rs new file mode 100644 index 0000000..59603a8 --- /dev/null +++ b/src/custom_button/mod.rs @@ -0,0 +1,27 @@ +mod imp; + +use glib::Object; +use gtk::glib; +use gtk4 as gtk; + +glib::wrapper! { + pub struct CustomButton(ObjectSubclass) + @extends gtk::Button, gtk::Widget, + @implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget; +} + +impl CustomButton { + pub fn new() -> Self { + Object::builder().build() + } + + pub fn with_label(label: &str) -> Self { + Object::builder().property("label", label).build() + } +} + +impl Default for CustomButton { + fn default() -> Self { + Self::new() + } +} diff --git a/src/main.rs b/src/main.rs index 88c7ed8..34c99cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,106 +1,12 @@ -// Based on examples -// - https://crates.io/crates/gtk4 -// - https://github.com/gtk-rs/gtk4-rs/blob/main/examples/clock/main.rs -// - https://github.com/gtk-rs/gtk4-rs/blob/main/examples/clipboard/main.rs -// - https://github.com/kashifsoofi/blog-code-samples/blob/gtk4-c-counter-app/gtk4-rust-counter-app/src/main.rs +mod custom_button; +mod custom_app; -use chrono::Local; -use std::cell::Cell; -use std::rc::Rc; - -use gtk::gio::{Notification, NotificationPriority}; use gtk::glib; -use gtk::glib::clone; -use gtk::prelude::*; use gtk4 as gtk; - -const APP_ID: &str = "de.privacy1st.pdt"; - -const DUR_MIN: f64 = 1.0; -const INTERVAL_SEC: u32 = 1; +use crate::custom_app::CustomApp; fn main() -> glib::ExitCode { - let app = gtk::Application::builder().application_id(APP_ID).build(); - app.connect_activate(move |app| build_ui(app)); + let app = CustomApp::new(); + app.send_record_success_notification().await; app.run() } - -fn build_ui(app: >k::Application) { - let window = gtk::ApplicationWindow::builder() - .application(app) - .title("PleaseDisturbTimer") - .default_width(350) - .default_height(70) - .build(); - - let container = gtk::Box::builder() - .orientation(gtk::Orientation::Vertical) - .margin_top(24) - .margin_bottom(24) - .margin_start(24) - .margin_end(24) - .halign(gtk::Align::Center) - .valign(gtk::Align::Center) - .spacing(24) - .build(); - - // How to change variable using GTK4 Button? https://stackoverflow.com/a/76657798/6334421 - let running = Rc::new(Cell::new(false)); - - let button_start = gtk::Button::with_label("Start"); - button_start.connect_clicked(clone!(@strong running => move |_| { - running.set(true); - eprintln!("Timer started."); - })); - container.append(&button_start); - - let button_stop = gtk::Button::with_label("Stop"); - button_stop.connect_clicked(clone!(@strong running => move |_| { - running.set(false); - eprintln!("Timer stopped."); - })); - container.append(&button_stop); - - let mut progress: f64 = 0.0; - let progress_bar = gtk::ProgressBar::new(); - progress_bar.set_fraction(progress); - container.append(&progress_bar); - - let time = current_time(); - let label = gtk::Label::default(); - label.set_text(&time); - // label.set_halign(gtk::Align::Center); - container.append(&label); - - window.set_child(Some(&container)); - window.present(); - - let tick = clone!(@strong app => move || { - if running.get() { - progress += ((INTERVAL_SEC as f64) / 60.0) / DUR_MIN; - progress_bar.set_fraction(progress); - } - - if progress > 1.0 { - eprintln!("Time over."); - - // TODO: Notification does not show up - let notification = Notification::new("Alert"); - notification.set_body(Some("Time is over")); - notification.set_priority(NotificationPriority::Urgent); - app.send_notification(Some(APP_ID), ¬ification); - } - - let time = current_time(); - label.set_text(&time); - // we could return glib::ControlFlow::Break to stop our clock after this tick - glib::ControlFlow::Continue - }); - - // executes the closure once every `INTERVAL_SEC` seconds - glib::timeout_add_seconds_local(INTERVAL_SEC, tick); -} - -fn current_time() -> String { - format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")) -} diff --git a/src/notification.rs b/src/notification.rs deleted file mode 100644 index 52509d0..0000000 --- a/src/notification.rs +++ /dev/null @@ -1,29 +0,0 @@ -use gtk4 as gtk; -use gtk::glib; -use gtk::prelude::*; -use gtk::gio::{Notification, NotificationPriority}; - -const APP_ID: &str = "de.privacy1st.pdt"; - -fn main() -> glib::ExitCode { - let app = gtk::Application::builder().application_id(APP_ID).build(); - app.connect_activate(move |app| build_ui(app)); - app.run() -} - -fn build_ui(app: >k::Application) { - let window = gtk::ApplicationWindow::builder() - .application(app) - .title("System Notification") - .default_width(350) - .default_height(70) - .build(); - window.present(); - - // TODO: Notification does not show up - let notification = Notification::new("Alert"); - notification.set_body(Some("Time is over")); - notification.set_priority(NotificationPriority::Urgent); - let notification_id = "pdt.timeout"; - app.send_notification(Some(notification_id), ¬ification); -}