diff --git a/Cargo.lock b/Cargo.lock index 3ba7cdd..4567cfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.18" @@ -78,6 +93,7 @@ name = "bRAC" version = "0.1.2+2.0" dependencies = [ "cfg-if", + "chrono", "clap", "colored", "crossterm", @@ -97,6 +113,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + [[package]] name = "byteorder" version = "1.5.0" @@ -157,6 +179,20 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + [[package]] name = "clap" version = "4.5.36" @@ -688,6 +724,30 @@ dependencies = [ "windows", ] +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "indexmap" version = "2.7.1" @@ -710,6 +770,16 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -822,6 +892,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.20.3" @@ -1077,6 +1156,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + [[package]] name = "ryu" version = "1.0.19" @@ -1357,6 +1442,64 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + [[package]] name = "widestring" version = "1.1.0" @@ -1429,6 +1572,12 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + [[package]] name = "windows-result" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index 558e582..539b74b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,12 +14,14 @@ serde_yml = "0.0.12" crossterm = { version = "0.29.0", optional = true } homedir = { version = "0.3.4", optional = true } native-tls = { version = "0.2.14", optional = true } -gtk4 = { version = "0.9.6", optional = true } +gtk4 = { version = "0.9.6", optional = true, features = [ "v4_10" ] } cfg-if = "1.0.0" +chrono = { version = "0.4.40", optional = true } [features] default = ["ssl", "homedir", "gtk_gui"] +tui = ["ssl", "homedir", "pretty_tui"] ssl = ["dep:native-tls"] pretty_tui = ["dep:crossterm"] -gtk_gui = ["dep:gtk4"] +gtk_gui = ["dep:gtk4", "dep:chrono"] homedir = ["dep:homedir"] diff --git a/README.md b/README.md index 074d45e..d6b841d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ better RAC client ## features +- gtk4 GUI - cheat commands (type /help) - no ip and date visible - uses TOR proxy server by default (meex.lol:11234) @@ -19,7 +20,7 @@ better RAC client - RACS compatible (--enable-ssl or in --configure enable SSL) - chunked reading messages -![image](https://github.com/user-attachments/assets/a2858662-50f1-4554-949c-f55addf48fcc) +![screenshot](image.png) ## how to run @@ -43,18 +44,19 @@ cargo build -r # build release (target/release/bRAC) cargo run -r # run (builds and runs bRAC itself) ``` +TUI version: + +```bash +cargo build -r --no-default-features -F tui +cargo run -r --no-default-features -F tui +``` + Minimal version: ```bash cargo build -r --no-default-features cargo run -r --no-default-features ``` -GTK version: - -```bash -cargo build -rF gtk_gui -cargo run -rF gtk_gui -``` ### nix package @@ -72,11 +74,11 @@ nix build github:MeexReay/bRAC#bRAC-minimal nix run github:MeexReay/bRAC#bRAC-minimal ``` -GTK version: +TUI version: ```bash -nix build github:MeexReay/bRAC#bRAC-gtk -nix run github:MeexReay/bRAC#bRAC-gtk +nix build github:MeexReay/bRAC#bRAC-tui +nix run github:MeexReay/bRAC#bRAC-tui ``` ## default config diff --git a/brac_logo.png b/brac_logo.png deleted file mode 100644 index 56c41e5..0000000 Binary files a/brac_logo.png and /dev/null differ diff --git a/flake.nix b/flake.nix index 5b868d6..1d30b00 100644 --- a/flake.nix +++ b/flake.nix @@ -55,15 +55,20 @@ devShells.default = self'.devShells.stable; packages.bRAC = (rustPackage { + version = "-gtk"; + features = "ssl homedir gtk_gui"; + deps = with pkgs; [ + pkg-config + openssl + gtk4 + pango + ]; + }); + packages.bRAC-tui = (rustPackage { version = ""; features = "default"; deps = with pkgs; [ pkg-config openssl ]; }); - packages.bRAC-gtk = (rustPackage { - version = "-gtk"; - features = "ssl homedir gtk_gui"; - deps = with pkgs; [ pkg-config openssl gtk4 pango ]; - }); packages.bRAC-minimal = (rustPackage { version = "-minimal"; features = ""; diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..7e1fd4e Binary files /dev/null and b/icon.png differ diff --git a/image.png b/image.png new file mode 100644 index 0000000..99109d0 Binary files /dev/null and b/image.png differ diff --git a/konata.png b/konata.png new file mode 100644 index 0000000..e29a4cb Binary files /dev/null and b/konata.png differ diff --git a/logo.gif b/logo.gif new file mode 100644 index 0000000..930fdc1 Binary files /dev/null and b/logo.gif differ diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 30a1dbe..0000000 --- a/shell.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ pkgs ? import {} }: - let - devDeps = with pkgs; [ - pkg-config - openssl - gtk4 - pango - ]; - in pkgs.mkShell { - shellHook = '' - export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc} - ''; - buildInputs = devDeps; - nativeBuildInputs = with pkgs; [ - rustc - cargo - ] ++ devDeps; - } \ No newline at end of file diff --git a/src/chat.rs b/src/chat.rs index 29905fb..7e49457 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -32,12 +32,12 @@ lazy_static! { cfg_if! { - if #[cfg(feature = "gtk_gui")] { - mod gtk_gui; - pub use gtk_gui::*; - } else if #[cfg(feature = "pretty_tui")] { + if #[cfg(feature = "pretty_tui")] { mod pretty_tui; pub use pretty_tui::*; + } else if #[cfg(feature = "gtk_gui")] { + mod gtk_gui; + pub use gtk_gui::*; } else { mod minimal_tui; pub use minimal_tui::*; diff --git a/src/chat/gtk_gui.rs b/src/chat/gtk_gui.rs index 1751acb..4d14a92 100644 --- a/src/chat/gtk_gui.rs +++ b/src/chat/gtk_gui.rs @@ -1,20 +1,26 @@ +use std::rc::Rc; use std::sync::{Arc, RwLock}; -use std::time::Duration; +use std::time::{Duration, SystemTime}; +use chrono::Local; use colored::{Color, Colorize}; -use gtk4::gdk::{Cursor, Display}; -use gtk4::gdk_pixbuf::PixbufLoader; -use gtk4::gio::MenuModel; +use gtk4::gdk::{Cursor, Display, Texture}; +use gtk4::gdk_pixbuf::{Pixbuf, PixbufAnimation, PixbufLoader}; +use gtk4::gio::{ActionEntry, ApplicationFlags, MemoryInputStream, Menu, MenuItem, MenuModel}; use gtk4::glib::clone::Downgrade; +use gtk4::{gio, Image, Justification, ListBox, pango::WrapMode}; +use gtk4::glib::timeout_add_local; use gtk4::glib::{idle_add_local, idle_add_local_once, ControlFlow, source::timeout_add_local_once}; use gtk4::{glib, glib::clone, Align, Box as GtkBox, Label, ScrolledWindow}; -use gtk4::{CssProvider, Entry, Orientation, Overlay, Picture, PopoverMenuBar}; +use gtk4::{AboutDialog, AlertDialog, ButtonsType, Calendar, CssProvider, Entry, Fixed, License, MessageDialog, MessageType, Orientation, Overlay, Picture, PopoverMenuBar, SelectionMode, Window}; use gtk4::prelude::*; use gtk4::{Application, ApplicationWindow, Button}; +use rand::Rng; use std::sync::mpsc::{channel, Sender, Receiver}; use std::error::Error; use std::thread; use std::cell::RefCell; +use std::io::Bytes; use crate::config::Context; use crate::proto::{connect, read_messages}; @@ -85,15 +91,217 @@ pub fn recv_tick(ctx: Arc) -> Result<(), Box> { Ok(()) } -fn build_ui(ctx: Arc, app: &Application) { +fn load_pixbuf(data: &[u8]) -> Pixbuf { + let loader = PixbufLoader::new(); + loader.write(data).unwrap(); + loader.close().unwrap(); + loader.pixbuf().unwrap() +} + +fn build_menu(ctx: Arc, 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")); + + 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")); + + menu.append_submenu(Some("File"), &file_menu); + menu.append_submenu(Some("Edit"), &edit_menu); + + app.set_menubar(Some((&menu).into())); + + // GtkAlertDialog::builder() + // .title("Successful editioning") + // .text("your file was edited") + // .buttons(ButtonsType::Ok) + // .application(&app) + // .message_type(MessageType::Info) + // .build() + // .present(); + // AlertDialog::builder() + // .message("Successful editioning") + // .detail("your file was edited") + // .buttons(["okey"]) + // .build() + // .present(None); + + 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])); + } + ) + .build(), + ActionEntry::builder("make_bottleflip") + .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])); + } + ) + .build(), + ActionEntry::builder("about") + .activate(clone!( + #[weak] app, + move |_, _, _| { + AboutDialog::builder() + .application(&app) + .authors(["TheMixRay", "MeexReay"]) + .license(" DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO.") + .comments("better RAC client") + .website("https://github.com/MeexReay/bRAC") + .website_label("source code") + .logo(&Texture::for_pixbuf(&load_pixbuf(include_bytes!("../../icon.png")))) + .build() + .present(); + } + )) + .build() + ]); +} + +fn build_ui(ctx: Arc, app: &Application) -> UiModel { let main_box = GtkBox::new(Orientation::Vertical, 5); + let widget_box_overlay = Overlay::new(); + + let widget_box = GtkBox::new(Orientation::Horizontal, 5); + + widget_box.set_css_classes(&["widget_box"]); + + widget_box.append(&Calendar::builder() + .css_classes(["calendar"]) + .show_heading(false) + .can_target(false) + .build()); + + let server_list_vbox = GtkBox::new(Orientation::Vertical, 5); + + let server_list = ListBox::new(); + + server_list.append(&Label::builder().label("meex.lol:42666").halign(Align::Start).selectable(true).build()); + server_list.append(&Label::builder().label("meex.lol:11234").halign(Align::Start).selectable(true).build()); + server_list.append(&Label::builder().label("91.192.22.20:42666").halign(Align::Start).selectable(true).build()); + + server_list_vbox.append(&Label::builder().label("Server List:").build()); + + server_list_vbox.append(&server_list); + + widget_box.append(&server_list_vbox); + + let fixed = Fixed::new(); + fixed.set_can_target(false); + + let konata = Picture::for_pixbuf(&load_pixbuf(include_bytes!("../../konata.png"))); + konata.set_size_request(174, 127); + + fixed.put(&konata, 325.0, 4.0); + + let logo = Picture::for_pixbuf(&load_pixbuf(include_bytes!("../../logo.gif"))); + logo.set_size_request(152, 64); + + let logo_anim = PixbufAnimation::from_stream( + &MemoryInputStream::from_bytes( + &glib::Bytes::from(include_bytes!("../../logo.gif")) + ), + None::<&gio::Cancellable> + ).unwrap().iter(Some(SystemTime::now())); + + timeout_add_local(Duration::from_millis(30), { + let logo = logo.clone(); + let logo_anim = logo_anim.clone(); + + move || { + logo.set_pixbuf(Some(&logo_anim.pixbuf())); + logo_anim.advance(SystemTime::now()); + + ControlFlow::Continue + } + }); + + fixed.put(&logo, 262.0, 4.0); + + let time = Label::builder() + .label(&Local::now().format("%H:%M").to_string()) + .justify(Justification::Right) + .css_classes(["time"]) + .build(); + + timeout_add_local(Duration::from_secs(1), { + let time = time.clone(); + + move || { + time.set_label(&Local::now().format("%H:%M").to_string()); + + ControlFlow::Continue + } + }); + + fixed.put(&time, 432.0, 4.0); + + widget_box_overlay.add_overlay(&fixed); + + widget_box_overlay.set_child(Some(&widget_box)); + + main_box.append(&widget_box_overlay); + let chat_box = GtkBox::new(Orientation::Vertical, 2); let chat_scrolled = ScrolledWindow::builder() .child(&chat_box) .vexpand(true) .hexpand(true) + .margin_bottom(5) + .margin_end(5) + .margin_start(5) .propagate_natural_height(true) .build(); @@ -101,8 +309,13 @@ fn build_ui(ctx: Arc, app: &Application) { let send_box = GtkBox::new(Orientation::Horizontal, 5); + send_box.set_margin_bottom(5); + send_box.set_margin_end(5); + send_box.set_margin_start(5); + let text_entry = Entry::builder() .placeholder_text("Message") + .css_classes(["send-button"]) .hexpand(true) .build(); @@ -110,6 +323,7 @@ fn build_ui(ctx: Arc, app: &Application) { let send_btn = Button::builder() .label("Send") + .css_classes(["send-text"]) .cursor(&Cursor::from_name("pointer", None).unwrap()) .build(); @@ -168,25 +382,14 @@ fn build_ui(ctx: Arc, app: &Application) { } }); - let overlay = Overlay::new(); + // let logo = Picture::for_pixbuf(&load_pixbuf(include_bytes!("../../brac_logo.png"))); + // logo.set_size_request(500, 189); + // logo.set_can_target(false); + // logo.set_can_focus(false); + // logo.set_halign(Align::End); + // logo.set_valign(Align::Start); + // overlay.add_overlay(&logo); - overlay.set_child(Some(&main_box)); - - let bytes = include_bytes!("../../brac_logo.png"); - let loader = PixbufLoader::new(); - loader.write(bytes).unwrap(); - loader.close().unwrap(); - let pixbuf = loader.pixbuf().unwrap(); - - let logo = Picture::for_pixbuf(&pixbuf); - logo.set_size_request(500, 189); - logo.set_can_target(false); - logo.set_can_focus(false); - logo.set_halign(Align::End); - logo.set_valign(Align::Start); - - overlay.add_overlay(&logo); - let window = ApplicationWindow::builder() .application(app) .title(format!("bRAC - Connected to {} as {}", &ctx.host, &ctx.name)) @@ -195,7 +398,7 @@ fn build_ui(ctx: Arc, app: &Application) { .resizable(false) .decorated(true) .show_menubar(true) - .child(&overlay) + .child(&main_box) .build(); window.connect_default_width_notify({ @@ -212,15 +415,12 @@ fn build_ui(ctx: Arc, app: &Application) { } }); - window.show(); + window.present(); - let ui = UiModel { + UiModel { chat_scrolled, chat_box - }; - - setup(ctx.clone(), ui); - load_css(); + } } fn setup(ctx: Arc, ui: UiModel) { @@ -273,91 +473,42 @@ fn setup(ctx: Arc, ui: UiModel) { fn load_css() { let provider = CssProvider::new(); provider.load_from_data(" - - * { - border-radius: 0; + .send-button, .send-text { border-radius: 0; } + .calendar { + transform: scale(0.6); + margin: -35px; } - - .message-content { - color: #000000; + .widget_box { + box-shadow: 0 10px 10px rgba(0, 0, 0, 0.20); + border-bottom: 2px solid rgba(0, 0, 0, 0.20); } - - .message-date { - color: #555555; - } - - .message-ip { - color: #777777; - } - - .message-name { + .time { + font-size: 20px; + font-family: monospace; font-weight: bold; } - .message-name-black { - color: #2E2E2E; /* Темный черный */ - } - - .message-name-red { - color: #8B0000; /* Темный красный */ - } - - .message-name-green { - color: #006400; /* Темный зеленый */ - } - - .message-name-yellow { - color: #8B8B00; /* Темный желтый */ - } - - .message-name-blue { - color: #00008B; /* Темный синий */ - } - - .message-name-magenta { - color: #8B008B; /* Темный пурпурный */ - } - - .message-name-cyan { - color: #008B8B; /* Темный бирюзовый */ - } - - .message-name-white { - color: #A9A9A9; /* Темный белый */ - } - - .message-name-bright-black { - color: #555555; /* Яркий черный */ - } - - .message-name-bright-red { - color: #FF0000; /* Яркий красный */ - } - - .message-name-bright-green { - color: #00FF00; /* Яркий зеленый */ - } - - .message-name-bright-yellow { - color: #FFFF00; /* Яркий желтый */ - } - - .message-name-bright-blue { - color: #0000FF; /* Яркий синий */ - } - - .message-name-bright-magenta { - color: #FF00FF; /* Яркий пурпурный */ - } - - .message-name-bright-cyan { - color: #00FFFF; /* Яркий бирюзовый */ - } - - .message-name-bright-white { - color: #FFFFFF; /* Яркий белый */ - } + .message-content { color:rgb(255, 255, 255); } + .message-date { color:rgb(146, 146, 146); } + .message-ip { color:rgb(73, 73, 73); } + .message-name { font-weight: bold; } + .message-name-black { color: #2E2E2E; } + .message-name-bright-black { color: #555555; } + .message-name-red { color: #8B0000; } + .message-name-bright-red { color: #FF0000; } + .message-name-green { color: #006400; } + .message-name-bright-green { color: #00FF00; } + .message-name-yellow { color: #8B8B00; } + .message-name-bright-yellow { color: #FFFF00; } + .message-name-blue { color: #00008B; } + .message-name-bright-blue { color: #0000FF; } + .message-name-bright-magenta { color: #FF00FF; } + .message-name-magenta { color: #8B008B; } + .message-name-cyan { color: #008B8B; } + .message-name-bright-cyan { color: #00FFFF; } + .message-name-white { color: #A9A9A9; } + .message-name-bright-white { color: #FFFFFF; } "); gtk4::style_context_add_provider_for_display( @@ -382,6 +533,9 @@ fn on_add_message(ctx: Arc, ui: &UiModel, message: String) { .margin_end(10) .halign(Align::Start) .css_classes(["message-ip"]) + .selectable(true) + .wrap(true) + .wrap_mode(WrapMode::Char) .build(); hbox.append(&ip); @@ -392,6 +546,9 @@ fn on_add_message(ctx: Arc, ui: &UiModel, message: String) { .label(format!("[{date}]")) .halign(Align::Start) .css_classes(["message-date"]) + .selectable(true) + .wrap(true) + .wrap_mode(WrapMode::Char) .build(); hbox.append(&date); @@ -421,6 +578,9 @@ fn on_add_message(ctx: Arc, ui: &UiModel, message: String) { .label(format!("<{name}>")) .halign(Align::Start) .css_classes(["message-name", &format!("message-name-{}", color)]) + .selectable(true) + .wrap(true) + .wrap_mode(WrapMode::Char) .build(); hbox.append(&name); @@ -430,6 +590,9 @@ fn on_add_message(ctx: Arc, ui: &UiModel, message: String) { .label(content) .halign(Align::Start) .css_classes(["message-content"]) + .selectable(true) + .wrap(true) + .wrap_mode(WrapMode::Char) .build(); hbox.append(&content); @@ -438,6 +601,9 @@ fn on_add_message(ctx: Arc, ui: &UiModel, message: String) { .label(message) .halign(Align::Start) .css_classes(["message-content"]) + .selectable(true) + .wrap(true) + .wrap_mode(WrapMode::Char) .build(); hbox.append(&content); @@ -445,28 +611,40 @@ fn on_add_message(ctx: Arc, ui: &UiModel, message: String) { ui.chat_box.append(&hbox); - timeout_add_local_once(Duration::from_millis(100), move || { + timeout_add_local_once(Duration::from_millis(1000), move || { GLOBAL.with(|global| { if let Some((ui, _)) = &*global.borrow() { let o = &ui.chat_scrolled; o.vadjustment().set_value(o.vadjustment().upper() - o.vadjustment().page_size()); } }); + println!("12s3"); }); } pub fn run_main_loop(ctx: Arc) { let application = Application::builder() .application_id("ru.themixray.bRAC") + .flags(ApplicationFlags::empty()) .build(); application.connect_activate({ let ctx = ctx.clone(); move |app| { - build_ui(ctx.clone(), app); + let ui = build_ui(ctx.clone(), app); + setup(ctx.clone(), ui); + load_css(); } }); - application.run(); + application.connect_startup({ + let ctx = ctx.clone(); + + move |app| { + build_menu(ctx.clone(), app); + } + }); + + application.run_with_args::<&str>(&[]); } \ No newline at end of file