mirror of
https://github.com/MeexReay/bRAC.git
synced 2025-06-24 19:02:58 +03:00
Compare commits
9 Commits
9c2bec33e1
...
efc202153f
Author | SHA1 | Date | |
---|---|---|---|
efc202153f | |||
97948aa420 | |||
01b3643c14 | |||
9ef7560963 | |||
b206f18829 | |||
2c35c0a18f | |||
89147235dc | |||
d390c1f28b | |||
8c5dad7aa5 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -1,3 +1,3 @@
|
|||||||
*.png filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.gif filter=lfs diff=lfs merge=lfs -text
|
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||||
*.ico filter=lfs diff=lfs merge=lfs -text
|
*.ico filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -90,7 +90,7 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bRAC"
|
name = "bRAC"
|
||||||
version = "0.1.4+2.0"
|
version = "0.1.5+2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1494,9 +1494,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.26.2"
|
version = "0.27.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
|
checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
|
33
Makefile
33
Makefile
@ -1,15 +1,42 @@
|
|||||||
.PHONY: clean install uninstall
|
.PHONY: clean install uninstall build_linux build_windows build_all
|
||||||
|
|
||||||
|
TARGETS = \
|
||||||
|
i686-unknown-linux-gnu \
|
||||||
|
i686-unknown-linux-musl \
|
||||||
|
x86_64-unknown-linux-none \
|
||||||
|
x86_64-unknown-linux-gnu \
|
||||||
|
x86_64-unknown-linux-musl \
|
||||||
|
aarch64-unknown-linux-gnu \
|
||||||
|
aarch64-unknown-linux-musl
|
||||||
|
|
||||||
install: target/release/bRAC
|
install: target/release/bRAC
|
||||||
|
mkdir -p ~/.local
|
||||||
|
mkdir -p ~/.local/bin
|
||||||
|
mkdir -p ~/.local/share
|
||||||
cp $< ~/.local/bin/bRAC
|
cp $< ~/.local/bin/bRAC
|
||||||
chmod +x ~/.local/bin/bRAC
|
chmod +x ~/.local/bin/bRAC
|
||||||
mkdir ~/.local/share/bRAC -p
|
mkdir ~/.local/share/bRAC -p
|
||||||
cp misc/bRAC.png ~/.local/share/bRAC/icon.png
|
cp misc/bRAC.png ~/.local/share/bRAC/icon.png
|
||||||
cp misc/bRAC.desktop ~/.local/share/applications/ru.themixray.bRAC.desktop
|
./misc/create-desktop.sh > ~/.local/share/applications/ru.themixray.bRAC.desktop
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -rf ~/.config/bRAC ~/.local/share/bRAC
|
rm -rf ~/.config/bRAC ~/.local/share/bRAC
|
||||||
rm -f ~/.local/share/applications/ru.themixray.bRAC.desktop
|
rm -f ~/.local/share/applications/ru.themixray.bRAC.desktop
|
||||||
target/release/bRAC:
|
target/release/bRAC:
|
||||||
cargo build -r
|
cargo build -r
|
||||||
|
|
||||||
|
build_all: build_linux build_windows
|
||||||
|
|
||||||
|
build_linux:
|
||||||
|
mkdir -p build
|
||||||
|
mkdir -p build/linux
|
||||||
|
for target in $(TARGETS); do \
|
||||||
|
cargo build -r --target $$target; \
|
||||||
|
cp target/$$target/bRAC build/linux/$$target-bRAC; \
|
||||||
|
done
|
||||||
|
|
||||||
|
build_windows:
|
||||||
|
echo "Windows build is in development!!!"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
cargo clean
|
cargo clean
|
||||||
|
rm -rf build
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Name=bRAC
|
|
||||||
Version=0.1.4
|
|
||||||
Type=Application
|
|
||||||
Comment=better RAC client
|
|
||||||
Icon=~/.local/share/bRAC/icon.png
|
|
||||||
Exec=~/.local/bin/bRAC
|
|
||||||
Categories=Network;
|
|
||||||
StartupNotify=true
|
|
||||||
DBusActivatable=true
|
|
||||||
Terminal=false
|
|
||||||
X-GNOME-UsesNotifications=true
|
|
15
misc/create-desktop.sh
Normal file
15
misc/create-desktop.sh
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
version=$(grep -m1 '^version' Cargo.toml | sed -E 's/version *= *"(.*)"/\1/')
|
||||||
|
|
||||||
|
echo "[Desktop Entry]"
|
||||||
|
echo "Name=bRAC"
|
||||||
|
echo "Version=$version"
|
||||||
|
echo "Type=Application"
|
||||||
|
echo "Comment=better RAC client"
|
||||||
|
echo "Icon=$HOME/.local/share/bRAC/icon.png"
|
||||||
|
echo "Exec=$HOME/.local/bin/bRAC"
|
||||||
|
echo "Categories=Network;"
|
||||||
|
echo "StartupNotify=true"
|
||||||
|
echo "Terminal=false"
|
||||||
|
echo "X-GNOME-UsesNotifications=true"
|
@ -27,4 +27,4 @@ set "v=%TEMP%\_s.vbs"
|
|||||||
>>"%v%" echo l.Save
|
>>"%v%" echo l.Save
|
||||||
wscript "%v%" >nul
|
wscript "%v%" >nul
|
||||||
del "%v%" >nul
|
del "%v%" >nul
|
||||||
exit /b
|
exit /b
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "this script is deprecated, fix it yourself if you wanna to"; exit
|
||||||
|
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
echo "This script must be run as root"
|
echo "This script must be run as root"
|
||||||
exit 1
|
exit 1
|
||||||
@ -8,4 +10,4 @@ fi
|
|||||||
cp bRAC /bin/bRAC
|
cp bRAC /bin/bRAC
|
||||||
chmod +x /bin/bRAC
|
chmod +x /bin/bRAC
|
||||||
cp ru.themixray.bRAC.png /usr/share/pixmaps
|
cp ru.themixray.bRAC.png /usr/share/pixmaps
|
||||||
cp ru.themixray.bRAC.desktop /usr/share/applications
|
cp ru.themixray.bRAC.desktop /usr/share/applications
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "this script is deprecated, fix it yourself if you wanna to"; exit
|
||||||
|
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
echo "This script must be run as root"
|
echo "This script must be run as root"
|
||||||
exit 1
|
exit 1
|
||||||
@ -11,4 +13,4 @@ done
|
|||||||
|
|
||||||
rm -f /bin/bRAC
|
rm -f /bin/bRAC
|
||||||
rm -f /usr/share/pixmaps/ru.themixray.bRAC.png
|
rm -f /usr/share/pixmaps/ru.themixray.bRAC.png
|
||||||
rm -f /usr/share/applications/ru.themixray.bRAC.desktop
|
rm -f /usr/share/applications/ru.themixray.bRAC.desktop
|
||||||
|
@ -4,4 +4,4 @@ cp bRAC ~/.local/bin/bRAC
|
|||||||
chmod +x ~/.local/bin/bRAC
|
chmod +x ~/.local/bin/bRAC
|
||||||
mkdir ~/.local/share/bRAC -p
|
mkdir ~/.local/share/bRAC -p
|
||||||
cp misc/bRAC.png ~/.local/share/bRAC/icon.png
|
cp misc/bRAC.png ~/.local/share/bRAC/icon.png
|
||||||
cp misc/bRAC.desktop ~/.local/share/applications/ru.themixray.bRAC.desktop
|
./misc/create-desktop.sh > ~/.local/share/applications/ru.themixray.bRAC.desktop
|
||||||
|
@ -8,7 +8,9 @@ const MESSAGE_FORMAT: &str = "\u{B9AC}\u{3E70}<{name}> {text}";
|
|||||||
|
|
||||||
fn default_true() -> bool { true }
|
fn default_true() -> bool { true }
|
||||||
pub fn default_max_messages() -> usize { 200 }
|
pub fn default_max_messages() -> usize { 200 }
|
||||||
pub fn default_update_time() -> usize { 50 }
|
pub fn default_update_time() -> usize { 100 }
|
||||||
|
pub fn default_oof_update_time() -> usize { 10000 }
|
||||||
|
pub fn default_konata_size() -> usize { 100 }
|
||||||
pub fn default_host() -> String { "meex.lol:11234".to_string() }
|
pub fn default_host() -> String { "meex.lol:11234".to_string() }
|
||||||
pub fn default_message_format() -> String { MESSAGE_FORMAT.to_string() }
|
pub fn default_message_format() -> String { MESSAGE_FORMAT.to_string() }
|
||||||
|
|
||||||
@ -18,7 +20,10 @@ pub struct Config {
|
|||||||
#[serde(default)] pub name: Option<String>,
|
#[serde(default)] pub name: Option<String>,
|
||||||
#[serde(default = "default_message_format")] pub message_format: String,
|
#[serde(default = "default_message_format")] pub message_format: String,
|
||||||
#[serde(default = "default_update_time")] pub update_time: usize,
|
#[serde(default = "default_update_time")] pub update_time: usize,
|
||||||
|
#[serde(default = "default_oof_update_time")] pub oof_update_time: usize,
|
||||||
#[serde(default = "default_max_messages")] pub max_messages: usize,
|
#[serde(default = "default_max_messages")] pub max_messages: usize,
|
||||||
|
#[serde(default = "default_konata_size")] pub konata_size: usize,
|
||||||
|
#[serde(default)] pub remove_gui_shit: bool,
|
||||||
#[serde(default = "default_true")] pub hide_my_ip: bool,
|
#[serde(default = "default_true")] pub hide_my_ip: bool,
|
||||||
#[serde(default)] pub show_other_ip: bool,
|
#[serde(default)] pub show_other_ip: bool,
|
||||||
#[serde(default)] pub auth_enabled: bool,
|
#[serde(default)] pub auth_enabled: bool,
|
||||||
@ -29,6 +34,7 @@ pub struct Config {
|
|||||||
#[serde(default)] pub wrac_enabled: bool,
|
#[serde(default)] pub wrac_enabled: bool,
|
||||||
#[serde(default)] pub proxy: Option<String>,
|
#[serde(default)] pub proxy: Option<String>,
|
||||||
#[serde(default = "default_true")] pub notifications_enabled: bool,
|
#[serde(default = "default_true")] pub notifications_enabled: bool,
|
||||||
|
#[serde(default)] pub debug_logs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_config_path() -> PathBuf {
|
pub fn get_config_path() -> PathBuf {
|
||||||
@ -109,17 +115,21 @@ pub struct Args {
|
|||||||
#[arg(short='n', long)] pub name: Option<String>,
|
#[arg(short='n', long)] pub name: Option<String>,
|
||||||
#[arg(long)] pub message_format: Option<String>,
|
#[arg(long)] pub message_format: Option<String>,
|
||||||
#[arg(long)] pub update_time: Option<usize>,
|
#[arg(long)] pub update_time: Option<usize>,
|
||||||
|
#[arg(long)] pub oof_update_time: Option<usize>,
|
||||||
#[arg(long)] pub max_messages: Option<usize>,
|
#[arg(long)] pub max_messages: Option<usize>,
|
||||||
|
#[arg(long)] pub konata_size: Option<usize>,
|
||||||
#[arg(long)] pub hide_my_ip: Option<bool>,
|
#[arg(long)] pub hide_my_ip: Option<bool>,
|
||||||
#[arg(long)] pub show_other_ip: Option<bool>,
|
#[arg(long)] pub show_other_ip: Option<bool>,
|
||||||
#[arg(long)] pub auth_enabled:Option <bool>,
|
#[arg(long)] pub auth_enabled:Option <bool>,
|
||||||
#[arg(long)] pub ssl_enabled: Option<bool>,
|
#[arg(long)] pub ssl_enabled: Option<bool>,
|
||||||
|
#[arg(long)] pub remove_gui_shit: Option<bool>,
|
||||||
#[arg(long)] pub chunked_enabled: Option<bool>,
|
#[arg(long)] pub chunked_enabled: Option<bool>,
|
||||||
#[arg(long)] pub formatting_enabled: Option<bool>,
|
#[arg(long)] pub formatting_enabled: Option<bool>,
|
||||||
#[arg(long)] pub commands_enabled: Option<bool>,
|
#[arg(long)] pub commands_enabled: Option<bool>,
|
||||||
#[arg(long)] pub notifications_enabled: Option<bool>,
|
#[arg(long)] pub notifications_enabled: Option<bool>,
|
||||||
#[arg(long)] pub wrac_enabled: Option<bool>,
|
#[arg(long)] pub wrac_enabled: Option<bool>,
|
||||||
#[arg(long)] pub proxy: Option<String>,
|
#[arg(long)] pub proxy: Option<String>,
|
||||||
|
#[arg(long)] pub debug_logs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
@ -129,9 +139,12 @@ impl Args {
|
|||||||
if let Some(v) = self.proxy.clone() { config.proxy = Some(v) }
|
if let Some(v) = self.proxy.clone() { config.proxy = Some(v) }
|
||||||
if let Some(v) = self.message_format.clone() { config.message_format = v }
|
if let Some(v) = self.message_format.clone() { config.message_format = v }
|
||||||
if let Some(v) = self.update_time { config.update_time = v }
|
if let Some(v) = self.update_time { config.update_time = v }
|
||||||
|
if let Some(v) = self.oof_update_time { config.oof_update_time = v }
|
||||||
if let Some(v) = self.max_messages { config.max_messages = v }
|
if let Some(v) = self.max_messages { config.max_messages = v }
|
||||||
|
if let Some(v) = self.konata_size { config.konata_size = v }
|
||||||
if let Some(v) = self.hide_my_ip { config.hide_my_ip = v }
|
if let Some(v) = self.hide_my_ip { config.hide_my_ip = v }
|
||||||
if let Some(v) = self.show_other_ip { config.show_other_ip = v }
|
if let Some(v) = self.show_other_ip { config.show_other_ip = v }
|
||||||
|
if let Some(v) = self.remove_gui_shit { config.remove_gui_shit = v }
|
||||||
if let Some(v) = self.auth_enabled { config.auth_enabled = v }
|
if let Some(v) = self.auth_enabled { config.auth_enabled = v }
|
||||||
if let Some(v) = self.ssl_enabled { config.ssl_enabled = v }
|
if let Some(v) = self.ssl_enabled { config.ssl_enabled = v }
|
||||||
if let Some(v) = self.chunked_enabled { config.chunked_enabled = v }
|
if let Some(v) = self.chunked_enabled { config.chunked_enabled = v }
|
||||||
@ -139,5 +152,6 @@ impl Args {
|
|||||||
if let Some(v) = self.commands_enabled { config.commands_enabled = v }
|
if let Some(v) = self.commands_enabled { config.commands_enabled = v }
|
||||||
if let Some(v) = self.notifications_enabled { config.notifications_enabled = v }
|
if let Some(v) = self.notifications_enabled { config.notifications_enabled = v }
|
||||||
if let Some(v) = self.wrac_enabled { config.wrac_enabled = v }
|
if let Some(v) = self.wrac_enabled { config.wrac_enabled = v }
|
||||||
|
if self.debug_logs { config.debug_logs = true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::sync::{atomic::{AtomicUsize, Ordering}, mpsc::Sender, Arc, RwLock};
|
use std::sync::{atomic::{AtomicBool, AtomicUsize, Ordering}, mpsc::Sender, Arc, RwLock};
|
||||||
|
|
||||||
use rand::random;
|
use rand::random;
|
||||||
|
|
||||||
@ -10,7 +10,8 @@ pub struct Context {
|
|||||||
pub sender: RwLock<Option<Arc<Sender<(String, bool)>>>>,
|
pub sender: RwLock<Option<Arc<Sender<(String, bool)>>>>,
|
||||||
pub messages: RwLock<Vec<String>>,
|
pub messages: RwLock<Vec<String>>,
|
||||||
pub packet_size: AtomicUsize,
|
pub packet_size: AtomicUsize,
|
||||||
pub name: RwLock<String>
|
pub name: RwLock<String>,
|
||||||
|
pub is_focused: AtomicBool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
@ -22,6 +23,7 @@ impl Context {
|
|||||||
messages: RwLock::new(Vec::new()),
|
messages: RwLock::new(Vec::new()),
|
||||||
packet_size: AtomicUsize::default(),
|
packet_size: AtomicUsize::default(),
|
||||||
name: RwLock::new(config.name.clone().unwrap_or_else(|| format!("Anon#{:X}", random::<u16>()))),
|
name: RwLock::new(config.name.clone().unwrap_or_else(|| format!("Anon#{:X}", random::<u16>()))),
|
||||||
|
is_focused: AtomicBool::new(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
363
src/chat/gui.rs
363
src/chat/gui.rs
@ -1,7 +1,8 @@
|
|||||||
use std::sync::{mpsc::{channel, Receiver}, Arc, RwLock};
|
use std::sync::{atomic::Ordering, mpsc::{channel, Receiver}, Arc, RwLock};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
|
||||||
@ -26,12 +27,13 @@ use gtk::{
|
|||||||
Justification, Label, ListBox, Orientation, Overlay, Picture, ScrolledWindow, Settings, Window
|
Justification, Label, ListBox, Orientation, Overlay, Picture, ScrolledWindow, Settings, Window
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::proto::parse_rac_url;
|
use crate::{chat::config::{default_konata_size, default_oof_update_time}, proto::parse_rac_url};
|
||||||
|
|
||||||
use super::{config::{default_max_messages, default_update_time, get_config_path, save_config, Config},
|
use super::{config::{default_max_messages, default_update_time, get_config_path, save_config, Config},
|
||||||
ctx::Context, on_send_message, parse_message, print_message, recv_tick, sanitize_message, SERVER_LIST};
|
ctx::Context, on_send_message, parse_message, print_message, recv_tick, sanitize_message, SERVER_LIST};
|
||||||
|
|
||||||
struct UiModel {
|
struct UiModel {
|
||||||
|
is_dark_theme: bool,
|
||||||
chat_box: GtkBox,
|
chat_box: GtkBox,
|
||||||
chat_scrolled: ScrolledWindow,
|
chat_scrolled: ScrolledWindow,
|
||||||
app: Application,
|
app: Application,
|
||||||
@ -54,11 +56,11 @@ pub fn add_chat_message(ctx: Arc<Context>, message: String) {
|
|||||||
let _ = ctx.sender.read().unwrap().clone().unwrap().send((message, false));
|
let _ = ctx.sender.read().unwrap().clone().unwrap().send((message, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_pixbuf(data: &[u8]) -> Pixbuf {
|
fn load_pixbuf(data: &[u8]) -> Result<Pixbuf, Box<dyn Error>> {
|
||||||
let loader = PixbufLoader::new();
|
let loader = PixbufLoader::new();
|
||||||
loader.write(data).unwrap();
|
loader.write(data)?;
|
||||||
loader.close().unwrap();
|
loader.close()?;
|
||||||
loader.pixbuf().unwrap()
|
Ok(loader.pixbuf().ok_or("laod pixbuf error")?)
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! gui_entry_setting {
|
macro_rules! gui_entry_setting {
|
||||||
@ -162,6 +164,7 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
let message_format_entry = gui_entry_setting!("Message Format", message_format, ctx, vbox);
|
let message_format_entry = gui_entry_setting!("Message Format", message_format, ctx, vbox);
|
||||||
let proxy_entry = gui_option_entry_setting!("Socks5 proxy", proxy, ctx, vbox);
|
let proxy_entry = gui_option_entry_setting!("Socks5 proxy", proxy, ctx, vbox);
|
||||||
let update_time_entry = gui_usize_entry_setting!("Update Time", update_time, ctx, vbox);
|
let update_time_entry = gui_usize_entry_setting!("Update Time", update_time, ctx, vbox);
|
||||||
|
let oof_update_time_entry = gui_usize_entry_setting!("Out-of-focus Update Time", oof_update_time, ctx, vbox);
|
||||||
let max_messages_entry = gui_usize_entry_setting!("Max Messages", max_messages, ctx, vbox);
|
let max_messages_entry = gui_usize_entry_setting!("Max Messages", max_messages, ctx, vbox);
|
||||||
let hide_my_ip_entry = gui_checkbox_setting!("Hide My IP", hide_my_ip, ctx, vbox);
|
let hide_my_ip_entry = gui_checkbox_setting!("Hide My IP", hide_my_ip, ctx, vbox);
|
||||||
let show_other_ip_entry = gui_checkbox_setting!("Show Other IP", show_other_ip, ctx, vbox);
|
let show_other_ip_entry = gui_checkbox_setting!("Show Other IP", show_other_ip, ctx, vbox);
|
||||||
@ -172,6 +175,9 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
let formatting_enabled_entry = gui_checkbox_setting!("Formatting Enabled", formatting_enabled, ctx, vbox);
|
let formatting_enabled_entry = gui_checkbox_setting!("Formatting Enabled", formatting_enabled, ctx, vbox);
|
||||||
let commands_enabled_entry = gui_checkbox_setting!("Commands Enabled", commands_enabled, ctx, vbox);
|
let commands_enabled_entry = gui_checkbox_setting!("Commands Enabled", commands_enabled, ctx, vbox);
|
||||||
let notifications_enabled_entry = gui_checkbox_setting!("Notifications Enabled", notifications_enabled, ctx, vbox);
|
let notifications_enabled_entry = gui_checkbox_setting!("Notifications Enabled", notifications_enabled, ctx, vbox);
|
||||||
|
let debug_logs_entry = gui_checkbox_setting!("Debug Logs", debug_logs, ctx, vbox);
|
||||||
|
let konata_size_entry = gui_usize_entry_setting!("Konata Size", konata_size, ctx, vbox);
|
||||||
|
let remove_gui_shit_entry = gui_checkbox_setting!("Remove Gui Shit", remove_gui_shit, ctx, vbox);
|
||||||
|
|
||||||
let save_button = Button::builder()
|
let save_button = Button::builder()
|
||||||
.label("Save")
|
.label("Save")
|
||||||
@ -196,6 +202,10 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
#[weak] notifications_enabled_entry,
|
#[weak] notifications_enabled_entry,
|
||||||
#[weak] wrac_enabled_entry,
|
#[weak] wrac_enabled_entry,
|
||||||
#[weak] proxy_entry,
|
#[weak] proxy_entry,
|
||||||
|
#[weak] debug_logs_entry,
|
||||||
|
#[weak] oof_update_time_entry,
|
||||||
|
#[weak] konata_size_entry,
|
||||||
|
#[weak] remove_gui_shit_entry,
|
||||||
move |_| {
|
move |_| {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
host: host_entry.text().to_string(),
|
host: host_entry.text().to_string(),
|
||||||
@ -220,6 +230,28 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
update_time
|
update_time
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
oof_update_time: {
|
||||||
|
let oof_update_time = oof_update_time_entry.text();
|
||||||
|
|
||||||
|
if let Ok(oof_update_time) = oof_update_time.parse::<usize>() {
|
||||||
|
oof_update_time
|
||||||
|
} else {
|
||||||
|
let oof_update_time = default_oof_update_time();
|
||||||
|
oof_update_time_entry.set_text(&oof_update_time.to_string());
|
||||||
|
oof_update_time
|
||||||
|
}
|
||||||
|
},
|
||||||
|
konata_size: {
|
||||||
|
let konata_size = konata_size_entry.text();
|
||||||
|
|
||||||
|
if let Ok(konata_size) = konata_size.parse::<usize>() {
|
||||||
|
konata_size.max(0).min(200)
|
||||||
|
} else {
|
||||||
|
let konata_size = default_konata_size();
|
||||||
|
konata_size_entry.set_text(&konata_size.to_string());
|
||||||
|
konata_size
|
||||||
|
}
|
||||||
|
},
|
||||||
max_messages: {
|
max_messages: {
|
||||||
let max_messages = max_messages_entry.text();
|
let max_messages = max_messages_entry.text();
|
||||||
|
|
||||||
@ -232,6 +264,7 @@ fn open_settings(ctx: Arc<Context>, app: &Application) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
hide_my_ip: hide_my_ip_entry.is_active(),
|
hide_my_ip: hide_my_ip_entry.is_active(),
|
||||||
|
remove_gui_shit: remove_gui_shit_entry.is_active(),
|
||||||
show_other_ip: show_other_ip_entry.is_active(),
|
show_other_ip: show_other_ip_entry.is_active(),
|
||||||
auth_enabled: auth_enabled_entry.is_active(),
|
auth_enabled: auth_enabled_entry.is_active(),
|
||||||
ssl_enabled: ssl_enabled_entry.is_active(),
|
ssl_enabled: ssl_enabled_entry.is_active(),
|
||||||
@ -240,6 +273,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(),
|
||||||
|
debug_logs: debug_logs_entry.is_active(),
|
||||||
proxy: {
|
proxy: {
|
||||||
let proxy = proxy_entry.text().to_string();
|
let proxy = proxy_entry.text().to_string();
|
||||||
|
|
||||||
@ -361,7 +395,7 @@ fn build_menu(ctx: Arc<Context>, app: &Application) {
|
|||||||
.activate(clone!(
|
.activate(clone!(
|
||||||
#[weak] app,
|
#[weak] app,
|
||||||
move |_, _, _| {
|
move |_, _, _| {
|
||||||
AboutDialog::builder()
|
AboutDialog::builder()
|
||||||
.application(&app)
|
.application(&app)
|
||||||
.authors(["TheMixRay", "MeexReay"])
|
.authors(["TheMixRay", "MeexReay"])
|
||||||
.license(" DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
.license(" DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
@ -380,7 +414,7 @@ fn build_menu(ctx: Arc<Context>, app: &Application) {
|
|||||||
.comments("better RAC client")
|
.comments("better RAC client")
|
||||||
.website("https://github.com/MeexReay/bRAC")
|
.website("https://github.com/MeexReay/bRAC")
|
||||||
.website_label("source code")
|
.website_label("source code")
|
||||||
.logo(&Texture::for_pixbuf(&load_pixbuf(include_bytes!("images/icon.png"))))
|
.logo(&Texture::for_pixbuf(&load_pixbuf(include_bytes!("images/icon.png")).unwrap()))
|
||||||
.build()
|
.build()
|
||||||
.present();
|
.present();
|
||||||
}
|
}
|
||||||
@ -390,6 +424,14 @@ fn build_menu(ctx: Arc<Context>, app: &Application) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
||||||
|
let is_dark_theme = if let Some(settings) = Settings::default() {
|
||||||
|
settings.is_gtk_application_prefer_dark_theme() || settings.gtk_theme_name()
|
||||||
|
.map(|o| o.to_lowercase().contains("dark"))
|
||||||
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
let main_box = GtkBox::new(Orientation::Vertical, 5);
|
let main_box = GtkBox::new(Orientation::Vertical, 5);
|
||||||
|
|
||||||
main_box.set_css_classes(&["main-box"]);
|
main_box.set_css_classes(&["main-box"]);
|
||||||
@ -400,11 +442,15 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
|||||||
|
|
||||||
widget_box.set_css_classes(&["widget_box"]);
|
widget_box.set_css_classes(&["widget_box"]);
|
||||||
|
|
||||||
widget_box.append(&Calendar::builder()
|
let remove_gui_shit = ctx.config(|c| c.remove_gui_shit);
|
||||||
.css_classes(["calendar"])
|
|
||||||
.show_heading(false)
|
if !remove_gui_shit {
|
||||||
.can_target(false)
|
widget_box.append(&Calendar::builder()
|
||||||
.build());
|
.css_classes(["calendar"])
|
||||||
|
.show_heading(false)
|
||||||
|
.can_target(false)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
let server_list_vbox = GtkBox::new(Orientation::Vertical, 5);
|
let server_list_vbox = GtkBox::new(Orientation::Vertical, 5);
|
||||||
|
|
||||||
@ -445,60 +491,68 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
|||||||
|
|
||||||
widget_box.append(&server_list_vbox);
|
widget_box.append(&server_list_vbox);
|
||||||
|
|
||||||
let fixed = Fixed::new();
|
if !remove_gui_shit {
|
||||||
fixed.set_can_target(false);
|
let fixed = Fixed::new();
|
||||||
|
fixed.set_can_target(false);
|
||||||
|
|
||||||
let konata = Picture::for_pixbuf(&load_pixbuf(include_bytes!("images/konata.png")));
|
let konata_size = ctx.config(|c| c.konata_size) as i32;
|
||||||
konata.set_size_request(174, 127);
|
|
||||||
|
|
||||||
fixed.put(&konata, 325.0, 4.0);
|
|
||||||
|
|
||||||
let logo_gif = include_bytes!("images/logo.gif");
|
let konata = Picture::for_pixbuf(&load_pixbuf(include_bytes!("images/konata.png")).unwrap());
|
||||||
|
konata.set_size_request(174 * konata_size / 100, 127 * konata_size / 100);
|
||||||
|
|
||||||
|
fixed.put(&konata, (499 - 174 * konata_size / 100) as f64, (131 - 127 * konata_size / 100) as f64);
|
||||||
|
|
||||||
let logo = Picture::for_pixbuf(&load_pixbuf(logo_gif));
|
let logo_gif = include_bytes!("images/logo.gif");
|
||||||
logo.set_size_request(152, 64);
|
|
||||||
|
|
||||||
let logo_anim = PixbufAnimation::from_stream(
|
let logo = Picture::for_pixbuf(&load_pixbuf(logo_gif).unwrap());
|
||||||
&MemoryInputStream::from_bytes(
|
logo.set_size_request(152 * konata_size / 100, 64 * konata_size / 100);
|
||||||
&glib::Bytes::from(logo_gif)
|
|
||||||
),
|
|
||||||
None::<&gio::Cancellable>
|
|
||||||
).unwrap().iter(Some(SystemTime::now()));
|
|
||||||
|
|
||||||
timeout_add_local(Duration::from_millis(30), {
|
let logo_anim = PixbufAnimation::from_stream(
|
||||||
let logo = logo.clone();
|
&MemoryInputStream::from_bytes(
|
||||||
let logo_anim = logo_anim.clone();
|
&glib::Bytes::from(logo_gif)
|
||||||
|
),
|
||||||
|
None::<&gio::Cancellable>
|
||||||
|
).unwrap().iter(Some(SystemTime::now()));
|
||||||
|
|
||||||
move || {
|
timeout_add_local(Duration::from_millis(30), {
|
||||||
logo.set_pixbuf(Some(&logo_anim.pixbuf()));
|
let logo = logo.clone();
|
||||||
logo_anim.advance(SystemTime::now());
|
let logo_anim = logo_anim.clone();
|
||||||
|
let ctx = ctx.clone();
|
||||||
|
|
||||||
ControlFlow::Continue
|
move || {
|
||||||
}
|
if ctx.is_focused.load(Ordering::SeqCst) {
|
||||||
});
|
logo.set_pixbuf(Some(&logo_anim.pixbuf()));
|
||||||
|
logo_anim.advance(SystemTime::now());
|
||||||
fixed.put(&logo, 262.0, 4.0);
|
}
|
||||||
|
ControlFlow::Continue
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 262, 4
|
||||||
|
fixed.put(&logo, (436 - 174 * konata_size / 100) as f64, (131 - 127 * konata_size / 100) as f64);
|
||||||
|
|
||||||
let time = Label::builder()
|
let time = Label::builder()
|
||||||
.label(&Local::now().format("%H:%M").to_string())
|
.label(&Local::now().format("%H:%M").to_string())
|
||||||
.justify(Justification::Right)
|
.justify(Justification::Right)
|
||||||
.css_classes(["time"])
|
.css_classes(["time"])
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
timeout_add_local(Duration::from_secs(1), {
|
timeout_add_local(Duration::from_secs(1), {
|
||||||
let time = time.clone();
|
let time = time.clone();
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
time.set_label(&Local::now().format("%H:%M").to_string());
|
time.set_label(&Local::now().format("%H:%M").to_string());
|
||||||
|
|
||||||
ControlFlow::Continue
|
ControlFlow::Continue
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fixed.put(&time, 432.0, 4.0);
|
fixed.put(&time, 432.0, 4.0);
|
||||||
fixed.set_halign(Align::End);
|
fixed.set_halign(Align::End);
|
||||||
|
|
||||||
widget_box_overlay.add_overlay(&fixed);
|
widget_box_overlay.add_overlay(&fixed);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
widget_box_overlay.set_child(Some(&widget_box));
|
widget_box_overlay.set_child(Some(&widget_box));
|
||||||
|
|
||||||
@ -553,8 +607,10 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
|||||||
));
|
));
|
||||||
|
|
||||||
if let Err(e) = on_send_message(ctx.clone(), &text_entry.text()) {
|
if let Err(e) = on_send_message(ctx.clone(), &text_entry.text()) {
|
||||||
let msg = format!("Send message error: {}", e.to_string()).to_string();
|
if ctx.config(|o| o.debug_logs) {
|
||||||
add_chat_message(ctx.clone(), msg);
|
let msg = format!("Send message error: {}", e.to_string()).to_string();
|
||||||
|
add_chat_message(ctx.clone(), msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
@ -572,8 +628,10 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
|||||||
));
|
));
|
||||||
|
|
||||||
if let Err(e) = on_send_message(ctx.clone(), &text_entry.text()) {
|
if let Err(e) = on_send_message(ctx.clone(), &text_entry.text()) {
|
||||||
let msg = format!("Send message error: {}", e.to_string()).to_string();
|
if ctx.config(|o| o.debug_logs) {
|
||||||
add_chat_message(ctx.clone(), msg);
|
let msg = format!("Send message error: {}", e.to_string()).to_string();
|
||||||
|
add_chat_message(ctx.clone(), msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
@ -621,6 +679,7 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
|
|||||||
window.present();
|
window.present();
|
||||||
|
|
||||||
UiModel {
|
UiModel {
|
||||||
|
is_dark_theme,
|
||||||
chat_scrolled,
|
chat_scrolled,
|
||||||
chat_box,
|
chat_box,
|
||||||
app: app.clone(),
|
app: app.clone(),
|
||||||
@ -640,30 +699,31 @@ fn setup(_: &Application, ctx: Arc<Context>, ui: UiModel) {
|
|||||||
run_recv_loop(ctx.clone());
|
run_recv_loop(ctx.clone());
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
|
ui.window.connect_notify(Some("is-active"), {
|
||||||
|
let ctx = ctx.clone();
|
||||||
|
|
||||||
#[cfg(feature = "libnotify")]
|
move |a, _| {
|
||||||
ui.window.connect_notify(Some("is-active"), move |a, _| {
|
let is_focused = a.is_active();
|
||||||
if a.is_active() {
|
|
||||||
GLOBAL.with(|global| {
|
|
||||||
if let Some((ui, _)) = &*global.borrow() {
|
|
||||||
for i in ui.notifications.read().unwrap().clone() {
|
|
||||||
i.close().expect("libnotify close error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
#[cfg(not(feature = "libnotify"))]
|
ctx.is_focused.store(is_focused, Ordering::SeqCst);
|
||||||
ui.window.connect_notify(Some("is-active"), move |a, _| {
|
|
||||||
if a.is_active() {
|
if is_focused {
|
||||||
GLOBAL.with(|global| {
|
make_recv_tick(ctx.clone());
|
||||||
if let Some((ui, _)) = &*global.borrow() {
|
|
||||||
for i in ui.notifications.read().unwrap().clone() {
|
GLOBAL.with(|global| {
|
||||||
ui.app.withdraw_notification(&i);
|
if let Some((ui, _)) = &*global.borrow() {
|
||||||
|
#[cfg(feature = "libnotify")]
|
||||||
|
for i in ui.notifications.read().unwrap().clone() {
|
||||||
|
i.close().expect("libnotify close error");
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "libnotify"))]
|
||||||
|
for i in ui.notifications.read().unwrap().clone() {
|
||||||
|
ui.app.withdraw_notification(&i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -686,7 +746,7 @@ fn setup(_: &Application, ctx: Arc<Context>, ui: UiModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let message: String = rx.recv().unwrap();
|
let message: String = rx.recv().unwrap();
|
||||||
on_add_message(ctx.clone(), &ui, message);
|
on_add_message(ctx.clone(), &ui, message, !clear);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -695,15 +755,7 @@ fn setup(_: &Application, ctx: Arc<Context>, ui: UiModel) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_css() {
|
fn load_css(is_dark_theme: bool) {
|
||||||
let is_dark_theme = if let Some(settings) = Settings::default() {
|
|
||||||
settings.is_gtk_application_prefer_dark_theme() || settings.gtk_theme_name()
|
|
||||||
.map(|o| o.to_lowercase().contains("dark"))
|
|
||||||
.unwrap_or_default()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
let provider = CssProvider::new();
|
let provider = CssProvider::new();
|
||||||
provider.load_from_data(&format!(
|
provider.load_from_data(&format!(
|
||||||
"{}\n{}",
|
"{}\n{}",
|
||||||
@ -756,105 +808,80 @@ 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) {
|
fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String, notify: bool) {
|
||||||
let Some(message) = sanitize_message(message) else { return; };
|
let Some(message) = sanitize_message(message) else { return; };
|
||||||
|
|
||||||
if message.is_empty() {
|
if message.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hbox = GtkBox::new(Orientation::Horizontal, 2);
|
// TODO: cache these colors maybe??
|
||||||
|
|
||||||
|
let (ip_color, date_color, text_color) = if ui.is_dark_theme {
|
||||||
|
(
|
||||||
|
"#494949",
|
||||||
|
"#929292",
|
||||||
|
"#FFFFFF"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
"#585858",
|
||||||
|
"#292929",
|
||||||
|
"#000000"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut label = String::new();
|
||||||
|
|
||||||
if let Some((date, ip, content, nick)) = parse_message(message.clone()) {
|
if let Some((date, ip, content, nick)) = parse_message(message.clone()) {
|
||||||
if let Some(ip) = ip {
|
if let Some(ip) = ip {
|
||||||
if ctx.config(|o| o.show_other_ip) {
|
if ctx.config(|o| o.show_other_ip) {
|
||||||
let ip_label = Label::builder()
|
label.push_str(&format!("<span color=\"{ip_color}\">{}</span> ", glib::markup_escape_text(&ip)));
|
||||||
.label(&ip)
|
|
||||||
.margin_end(10)
|
|
||||||
.halign(Align::Start)
|
|
||||||
.valign(Align::Start)
|
|
||||||
.css_classes(["message-ip"])
|
|
||||||
.selectable(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
hbox.append(&ip_label);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let date_label = Label::builder()
|
label.push_str(&format!("<span color=\"{date_color}\">[{}]</span> ", glib::markup_escape_text(&date)));
|
||||||
.label(format!("[{date}]"))
|
|
||||||
.halign(Align::Start)
|
|
||||||
.valign(Align::Start)
|
|
||||||
.css_classes(["message-date"])
|
|
||||||
.selectable(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
hbox.append(&date_label);
|
|
||||||
|
|
||||||
if let Some((name, color)) = nick {
|
if let Some((name, color)) = nick {
|
||||||
let name_label = Label::builder()
|
label.push_str(&format!("<span font_weight=\"bold\" color=\"{}\"><{}></span> ", color.to_uppercase(), glib::markup_escape_text(&name)));
|
||||||
.label(format!("<{name}>"))
|
|
||||||
.halign(Align::Start)
|
|
||||||
.valign(Align::Start)
|
|
||||||
.css_classes(["message-name", &format!("message-name-{}", color)])
|
|
||||||
.selectable(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
hbox.append(&name_label);
|
if notify && !ui.window.is_active() {
|
||||||
|
|
||||||
if !ui.window.is_active() {
|
|
||||||
if ctx.config(|o| o.chunked_enabled) {
|
if ctx.config(|o| o.chunked_enabled) {
|
||||||
send_notification(ctx.clone(), ui, &format!("{}'s Message", &name), &content);
|
send_notification(ctx.clone(), ui, &format!("{}'s Message", &name), &glib::markup_escape_text(&content));
|
||||||
// let notif = Notification::new(&format!("{}'s Message", &name));
|
|
||||||
// notif.set_body(Some(&content));
|
|
||||||
// app.send_notification(Some("user-message"), ¬if);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !ui.window.is_active() {
|
if notify && !ui.window.is_active() {
|
||||||
if ctx.config(|o| o.chunked_enabled) {
|
if ctx.config(|o| o.chunked_enabled) {
|
||||||
send_notification(ctx.clone(), ui, "System Message", &content);
|
send_notification(ctx.clone(), ui, "System Message", &content);
|
||||||
// let notif = Notification::new("System Message");
|
|
||||||
// notif.set_body(Some(&content));
|
|
||||||
// app.send_notification(Some("system-message"), ¬if);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let content_label = Label::builder()
|
label.push_str(&format!("<span color=\"{text_color}\">{}</span>", glib::markup_escape_text(&content)));
|
||||||
.label(&content)
|
|
||||||
.halign(Align::Start)
|
|
||||||
.valign(Align::Start)
|
|
||||||
.css_classes(["message-content"])
|
|
||||||
.selectable(true)
|
|
||||||
.wrap(true)
|
|
||||||
.wrap_mode(WrapMode::Char)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
hbox.append(&content_label);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
let content_label = Label::builder()
|
label.push_str(&format!("<span color=\"{text_color}\">{}</span>", glib::markup_escape_text(&message)));
|
||||||
.label(&message)
|
|
||||||
.halign(Align::Start)
|
|
||||||
.valign(Align::Start)
|
|
||||||
.css_classes(["message-content"])
|
|
||||||
.selectable(true)
|
|
||||||
.wrap(true)
|
|
||||||
.wrap_mode(WrapMode::Char)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
hbox.append(&content_label);
|
if notify && !ui.window.is_active() {
|
||||||
|
|
||||||
if !ui.window.is_active() {
|
|
||||||
if ctx.config(|o| o.chunked_enabled) {
|
if ctx.config(|o| o.chunked_enabled) {
|
||||||
send_notification(ctx.clone(), ui, "Chat Message", &message);
|
send_notification(ctx.clone(), ui, "Chat Message", &message);
|
||||||
// let notif = Notification::new("Chat Message");
|
|
||||||
// notif.set_body(Some(&message));
|
|
||||||
// app.send_notification(Some("chat-message"), ¬if);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hbox = GtkBox::new(Orientation::Horizontal, 2);
|
||||||
|
|
||||||
|
hbox.append(&Label::builder()
|
||||||
|
.label(&label)
|
||||||
|
.halign(Align::Start)
|
||||||
|
.valign(Align::Start)
|
||||||
|
.selectable(true)
|
||||||
|
.wrap(true)
|
||||||
|
.wrap_mode(WrapMode::WordChar)
|
||||||
|
.use_markup(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
hbox.set_hexpand(true);
|
||||||
|
|
||||||
ui.chat_box.append(&hbox);
|
ui.chat_box.append(&hbox);
|
||||||
|
|
||||||
@ -868,13 +895,27 @@ fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_recv_tick(ctx: Arc<Context>) {
|
||||||
|
if let Err(e) = recv_tick(ctx.clone()) {
|
||||||
|
if ctx.config(|o| o.debug_logs) {
|
||||||
|
let _ = print_message(ctx.clone(), format!("Print messages error: {}", e.to_string()).to_string());
|
||||||
|
}
|
||||||
|
thread::sleep(Duration::from_secs(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn run_recv_loop(ctx: Arc<Context>) {
|
fn run_recv_loop(ctx: Arc<Context>) {
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
if let Err(e) = recv_tick(ctx.clone()) {
|
make_recv_tick(ctx.clone());
|
||||||
let _ = print_message(ctx.clone(), format!("Print messages error: {}", e.to_string()).to_string());
|
|
||||||
thread::sleep(Duration::from_secs(1));
|
thread::sleep(Duration::from_millis(
|
||||||
}
|
if ctx.is_focused.load(Ordering::SeqCst) {
|
||||||
|
ctx.config(|o| o.update_time) as u64
|
||||||
|
} else {
|
||||||
|
ctx.config(|o| o.oof_update_time) as u64
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -895,8 +936,8 @@ pub fn run_main_loop(ctx: Arc<Context>) {
|
|||||||
|
|
||||||
move |app| {
|
move |app| {
|
||||||
let ui = build_ui(ctx.clone(), app);
|
let ui = build_ui(ctx.clone(), app);
|
||||||
|
load_css(ui.is_dark_theme);
|
||||||
setup(app, ctx.clone(), ui);
|
setup(app, ctx.clone(), ui);
|
||||||
load_css();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -914,4 +955,4 @@ pub fn run_main_loop(ctx: Arc<Context>) {
|
|||||||
{
|
{
|
||||||
libnotify::uninit();
|
libnotify::uninit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
error::Error, sync::Arc, thread, time::{Duration, SystemTime, UNIX_EPOCH}
|
error::Error, sync::Arc, time::{SystemTime, UNIX_EPOCH}
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::connect_rac;
|
use crate::connect_rac;
|
||||||
@ -31,10 +31,10 @@ lazy_static! {
|
|||||||
pub static ref IP_REGEX: Regex = Regex::new(r"\{(.*?)\} (.*)").unwrap();
|
pub static ref IP_REGEX: Regex = Regex::new(r"\{(.*?)\} (.*)").unwrap();
|
||||||
|
|
||||||
pub static ref COLORED_USERNAMES: Vec<(Regex, String)> = vec![
|
pub static ref COLORED_USERNAMES: Vec<(Regex, String)> = vec![
|
||||||
(Regex::new(r"\u{B9AC}\u{3E70}<(.*?)> (.*)").unwrap(), "green".to_string()), // bRAC
|
(Regex::new(r"\u{B9AC}\u{3E70}<(.*?)> (.*)").unwrap(), "#70fa7a".to_string()), // bRAC
|
||||||
(Regex::new(r"\u{2550}\u{2550}\u{2550}<(.*?)> (.*)").unwrap(), "red".to_string()), // CRAB
|
(Regex::new(r"\u{2550}\u{2550}\u{2550}<(.*?)> (.*)").unwrap(), "#fa7070".to_string()), // CRAB
|
||||||
(Regex::new(r"\u{00B0}\u{0298}<(.*?)> (.*)").unwrap(), "magenta".to_string()), // Mefidroniy
|
(Regex::new(r"\u{00B0}\u{0298}<(.*?)> (.*)").unwrap(), "#da70fa".to_string()), // Mefidroniy
|
||||||
(Regex::new(r"<(.*?)> (.*)").unwrap(), "cyan".to_string()), // clRAC
|
(Regex::new(r"<(.*?)> (.*)").unwrap(), "#70fadc".to_string()), // clRAC
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static ref SERVER_LIST: Vec<String> = vec![
|
pub static ref SERVER_LIST: Vec<String> = vec![
|
||||||
@ -215,11 +215,13 @@ pub fn recv_tick(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Read messages error: {}", e.to_string())
|
if ctx.config(|o| o.debug_logs) {
|
||||||
|
add_chat_message(ctx.clone(), format!("Read messages error: {}", e.to_string()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
thread::sleep(Duration::from_millis(ctx.config(|o| o.update_time) as u64));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,4 +297,4 @@ pub fn find_username_color(message: &str) -> Option<(String, String, String)> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
.message-content { color:rgb(255, 255, 255); }
|
|
||||||
|
/* Now made with GTK Pango Markup */
|
||||||
|
|
||||||
|
/* .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,3 +1,6 @@
|
|||||||
.message-content { color:rgb(0, 0, 0); }
|
|
||||||
|
/* Now made with GTK Pango Markup */
|
||||||
|
|
||||||
|
/* .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); } */
|
@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
.send-button, .send-text { border-radius: 0; }
|
.send-button, .send-text { border-radius: 0; }
|
||||||
.calendar {
|
.calendar {
|
||||||
transform: scale(0.6);
|
transform: scale(0.6);
|
||||||
@ -16,9 +14,11 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-name { font-weight: bold; }
|
/* Now made with GTK Pango Markup */
|
||||||
|
|
||||||
|
/* .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