mirror of
https://github.com/MeexReay/bRAC.git
synced 2025-09-13 23:47:39 +03:00
refactor: replace pictures with libadwaita avatars + add max_avatar_size setting
This commit is contained in:
parent
2ea0414710
commit
544de1df8b
17
Cargo.lock
generated
17
Cargo.lock
generated
@ -246,6 +246,7 @@ version = "0.1.6+2.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
"futures",
|
||||||
"gdk-pixbuf 0.3.0",
|
"gdk-pixbuf 0.3.0",
|
||||||
"homedir",
|
"homedir",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@ -711,6 +712,21 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -786,6 +802,7 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
|
@ -23,6 +23,9 @@ winapi = { version = "0.3.9", optional = true, features = ["wincon", "winuser"]
|
|||||||
tungstenite = "0.27.0"
|
tungstenite = "0.27.0"
|
||||||
reqwest = { version = "0.12.20", features = ["blocking"] }
|
reqwest = { version = "0.12.20", features = ["blocking"] }
|
||||||
libadwaita = { version = "0.8.0", optional = true, features = ["v1_5"] }
|
libadwaita = { version = "0.8.0", optional = true, features = ["v1_5"] }
|
||||||
|
# glycin = { version = "2.1.1", optional = true }
|
||||||
|
futures = "0.3.31"
|
||||||
|
# gio = { version = "0.20.12", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
winresource = { version = "0.1.20", optional = true }
|
winresource = { version = "0.1.20", optional = true }
|
||||||
|
@ -11,6 +11,9 @@ fn default_true() -> bool {
|
|||||||
pub fn default_max_messages() -> usize {
|
pub fn default_max_messages() -> usize {
|
||||||
200
|
200
|
||||||
}
|
}
|
||||||
|
pub fn default_max_avatar_size() -> u64 {
|
||||||
|
5248000 // 5MB
|
||||||
|
}
|
||||||
pub fn default_update_time() -> usize {
|
pub fn default_update_time() -> usize {
|
||||||
100
|
100
|
||||||
}
|
}
|
||||||
@ -41,6 +44,8 @@ pub struct Config {
|
|||||||
pub oof_update_time: usize,
|
pub oof_update_time: usize,
|
||||||
#[serde(default = "default_max_messages")]
|
#[serde(default = "default_max_messages")]
|
||||||
pub max_messages: usize,
|
pub max_messages: usize,
|
||||||
|
#[serde(default = "default_max_avatar_size")]
|
||||||
|
pub max_avatar_size: u64,
|
||||||
#[serde(default = "default_konata_size")]
|
#[serde(default = "default_konata_size")]
|
||||||
pub konata_size: usize,
|
pub konata_size: usize,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -149,6 +154,8 @@ pub struct Args {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub update_time: Option<usize>,
|
pub update_time: Option<usize>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
pub max_avatar_size: Option<u64>,
|
||||||
|
#[arg(long)]
|
||||||
pub oof_update_time: Option<usize>,
|
pub oof_update_time: Option<usize>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub max_messages: Option<usize>,
|
pub max_messages: Option<usize>,
|
||||||
@ -204,6 +211,9 @@ impl Args {
|
|||||||
if let Some(v) = self.konata_size {
|
if let Some(v) = self.konata_size {
|
||||||
config.konata_size = v
|
config.konata_size = v
|
||||||
}
|
}
|
||||||
|
if let Some(v) = self.max_avatar_size {
|
||||||
|
config.max_avatar_size = v
|
||||||
|
}
|
||||||
if let Some(v) = self.hide_my_ip {
|
if let Some(v) = self.hide_my_ip {
|
||||||
config.hide_my_ip = v
|
config.hide_my_ip = v
|
||||||
}
|
}
|
||||||
|
168
src/chat/gui.rs
168
src/chat/gui.rs
@ -1,10 +1,9 @@
|
|||||||
// TODO: REFACTOR THIS SHIT!!!!!!!!!!!!!!!
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::hash::{DefaultHasher, Hasher};
|
use std::hash::{DefaultHasher, Hasher};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU64};
|
use std::io::Read;
|
||||||
|
use std::sync::atomic::AtomicU64;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::sync::{atomic::Ordering, mpsc::channel, Arc, RwLock};
|
use std::sync::{atomic::Ordering, mpsc::channel, Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
@ -13,25 +12,31 @@ use std::time::{Duration, SystemTime};
|
|||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use clap::crate_version;
|
use clap::crate_version;
|
||||||
|
|
||||||
use libadwaita::gtk::Adjustment;
|
use libadwaita::gdk::{MemoryTexture, Texture};
|
||||||
use libadwaita::{self as adw, ActionRow, EntryRow, PreferencesDialog, PreferencesGroup, PreferencesPage, PreferencesRow, SpinRow, SwitchRow};
|
use libadwaita::gtk::gdk_pixbuf::InterpType;
|
||||||
use adw::gdk::{Cursor, Display, Texture};
|
use libadwaita::gtk::{Adjustment, Image};
|
||||||
use adw::gio::{self, ActionEntry, ApplicationFlags, MemoryInputStream, Menu};
|
use libadwaita::{
|
||||||
|
self as adw, Avatar, EntryRow,
|
||||||
|
PreferencesDialog, PreferencesGroup, PreferencesPage,
|
||||||
|
SpinRow, SwitchRow
|
||||||
|
};
|
||||||
|
use adw::gdk::{Cursor, Display};
|
||||||
|
use adw::gio::{ActionEntry, ApplicationFlags, MemoryInputStream, Menu};
|
||||||
use adw::glib::clone;
|
use adw::glib::clone;
|
||||||
use adw::glib::{
|
use adw::glib::{
|
||||||
self, clone::Downgrade, source::timeout_add_local_once, timeout_add_local, timeout_add_once,
|
self, clone::Downgrade, source::timeout_add_local_once,
|
||||||
|
timeout_add_local, timeout_add_once,
|
||||||
ControlFlow,
|
ControlFlow,
|
||||||
};
|
};
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use adw::{Application, ApplicationWindow, Window};
|
use adw::{Application, ApplicationWindow};
|
||||||
|
|
||||||
use adw::gtk;
|
use adw::gtk;
|
||||||
use gtk::gdk_pixbuf::InterpType;
|
|
||||||
use gtk::gdk_pixbuf::{Pixbuf, PixbufAnimation, PixbufLoader};
|
use gtk::gdk_pixbuf::{Pixbuf, PixbufAnimation, PixbufLoader};
|
||||||
use gtk::pango::WrapMode;
|
use gtk::pango::WrapMode;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
AboutDialog, Align, Box as GtkBox, Button, Calendar,
|
Align, Box as GtkBox, Button, Calendar,
|
||||||
CheckButton, CssProvider, Entry, Fixed, GestureClick, Justification, Label, ListBox,
|
CssProvider, Entry, Fixed, GestureClick, Justification, Label, ListBox,
|
||||||
Orientation, Overlay, Picture, ScrolledWindow, Settings,
|
Orientation, Overlay, Picture, ScrolledWindow, Settings,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,7 +44,6 @@ use crate::chat::grab_avatar;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
config::{
|
config::{
|
||||||
default_konata_size, default_max_messages, default_oof_update_time, default_update_time,
|
|
||||||
get_config_path, save_config, Config,
|
get_config_path, save_config, Config,
|
||||||
},
|
},
|
||||||
ctx::Context,
|
ctx::Context,
|
||||||
@ -56,8 +60,7 @@ struct UiModel {
|
|||||||
notifications: Arc<RwLock<Vec<libnotify::Notification>>>,
|
notifications: Arc<RwLock<Vec<libnotify::Notification>>>,
|
||||||
#[cfg(all(not(feature = "libnotify"), not(feature = "notify-rust")))]
|
#[cfg(all(not(feature = "libnotify"), not(feature = "notify-rust")))]
|
||||||
notifications: Arc<RwLock<Vec<String>>>,
|
notifications: Arc<RwLock<Vec<String>>>,
|
||||||
default_avatar: Pixbuf,
|
avatars: Arc<Mutex<HashMap<u64, Vec<Avatar>>>>,
|
||||||
avatars: Arc<Mutex<HashMap<u64, Vec<Picture>>>>,
|
|
||||||
latest_sign: Arc<AtomicU64>
|
latest_sign: Arc<AtomicU64>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,6 +267,23 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
|
|
||||||
group.add(&proxy);
|
group.add(&proxy);
|
||||||
|
|
||||||
|
|
||||||
|
// Max avatar size preference
|
||||||
|
|
||||||
|
let max_avatar_size = SpinRow::builder()
|
||||||
|
.title("Max avatar size")
|
||||||
|
.subtitle("Maximum avatar size in bytes")
|
||||||
|
.adjustment(&Adjustment::builder()
|
||||||
|
.lower(0.0)
|
||||||
|
.upper(1074790400.0)
|
||||||
|
.page_increment(1024.0)
|
||||||
|
.step_increment(1024.0)
|
||||||
|
.value(ctx.config(|o| o.max_avatar_size) as f64)
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
group.add(&max_avatar_size);
|
||||||
|
|
||||||
|
|
||||||
page.add(&group);
|
page.add(&group);
|
||||||
|
|
||||||
@ -451,50 +471,11 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
message_format: message_format.text().to_string(),
|
message_format: message_format.text().to_string(),
|
||||||
update_time: {
|
update_time: update_interval.value() as usize,
|
||||||
let update_time = update_interval.text();
|
oof_update_time: update_interval_oof.value() as usize,
|
||||||
|
konata_size: konata_size.value() as usize,
|
||||||
if let Ok(update_time) = update_time.parse::<usize>() {
|
max_messages: messages_limit.value() as usize,
|
||||||
update_time
|
max_avatar_size: max_avatar_size.value() as u64,
|
||||||
} else {
|
|
||||||
let update_time = default_update_time();
|
|
||||||
update_interval.set_text(&update_time.to_string());
|
|
||||||
update_time
|
|
||||||
}
|
|
||||||
},
|
|
||||||
oof_update_time: {
|
|
||||||
let oof_update_time = update_interval_oof.text();
|
|
||||||
|
|
||||||
if let Ok(oof_update_time) = oof_update_time.parse::<usize>() {
|
|
||||||
oof_update_time
|
|
||||||
} else {
|
|
||||||
let oof_update_time = default_oof_update_time();
|
|
||||||
update_interval_oof.set_text(&oof_update_time.to_string());
|
|
||||||
oof_update_time
|
|
||||||
}
|
|
||||||
},
|
|
||||||
konata_size: {
|
|
||||||
let konata_size_n = konata_size.text();
|
|
||||||
|
|
||||||
if let Ok(konata_size_n) = konata_size_n.parse::<usize>() {
|
|
||||||
konata_size_n.max(0).min(200)
|
|
||||||
} else {
|
|
||||||
let konata_size_n = default_konata_size();
|
|
||||||
konata_size.set_text(&konata_size_n.to_string());
|
|
||||||
konata_size_n
|
|
||||||
}
|
|
||||||
},
|
|
||||||
max_messages: {
|
|
||||||
let max_messages = messages_limit.text();
|
|
||||||
|
|
||||||
if let Ok(max_messages) = max_messages.parse::<usize>() {
|
|
||||||
max_messages
|
|
||||||
} else {
|
|
||||||
let max_messages = default_max_messages();
|
|
||||||
messages_limit.set_text(&max_messages.to_string());
|
|
||||||
max_messages
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hide_my_ip: hide_my_ip.is_active(),
|
hide_my_ip: hide_my_ip.is_active(),
|
||||||
remove_gui_shit: remove_gui_shit.is_active(),
|
remove_gui_shit: remove_gui_shit.is_active(),
|
||||||
show_other_ip: show_ips.is_active(),
|
show_other_ip: show_ips.is_active(),
|
||||||
@ -666,7 +647,7 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
|||||||
|
|
||||||
let logo_anim = PixbufAnimation::from_stream(
|
let logo_anim = PixbufAnimation::from_stream(
|
||||||
&MemoryInputStream::from_bytes(&glib::Bytes::from(logo_gif)),
|
&MemoryInputStream::from_bytes(&glib::Bytes::from(logo_gif)),
|
||||||
None::<&gio::Cancellable>,
|
None::<&adw::gtk::gio::Cancellable>,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter(Some(SystemTime::now()));
|
.iter(Some(SystemTime::now()));
|
||||||
@ -866,7 +847,6 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
|||||||
notifications: Arc::new(RwLock::new(Vec::<libnotify::Notification>::new())),
|
notifications: Arc::new(RwLock::new(Vec::<libnotify::Notification>::new())),
|
||||||
#[cfg(all(not(feature = "libnotify"), not(feature = "notify-rust")))]
|
#[cfg(all(not(feature = "libnotify"), not(feature = "notify-rust")))]
|
||||||
notifications: Arc::new(RwLock::new(Vec::<String>::new())),
|
notifications: Arc::new(RwLock::new(Vec::<String>::new())),
|
||||||
default_avatar: load_pixbuf(include_bytes!("images/avatar.png")).unwrap(),
|
|
||||||
avatars: Arc::new(Mutex::new(HashMap::new())),
|
avatars: Arc::new(Mutex::new(HashMap::new())),
|
||||||
latest_sign: Arc::new(AtomicU64::new(0))
|
latest_sign: Arc::new(AtomicU64::new(0))
|
||||||
}
|
}
|
||||||
@ -951,38 +931,18 @@ fn setup(_: &Application, ctx: Arc<Context>, ui: UiModel) {
|
|||||||
let Some(avatar_url) = grab_avatar(message) else { continue };
|
let Some(avatar_url) = grab_avatar(message) else { continue };
|
||||||
let avatar_id = get_avatar_id(&avatar_url);
|
let avatar_id = get_avatar_id(&avatar_url);
|
||||||
|
|
||||||
let Some(avatar) = load_avatar(&avatar_url)
|
let Some(avatar) = load_avatar(&avatar_url, ctx.config(|o| o.max_avatar_size as usize)) else { println!("cant load avatar: {avatar_url} request error"); continue };
|
||||||
.and_then(|avatar| load_pixbuf(&avatar).ok())
|
let Ok(pixbuf) = load_pixbuf(&avatar) else { println!("cant load avatar: {avatar_url} pixbuf error"); continue; };
|
||||||
.and_then(|pixbuf|
|
let Some(pixbuf) = pixbuf.scale_simple(32, 32, InterpType::Bilinear) else { println!("cant load avatar: {avatar_url} scale image error"); continue };
|
||||||
pixbuf.scale_simple(32, 32, InterpType::Bilinear
|
let texture = Texture::for_pixbuf(&pixbuf);
|
||||||
))
|
|
||||||
.and_then(|pixbuf| Some((
|
|
||||||
pixbuf.pixel_bytes()?,
|
|
||||||
pixbuf.colorspace(),
|
|
||||||
pixbuf.has_alpha(),
|
|
||||||
pixbuf.bits_per_sample(),
|
|
||||||
pixbuf.width(),
|
|
||||||
pixbuf.height(),
|
|
||||||
pixbuf.rowstride()
|
|
||||||
))) else { continue };
|
|
||||||
|
|
||||||
timeout_add_once(Duration::ZERO, {
|
timeout_add_once(Duration::ZERO, {
|
||||||
move || {
|
move || {
|
||||||
GLOBAL.with(|global| {
|
GLOBAL.with(|global| {
|
||||||
if let Some(ui) = &*global.borrow() {
|
if let Some(ui) = &*global.borrow() {
|
||||||
let pixbuf = Pixbuf::from_bytes(
|
|
||||||
&avatar.0,
|
|
||||||
avatar.1,
|
|
||||||
avatar.2,
|
|
||||||
avatar.3,
|
|
||||||
avatar.4,
|
|
||||||
avatar.5,
|
|
||||||
avatar.6
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(pics) = ui.avatars.lock().unwrap().remove(&avatar_id) {
|
if let Some(pics) = ui.avatars.lock().unwrap().remove(&avatar_id) {
|
||||||
for pic in pics {
|
for pic in pics {
|
||||||
pic.set_pixbuf(Some(&pixbuf));
|
pic.set_custom_image(Some(&texture));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1178,10 +1138,28 @@ fn get_avatar_id(url: &str) -> u64 {
|
|||||||
hasher.finish()
|
hasher.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_avatar(url: &str) -> Option<Vec<u8>> {
|
fn load_avatar(url: &str, response_limit: usize) -> Option<Vec<u8>> {
|
||||||
reqwest::blocking::get(url).ok()
|
reqwest::blocking::get(url).ok()
|
||||||
.and_then(|resp| resp.bytes().ok())
|
.and_then(|mut resp| {
|
||||||
.map(|bytes| bytes.to_vec())
|
let mut data = Vec::new();
|
||||||
|
let mut length = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if length >= response_limit {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let mut buf = vec![0; (response_limit - length).min(1024)];
|
||||||
|
let now_len = resp.read(&mut buf).ok()?;
|
||||||
|
if now_len == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf.truncate(now_len);
|
||||||
|
length += now_len;
|
||||||
|
data.append(&mut buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(data)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_new_message_box(
|
fn get_new_message_box(
|
||||||
@ -1256,13 +1234,19 @@ fn get_new_message_box(
|
|||||||
let fixed = Fixed::new();
|
let fixed = Fixed::new();
|
||||||
fixed.set_can_target(false);
|
fixed.set_can_target(false);
|
||||||
|
|
||||||
let avatar_picture = Picture::for_pixbuf(&ui.default_avatar.clone());
|
let avatar_picture = Avatar::builder()
|
||||||
avatar_picture.set_css_classes(&["message-avatar"]);
|
.text(&name)
|
||||||
|
.show_initials(true)
|
||||||
|
// .width_request(64)
|
||||||
|
// .height_request(64)
|
||||||
|
.size(32)
|
||||||
|
.build();
|
||||||
|
// avatar_picture.set_css_classes(&["message-avatar"]);
|
||||||
avatar_picture.set_vexpand(false);
|
avatar_picture.set_vexpand(false);
|
||||||
avatar_picture.set_hexpand(false);
|
avatar_picture.set_hexpand(false);
|
||||||
avatar_picture.set_valign(Align::Start);
|
avatar_picture.set_valign(Align::Start);
|
||||||
avatar_picture.set_halign(Align::Start);
|
avatar_picture.set_halign(Align::Start);
|
||||||
avatar_picture.set_size_request(32, 32);
|
// avatar_picture.set_size_request(64, 64);
|
||||||
|
|
||||||
if avatar != 0 {
|
if avatar != 0 {
|
||||||
let mut lock = ui.avatars.lock().unwrap();
|
let mut lock = ui.avatars.lock().unwrap();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user