mirror of
https://github.com/MeexReay/bRAC.git
synced 2025-07-02 06:23:00 +03:00
feat: new ui prototype
This commit is contained in:
parent
56f66232eb
commit
47342294e8
878
Cargo.lock
generated
878
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@ notify-rust = { version = "4.11.7", optional = true }
|
|||||||
gdk-pixbuf = { version = "0.3.0", optional = true } # DO NOT UPDATE
|
gdk-pixbuf = { version = "0.3.0", optional = true } # DO NOT UPDATE
|
||||||
winapi = { version = "0.3.9", optional = true, features = ["wincon", "winuser"] }
|
winapi = { version = "0.3.9", optional = true, features = ["wincon", "winuser"] }
|
||||||
tungstenite = "0.27.0"
|
tungstenite = "0.27.0"
|
||||||
|
reqwest = "0.12.20"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
winresource = { version = "0.1.20", optional = true }
|
winresource = { version = "0.1.20", optional = true }
|
||||||
|
@ -59,6 +59,8 @@ pub struct Config {
|
|||||||
pub proxy: Option<String>,
|
pub proxy: Option<String>,
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub notifications_enabled: bool,
|
pub notifications_enabled: bool,
|
||||||
|
#[serde(default = "default_true")]
|
||||||
|
pub new_ui_enabled: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub debug_logs: bool,
|
pub debug_logs: bool,
|
||||||
}
|
}
|
||||||
@ -148,6 +150,8 @@ pub struct Args {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub notifications_enabled: Option<bool>,
|
pub notifications_enabled: Option<bool>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
pub new_ui_enabled: Option<bool>,
|
||||||
|
#[arg(long)]
|
||||||
pub proxy: Option<String>,
|
pub proxy: Option<String>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub debug_logs: bool,
|
pub debug_logs: bool,
|
||||||
@ -200,6 +204,9 @@ impl Args {
|
|||||||
if let Some(v) = self.notifications_enabled {
|
if let Some(v) = self.notifications_enabled {
|
||||||
config.notifications_enabled = v
|
config.notifications_enabled = v
|
||||||
}
|
}
|
||||||
|
if let Some(v) = self.new_ui_enabled {
|
||||||
|
config.new_ui_enabled = v
|
||||||
|
}
|
||||||
if self.debug_logs {
|
if self.debug_logs {
|
||||||
config.debug_logs = true
|
config.debug_logs = true
|
||||||
}
|
}
|
||||||
|
169
src/chat/gui.rs
169
src/chat/gui.rs
@ -1,4 +1,5 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::{atomic::Ordering, mpsc::channel, Arc, RwLock};
|
use std::sync::{atomic::Ordering, mpsc::channel, Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
@ -6,7 +7,8 @@ use std::time::{Duration, SystemTime};
|
|||||||
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
|
||||||
use gtk4::{self as gtk};
|
use gtk4::ffi::GtkGrid;
|
||||||
|
use gtk4 as gtk;
|
||||||
|
|
||||||
use gtk::gdk::{Cursor, Display, Texture};
|
use gtk::gdk::{Cursor, Display, Texture};
|
||||||
use gtk::gdk_pixbuf::{Pixbuf, PixbufAnimation, PixbufLoader};
|
use gtk::gdk_pixbuf::{Pixbuf, PixbufAnimation, PixbufLoader};
|
||||||
@ -43,6 +45,8 @@ 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<RwLock<HashMap<u64, Pixbuf>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local!(
|
thread_local!(
|
||||||
@ -203,6 +207,7 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
gui_usize_entry_setting!("Konata Size", konata_size, ctx, settings_vbox);
|
gui_usize_entry_setting!("Konata Size", konata_size, ctx, settings_vbox);
|
||||||
let remove_gui_shit_entry =
|
let remove_gui_shit_entry =
|
||||||
gui_checkbox_setting!("Remove Gui Shit", remove_gui_shit, ctx, settings_vbox);
|
gui_checkbox_setting!("Remove Gui Shit", remove_gui_shit, ctx, settings_vbox);
|
||||||
|
let new_ui_enabled_entry = gui_checkbox_setting!("New UI", new_ui_enabled, ctx, settings_vbox);
|
||||||
|
|
||||||
let scrollable = ScrolledWindow::builder()
|
let scrollable = ScrolledWindow::builder()
|
||||||
.child(&settings_vbox)
|
.child(&settings_vbox)
|
||||||
@ -251,6 +256,8 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
konata_size_entry,
|
konata_size_entry,
|
||||||
#[weak]
|
#[weak]
|
||||||
remove_gui_shit_entry,
|
remove_gui_shit_entry,
|
||||||
|
#[weak]
|
||||||
|
new_ui_enabled_entry,
|
||||||
move |_| {
|
move |_| {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
host: host_entry.text().to_string(),
|
host: host_entry.text().to_string(),
|
||||||
@ -315,6 +322,7 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
formatting_enabled: formatting_enabled_entry.is_active(),
|
formatting_enabled: formatting_enabled_entry.is_active(),
|
||||||
commands_enabled: commands_enabled_entry.is_active(),
|
commands_enabled: commands_enabled_entry.is_active(),
|
||||||
notifications_enabled: notifications_enabled_entry.is_active(),
|
notifications_enabled: notifications_enabled_entry.is_active(),
|
||||||
|
new_ui_enabled: new_ui_enabled_entry.is_active(),
|
||||||
debug_logs: debug_logs_entry.is_active(),
|
debug_logs: debug_logs_entry.is_active(),
|
||||||
proxy: {
|
proxy: {
|
||||||
let proxy = proxy_entry.text().to_string();
|
let proxy = proxy_entry.text().to_string();
|
||||||
@ -371,6 +379,8 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
konata_size_entry,
|
konata_size_entry,
|
||||||
#[weak]
|
#[weak]
|
||||||
remove_gui_shit_entry,
|
remove_gui_shit_entry,
|
||||||
|
#[weak]
|
||||||
|
new_ui_enabled_entry,
|
||||||
move |_| {
|
move |_| {
|
||||||
let config = Config::default();
|
let config = Config::default();
|
||||||
ctx.set_config(&config);
|
ctx.set_config(&config);
|
||||||
@ -391,6 +401,7 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
oof_update_time_entry.set_text(&config.oof_update_time.to_string());
|
oof_update_time_entry.set_text(&config.oof_update_time.to_string());
|
||||||
konata_size_entry.set_text(&config.konata_size.to_string());
|
konata_size_entry.set_text(&config.konata_size.to_string());
|
||||||
remove_gui_shit_entry.set_active(config.remove_gui_shit);
|
remove_gui_shit_entry.set_active(config.remove_gui_shit);
|
||||||
|
new_ui_enabled_entry.set_active(config.new_ui_enabled);
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
let window = Window::builder()
|
let window = Window::builder()
|
||||||
@ -779,6 +790,8 @@ 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(RwLock::new(HashMap::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -929,23 +942,13 @@ fn send_notification(_: Arc<Context>, ui: &UiModel, title: &str, message: &str)
|
|||||||
ui.notifications.write().unwrap().push(id);
|
ui.notifications.write().unwrap().push(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String, notify: bool) {
|
fn get_message_box(
|
||||||
let notify = notify && ctx.config(|c| c.notifications_enabled);
|
ctx: Arc<Context>,
|
||||||
|
ui: &UiModel,
|
||||||
let formatting_enabled = ctx.config(|c| c.formatting_enabled);
|
message: String,
|
||||||
|
notify: bool,
|
||||||
let Some(sanitized) = (if formatting_enabled {
|
formatting_enabled: bool,
|
||||||
sanitize_message(message.clone())
|
) -> GtkBox {
|
||||||
} else {
|
|
||||||
Some(message.clone())
|
|
||||||
}) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if sanitized.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: softcode these colors
|
// TODO: softcode these colors
|
||||||
|
|
||||||
let (ip_color, date_color, text_color) = if ui.is_dark_theme {
|
let (ip_color, date_color, text_color) = if ui.is_dark_theme {
|
||||||
@ -956,7 +959,7 @@ fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String, notify: bool
|
|||||||
|
|
||||||
let mut label = String::new();
|
let mut label = String::new();
|
||||||
|
|
||||||
if let (true, Some((date, ip, content, nick, avatar))) =
|
if let (true, Some((date, ip, content, nick, _))) =
|
||||||
(formatting_enabled, parse_message(message.clone()))
|
(formatting_enabled, parse_message(message.clone()))
|
||||||
{
|
{
|
||||||
if let Some(ip) = ip {
|
if let Some(ip) = ip {
|
||||||
@ -1031,7 +1034,133 @@ fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String, notify: bool
|
|||||||
|
|
||||||
hbox.set_hexpand(true);
|
hbox.set_hexpand(true);
|
||||||
|
|
||||||
ui.chat_box.append(&hbox);
|
hbox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_avatar(ui: &UiModel, url: &str) -> Option<Pixbuf> {
|
||||||
|
Some(ui.default_avatar.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_new_message_box(
|
||||||
|
ctx: Arc<Context>,
|
||||||
|
ui: &UiModel,
|
||||||
|
message: String,
|
||||||
|
notify: bool,
|
||||||
|
formatting_enabled: bool,
|
||||||
|
) -> GtkBox {
|
||||||
|
// TODO: softcode these colors
|
||||||
|
|
||||||
|
let (ip_color, date_color, text_color) = if ui.is_dark_theme {
|
||||||
|
("#494949", "#929292", "#FFFFFF")
|
||||||
|
} else {
|
||||||
|
("#585858", "#292929", "#000000")
|
||||||
|
};
|
||||||
|
|
||||||
|
let (date, ip, content, name, color, avatar) =
|
||||||
|
if let (true, Some((date, ip, content, nick, avatar))) =
|
||||||
|
(formatting_enabled, parse_message(message.clone()))
|
||||||
|
{
|
||||||
|
(
|
||||||
|
date,
|
||||||
|
ip,
|
||||||
|
content,
|
||||||
|
nick.as_ref()
|
||||||
|
.map(|o| o.0.to_string())
|
||||||
|
.unwrap_or("System".to_string()),
|
||||||
|
nick.as_ref()
|
||||||
|
.map(|o| o.1.to_string())
|
||||||
|
.unwrap_or("#DDDDDD".to_string()),
|
||||||
|
avatar
|
||||||
|
.and_then(|o| load_avatar(ui, &o))
|
||||||
|
.unwrap_or(ui.default_avatar.clone()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
Local::now().format("%d.%m.%Y %H:%M").to_string(),
|
||||||
|
None,
|
||||||
|
message,
|
||||||
|
"System".to_string(),
|
||||||
|
"#DDDDDD".to_string(),
|
||||||
|
ui.default_avatar.clone(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let hbox = GtkBox::new(Orientation::Horizontal, 2);
|
||||||
|
|
||||||
|
let avatar_picture = Picture::for_pixbuf(&avatar);
|
||||||
|
avatar_picture.set_css_classes(&["message-avatar"]);
|
||||||
|
avatar_picture.set_size_request(32, 32);
|
||||||
|
avatar_picture.set_vexpand(false);
|
||||||
|
avatar_picture.set_hexpand(false);
|
||||||
|
avatar_picture.set_valign(Align::Start);
|
||||||
|
avatar_picture.set_halign(Align::Start);
|
||||||
|
|
||||||
|
hbox.append(&avatar_picture);
|
||||||
|
|
||||||
|
let vbox = GtkBox::new(Orientation::Vertical, 2);
|
||||||
|
|
||||||
|
vbox.append(&Label::builder()
|
||||||
|
.label(format!(
|
||||||
|
"<span color=\"{color}\">{}</span> <span color=\"{date_color}\">{}</span> <span color=\"{ip_color}\">{}</span>",
|
||||||
|
glib::markup_escape_text(&name),
|
||||||
|
glib::markup_escape_text(&date),
|
||||||
|
glib::markup_escape_text(&ip.unwrap_or_default()),
|
||||||
|
))
|
||||||
|
.halign(Align::Start)
|
||||||
|
.valign(Align::Start)
|
||||||
|
.selectable(true)
|
||||||
|
.wrap(true)
|
||||||
|
.wrap_mode(WrapMode::WordChar)
|
||||||
|
.use_markup(true)
|
||||||
|
.vexpand(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
vbox.append(&Label::builder()
|
||||||
|
.label(format!(
|
||||||
|
"<span color=\"{text_color}\">{}</span>",
|
||||||
|
glib::markup_escape_text(&content)
|
||||||
|
))
|
||||||
|
.halign(Align::Start)
|
||||||
|
.valign(Align::Start)
|
||||||
|
.selectable(true)
|
||||||
|
.wrap(true)
|
||||||
|
.wrap_mode(WrapMode::WordChar)
|
||||||
|
.use_markup(true)
|
||||||
|
.vexpand(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
vbox.set_valign(Align::Fill);
|
||||||
|
vbox.set_halign(Align::Fill);
|
||||||
|
vbox.set_vexpand(true);
|
||||||
|
vbox.set_hexpand(true);
|
||||||
|
|
||||||
|
hbox.append(&vbox);
|
||||||
|
|
||||||
|
hbox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String, notify: bool) {
|
||||||
|
let notify = notify && ctx.config(|c| c.notifications_enabled);
|
||||||
|
|
||||||
|
let formatting_enabled = ctx.config(|c| c.formatting_enabled);
|
||||||
|
|
||||||
|
let Some(sanitized) = (if formatting_enabled {
|
||||||
|
sanitize_message(message.clone())
|
||||||
|
} else {
|
||||||
|
Some(message.clone())
|
||||||
|
}) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if sanitized.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.config(|o| o.new_ui_enabled) {
|
||||||
|
ui.chat_box.append(&get_new_message_box(ctx.clone(), ui, message, notify, formatting_enabled));
|
||||||
|
} else {
|
||||||
|
ui.chat_box.append(&get_message_box(ctx.clone(), ui, message, notify, formatting_enabled));
|
||||||
|
}
|
||||||
|
|
||||||
timeout_add_local_once(Duration::from_millis(1000), move || {
|
timeout_add_local_once(Duration::from_millis(1000), move || {
|
||||||
GLOBAL.with(|global| {
|
GLOBAL.with(|global| {
|
||||||
|
BIN
src/chat/images/avatar.png
Normal file
BIN
src/chat/images/avatar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
/* Now made with GTK Pango Markup */
|
/* Now made with GTK Pango Markup */
|
||||||
|
|
||||||
/* .message-content { color:rgb(255, 255, 255); }
|
.message-content { color:rgb(255, 255, 255); }
|
||||||
.message-date { color:rgb(146, 146, 146); }
|
.message-date { color:rgb(146, 146, 146); }
|
||||||
.message-ip { color:rgb(73, 73, 73); } */
|
.message-ip { color:rgb(73, 73, 73); }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
/* Now made with GTK Pango Markup */
|
/* Now made with GTK Pango Markup */
|
||||||
|
|
||||||
/* .message-content { color:rgb(0, 0, 0); }
|
.message-content { color:rgb(0, 0, 0); }
|
||||||
.message-date { color:rgb(41, 41, 41); }
|
.message-date { color:rgb(41, 41, 41); }
|
||||||
.message-ip { color:rgb(88, 88, 88); } */
|
.message-ip { color:rgb(88, 88, 88); }
|
@ -14,11 +14,15 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-avatar {
|
||||||
|
border-radius: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Now made with GTK Pango Markup */
|
/* Now made with GTK Pango Markup */
|
||||||
|
|
||||||
/* .message-name { font-weight: bold; }
|
.message-name { font-weight: bold; }
|
||||||
|
|
||||||
.message-name-green { color: #70fa7a; }
|
/* .message-name-green { color: #70fa7a; }
|
||||||
.message-name-red { color: #fa7070; }
|
.message-name-red { color: #fa7070; }
|
||||||
.message-name-magenta { color: #da70fa; }
|
.message-name-magenta { color: #da70fa; }
|
||||||
.message-name-cyan { color: #70fadc; } */
|
.message-name-cyan { color: #70fadc; } */
|
Loading…
x
Reference in New Issue
Block a user