mod control; mod file; mod header; mod tab; mod text; use control::Control; use file::File; use gtk::{glib::Bytes, Notebook}; pub use header::Header; use tab::Tab; use text::Text; pub trait Titan { fn titan(callback: impl Fn(Header, Bytes, Box) + 'static) -> Self; } impl Titan for gtk::Box { fn titan(callback: impl Fn(Header, Bytes, Box) + 'static) -> Self { use gtk::{glib::uuid_string_random, prelude::ButtonExt, Label}; use std::rc::Rc; // Init components let control = Rc::new(Control::build()); let file = Rc::new(File::build(&control)); let text = Rc::new(Text::build(&control)); let notebook = { let notebook = Notebook::builder() .name(format!("s{}", uuid_string_random())) .show_border(false) .build(); notebook.append_page(&text.text_view, Some(&Label::tab("Text"))); notebook.append_page(&file.button, Some(&Label::tab("File"))); notebook.connect_switch_page({ let control = control.clone(); let file = file.clone(); let text = text.clone(); move |_, _, tab_number| { if tab_number == 0 { control.update(Some(text.len()), Some(text.count())) } else { control.update(file.size(), None) } } }); notebook_css_patch(¬ebook); notebook }; // Init main widget let g_box = { use gtk::{prelude::BoxExt, Box, Orientation}; let g_box = { const MARGIN: i32 = 8; Box::builder() .margin_end(MARGIN) .margin_start(MARGIN) .orientation(Orientation::Vertical) .spacing(MARGIN) .build() }; g_box.append(¬ebook); g_box.append(&control.g_box); g_box }; // Init events control.options.connect_clicked({ let text = text.clone(); let file = file.clone(); let notebook = notebook.clone(); move |this| { use gtk::prelude::WidgetExt; this.set_sensitive(false); // lock let tab_number = notebook.current_page().unwrap(); match tab_number { 0 => text.header(), 1 => file.header(), _ => panic!(), } .dialog(Some(this), { let this = this.clone(); let text = text.clone(); let file = file.clone(); move |header| { match tab_number { 0 => text.set_header(header), 1 => file.set_header(header), _ => panic!(), }; this.set_sensitive(true); // unlock } }) } }); control.upload.connect_clicked({ move |this| { use control::Upload; this.set_uploading(); let tab_number = notebook.current_page().unwrap(); callback( match tab_number { 0 => text.header(), 1 => file.header(), _ => panic!(), }, match tab_number { 0 => text.bytes(), 1 => file.bytes().unwrap(), _ => panic!(), }, Box::new({ let this = this.clone(); move || this.set_resend() // re-activate button on failure }), ) } }); g_box } } // Tools fn notebook_css_patch(notebook: &Notebook) { use gtk::prelude::WidgetExt; let name = notebook.widget_name(); let provider = gtk::CssProvider::new(); provider.load_from_string(&format!( " #{name} stack {{ background: transparent; }} #{name} header {{ border-bottom-color: transparent; }} #{name} tab {{ opacity: 0.9; }} " )); gtk::style_context_add_provider_for_display( ¬ebook.display(), &provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, ); } // @TODO replace `Notebook` with `ToggleGroup` in Adw 1.7 / Ubuntu 26.04 // https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ToggleGroup.html