From 005b7574ca0eb9c9e020ceb700c72c0e2c0e5780 Mon Sep 17 00:00:00 2001 From: yggverse Date: Fri, 7 Feb 2025 23:16:44 +0200 Subject: [PATCH] use separated headers for tabs, use `plain/text` by default for text --- .../window/tab/item/page/input/titan.rs | 76 ++++++++++++----- .../tab/item/page/input/titan/control.rs | 8 +- .../item/page/input/titan/control/options.rs | 32 ++------ .../window/tab/item/page/input/titan/file.rs | 38 +++++++-- .../window/tab/item/page/input/titan/text.rs | 81 ++++++++++++------- 5 files changed, 147 insertions(+), 88 deletions(-) diff --git a/src/app/browser/window/tab/item/page/input/titan.rs b/src/app/browser/window/tab/item/page/input/titan.rs index adeaa4c0..fe410af8 100644 --- a/src/app/browser/window/tab/item/page/input/titan.rs +++ b/src/app/browser/window/tab/item/page/input/titan.rs @@ -17,17 +17,13 @@ pub trait Titan { impl Titan for gtk::Box { fn titan(callback: impl Fn(Header, Bytes, Box) + 'static) -> Self { - use gtk::{glib::uuid_string_random, prelude::ButtonExt, Label, TextView}; - use std::{cell::RefCell, rc::Rc}; + use gtk::{glib::uuid_string_random, prelude::ButtonExt, Label}; + use std::rc::Rc; // Init components - let header = Rc::new(RefCell::new(Header { - mime: None, - token: None, - })); - let control = Rc::new(Control::build(&header)); + let control = Rc::new(Control::build()); let file = Rc::new(File::build(&control)); - let text = TextView::text(&control); + let text = Rc::new(Text::build(&control)); let notebook = { let notebook = Notebook::builder() @@ -35,7 +31,7 @@ impl Titan for gtk::Box { .show_border(false) .build(); - notebook.append_page(&text, Some(&Label::tab("Text"))); + notebook.append_page(&text.text_view, Some(&Label::tab("Text"))); notebook.append_page(&file.button, Some(&Label::tab("File"))); notebook.connect_switch_page({ @@ -75,21 +71,57 @@ impl Titan for gtk::Box { }; // Init events - control.upload.connect_clicked(move |this| { - use control::Upload; - this.set_uploading(); - callback( - header.borrow().clone(), // keep original header to have re-send ability - match notebook.current_page().unwrap() { - 0 => text.to_bytes(), - 1 => file.to_bytes().unwrap(), + 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 page = notebook.current_page().unwrap(); + match page { + 0 => text.header(), + 1 => file.header(), _ => panic!(), - }, - Box::new({ + } + .dialog(Some(this), { let this = this.clone(); - move || this.set_resend() // re-activate button on failure - }), - ) + let text = text.clone(); + let file = file.clone(); + move |header| { + match page { + 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 page = notebook.current_page().unwrap(); + callback( + match page { + 0 => text.header(), + 1 => file.header(), + _ => panic!(), + }, + match page { + 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 diff --git a/src/app/browser/window/tab/item/page/input/titan/control.rs b/src/app/browser/window/tab/item/page/input/titan/control.rs index 4929a14b..00cee1b7 100644 --- a/src/app/browser/window/tab/item/page/input/titan/control.rs +++ b/src/app/browser/window/tab/item/page/input/titan/control.rs @@ -2,18 +2,17 @@ mod counter; mod options; mod upload; -use super::Header; use counter::Counter; use gtk::{ prelude::{BoxExt, WidgetExt}, Align, Box, Button, Label, Orientation, }; use options::Options; -use std::{cell::RefCell, rc::Rc}; pub use upload::Upload; pub struct Control { pub counter: Label, + pub options: Button, pub upload: Button, pub g_box: Box, } @@ -22,10 +21,10 @@ impl Control { // Constructors /// Build new `Self` - pub fn build(header: &Rc>) -> Self { + pub fn build() -> Self { // Init components let counter = Label::counter(); - let options = Button::options(header); + let options = Button::options(); let upload = Button::upload(); // Init main widget @@ -46,6 +45,7 @@ impl Control { // Return activated struct Self { counter, + options, upload, g_box, } diff --git a/src/app/browser/window/tab/item/page/input/titan/control/options.rs b/src/app/browser/window/tab/item/page/input/titan/control/options.rs index d59e304f..e91a343a 100644 --- a/src/app/browser/window/tab/item/page/input/titan/control/options.rs +++ b/src/app/browser/window/tab/item/page/input/titan/control/options.rs @@ -1,36 +1,14 @@ -use super::Header; -use gtk::{ - prelude::{ButtonExt, WidgetExt}, - Button, -}; -use std::{cell::RefCell, rc::Rc}; +use gtk::Button; pub trait Options { - fn options(header: &Rc>) -> Self; + fn options() -> Self; } impl Options for Button { - fn options(header: &Rc>) -> Self { - let button = Button::builder() + fn options() -> Self { + Button::builder() .icon_name("emblem-system-symbolic") .tooltip_text("Options") - .build(); - - button.connect_clicked({ - let header = header.clone(); - move |this| { - this.set_sensitive(false); // lock - header.take().dialog(Some(this), { - let this = this.clone(); - let header = header.clone(); - move |options| { - header.replace(options); - this.set_sensitive(true); // unlock - } - }) - } - }); - - button + .build() } } diff --git a/src/app/browser/window/tab/item/page/input/titan/file.rs b/src/app/browser/window/tab/item/page/input/titan/file.rs index fdc3bc8c..fc91c720 100644 --- a/src/app/browser/window/tab/item/page/input/titan/file.rs +++ b/src/app/browser/window/tab/item/page/input/titan/file.rs @@ -1,8 +1,9 @@ -use super::Control; +use super::{Control, Header}; use gtk::{glib::Bytes, Button}; use std::{cell::RefCell, rc::Rc}; pub struct File { + header: Rc>, buffer: Rc>>, pub button: Button, } @@ -16,6 +17,11 @@ impl File { }; // Init components + let header = Rc::new(RefCell::new(Header { + mime: None, + token: None, + })); + let buffer = Rc::new(RefCell::new(None)); let button = Button::builder() @@ -77,21 +83,37 @@ impl File { } }); - Self { buffer, button } + Self { + header, + buffer, + button, + } } - /* this method is less-expensive but not useful as user - will not able re-upload existing form on failure @TODO + // Getters - pub fn take_bytes(&self) -> Option { - self.buffer.borrow_mut().take() - } */ + /// Get `Header` copy + /// * borrow, do not take to have form re-send ability + pub fn header(&self) -> Header { + self.header.borrow().clone() + } - pub fn to_bytes(&self) -> Option { + /// Get cloned [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) + // * borrow, do not take to have form re-send ability + pub fn bytes(&self) -> Option { self.buffer.borrow().as_ref().map(|bytes| bytes.clone()) } + /// Get size pub fn size(&self) -> Option { self.buffer.borrow().as_ref().map(|bytes| bytes.len()) } + + // Setters + + /// Replace current `Header` + /// * return previous object + pub fn set_header(&self, header: Header) -> Header { + self.header.replace(header) + } } diff --git a/src/app/browser/window/tab/item/page/input/titan/text.rs b/src/app/browser/window/tab/item/page/input/titan/text.rs index 2f72591f..b660a671 100644 --- a/src/app/browser/window/tab/item/page/input/titan/text.rs +++ b/src/app/browser/window/tab/item/page/input/titan/text.rs @@ -1,55 +1,82 @@ mod form; -use super::Control; +use super::{Control, Header}; use gtk::{ glib::{Bytes, GString}, prelude::{TextBufferExt, TextViewExt}, - TextView, + TextBuffer, TextView, }; -use std::rc::Rc; +use std::{cell::RefCell, rc::Rc}; -pub trait Text { - fn text(control: &Rc) -> Self; - fn to_bytes(&self) -> Bytes; - fn to_gstring(&self) -> GString; - fn len(&self) -> usize; - fn count(&self) -> i32; +pub struct Text { + header: Rc>, + pub text_view: TextView, } -impl Text for TextView { - fn text(control: &Rc) -> Self { +impl Text { + // Constructors + + /// Build new `Self` + pub fn build(control: &Rc) -> Self { use form::Form; + // Init components + let header = Rc::new(RefCell::new(Header { + mime: Some("text/plain".into()), // some servers may reject request without MIME @TODO optional defaults + token: None, + })); + + // Init main widget let text_view = TextView::form(); text_view.buffer().connect_changed({ let control = control.clone(); - let text_view = text_view.clone(); - move |text_buffer| control.update(Some(text_view.len()), Some(text_buffer.char_count())) + move |text_buffer| { + control.update( + Some(gstring(text_buffer).len()), + Some(text_buffer.char_count()), + ) + } }); - text_view + Self { header, text_view } } - fn to_bytes(&self) -> Bytes { - Bytes::from(self.to_gstring().as_bytes()) + // Getters + + /// Get `Header` copy + /// * borrow, do not take to have form re-send ability + pub fn header(&self) -> Header { + self.header.borrow().clone() } - fn to_gstring(&self) -> GString { - let buffer = self.buffer(); - self.buffer() - .text(&buffer.start_iter(), &buffer.end_iter(), true) + pub fn bytes(&self) -> Bytes { + Bytes::from(self.gstring().as_bytes()) } - fn count(&self) -> i32 { - self.buffer().char_count() + pub fn gstring(&self) -> GString { + gstring(&self.text_view.buffer()) } - fn len(&self) -> usize { - let buffer = self.buffer(); + pub fn count(&self) -> i32 { + self.text_view.buffer().char_count() + } - buffer - .text(&buffer.start_iter(), &buffer.end_iter(), true) - .len() + pub fn len(&self) -> usize { + self.gstring().len() + } + + // Setters + + /// Replace current `Header` + /// * return previous object + pub fn set_header(&self, header: Header) -> Header { + self.header.replace(header) } } + +// Tools + +fn gstring(text_buffer: &TextBuffer) -> GString { + text_buffer.text(&text_buffer.start_iter(), &text_buffer.end_iter(), true) +}