mirror of
https://github.com/danbulant/rshell
synced 2026-05-24 12:33:57 +00:00
workspace and memory support
This commit is contained in:
parent
715527b138
commit
ecf63c0e3b
9 changed files with 377 additions and 68 deletions
23
Cargo.lock
generated
23
Cargo.lock
generated
|
|
@ -1,6 +1,6 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ab_glyph"
|
name = "ab_glyph"
|
||||||
|
|
@ -724,6 +724,12 @@ version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytesize"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cacache"
|
name = "cacache"
|
||||||
version = "13.1.0"
|
version = "13.1.0"
|
||||||
|
|
@ -4323,6 +4329,7 @@ dependencies = [
|
||||||
"plotters",
|
"plotters",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
|
"systemstat",
|
||||||
"tokio",
|
"tokio",
|
||||||
"which",
|
"which",
|
||||||
]
|
]
|
||||||
|
|
@ -4905,6 +4912,20 @@ dependencies = [
|
||||||
"version-compare",
|
"version-compare",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "systemstat"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "668a4db78b439df482c238f559e4ea869017f9e62ef0a059c8bfcd841a4df544"
|
||||||
|
dependencies = [
|
||||||
|
"bytesize",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"nom",
|
||||||
|
"time",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.16"
|
version = "0.12.16"
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ pipewire = "0.8.0"
|
||||||
networkmanager = "0.4"
|
networkmanager = "0.4"
|
||||||
dbus = "0.9"
|
dbus = "0.9"
|
||||||
battery = "0.7.8"
|
battery = "0.7.8"
|
||||||
|
systemstat = "0.2.4"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
winit = { path = "../winit" }
|
winit = { path = "../winit" }
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,39 @@
|
||||||
use battery::State;
|
use battery::{Battery, State};
|
||||||
use cushy::{styles::components::TextColor, widget::MakeWidget};
|
use cushy::{
|
||||||
|
styles::components::TextColor,
|
||||||
|
value::{Destination, Dynamic},
|
||||||
|
widget::MakeWidget,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::theme::{TEXT_BATTERY, WIDGET_PADDING};
|
use crate::{
|
||||||
|
rt::tokio_runtime,
|
||||||
|
theme::{TEXT_BATTERY, WIDGET_PADDING},
|
||||||
|
};
|
||||||
|
|
||||||
const BATTERY_LOW: &str = "";
|
const BATTERY_LOW: &str = "";
|
||||||
|
|
||||||
const BATTERY_CHARGING: [&str; 11] = ["", "", "", "", "", "", "", "", "", "", ""];
|
const BATTERY_CHARGING: [&str; 11] = ["", "", "", "", "", "", "", "", "", "", ""];
|
||||||
const BATTERY_NORMAL: [&str; 11] = ["", "", "", "", "", "", "", "", "", "", ""];
|
const BATTERY_NORMAL: [&str; 11] = ["", "", "", "", "", "", "", "", "", "", ""];
|
||||||
|
|
||||||
|
const BATTERY_UNKNOWN: &str = "";
|
||||||
|
const BATTERY_FULL: &str = "";
|
||||||
|
|
||||||
|
fn format_battery(battery: &Battery) -> String {
|
||||||
|
let state = battery.state();
|
||||||
|
let charge = battery.state_of_charge();
|
||||||
|
|
||||||
|
let icon = match state {
|
||||||
|
State::Charging => BATTERY_CHARGING[(charge.value * 10.) as usize],
|
||||||
|
State::Discharging => BATTERY_NORMAL[(charge.value * 10.) as usize],
|
||||||
|
State::Empty => BATTERY_LOW,
|
||||||
|
State::Full => BATTERY_FULL,
|
||||||
|
State::Unknown | _ => BATTERY_UNKNOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
let percent = (charge.value * 100.) as u8;
|
||||||
|
format!(" {} {}% ", icon, percent)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn battery() -> impl MakeWidget {
|
pub fn battery() -> impl MakeWidget {
|
||||||
let manager = battery::Manager::new();
|
let manager = battery::Manager::new();
|
||||||
let Ok(manager) = manager else {
|
let Ok(manager) = manager else {
|
||||||
|
|
@ -36,22 +62,33 @@ pub fn battery() -> impl MakeWidget {
|
||||||
let Some(Ok(battery)) = batteries.next() else {
|
let Some(Ok(battery)) = batteries.next() else {
|
||||||
return "".make_widget();
|
return "".make_widget();
|
||||||
};
|
};
|
||||||
|
let info = Dynamic::new(format_battery(&battery));
|
||||||
|
|
||||||
let state = battery.state();
|
tokio_runtime().spawn({
|
||||||
let charge = battery.state_of_charge();
|
let info = info.clone();
|
||||||
|
async move {
|
||||||
|
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(1));
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
|
||||||
let icon = match state {
|
let Ok(manager) = battery::Manager::new() else {
|
||||||
State::Charging => BATTERY_CHARGING[(charge.value * 10.) as usize],
|
info.set(BATTERY_UNKNOWN.to_string());
|
||||||
State::Discharging => BATTERY_NORMAL[(charge.value * 10.) as usize],
|
return;
|
||||||
State::Empty => BATTERY_LOW,
|
};
|
||||||
State::Full => "",
|
let Ok(mut batteries) = manager.batteries() else {
|
||||||
State::Unknown | _ => "",
|
info.set(BATTERY_UNKNOWN.to_string());
|
||||||
};
|
return;
|
||||||
|
};
|
||||||
|
let Some(Ok(battery)) = batteries.next() else {
|
||||||
|
info.set(BATTERY_UNKNOWN.to_string());
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
info.set(format_battery(&battery));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let percent = (charge.value * 100.) as u8;
|
info.with(&TextColor, TEXT_BATTERY)
|
||||||
|
|
||||||
format!(" {} {}% ", icon, percent)
|
|
||||||
.with(&TextColor, TEXT_BATTERY)
|
|
||||||
.pad_by(WIDGET_PADDING)
|
.pad_by(WIDGET_PADDING)
|
||||||
.centered()
|
.centered()
|
||||||
.make_widget()
|
.make_widget()
|
||||||
|
|
|
||||||
155
src/bar/hypr.rs
Normal file
155
src/bar/hypr.rs
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use cushy::{
|
||||||
|
styles::{
|
||||||
|
components::{CornerRadius, TextColor, WidgetBackground},
|
||||||
|
Styles,
|
||||||
|
},
|
||||||
|
value::{Destination, Dynamic, Source},
|
||||||
|
widget::{MakeWidget, WidgetList},
|
||||||
|
widgets::Style,
|
||||||
|
};
|
||||||
|
use hyprland::{
|
||||||
|
data::{Clients, Devices, Monitors, Workspaces},
|
||||||
|
event_listener::AsyncEventListener,
|
||||||
|
shared::{HyprData, HyprDataVec, HyprError},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
rt::tokio_runtime,
|
||||||
|
theme::{
|
||||||
|
BG_WORKSPACE_ACTIVE, TEXT_TOOL, TEXT_WORKSPACE_ACTIVE, WIDGET_PADDING,
|
||||||
|
WORKSPACE_CORNER_RADIUS, WORKSPACE_PADDING,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::HyprlandState;
|
||||||
|
|
||||||
|
pub fn get_hyprland_state_sync() -> Result<HyprlandState, HyprError> {
|
||||||
|
let monitors = Monitors::get()?.to_vec();
|
||||||
|
let mut workspaces = Workspaces::get()?.to_vec();
|
||||||
|
workspaces.sort_by_key(|a| a.id);
|
||||||
|
let devices = Devices::get()?;
|
||||||
|
let client = Clients::get()?.to_vec();
|
||||||
|
Ok(HyprlandState {
|
||||||
|
monitors,
|
||||||
|
workspaces,
|
||||||
|
devices,
|
||||||
|
clients: client,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_hyprland_state() -> Result<HyprlandState, HyprError> {
|
||||||
|
let monitors = Monitors::get_async().await?.to_vec();
|
||||||
|
let mut workspaces = Workspaces::get_async().await?.to_vec();
|
||||||
|
workspaces.sort_by_key(|a| a.id);
|
||||||
|
let devices = Devices::get_async().await?;
|
||||||
|
let client = Clients::get_async().await?.to_vec();
|
||||||
|
Ok(HyprlandState {
|
||||||
|
monitors,
|
||||||
|
workspaces,
|
||||||
|
devices,
|
||||||
|
clients: client,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_callbacks(state: Dynamic<HyprlandState>) {
|
||||||
|
tokio_runtime().spawn({
|
||||||
|
let state = state.clone();
|
||||||
|
async move {
|
||||||
|
let mut event_listener = AsyncEventListener::new();
|
||||||
|
|
||||||
|
let update = Arc::new(move || {
|
||||||
|
let state = state.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
if let Ok(unwrap) = get_hyprland_state().await {
|
||||||
|
state.set(unwrap);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// I prefer winit-like event loop iterators/single callback with enum...
|
||||||
|
|
||||||
|
event_listener.add_workspace_added_handler({
|
||||||
|
let update = update.clone();
|
||||||
|
move |_| update()
|
||||||
|
});
|
||||||
|
event_listener.add_workspace_changed_handler({
|
||||||
|
let update = update.clone();
|
||||||
|
move |_| update()
|
||||||
|
});
|
||||||
|
event_listener.add_workspace_deleted_handler({
|
||||||
|
let update = update.clone();
|
||||||
|
move |_| update()
|
||||||
|
});
|
||||||
|
event_listener.add_window_opened_handler({
|
||||||
|
let update = update.clone();
|
||||||
|
move |_| update()
|
||||||
|
});
|
||||||
|
event_listener.add_window_closed_handler({
|
||||||
|
let update = update.clone();
|
||||||
|
move |_| update()
|
||||||
|
});
|
||||||
|
event_listener.add_window_title_changed_handler({
|
||||||
|
let update = update.clone();
|
||||||
|
move |_| update()
|
||||||
|
});
|
||||||
|
event_listener.add_urgent_state_changed_handler({
|
||||||
|
let update = update.clone();
|
||||||
|
move |_| update()
|
||||||
|
});
|
||||||
|
event_listener.add_screencast_handler({
|
||||||
|
let update = update.clone();
|
||||||
|
move |_| update()
|
||||||
|
});
|
||||||
|
event_listener.add_layout_changed_handler({
|
||||||
|
let update = update.clone();
|
||||||
|
move |_| update()
|
||||||
|
});
|
||||||
|
|
||||||
|
event_listener.start_listener_async().await.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hyprland_workspaces(state: Dynamic<HyprlandState>) -> impl MakeWidget {
|
||||||
|
state.map_each(|state| {
|
||||||
|
let monitor = state.monitors.first().unwrap();
|
||||||
|
let workspaces = &state.workspaces;
|
||||||
|
WidgetList::from_iter(workspaces.iter().map(|w| {
|
||||||
|
let active = monitor.active_workspace.id == w.id;
|
||||||
|
|
||||||
|
let name = w.name.clone();
|
||||||
|
|
||||||
|
if active {
|
||||||
|
name.pad_by(WORKSPACE_PADDING)
|
||||||
|
.centered()
|
||||||
|
.with(&WidgetBackground, BG_WORKSPACE_ACTIVE)
|
||||||
|
.with(&TextColor, TEXT_WORKSPACE_ACTIVE)
|
||||||
|
.with(&CornerRadius, WORKSPACE_CORNER_RADIUS)
|
||||||
|
.make_widget()
|
||||||
|
} else {
|
||||||
|
name.pad_by(WORKSPACE_PADDING).centered().make_widget()
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.into_columns()
|
||||||
|
.make_widget()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hyprland_active_title(state: Dynamic<HyprlandState>) -> impl MakeWidget {
|
||||||
|
state.map_each(|state| {
|
||||||
|
let monitor = state.monitors.first().unwrap();
|
||||||
|
let active_workspace = state
|
||||||
|
.workspaces
|
||||||
|
.iter()
|
||||||
|
.find(|w| w.id == monitor.active_workspace.id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
format!(" {}", active_workspace.last_window_title.clone())
|
||||||
|
.with(&TextColor, TEXT_TOOL)
|
||||||
|
.pad_by(WIDGET_PADDING)
|
||||||
|
.centered()
|
||||||
|
.make_widget()
|
||||||
|
})
|
||||||
|
}
|
||||||
45
src/bar/memory.rs
Normal file
45
src/bar/memory.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use cushy::{
|
||||||
|
styles::components::TextColor,
|
||||||
|
value::{Destination, Dynamic, Source},
|
||||||
|
widget::MakeWidget,
|
||||||
|
};
|
||||||
|
use systemstat::{saturating_sub_bytes, Platform, System};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
rt::tokio_runtime,
|
||||||
|
theme::{TEXT_MEM, WIDGET_PADDING},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn get_memory_usage() -> f64 {
|
||||||
|
let sys = System::new();
|
||||||
|
sys.memory()
|
||||||
|
.map(|mem| {
|
||||||
|
let used = saturating_sub_bytes(mem.total, mem.free);
|
||||||
|
let used_percentage = (used.as_u64() as f64 / mem.total.as_u64() as f64) * 100.;
|
||||||
|
used_percentage
|
||||||
|
})
|
||||||
|
.unwrap_or(0.)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_widget() -> impl MakeWidget {
|
||||||
|
let percentage = Dynamic::new(get_memory_usage());
|
||||||
|
tokio_runtime().spawn({
|
||||||
|
let current_time = percentage.clone();
|
||||||
|
async move {
|
||||||
|
let mut interval = tokio::time::interval(tokio::time::Duration::from_millis(1000));
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
current_time.set(get_memory_usage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
percentage
|
||||||
|
.map_each(|percentage| {
|
||||||
|
let icon = "".to_string();
|
||||||
|
format!(" {} {:.0}%", icon, percentage)
|
||||||
|
})
|
||||||
|
.with(&TextColor, TEXT_MEM)
|
||||||
|
.pad_by(WIDGET_PADDING)
|
||||||
|
.centered()
|
||||||
|
}
|
||||||
|
|
@ -2,19 +2,22 @@ use battery::battery;
|
||||||
use cushy::{
|
use cushy::{
|
||||||
figures::{
|
figures::{
|
||||||
units::{Lp, UPx},
|
units::{Lp, UPx},
|
||||||
Size,
|
Size, Zero,
|
||||||
},
|
},
|
||||||
kludgine::app::winit::{platform::wayland::Anchor, window::WindowLevel},
|
kludgine::app::winit::{error::EventLoopError, platform::wayland::Anchor, window::WindowLevel},
|
||||||
styles::{
|
styles::{
|
||||||
components::{
|
components::{
|
||||||
BaseLineHeight, BaseTextSize, CornerRadius, DefaultBackgroundColor, FontWeight,
|
BaseLineHeight, BaseTextSize, CornerRadius, DefaultBackgroundColor, FontWeight,
|
||||||
|
IntrinsicPadding,
|
||||||
},
|
},
|
||||||
FontFamilyList,
|
Dimension, FontFamilyList,
|
||||||
},
|
},
|
||||||
value::Dynamic,
|
value::Dynamic,
|
||||||
widget::MakeWidget,
|
widget::MakeWidget,
|
||||||
Application, Open,
|
Application, Open,
|
||||||
};
|
};
|
||||||
|
use hypr::{get_hyprland_state_sync, hyprland_active_title, hyprland_workspaces, init_callbacks};
|
||||||
|
use hyprland::data::{Client, Devices, Monitor, Workspace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
theme::{BG_DEFAULT, CORNER_RADIUS, DEFAULT_FONT_WEIGHT, TEXT_FONT, TEXT_SIZE},
|
theme::{BG_DEFAULT, CORNER_RADIUS, DEFAULT_FONT_WEIGHT, TEXT_FONT, TEXT_SIZE},
|
||||||
|
|
@ -22,38 +25,70 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod battery;
|
mod battery;
|
||||||
|
mod hypr;
|
||||||
|
mod memory;
|
||||||
mod spotify;
|
mod spotify;
|
||||||
mod time;
|
mod time;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct HyprlandState {
|
||||||
|
monitors: Vec<Monitor>,
|
||||||
|
workspaces: Vec<Workspace>,
|
||||||
|
devices: Devices,
|
||||||
|
clients: Vec<Client>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start_bar(app: &mut impl Application) -> cushy::Result {
|
pub fn start_bar(app: &mut impl Application) -> cushy::Result {
|
||||||
|
let state = Dynamic::new(get_hyprland_state_sync().map_err(|err| {
|
||||||
|
dbg!(err);
|
||||||
|
EventLoopError::ExitFailure(1)
|
||||||
|
})?);
|
||||||
|
|
||||||
|
init_callbacks(state.clone());
|
||||||
|
|
||||||
let monitors = (app.as_app().monitors()).unwrap();
|
let monitors = (app.as_app().monitors()).unwrap();
|
||||||
let mut monitor_size: Size<UPx> = monitors.available[0].size().into();
|
let mut monitor_size: Size<UPx> = monitors.available[0].size().into();
|
||||||
monitor_size.height = UPx::new(40);
|
monitor_size.height = UPx::new(40);
|
||||||
monitor_size.width = UPx::new((monitor_size.width.get() as f64 / 1.25) as _);
|
monitor_size.width = UPx::new((monitor_size.width.get() as f64 / 1.25) as _);
|
||||||
let size = Dynamic::new(monitor_size);
|
let size = Dynamic::new(monitor_size);
|
||||||
let mut window = ((time::time_widget().bar_pill())
|
|
||||||
|
let left_part = (hyprland_workspaces(state.clone())
|
||||||
|
.and(hyprland_active_title(state.clone()))
|
||||||
|
.into_columns()
|
||||||
|
.bar_pill()
|
||||||
|
.and(memory::memory_widget().bar_pill()))
|
||||||
|
.into_columns();
|
||||||
|
|
||||||
|
let middle_part = time::time_widget()
|
||||||
|
.bar_pill()
|
||||||
.and(spotify::spotify_controls().bar_pill())
|
.and(spotify::spotify_controls().bar_pill())
|
||||||
.into_columns()
|
.into_columns()
|
||||||
.centered()
|
.centered()
|
||||||
.expand_horizontally())
|
.expand_horizontally();
|
||||||
.and(battery().bar_pill())
|
|
||||||
.into_columns()
|
let right_part = battery().bar_pill();
|
||||||
.expand_horizontally()
|
|
||||||
.width(monitor_size.width)
|
let mut window = left_part
|
||||||
.height(Lp::points(30))
|
.and(middle_part)
|
||||||
.with(&BaseTextSize, TEXT_SIZE)
|
.and(right_part)
|
||||||
.with(&BaseLineHeight, TEXT_SIZE)
|
.into_columns()
|
||||||
.with(&DefaultBackgroundColor, BG_DEFAULT)
|
.expand_horizontally()
|
||||||
.with(&CornerRadius, CORNER_RADIUS)
|
.width(monitor_size.width)
|
||||||
.with(&FontWeight, DEFAULT_FONT_WEIGHT)
|
.height(Lp::points(30))
|
||||||
.into_window()
|
.with(&BaseTextSize, TEXT_SIZE)
|
||||||
.inner_size(size.clone())
|
.with(&BaseLineHeight, TEXT_SIZE)
|
||||||
.titled("rshell")
|
.with(&DefaultBackgroundColor, BG_DEFAULT)
|
||||||
.transparent()
|
.with(&CornerRadius, CORNER_RADIUS)
|
||||||
.app_name("rshell")
|
.with(&FontWeight, DEFAULT_FONT_WEIGHT)
|
||||||
.decorated(false)
|
.with(&IntrinsicPadding, Dimension::ZERO)
|
||||||
.resize_to_fit(false)
|
.into_window()
|
||||||
.window_level(WindowLevel::AlwaysOnTop);
|
.inner_size(size.clone())
|
||||||
|
.titled("rshell")
|
||||||
|
.transparent()
|
||||||
|
.app_name("rshell")
|
||||||
|
.decorated(false)
|
||||||
|
.resize_to_fit(false)
|
||||||
|
.window_level(WindowLevel::AlwaysOnTop);
|
||||||
|
|
||||||
let mut family = FontFamilyList::default();
|
let mut family = FontFamilyList::default();
|
||||||
for font in TEXT_FONT.iter() {
|
for font in TEXT_FONT.iter() {
|
||||||
|
|
|
||||||
|
|
@ -125,32 +125,33 @@ fn get_texture_dynamic(
|
||||||
let texture = texture.clone();
|
let texture = texture.clone();
|
||||||
let vibrancy = vibrancy.clone();
|
let vibrancy = vibrancy.clone();
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
let track_url = track.album_art.clone().unwrap();
|
if let Some(track_url) = track.album_art.clone() {
|
||||||
*prev_request_join = Some(tokio_runtime().spawn(async move {
|
*prev_request_join = Some(tokio_runtime().spawn(async move {
|
||||||
let response = client.get(track_url).send().await.unwrap();
|
let response = client.get(track_url).send().await.unwrap();
|
||||||
let bytes = response.bytes().await.unwrap();
|
let bytes = response.bytes().await.unwrap();
|
||||||
let image = image::load_from_memory(&bytes).unwrap();
|
let image = image::load_from_memory(&bytes).unwrap();
|
||||||
let image = image.resize(128, 128, FilterType::Lanczos3);
|
let image = image.resize(128, 128, FilterType::Lanczos3);
|
||||||
let image_vibrancy = Vibrancy::new(&image);
|
let image_vibrancy = Vibrancy::new(&image);
|
||||||
vibrancy.set(ImageVibrancy {
|
vibrancy.set(ImageVibrancy {
|
||||||
primary: image_vibrancy.primary.map(|c| rgb_to_color(c)),
|
primary: image_vibrancy.primary.map(|c| rgb_to_color(c)),
|
||||||
dark: image_vibrancy.dark.map(|c| rgb_to_color(c)),
|
dark: image_vibrancy.dark.map(|c| rgb_to_color(c)),
|
||||||
light: image_vibrancy.light.map(|c| rgb_to_color(c)),
|
light: image_vibrancy.light.map(|c| rgb_to_color(c)),
|
||||||
muted: image_vibrancy.muted.map(|c| rgb_to_color(c)),
|
muted: image_vibrancy.muted.map(|c| rgb_to_color(c)),
|
||||||
dark_muted: image_vibrancy.dark_muted.map(|c| rgb_to_color(c)),
|
dark_muted: image_vibrancy.dark_muted.map(|c| rgb_to_color(c)),
|
||||||
light_muted: image_vibrancy.light_muted.map(|c| rgb_to_color(c)),
|
light_muted: image_vibrancy.light_muted.map(|c| rgb_to_color(c)),
|
||||||
});
|
});
|
||||||
let image_texture = LazyTexture::from_image(
|
let image_texture = LazyTexture::from_image(
|
||||||
image,
|
image,
|
||||||
cushy::kludgine::wgpu::FilterMode::Linear,
|
cushy::kludgine::wgpu::FilterMode::Linear,
|
||||||
);
|
);
|
||||||
let image_texture = AnyTexture::Lazy(image_texture);
|
let image_texture = AnyTexture::Lazy(image_texture);
|
||||||
texture.set(image_texture);
|
texture.set(image_texture);
|
||||||
}));
|
}));
|
||||||
} else {
|
return;
|
||||||
vibrancy.set(ImageVibrancy::default());
|
}
|
||||||
texture.set(get_empty_texture());
|
|
||||||
}
|
}
|
||||||
|
vibrancy.set(ImageVibrancy::default());
|
||||||
|
texture.set(get_empty_texture());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.persist();
|
.persist();
|
||||||
|
|
|
||||||
16
src/theme.rs
16
src/theme.rs
|
|
@ -12,19 +12,33 @@ pub const WIDGET_PADDING: Edges<Dimension> = Edges {
|
||||||
bottom: Dimension::Px(Px::new(4)),
|
bottom: Dimension::Px(Px::new(4)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const WORKSPACE_PADDING: Edges<Dimension> = Edges {
|
||||||
|
left: Dimension::Px(Px::new(15)),
|
||||||
|
right: Dimension::Px(Px::new(15)),
|
||||||
|
top: Dimension::Px(Px::new(4)),
|
||||||
|
bottom: Dimension::Px(Px::new(4)),
|
||||||
|
};
|
||||||
|
pub const WORKSPACE_CORNER_RADIUS: Dimension = Dimension::Px(Px::new(12));
|
||||||
|
|
||||||
pub const DEFAULT_FONT_WEIGHT: Weight = Weight::MEDIUM;
|
pub const DEFAULT_FONT_WEIGHT: Weight = Weight::MEDIUM;
|
||||||
|
|
||||||
pub const BG_DEFAULT: Color = Color(0x191724FF);
|
pub const BG_DEFAULT: Color = Color(0x191724FF);
|
||||||
pub const TEXT_SPOTIFY: Color = Color(0x1DB954FF);
|
pub const TEXT_SPOTIFY: Color = Color(0x1DB954FF);
|
||||||
pub const TEXT_CLOCK: Color = Color(0xF6C177FF);
|
pub const TEXT_CLOCK: Color = Color(0xF6C177FF);
|
||||||
pub const TEXT_CPU: Color = Color(0xff671fFF);
|
pub const TEXT_CPU: Color = Color(0xff671fFF);
|
||||||
pub const TEXT_MEM: Color = Color(0x1DB954FF);
|
pub const TEXT_MEM: Color = Color(0xFFFFFFFF);
|
||||||
pub const TEXT_TEMP: Color = Color(0x97f993FF);
|
pub const TEXT_TEMP: Color = Color(0x97f993FF);
|
||||||
|
pub const TEXT_RED: Color = Color(0xD81E5BFF);
|
||||||
|
|
||||||
|
pub const TEXT_TOOL: Color = Color(0x4e9dc2ff);
|
||||||
|
|
||||||
pub const TEXT_AUDIO: Color = Color(0xF7E733FF);
|
pub const TEXT_AUDIO: Color = Color(0xF7E733FF);
|
||||||
pub const TEXT_AUDIO_MUTED: Color = Color(0xD81E5BFF);
|
pub const TEXT_AUDIO_MUTED: Color = Color(0xD81E5BFF);
|
||||||
pub const TEXT_BATTERY: Color = Color(0x6bb0d9FF);
|
pub const TEXT_BATTERY: Color = Color(0x6bb0d9FF);
|
||||||
|
|
||||||
|
pub const BG_WORKSPACE_ACTIVE: Color = Color(0x753a88ff);
|
||||||
|
pub const TEXT_WORKSPACE_ACTIVE: Color = Color(0xebbcbaff);
|
||||||
|
|
||||||
pub const TEXT_FONT: [&str; 5] = [
|
pub const TEXT_FONT: [&str; 5] = [
|
||||||
"Inter",
|
"Inter",
|
||||||
"Iosevka",
|
"Iosevka",
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use cushy::{styles::components::WidgetBackground, widget::MakeWidget, widgets::Container};
|
use cushy::{styles::components::WidgetBackground, widget::MakeWidget, widgets::Container};
|
||||||
|
|
||||||
use crate::theme::BG_DEFAULT;
|
use crate::theme::{BG_DEFAULT, WIDGET_PADDING};
|
||||||
|
|
||||||
pub trait WidgetExt: MakeWidget {
|
pub trait WidgetExt: MakeWidget {
|
||||||
fn bar_pill(self) -> Container {
|
fn bar_pill(self) -> Container {
|
||||||
self.expand_vertically()
|
self.expand_vertically()
|
||||||
.with(&WidgetBackground, BG_DEFAULT)
|
.with(&WidgetBackground, BG_DEFAULT)
|
||||||
.pad()
|
.pad_by(WIDGET_PADDING)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue