begin titan file uploads implementation

This commit is contained in:
yggverse 2025-02-05 22:25:01 +02:00
parent 00877ee36f
commit f5e850ead3
10 changed files with 184 additions and 98 deletions

View File

@ -4,7 +4,11 @@ mod titan;
use super::ItemAction; use super::ItemAction;
use adw::Clamp; use adw::Clamp;
use gtk::{glib::Uri, prelude::WidgetExt}; use gtk::{
glib::Uri,
prelude::{IsA, WidgetExt},
Widget,
};
use response::Response; use response::Response;
use sensitive::Sensitive; use sensitive::Sensitive;
use std::rc::Rc; use std::rc::Rc;
@ -37,10 +41,10 @@ impl Input {
// Actions // Actions
pub fn unset(&self) { pub fn unset(&self) {
self.update(None); self.update(Widget::NONE);
} }
pub fn update(&self, child: Option<&gtk::Box>) { pub fn update(&self, child: Option<&impl IsA<Widget>>) {
if child.is_some() { if child.is_some() {
self.clamp.set_visible(true); // widget may be hidden, make it visible to child redraw self.clamp.set_visible(true); // widget may be hidden, make it visible to child redraw
self.clamp.set_child(child); self.clamp.set_child(child);
@ -71,6 +75,6 @@ impl Input {
} }
pub fn set_new_titan(&self, on_send: impl Fn(&[u8], Box<dyn Fn()>) + 'static) { pub fn set_new_titan(&self, on_send: impl Fn(&[u8], Box<dyn Fn()>) + 'static) {
self.update(Some(&gtk::Box::titan(on_send))); self.update(Some(&gtk::Notebook::titan(on_send)));
} }
} }

View File

@ -1,67 +1,55 @@
mod control; mod file;
mod form; mod text;
mod title; mod title;
use control::Control; use file::File;
use control::Send; use gtk::{glib::uuid_string_random, prelude::WidgetExt, Label, Notebook};
use form::Form; use text::Text;
use gtk::{
prelude::{BoxExt, ButtonExt, TextBufferExt, TextViewExt},
Label, Orientation, TextView,
};
use std::rc::Rc;
use title::Title; use title::Title;
const MARGIN: i32 = 6;
const SPACING: i32 = 8;
pub trait Titan { pub trait Titan {
fn titan(callback: impl Fn(&[u8], Box<dyn Fn()>) + 'static) -> Self; fn titan(callback: impl Fn(&[u8], Box<dyn Fn()>) + 'static) -> Self;
} }
impl Titan for gtk::Box { impl Titan for Notebook {
fn titan(callback: impl Fn(&[u8], Box<dyn Fn()>) + 'static) -> Self { fn titan(callback: impl Fn(&[u8], Box<dyn Fn()>) + 'static) -> Self {
// Init components let notebook = Notebook::builder()
let control = Rc::new(Control::build()); .name(format!("s{}", uuid_string_random()))
let form = TextView::form(); .show_border(false)
let title = Label::title(None);
// Init widget
let g_box = gtk::Box::builder()
.margin_bottom(MARGIN)
.margin_end(MARGIN)
.margin_start(MARGIN)
.margin_top(MARGIN)
.spacing(SPACING)
.orientation(Orientation::Vertical)
.build(); .build();
g_box.append(&title); notebook.append_page(&gtk::Box::text(callback), Some(&Label::title("Text")));
g_box.append(&form); notebook.append_page(&gtk::Box::file(), Some(&Label::title("File")));
g_box.append(&control.g_box);
// Connect events notebook_css_patch(&notebook);
control.send.connect_clicked({ notebook
let form = form.clone();
move |this| {
this.set_sending();
callback(
form.text().as_bytes(),
Box::new({
let this = this.clone();
move || this.set_resend() // on failure
}),
)
}
});
form.buffer().connect_changed({
let control = control.clone();
let form = form.clone();
move |this| control.update(this.char_count(), form.text().len())
});
// Return activated `Self`
g_box
} }
} }
// Tools
fn notebook_css_patch(notebook: &Notebook) {
let name = notebook.widget_name();
let provider = gtk::CssProvider::new();
provider.load_from_string(&format!(
"
#{name} stack {{
background-color:rgba(0,0,0,0);
}}
#{name} header {{
border-bottom-color:rgba(0,0,0,0);
}}
#{name} tab {{
opacity:0.9;
}}
"
));
gtk::style_context_add_provider_for_display(
&notebook.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

View File

@ -1,28 +0,0 @@
use gtk::{
prelude::{ButtonExt, WidgetExt},
Button,
};
pub trait Send {
fn send() -> Self;
fn set_sending(&self);
fn set_resend(&self);
}
impl Send for Button {
fn send() -> Self {
Button::builder()
.css_classes(["accent"]) // | `suggested-action`
.label("Send")
.sensitive(false)
.build()
}
fn set_sending(&self) {
self.set_sensitive(false);
self.set_label("sending..");
}
fn set_resend(&self) {
self.set_sensitive(true);
self.set_label("Resend");
}
}

View File

@ -0,0 +1,31 @@
use gtk::{prelude::BoxExt, Align, Label, Orientation};
const MARGIN: i32 = 8;
const SPACING: i32 = 8;
pub trait File {
fn file() -> Self;
}
impl File for gtk::Box {
fn file() -> Self {
// Init widget
let g_box = gtk::Box::builder()
.halign(Align::Center)
.margin_bottom(MARGIN)
.margin_end(MARGIN)
.margin_start(MARGIN)
.orientation(Orientation::Vertical)
.spacing(SPACING)
//.margin_top(MARGIN)
.build();
g_box.append(
&Label::builder()
.css_classes(["dim-label"])
.label("Soon..")
.build(),
); // @TODO
g_box
}
}

View File

@ -0,0 +1,62 @@
mod control;
mod form;
use control::Control;
use control::Upload;
use form::Form;
use gtk::{
prelude::{BoxExt, ButtonExt, TextBufferExt, TextViewExt},
Orientation, TextView,
};
use std::rc::Rc;
const MARGIN: i32 = 8;
const SPACING: i32 = 8;
pub trait Text {
fn text(callback: impl Fn(&[u8], Box<dyn Fn()>) + 'static) -> Self;
}
impl Text for gtk::Box {
fn text(callback: impl Fn(&[u8], Box<dyn Fn()>) + 'static) -> Self {
// Init components
let control = Rc::new(Control::build());
let form = TextView::form();
// Init widget
let g_box = gtk::Box::builder()
.margin_bottom(MARGIN)
.margin_end(MARGIN)
.margin_start(MARGIN)
.orientation(Orientation::Vertical)
.spacing(SPACING)
//.margin_top(MARGIN)
.build();
g_box.append(&form);
g_box.append(&control.g_box);
// Connect events
control.upload.connect_clicked({
let form = form.clone();
move |this| {
this.set_uploading();
callback(
form.text().as_bytes(),
Box::new({
let this = this.clone();
move || this.set_resend() // on failure
}),
)
}
});
form.buffer().connect_changed({
let control = control.clone();
let form = form.clone();
move |this| control.update(this.char_count(), form.text().len())
});
g_box
}
}

View File

@ -1,18 +1,18 @@
mod counter; mod counter;
mod send; mod upload;
use counter::Counter; use counter::Counter;
use gtk::{ use gtk::{
prelude::{BoxExt, WidgetExt}, prelude::{BoxExt, WidgetExt},
Align, Box, Button, Label, Orientation, Align, Box, Button, Label, Orientation,
}; };
pub use send::Send; pub use upload::Upload;
const SPACING: i32 = 8; const SPACING: i32 = 8;
pub struct Control { pub struct Control {
pub counter: Label, pub counter: Label,
pub send: Button, pub upload: Button,
pub g_box: Box, pub g_box: Box,
} }
@ -23,7 +23,7 @@ impl Control {
pub fn build() -> Self { pub fn build() -> Self {
// Init components // Init components
let counter = Label::counter(); let counter = Label::counter();
let send = Button::send(); let upload = Button::upload();
// Init main widget // Init main widget
let g_box = Box::builder() let g_box = Box::builder()
@ -33,12 +33,12 @@ impl Control {
.build(); .build();
g_box.append(&counter); g_box.append(&counter);
g_box.append(&send); g_box.append(&upload);
// Return activated struct // Return activated struct
Self { Self {
counter, counter,
send, upload,
g_box, g_box,
} }
} }
@ -47,6 +47,6 @@ impl Control {
pub fn update(&self, chars_count: i32, bytes_total: usize) { pub fn update(&self, chars_count: i32, bytes_total: usize) {
// Update children components // Update children components
self.counter.update(chars_count, bytes_total); self.counter.update(chars_count, bytes_total);
self.send.set_sensitive(bytes_total > 0); self.upload.set_sensitive(bytes_total > 0);
} }
} }

View File

@ -0,0 +1,30 @@
use gtk::{
prelude::{ButtonExt, WidgetExt},
Button,
};
pub trait Upload {
fn upload() -> Self;
fn set_uploading(&self);
fn set_resend(&self);
}
impl Upload for Button {
fn upload() -> Self {
Button::builder()
// @TODO this class not looks well with default GTK Notebook widget
// activate it after upgrade to `ToggleGroup` in Adw v1.7 / Ubuntu 26.04
// .css_classes(["accent"]) // | `suggested-action`
.label("Upload")
.sensitive(false)
.build()
}
fn set_uploading(&self) {
self.set_sensitive(false);
self.set_label("uploading..");
}
fn set_resend(&self) {
self.set_sensitive(true);
self.set_label("Resend");
}
}

View File

@ -33,7 +33,7 @@ impl Form for TextView {
.css_classes(["frame", "view"]) .css_classes(["frame", "view"])
.extra_menu(&adapter.menu_model()) .extra_menu(&adapter.menu_model())
.left_margin(MARGIN) .left_margin(MARGIN)
.margin_bottom(MARGIN / 4) .margin_bottom(MARGIN / 2)
.right_margin(MARGIN) .right_margin(MARGIN)
.top_margin(MARGIN) .top_margin(MARGIN)
.wrap_mode(WrapMode::Word) .wrap_mode(WrapMode::Word)

View File

@ -1,15 +1,14 @@
use gtk::{Align, Label}; use gtk::Label;
pub trait Title { pub trait Title {
fn title(title: Option<&str>) -> Self; fn title(label: &str) -> Self;
} }
impl Title for Label { impl Title for Label {
fn title(title: Option<&str>) -> Self { fn title(label: &str) -> Self {
Label::builder() Label::builder()
.css_classes(["heading"]) .css_classes(["heading"])
.halign(Align::Start) .label(label)
.label(title.unwrap_or("Titan input"))
.build() .build()
} }
} }