Add scss colors into rust via build-script, flesh out math evaluation in launcher, Restructure some code
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1598,6 +1598,7 @@ dependencies = [
|
||||
"nmrs",
|
||||
"notify",
|
||||
"nucleo-matcher",
|
||||
"regex",
|
||||
"rustsni",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -9,7 +9,7 @@ path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "jmbctl"
|
||||
path = "src/ctl.rs"
|
||||
path = "src/bin/ctl.rs"
|
||||
|
||||
[dependencies]
|
||||
grass = "0.13.4"
|
||||
@@ -49,3 +49,5 @@ toml = "1.1.8"
|
||||
|
||||
[build-dependencies]
|
||||
glib-build-tools = "0.22.8"
|
||||
grass = "0.13.4"
|
||||
regex = "1.13.1"
|
||||
|
||||
28
build.rs
28
build.rs
@@ -1,7 +1,35 @@
|
||||
use regex::Regex;
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs};
|
||||
|
||||
fn main() {
|
||||
glib_build_tools::compile_resources(
|
||||
&["resources"],
|
||||
"resources/resources.gresource.xml",
|
||||
"templates.gresource",
|
||||
);
|
||||
|
||||
let colors_path = "style/_colors.scss";
|
||||
|
||||
println!("cargo:rerun-if-changed={colors_path}");
|
||||
|
||||
let scss = fs::read_to_string(colors_path).expect("Failed to read SCSS colors");
|
||||
|
||||
let regex =
|
||||
Regex::new(r"(?m)^\s*\$([a-zA-Z_][a-zA-Z0-9_-]*)\s*:\s*(#[0-9a-fA-F]{3,8})\s*;").unwrap();
|
||||
|
||||
let mut generated = String::from("// Generated by build.rs. Do not edit\n\n");
|
||||
|
||||
for captures in regex.captures_iter(&scss) {
|
||||
let name = captures.get(1).unwrap().as_str();
|
||||
let value = captures.get(2).unwrap().as_str();
|
||||
|
||||
let constant_name = name.replace('-', "_").to_uppercase();
|
||||
|
||||
generated.push_str(&format!("pub const {constant_name}: &str = \"{value}\";\n"));
|
||||
}
|
||||
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
|
||||
fs::write(out_dir.join("theme.rs"), generated).expect("Failed to generate theme.rs");
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ use crate::backend::json_socket::{JsonSocketBackend, State};
|
||||
use crate::backend::network::{NetworkBackendHandle, NetworkCommand, NetworkState};
|
||||
use crate::backend::perf_stats::{Stats, StatsBackend};
|
||||
use crate::backend::window_manager_hypr::{HyprlandComms, WindowManagerEvent};
|
||||
use crate::config::Config;
|
||||
use crate::ctl::ControlCommand;
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use jmb_bar::ControlCommand;
|
||||
use jmb_bar::config::Config;
|
||||
use jmb_logger_rs::*;
|
||||
use std::fmt::Debug;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
@@ -86,6 +86,7 @@ pub struct Backend {
|
||||
control_registrator: Registrator<ControlCommand>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum ChannelType {
|
||||
Net,
|
||||
Hypr,
|
||||
@@ -174,8 +175,8 @@ impl Backend {
|
||||
channels
|
||||
}
|
||||
|
||||
pub fn subscribe_all(&self) -> BackendChannels {
|
||||
self.subscribe(&[
|
||||
pub fn subscribe_except(&self, channel_types: &[ChannelType]) -> BackendChannels {
|
||||
let mut types = vec![
|
||||
ChannelType::Net,
|
||||
ChannelType::Idle,
|
||||
ChannelType::Netbird,
|
||||
@@ -185,7 +186,9 @@ impl Backend {
|
||||
ChannelType::Audio,
|
||||
ChannelType::Media,
|
||||
ChannelType::Hypr,
|
||||
])
|
||||
];
|
||||
types.retain(|i| !channel_types.contains(i));
|
||||
self.subscribe(&types)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -516,30 +516,32 @@ impl BackendThread<MediaMessage, Sender<MediaMessage>> for MediaBackend {
|
||||
return;
|
||||
};
|
||||
while let Ok(msg) = rx.recv() {
|
||||
let mut instance_guard = instance.lock().unwrap();
|
||||
if let Some(name) = &instance_guard.active_player
|
||||
&& let Some(status) = instance_guard.players.get(name)
|
||||
{
|
||||
match msg {
|
||||
MediaMessage::PlayerPlay => {
|
||||
if let PlayerStatus::Paused(_) = status {
|
||||
instance_guard.active_player_play(&connection).await;
|
||||
}
|
||||
let Some(name) = &instance.lock().unwrap().active_player.clone() else {
|
||||
continue;
|
||||
};
|
||||
let Some(status) = instance.lock().unwrap().players.get(name).cloned()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
match msg {
|
||||
MediaMessage::PlayerPlay => {
|
||||
if let PlayerStatus::Paused(_) = status {
|
||||
MediaBackend::player_play(name, &connection).await;
|
||||
}
|
||||
MediaMessage::PlayerPause => {
|
||||
if let PlayerStatus::Playing(_) = status {
|
||||
instance_guard.active_player_pause(&connection).await;
|
||||
}
|
||||
}
|
||||
MediaMessage::PlayerStop => {
|
||||
if let PlayerStatus::Playing(_) | PlayerStatus::Paused(_) =
|
||||
status
|
||||
{
|
||||
instance_guard.active_player_stop(&connection).await;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
MediaMessage::PlayerPause => {
|
||||
if let PlayerStatus::Playing(_) = status {
|
||||
MediaBackend::player_pause(name, &connection).await;
|
||||
instance.lock().unwrap().update_active_player();
|
||||
}
|
||||
}
|
||||
MediaMessage::PlayerStop => {
|
||||
if let PlayerStatus::Playing(_) | PlayerStatus::Paused(_) = status {
|
||||
MediaBackend::player_stop(name, &connection).await;
|
||||
instance.lock().unwrap().select_active_player(None);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -756,54 +758,35 @@ impl MediaBackend {
|
||||
}
|
||||
}
|
||||
|
||||
async fn active_player_play(&self, connection: &zbus::Connection) {
|
||||
if let Some(player_name) = &self.active_player
|
||||
&& let Ok(proxy) = zbus::Proxy::new(
|
||||
connection,
|
||||
player_name.as_str(),
|
||||
MPRIS_PATH,
|
||||
MPRIS_PLAYER_INTERFACE,
|
||||
)
|
||||
.await
|
||||
async fn player_play(name: &str, connection: &zbus::Connection) {
|
||||
if let Ok(proxy) =
|
||||
zbus::Proxy::new(connection, name, MPRIS_PATH, MPRIS_PLAYER_INTERFACE).await
|
||||
&& let Err(e) = proxy.call_method("Play", &()).await
|
||||
{
|
||||
warn!(
|
||||
&LOGGER,
|
||||
"Error starting play for player: {player_name}, {e}"
|
||||
);
|
||||
warn!(&LOGGER, "Error starting play for player: {name}, {e}");
|
||||
}
|
||||
}
|
||||
|
||||
async fn active_player_pause(&mut self, connection: &zbus::Connection) {
|
||||
if let Some(player_name) = &self.active_player
|
||||
&& let Ok(proxy) = zbus::Proxy::new(
|
||||
connection,
|
||||
player_name.as_str(),
|
||||
MPRIS_PATH,
|
||||
MPRIS_PLAYER_INTERFACE,
|
||||
)
|
||||
.await
|
||||
async fn player_pause(name: &str, connection: &zbus::Connection) {
|
||||
if let Ok(proxy) =
|
||||
zbus::Proxy::new(connection, name, MPRIS_PATH, MPRIS_PLAYER_INTERFACE).await
|
||||
&& let Err(e) = proxy.call_method("Pause", &()).await
|
||||
{
|
||||
warn!(&LOGGER, "Error pausing player: {player_name}, {e}");
|
||||
self.select_active_player(self.active_player.clone());
|
||||
warn!(&LOGGER, "Error pausing player: {name}, {e}");
|
||||
}
|
||||
}
|
||||
|
||||
async fn active_player_stop(&mut self, connection: &zbus::Connection) {
|
||||
if let Some(player_name) = &self.active_player
|
||||
&& let Ok(proxy) = zbus::Proxy::new(
|
||||
connection,
|
||||
player_name.as_str(),
|
||||
MPRIS_PATH,
|
||||
MPRIS_PLAYER_INTERFACE,
|
||||
)
|
||||
.await
|
||||
async fn player_stop(name: &str, connection: &zbus::Connection) {
|
||||
if let Ok(proxy) =
|
||||
zbus::Proxy::new(connection, name, MPRIS_PATH, MPRIS_PLAYER_INTERFACE).await
|
||||
&& let Err(e) = proxy.call_method("Stop", &()).await
|
||||
{
|
||||
warn!(&LOGGER, "Error stopping player: {player_name}, {e}");
|
||||
warn!(&LOGGER, "Error stopping player: {name}, {e}");
|
||||
}
|
||||
self.select_active_player(None);
|
||||
}
|
||||
|
||||
fn update_active_player(&mut self) {
|
||||
self.select_active_player(self.active_player.clone());
|
||||
}
|
||||
|
||||
fn select_active_player(&mut self, changed_player: Option<String>) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::LOGGER;
|
||||
use crate::backend::{BackendThread, SimpleBackendThread};
|
||||
use crate::ctl::{ControlCommand, SOCKET_PATH};
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use jmb_bar::{ControlCommand, SOCKET_PATH};
|
||||
use jmb_logger_rs::*;
|
||||
use std::io::Read;
|
||||
use std::net::Shutdown;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::backend::{BackendThread, Registrator};
|
||||
use crate::config::IdleConfig;
|
||||
use crate::{APP_NAME, LOGGER};
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use jmb_bar::config::IdleConfig;
|
||||
use jmb_logger_rs::*;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
|
||||
@@ -1,36 +1,11 @@
|
||||
use jmb_bar::{ControlCommand, SOCKET_PATH};
|
||||
use jmb_logger_rs::*;
|
||||
use std::env;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::io::Write;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::str::FromStr;
|
||||
|
||||
const CTL_LOGGER: Logger = Logger::create_with_custom_format(Level::DEBUG, String::new());
|
||||
pub const SOCKET_PATH: &str = "/run/user/1000/jmb-bar.sock";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ControlCommand {
|
||||
OpenRunMenu,
|
||||
}
|
||||
|
||||
impl Display for ControlCommand {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ControlCommand::OpenRunMenu => f.write_str("menu"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ControlCommand {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"menu" => Ok(ControlCommand::OpenRunMenu),
|
||||
other => Err(format!("Unknown command: {other}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
info!(&CTL_LOGGER, "Hello, this is mission control");
|
||||
@@ -3,13 +3,13 @@ use std::default::Default;
|
||||
use std::fs;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Default)]
|
||||
pub(crate) struct Config {
|
||||
pub struct Config {
|
||||
pub idle: IdleConfig,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load() -> Config {
|
||||
fs::read_to_string("jmb-bar.conf")
|
||||
fs::read_to_string("../jmb-bar.conf")
|
||||
.ok()
|
||||
.and_then(|s| toml::from_str::<Config>(&s).ok())
|
||||
.unwrap_or_default()
|
||||
@@ -17,7 +17,7 @@ impl Config {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub(crate) struct IdleConfig {
|
||||
pub struct IdleConfig {
|
||||
idle_timeout: u64,
|
||||
dim_timeout: u64,
|
||||
lock_timeout: u64,
|
||||
|
||||
31
src/lib.rs
Normal file
31
src/lib.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub mod config;
|
||||
pub mod theme;
|
||||
|
||||
pub const SOCKET_PATH: &str = "/run/user/1000/jmb-bar.sock";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ControlCommand {
|
||||
OpenRunMenu,
|
||||
}
|
||||
|
||||
impl Display for ControlCommand {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ControlCommand::OpenRunMenu => f.write_str("menu"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ControlCommand {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"menu" => Ok(ControlCommand::OpenRunMenu),
|
||||
other => Err(format!("Unknown command: {other}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
use crate::backend::Backend;
|
||||
mod backend;
|
||||
mod config;
|
||||
mod css;
|
||||
mod ctl;
|
||||
mod ui;
|
||||
|
||||
use crate::config::Config;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Application, CssProvider, gdk, gio};
|
||||
use jmb_bar::config::Config;
|
||||
use jmb_logger_rs::*;
|
||||
use notify::RecommendedWatcher;
|
||||
use std::cell::RefCell;
|
||||
|
||||
1
src/theme.rs
Normal file
1
src/theme.rs
Normal file
@@ -0,0 +1 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/theme.rs"));
|
||||
@@ -2,7 +2,6 @@ use crate::LOGGER;
|
||||
use crate::backend::audio::MediaMessage;
|
||||
use crate::backend::window_manager_hypr::{HyprlandComms, WindowManagerEvent};
|
||||
use crate::backend::{Backend, BackendChannels, ChannelType};
|
||||
use crate::ctl::ControlCommand;
|
||||
use crate::ui::action_centre::ActionCentre;
|
||||
use crate::ui::run_menu::RunMenu;
|
||||
use glib::ControlFlow;
|
||||
@@ -11,6 +10,7 @@ use gtk::gdk::Display;
|
||||
use gtk::prelude::{BoxExt, ButtonExt, DisplayExt, GtkWindowExt, ListModelExt, WidgetExt};
|
||||
use gtk::{Align, Application, ApplicationWindow, Button, Orientation, gdk};
|
||||
use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell};
|
||||
use jmb_bar::ControlCommand;
|
||||
use jmb_logger_rs::*;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
@@ -29,11 +29,11 @@ pub fn create_bars(app: &Application, backend: Backend) {
|
||||
let display = Display::default().expect("Couldn't find GDK Display");
|
||||
let app_clone = app.clone();
|
||||
let bars: Rc<Mutex<HashMap<u32, ApplicationWindow>>> = Rc::new(Mutex::new(HashMap::new()));
|
||||
let ac_channels = backend.subscribe_all();
|
||||
let ac_channels = backend.subscribe(&[ChannelType::Net]);
|
||||
let action_centre = ActionCentre::create_action_centre(app.clone(), ac_channels);
|
||||
for monitor in 0..display.monitors().n_items() {
|
||||
debug!(&LOGGER, "Opening bar {monitor}");
|
||||
let channels = backend.subscribe_all();
|
||||
let channels = backend.subscribe_except(&[ChannelType::Control]);
|
||||
bars.lock().expect("Couldn't get active bars").insert(
|
||||
monitor,
|
||||
create_and_show_bar(
|
||||
@@ -58,7 +58,7 @@ pub fn create_bars(app: &Application, backend: Backend) {
|
||||
debug!(&LOGGER, "Detected new monitor: {pos}");
|
||||
let app_clone = app_clone.clone();
|
||||
let action_centre = action_centre.clone();
|
||||
let channels = backend_clone.subscribe_all();
|
||||
let channels = backend_clone.subscribe_except(&[ChannelType::Control]);
|
||||
let bars = bars.clone();
|
||||
glib::idle_add_local_once(move || {
|
||||
let display = Display::default().expect("Couldn't find GDK Display");
|
||||
|
||||
@@ -10,6 +10,7 @@ use gtk::prelude::{
|
||||
};
|
||||
use gtk::{Align, Application, ApplicationWindow, Orientation, PolicyType, gdk};
|
||||
use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell};
|
||||
use jmb_bar::theme;
|
||||
use jmb_logger_rs::*;
|
||||
use nucleo_matcher::pattern::{CaseMatching, Normalization, Pattern};
|
||||
use nucleo_matcher::{Config, Matcher};
|
||||
@@ -92,20 +93,42 @@ impl RunMenu {
|
||||
entry.connect_changed(clone!(
|
||||
#[strong]
|
||||
content_box,
|
||||
move |e| {
|
||||
move |entry| {
|
||||
while let Some(child) = content_box.first_child() {
|
||||
content_box.remove(&child);
|
||||
}
|
||||
if e.text_length() > 0 {
|
||||
let text = e.text();
|
||||
if entry.text_length() > 0 {
|
||||
let text = entry.text();
|
||||
if let Some(eval) = text.strip_prefix("=") {
|
||||
let label = gtk::Label::new(Some(eval));
|
||||
label.add_css_class("exec-button");
|
||||
label.set_halign(Align::Start);
|
||||
label.set_hexpand(true);
|
||||
content_box.append(&label);
|
||||
let new_eval = eval.replace("**", "^");
|
||||
if new_eval != eval {
|
||||
let cursor = entry.position() as usize;
|
||||
let removed_before_cursor = text[..cursor].matches("**").count();
|
||||
let new_cursor = cursor - removed_before_cursor;
|
||||
|
||||
glib::idle_add_local_once(clone!(
|
||||
#[weak]
|
||||
entry,
|
||||
move || {
|
||||
entry.set_text(&format!("={new_eval}"));
|
||||
entry.set_position(new_cursor as i32);
|
||||
}
|
||||
));
|
||||
}
|
||||
if let Ok(result) = meval::eval_str(eval) {
|
||||
let label = gtk::Label::new(Some(&format!("{}", result)));
|
||||
label.add_css_class("exec-button");
|
||||
label.set_halign(Align::Start);
|
||||
label.set_hexpand(true);
|
||||
content_box.append(&label);
|
||||
instance_clone.window.set_default_size(500, -1);
|
||||
}
|
||||
} else {
|
||||
instance_clone.list_matching(e, &content_box, &mut matcher_rc.borrow_mut());
|
||||
instance_clone.list_matching(
|
||||
entry,
|
||||
&content_box,
|
||||
&mut matcher_rc.borrow_mut(),
|
||||
);
|
||||
instance_clone.window.set_default_size(500, -1);
|
||||
}
|
||||
}
|
||||
@@ -128,10 +151,17 @@ impl RunMenu {
|
||||
#[strong]
|
||||
content_box,
|
||||
move |_| {
|
||||
if let Some(child) = content_box.first_child()
|
||||
&& let Ok(button) = child.downcast::<gtk::Button>()
|
||||
{
|
||||
button.activate();
|
||||
if let Some(child) = content_box.first_child() {
|
||||
if let Ok(button) = child.clone().downcast::<gtk::Button>() {
|
||||
button.activate();
|
||||
} else if let Ok(label) = child.downcast::<gtk::Label>() {
|
||||
display.clipboard().set_text(&label.text());
|
||||
label.set_markup(&format!(
|
||||
"{}<span foreground=\"{}\"><i> - Copied</i></span>",
|
||||
label.text(),
|
||||
theme::LIGHTGRAY
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Changing colors here will not apply to the constants in theme.rs until the app is recompiled
|
||||
$background: #232320;
|
||||
$background1: #3c3836;
|
||||
|
||||
Reference in New Issue
Block a user