gui settings

This commit is contained in:
MeexReay 2025-04-19 02:22:14 +03:00
parent 94680c95e1
commit 588e536077
4 changed files with 308 additions and 64 deletions

View File

@ -7,10 +7,10 @@ use clap::Parser;
const MESSAGE_FORMAT: &str = "\u{B9AC}\u{3E70}<{name}> {text}";
fn default_true() -> bool { true }
fn default_max_messages() -> usize { 200 }
fn default_update_time() -> usize { 50 }
fn default_host() -> String { "meex.lol:11234".to_string() }
fn default_message_format() -> String { MESSAGE_FORMAT.to_string() }
pub fn default_max_messages() -> usize { 200 }
pub fn default_update_time() -> usize { 50 }
pub fn default_host() -> String { "meex.lol:11234".to_string() }
pub fn default_message_format() -> String { MESSAGE_FORMAT.to_string() }
#[derive(serde::Serialize, serde::Deserialize, DefaultFromSerde, Clone)]
pub struct Config {
@ -19,11 +19,11 @@ pub struct Config {
#[serde(default = "default_message_format")] pub message_format: String,
#[serde(default = "default_update_time")] pub update_time: usize,
#[serde(default = "default_max_messages")] pub max_messages: usize,
#[serde(default)] pub hide_my_ip: bool,
#[serde(default = "default_true")] pub hide_my_ip: bool,
#[serde(default)] pub show_other_ip: bool,
#[serde(default)] pub auth_enabled: bool,
#[serde(default)] pub ssl_enabled: bool,
#[serde(default)] pub chunked_enabled: bool,
#[serde(default = "default_true")] pub chunked_enabled: bool,
#[serde(default = "default_true")] pub formatting_enabled: bool,
#[serde(default = "default_true")] pub commands_enabled: bool,
}
@ -80,8 +80,8 @@ pub fn load_config(path: PathBuf) -> Config {
}
}
pub fn save_config(path: PathBuf, config: Config) {
let config_text = serde_yml::to_string(&config).expect("Config save error");
pub fn save_config(path: PathBuf, config: &Config) {
let config_text = serde_yml::to_string(config).expect("Config save error");
fs::create_dir_all(&path.parent().expect("Config save error")).expect("Config save error");
fs::write(&path, config_text).expect("Config save error");
}

View File

@ -10,7 +10,7 @@ pub struct Context {
pub sender: RwLock<Option<Arc<Sender<String>>>>,
pub messages: RwLock<Vec<String>>,
pub packet_size: AtomicUsize,
pub name: String
pub name: RwLock<String>
}
impl Context {
@ -21,10 +21,22 @@ impl Context {
sender: RwLock::new(None),
messages: RwLock::new(Vec::new()),
packet_size: AtomicUsize::default(),
name: 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>()))),
}
}
pub fn name(&self) -> String {
self.name.read().unwrap().clone()
}
pub fn set_config(&self, config: &Config) {
*self.config.write().unwrap() = config.clone();
*self.name.write().unwrap() = config.name.clone().unwrap_or_else(|| format!("Anon#{:X}", random::<u16>()));
*self.registered.write().unwrap() = None;
*self.messages.write().unwrap() = Vec::new();
self.packet_size.store(0, Ordering::SeqCst);
}
pub fn config<T>(&self, map: fn (&Config) -> T) -> T {
map(&self.config.read().unwrap())
}

View File

@ -5,7 +5,6 @@ use std::error::Error;
use std::thread;
use chrono::Local;
use rand::Rng;
use gtk4::{
self as gtk, gdk::{Cursor, Display, Texture}, gdk_pixbuf::{Pixbuf, PixbufAnimation, PixbufLoader}, gio::{
@ -13,13 +12,14 @@ use gtk4::{
MemoryInputStream, Menu
}, glib::{
self, clone, clone::Downgrade, idle_add_local, idle_add_local_once, source::timeout_add_local_once, timeout_add_local, ControlFlow
}, pango::WrapMode, prelude::*, AboutDialog, AlertDialog, Align, Application, ApplicationWindow, Box as GtkBox,
Button, Calendar, CssProvider, Entry, Fixed, Justification, Label, ListBox, Orientation, Overlay, Picture, ScrolledWindow, Settings
}, pango::WrapMode, prelude::*, AboutDialog, Align, Application, ApplicationWindow, Box as GtkBox,
Button, Calendar, CheckButton, CssProvider, Entry, Fixed, Justification, Label, ListBox, Orientation,
Overlay, Picture, ScrolledWindow, Settings, Window
};
use crate::{connect_rac, proto::{connect, read_messages}};
use super::{on_send_message, parse_message, ctx::Context};
use super::{config::{default_max_messages, default_update_time, get_config_path, save_config, Config}, ctx::Context, on_send_message, parse_message};
struct UiModel {
chat_box: GtkBox,
@ -79,18 +79,277 @@ fn load_pixbuf(data: &[u8]) -> Pixbuf {
loader.pixbuf().unwrap()
}
fn build_menu(_: Arc<Context>, app: &Application) {
// chunked_enabled: bool
// formatting_enabled: bool
// commands_enabled: bool
fn open_settings(ctx: Arc<Context>, app: &Application) {
let vbox = GtkBox::new(Orientation::Vertical, 10);
vbox.set_margin_bottom(15);
vbox.set_margin_top(15);
vbox.set_margin_start(15);
vbox.set_margin_end(15);
let host_hbox = GtkBox::new(Orientation::Horizontal, 5);
host_hbox.append(&Label::builder()
.label("Host")
.build());
let host_entry = Entry::builder()
.text(&ctx.config(|o| o.host.clone()))
.build();
host_hbox.append(&host_entry);
vbox.append(&host_hbox);
let name_hbox = GtkBox::new(Orientation::Horizontal, 5);
name_hbox.append(&Label::builder()
.label("Name")
.build());
let name_entry = Entry::builder()
.text(&ctx.config(|o| o.name.clone()).unwrap_or_default())
.build();
name_hbox.append(&name_entry);
vbox.append(&name_hbox);
let message_format_hbox = GtkBox::new(Orientation::Horizontal, 5);
message_format_hbox.append(&Label::builder()
.label("Message Format")
.build());
let message_format_entry = Entry::builder()
.text(&ctx.config(|o| o.message_format.clone()))
.build();
message_format_hbox.append(&message_format_entry);
vbox.append(&message_format_hbox);
let update_time_hbox = GtkBox::new(Orientation::Horizontal, 5);
update_time_hbox.append(&Label::builder()
.label("Update Time")
.build());
let update_time_entry = Entry::builder()
.text(&ctx.config(|o| o.update_time.to_string()))
.build();
update_time_hbox.append(&update_time_entry);
vbox.append(&update_time_hbox);
let max_messages_hbox = GtkBox::new(Orientation::Horizontal, 5);
max_messages_hbox.append(&Label::builder()
.label("Max Messages")
.build());
let max_messages_entry = Entry::builder()
.text(&ctx.config(|o| o.max_messages.to_string()))
.build();
max_messages_hbox.append(&max_messages_entry);
vbox.append(&max_messages_hbox);
let max_messages_hbox = GtkBox::new(Orientation::Horizontal, 5);
max_messages_hbox.append(&Label::builder()
.label("Max Messages")
.build());
let max_messages_entry = Entry::builder()
.text(&ctx.config(|o| o.max_messages.to_string()))
.build();
max_messages_hbox.append(&max_messages_entry);
vbox.append(&max_messages_hbox);
let hide_my_ip_hbox = GtkBox::new(Orientation::Horizontal, 5);
hide_my_ip_hbox.append(&Label::builder()
.label("Hide My IP")
.build());
let hide_my_ip_entry = CheckButton::builder()
.active(ctx.config(|o| o.hide_my_ip))
.build();
hide_my_ip_hbox.append(&hide_my_ip_entry);
vbox.append(&hide_my_ip_hbox);
let show_other_ip_hbox = GtkBox::new(Orientation::Horizontal, 5);
show_other_ip_hbox.append(&Label::builder()
.label("Show Other IP")
.build());
let show_other_ip_entry = CheckButton::builder()
.active(ctx.config(|o| o.show_other_ip))
.build();
show_other_ip_hbox.append(&show_other_ip_entry);
vbox.append(&show_other_ip_hbox);
let auth_enabled_hbox = GtkBox::new(Orientation::Horizontal, 5);
auth_enabled_hbox.append(&Label::builder()
.label("Auth Enabled")
.build());
let auth_enabled_entry = CheckButton::builder()
.active(ctx.config(|o| o.auth_enabled))
.build();
auth_enabled_hbox.append(&auth_enabled_entry);
vbox.append(&auth_enabled_hbox);
let ssl_enabled_hbox = GtkBox::new(Orientation::Horizontal, 5);
ssl_enabled_hbox.append(&Label::builder()
.label("SSL Enabled")
.build());
let ssl_enabled_entry = CheckButton::builder()
.active(ctx.config(|o| o.ssl_enabled))
.build();
ssl_enabled_hbox.append(&ssl_enabled_entry);
vbox.append(&ssl_enabled_hbox);
let chunked_enabled_hbox = GtkBox::new(Orientation::Horizontal, 5);
chunked_enabled_hbox.append(&Label::builder()
.label("Chunked Enabled")
.build());
let chunked_enabled_entry = CheckButton::builder()
.active(ctx.config(|o| o.chunked_enabled))
.build();
chunked_enabled_hbox.append(&chunked_enabled_entry);
vbox.append(&chunked_enabled_hbox);
let formatting_enabled_hbox = GtkBox::new(Orientation::Horizontal, 5);
formatting_enabled_hbox.append(&Label::builder()
.label("Formatting Enabled")
.build());
let formatting_enabled_entry = CheckButton::builder()
.active(ctx.config(|o| o.formatting_enabled))
.build();
formatting_enabled_hbox.append(&formatting_enabled_entry);
vbox.append(&formatting_enabled_hbox);
let commands_enabled_hbox = GtkBox::new(Orientation::Horizontal, 5);
commands_enabled_hbox.append(&Label::builder()
.label("Commands Enabled")
.build());
let commands_enabled_entry = CheckButton::builder()
.active(ctx.config(|o| o.commands_enabled))
.build();
commands_enabled_hbox.append(&commands_enabled_entry);
vbox.append(&commands_enabled_hbox);
let save_button = Button::builder()
.label("Save")
.build();
vbox.append(&save_button);
save_button.connect_clicked(clone!(
#[weak] ctx,
move |_| {
let config = Config {
host: host_entry.text().to_string(),
name: {
let name = name_entry.text().to_string();
if name.is_empty() {
None
} else {
Some(name)
}
},
message_format: message_format_entry.text().to_string(),
update_time: {
let update_time = update_time_entry.text();
if let Ok(update_time) = update_time.parse::<usize>() {
update_time
} else {
let update_time = default_update_time();
update_time_entry.set_text(&update_time.to_string());
update_time
}
},
max_messages: {
let max_messages = max_messages_entry.text();
if let Ok(max_messages) = max_messages.parse::<usize>() {
max_messages
} else {
let max_messages = default_max_messages();
max_messages_entry.set_text(&max_messages.to_string());
max_messages
}
},
hide_my_ip: hide_my_ip_entry.is_active(),
show_other_ip: show_other_ip_entry.is_active(),
auth_enabled: auth_enabled_entry.is_active(),
ssl_enabled: ssl_enabled_entry.is_active(),
chunked_enabled: chunked_enabled_entry.is_active(),
formatting_enabled: formatting_enabled_entry.is_active(),
commands_enabled: commands_enabled_entry.is_active()
};
ctx.set_config(&config);
save_config(get_config_path(), &config);
}
));
let window = Window::builder()
.application(app)
.title("Settings")
.default_width(400)
.default_height(500)
.decorated(true)
.child(&vbox)
.build();
window.present();
}
fn build_menu(ctx: Arc<Context>, app: &Application) {
let menu = Menu::new();
let file_menu = Menu::new();
file_menu.append(Some("New File"), Some("app.file_new"));
file_menu.append(Some("Make a bottleflip"), Some("app.make_bottleflip"));
file_menu.append(Some("Export brain to jpeg"), Some("unavailable"));
file_menu.append(Some("About"), Some("app.about"));
file_menu.append(Some("Close"), Some("app.close"));
let edit_menu = Menu::new();
edit_menu.append(Some("Edit File"), Some("app.file_edit"));
edit_menu.append(Some("Create a new parallel reality"), Some("app.parallel_reality_create"));
edit_menu.append(Some("Settings"), Some("app.settings"));
menu.append_submenu(Some("File"), &file_menu);
menu.append_submenu(Some("Edit"), &edit_menu);
@ -98,49 +357,18 @@ fn build_menu(_: Arc<Context>, app: &Application) {
app.set_menubar(Some((&menu).into()));
app.add_action_entries([
ActionEntry::builder("file_new")
.activate(move |a: &Application, _, _| {
AlertDialog::builder()
.message("Successful creatin")
.detail("your file was created")
.buttons(["ok", "cancel", "confirm", "click"])
.build()
.show(Some(&a.windows()[0]));
ActionEntry::builder("settings")
.activate(clone!(
#[weak] ctx,
move |a: &Application, _, _| {
open_settings(ctx, a);
}
)
))
.build(),
ActionEntry::builder("make_bottleflip")
ActionEntry::builder("close")
.activate(move |a: &Application, _, _| {
AlertDialog::builder()
.message("Sorry")
.detail("bottleflip gone wrong :(")
.buttons(["yes", "no"])
.build()
.show(Some(&a.windows()[0]));
}
)
.build(),
ActionEntry::builder("parallel_reality_create")
.activate(move |a: &Application, _, _| {
AlertDialog::builder()
.message("Your new parallel reality has been created")
.detail(format!("Your parallel reality code: {}", rand::rng().random_range(1..100)))
.buttons(["chocolate"])
.build()
.show(Some(&a.windows()[0]));
}
)
.build(),
ActionEntry::builder("file_edit")
.activate(move |a: &Application, _, _| {
AlertDialog::builder()
.message("Successful editioning")
.detail("your file was edited")
.buttons(["okey"])
.build()
.show(Some(&a.windows()[0]));
}
)
a.quit();
})
.build(),
ActionEntry::builder("about")
.activate(clone!(
@ -356,7 +584,7 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
let window = ApplicationWindow::builder()
.application(app)
.title(format!("bRAC - Connected to {} as {}", ctx.config(|o| o.host.clone()), &ctx.name))
.title(format!("bRAC - Connected to {} as {}", ctx.config(|o| o.host.clone()), &ctx.name()))
.default_width(500)
.default_height(500)
.resizable(false)
@ -458,6 +686,10 @@ fn load_css() {
}
fn on_add_message(ctx: Arc<Context>, ui: &UiModel, message: String) {
if message.is_empty() {
return;
}
let hbox = GtkBox::new(Orientation::Horizontal, 2);
if let Some((date, ip, content, nick)) = parse_message(message.clone()) {

View File

@ -83,7 +83,7 @@ pub fn on_command(ctx: Arc<Context>, command: &str) -> Result<(), Box<dyn Error>
return Ok(())
};
match register_user(connect_rac!(ctx), &ctx.name, pass) {
match register_user(connect_rac!(ctx), &ctx.name(), pass) {
Ok(true) => {
add_message(ctx.clone(), "you was registered successfully bro")?;
*ctx.registered.write().unwrap() = Some(pass.to_string());
@ -170,12 +170,12 @@ pub fn on_send_message(ctx: Arc<Context>, message: &str) -> Result<(), Box<dyn E
let message = prepare_message(
ctx.clone(),
&ctx.config(|o| o.message_format.clone())
.replace("{name}", &ctx.name)
.replace("{name}", &ctx.name())
.replace("{text}", &message)
);
if let Some(password) = ctx.registered.read().unwrap().clone() {
send_message_auth(connect_rac!(ctx), &ctx.name, &password, &message)?;
send_message_auth(connect_rac!(ctx), &ctx.name(), &password, &message)?;
} else if ctx.config(|o| o.auth_enabled) {
send_message_spoof_auth(connect_rac!(ctx), &message)?;
} else {