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 adw::Clamp;
use gtk::{glib::Uri, prelude::WidgetExt};
use gtk::{
glib::Uri,
prelude::{IsA, WidgetExt},
Widget,
};
use response::Response;
use sensitive::Sensitive;
use std::rc::Rc;
@ -37,10 +41,10 @@ impl Input {
// Actions
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() {
self.clamp.set_visible(true); // widget may be hidden, make it visible to child redraw
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) {
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 form;
mod file;
mod text;
mod title;
use control::Control;
use control::Send;
use form::Form;
use gtk::{
prelude::{BoxExt, ButtonExt, TextBufferExt, TextViewExt},
Label, Orientation, TextView,
};
use std::rc::Rc;
use file::File;
use gtk::{glib::uuid_string_random, prelude::WidgetExt, Label, Notebook};
use text::Text;
use title::Title;
const MARGIN: i32 = 6;
const SPACING: i32 = 8;
pub trait Titan {
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 {
// Init components
let control = Rc::new(Control::build());
let form = TextView::form();
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)
let notebook = Notebook::builder()
.name(format!("s{}", uuid_string_random()))
.show_border(false)
.build();
g_box.append(&title);
g_box.append(&form);
g_box.append(&control.g_box);
notebook.append_page(&gtk::Box::text(callback), Some(&Label::title("Text")));
notebook.append_page(&gtk::Box::file(), Some(&Label::title("File")));
// Connect events
control.send.connect_clicked({
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
notebook_css_patch(&notebook);
notebook
}
}
// 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 send;
mod upload;
use counter::Counter;
use gtk::{
prelude::{BoxExt, WidgetExt},
Align, Box, Button, Label, Orientation,
};
pub use send::Send;
pub use upload::Upload;
const SPACING: i32 = 8;
pub struct Control {
pub counter: Label,
pub send: Button,
pub upload: Button,
pub g_box: Box,
}
@ -23,7 +23,7 @@ impl Control {
pub fn build() -> Self {
// Init components
let counter = Label::counter();
let send = Button::send();
let upload = Button::upload();
// Init main widget
let g_box = Box::builder()
@ -33,12 +33,12 @@ impl Control {
.build();
g_box.append(&counter);
g_box.append(&send);
g_box.append(&upload);
// Return activated struct
Self {
counter,
send,
upload,
g_box,
}
}
@ -47,6 +47,6 @@ impl Control {
pub fn update(&self, chars_count: i32, bytes_total: usize) {
// Update children components
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"])
.extra_menu(&adapter.menu_model())
.left_margin(MARGIN)
.margin_bottom(MARGIN / 4)
.margin_bottom(MARGIN / 2)
.right_margin(MARGIN)
.top_margin(MARGIN)
.wrap_mode(WrapMode::Word)

View File

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