refactoring of render and grouping, widget spacing WIP

This commit is contained in:
Csaba
2024-11-13 22:08:58 +01:00
parent c18e5f4dbe
commit cba2b2f7ac
14 changed files with 168 additions and 166 deletions

View File

@@ -1,16 +1,16 @@
use crate::config::Color32Ext;
use crate::config::KomobarConfig;
use crate::config::KomobarTheme;
use crate::config::Position;
use crate::config::PositionConfig;
use crate::group::Grouping;
use crate::komorebi::Komorebi;
use crate::komorebi::KomorebiNotificationState;
use crate::process_hwnd;
use crate::render::Color32Ext;
use crate::render::Grouping;
use crate::render::RenderConfig;
use crate::render::RenderExt;
use crate::widget::BarWidget;
use crate::widget::RenderConfig;
use crate::widget::WidgetConfig;
use crate::BACKGROUND_COLOR;
use crate::BAR_HEIGHT;
use crate::MAX_LABEL_WIDTH;
use crate::MONITOR_LEFT;
@@ -260,11 +260,10 @@ impl Komobar {
}
}
self.render_config.replace(config.into());
let theme_color = *self.bg_color.borrow();
BACKGROUND_COLOR.store(theme_color.to_u32(), Ordering::SeqCst);
self.render_config
.replace(config.new_renderconfig(theme_color));
self.bg_color
.replace(theme_color.try_apply_alpha(self.config.transparency_alpha));
@@ -351,7 +350,9 @@ impl Komobar {
render_config: Rc::new(RefCell::new(RenderConfig {
spacing: 0.0,
grouping: Grouping::None,
background_color: Color32::BLACK,
alignment: None,
no_spacing: None,
})),
komorebi_notification_state: None,
left_widgets: vec![],
@@ -472,16 +473,21 @@ impl eframe::App for Komobar {
CentralPanel::default().frame(frame).show(ctx, |ui| {
// Apply grouping logic for the bar as a whole
render_config.grouping.clone().apply_on_bar(ui, |ui| {
render_config.clone().apply_on_bar(ui, |ui| {
ui.horizontal_centered(|ui| {
// Left-aligned widgets layout
ui.with_layout(Layout::left_to_right(Align::Center), |ui| {
let mut render_conf = *render_config;
render_conf.alignment = Some(Alignment::Left);
render_config.grouping.apply_on_alignment(ui, |ui| {
for w in &mut self.left_widgets {
w.render(ctx, ui, render_conf);
render_config.apply_on_alignment(ui, |ui| {
if let Some((last, rest)) = self.left_widgets.split_last_mut() {
for w in rest.iter_mut() {
w.render(ctx, ui, render_conf);
}
render_conf.no_spacing = Some(true);
last.render(ctx, ui, render_conf);
}
});
});
@@ -491,9 +497,14 @@ impl eframe::App for Komobar {
let mut render_conf = *render_config;
render_conf.alignment = Some(Alignment::Right);
render_config.grouping.apply_on_alignment(ui, |ui| {
for w in &mut self.right_widgets {
w.render(ctx, ui, render_conf);
render_config.apply_on_alignment(ui, |ui| {
if let Some((last, rest)) = self.right_widgets.split_last_mut() {
for w in rest.iter_mut() {
w.render(ctx, ui, render_conf);
}
render_conf.no_spacing = Some(true);
last.render(ctx, ui, render_conf);
}
});
})

View File

@@ -1,6 +1,6 @@
use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::widget::BarWidget;
use crate::widget::RenderConfig;
use eframe::egui::text::LayoutJob;
use eframe::egui::Context;
use eframe::egui::FontId;
@@ -147,7 +147,7 @@ impl BarWidget for Battery {
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
);
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
ui.add(
Label::new(layout_job)
.selectable(false)

View File

@@ -1,7 +1,5 @@
use crate::group::Grouping;
use crate::widget::RenderConfig;
use crate::render::Grouping;
use crate::widget::WidgetConfig;
use eframe::egui::Color32;
use eframe::egui::Pos2;
use eframe::egui::TextBuffer;
use eframe::egui::Vec2;
@@ -33,7 +31,7 @@ pub struct KomobarConfig {
pub theme: Option<KomobarTheme>,
/// Alpha value for the color transparency [[0-255]] (default: 200)
pub transparency_alpha: Option<u8>,
/// Spacing between widgets
/// Spacing between widgets (default: 10.0)
pub widget_spacing: Option<f32>,
/// Visual grouping for widgets
pub grouping: Option<Grouping>,
@@ -72,16 +70,6 @@ impl KomobarConfig {
}
}
impl From<&KomobarConfig> for RenderConfig {
fn from(value: &KomobarConfig) -> Self {
RenderConfig {
spacing: value.widget_spacing.unwrap_or(10.0),
grouping: value.grouping.unwrap_or(Grouping::None),
alignment: None,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct PositionConfig {
/// The desired starting position of the bar (0,0 = top left of the screen)
@@ -197,37 +185,3 @@ pub enum LabelPrefix {
/// Show an icon and text
IconAndText,
}
pub trait Color32Ext {
fn to_u32(&self) -> u32;
fn from_u32(color: u32) -> Self;
fn try_apply_alpha(self, transparency_alpha: Option<u8>) -> Self;
}
impl Color32Ext for Color32 {
/// Converts Color32 to u32 (ARGB format)
fn to_u32(&self) -> u32 {
((self.a() as u32) << 24)
| ((self.r() as u32) << 16)
| ((self.g() as u32) << 8)
| (self.b() as u32)
}
/// Converts u32 back to Color32 (ARGB format)
fn from_u32(color: u32) -> Self {
let a = ((color >> 24) & 0xFF) as u8;
let r = ((color >> 16) & 0xFF) as u8;
let g = ((color >> 8) & 0xFF) as u8;
let b = (color & 0xFF) as u8;
Color32::from_rgba_premultiplied(r, g, b, a)
}
/// Tries to apply the alpha value to the Color32
fn try_apply_alpha(self, transparency_alpha: Option<u8>) -> Self {
if let Some(alpha) = transparency_alpha {
return Color32::from_rgba_unmultiplied(self.r(), self.g(), self.b(), alpha);
}
self
}
}

View File

@@ -1,6 +1,6 @@
use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::widget::BarWidget;
use crate::widget::RenderConfig;
use eframe::egui::text::LayoutJob;
use eframe::egui::Context;
use eframe::egui::FontId;
@@ -99,7 +99,7 @@ impl BarWidget for Cpu {
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
);
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
if ui
.add(
Label::new(layout_job)

View File

@@ -1,6 +1,6 @@
use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::widget::BarWidget;
use crate::widget::RenderConfig;
use eframe::egui::text::LayoutJob;
use eframe::egui::Context;
use eframe::egui::FontId;
@@ -119,7 +119,7 @@ impl BarWidget for Date {
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
);
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
if ui
.add(
Label::new(WidgetText::LayoutJob(layout_job.clone()))

View File

@@ -1,8 +1,8 @@
use crate::bar::apply_theme;
use crate::config::KomobarTheme;
use crate::render::RenderConfig;
use crate::ui::CustomUi;
use crate::widget::BarWidget;
use crate::widget::RenderConfig;
use crate::MAX_LABEL_WIDTH;
use crossbeam_channel::Receiver;
use crossbeam_channel::TryRecvError;
@@ -129,7 +129,7 @@ impl BarWidget for Komorebi {
let mut update = None;
// NOTE: There should always be at least one workspace.
config.grouping.apply_on_widget(false, config, ui, |ui| {
config.apply_on_widget(false, ui, |ui| {
for (i, (ws, should_show)) in
komorebi_notification_state.workspaces.iter().enumerate()
{
@@ -196,7 +196,7 @@ impl BarWidget for Komorebi {
if let Some(layout) = self.layout {
if layout.enable {
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
if ui
.add(
Label::new(komorebi_notification_state.layout.to_string())
@@ -247,7 +247,7 @@ impl BarWidget for Komorebi {
for (name, location) in configuration_switcher.configurations.iter() {
let path = PathBuf::from(location);
if path.is_file() {
config.grouping.apply_on_widget(true, config, ui,|ui|{
config.apply_on_widget(true, ui,|ui|{
if ui
.add(Label::new(name).selectable(false).sense(Sense::click()))
.clicked()
@@ -305,7 +305,7 @@ impl BarWidget for Komorebi {
let titles = &komorebi_notification_state.focused_container_information.0;
if !titles.is_empty() {
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
let icons = &komorebi_notification_state.focused_container_information.1;
let focused_window_idx =
komorebi_notification_state.focused_container_information.2;

View File

@@ -3,11 +3,11 @@ mod battery;
mod config;
mod cpu;
mod date;
mod group;
mod komorebi;
mod media;
mod memory;
mod network;
mod render;
mod storage;
mod time;
mod ui;
@@ -29,7 +29,6 @@ use std::io::BufReader;
use std::io::Read;
use std::path::PathBuf;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;
@@ -44,7 +43,6 @@ use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
use windows::Win32::UI::WindowsAndMessaging::EnumThreadWindows;
use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
pub static BACKGROUND_COLOR: AtomicU32 = AtomicU32::new(0);
pub static MAX_LABEL_WIDTH: AtomicI32 = AtomicI32::new(400);
pub static MONITOR_LEFT: AtomicI32 = AtomicI32::new(0);
pub static MONITOR_TOP: AtomicI32 = AtomicI32::new(0);

View File

@@ -1,6 +1,6 @@
use crate::render::RenderConfig;
use crate::ui::CustomUi;
use crate::widget::BarWidget;
use crate::widget::RenderConfig;
use crate::MAX_LABEL_WIDTH;
use eframe::egui::text::LayoutJob;
use eframe::egui::Context;
@@ -102,7 +102,7 @@ impl BarWidget for Media {
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
);
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
let available_height = ui.available_height();
let mut custom_ui = CustomUi(ui);

View File

@@ -1,6 +1,6 @@
use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::widget::BarWidget;
use crate::widget::RenderConfig;
use eframe::egui::text::LayoutJob;
use eframe::egui::Context;
use eframe::egui::FontId;
@@ -102,7 +102,7 @@ impl BarWidget for Memory {
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
);
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
if ui
.add(
Label::new(layout_job)

View File

@@ -1,6 +1,6 @@
use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::widget::BarWidget;
use crate::widget::RenderConfig;
use eframe::egui::text::LayoutJob;
use eframe::egui::Context;
use eframe::egui::FontId;
@@ -320,7 +320,7 @@ impl BarWidget for Network {
fn render(&mut self, ctx: &Context, ui: &mut Ui, mut config: RenderConfig) {
if self.show_total_data_transmitted {
for output in self.total_data_transmitted() {
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
ui.add(Label::new(output).selectable(false));
});
}
@@ -328,7 +328,7 @@ impl BarWidget for Network {
if self.show_network_activity {
for output in self.network_activity() {
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
ui.add(Label::new(output).selectable(false));
});
}
@@ -367,7 +367,7 @@ impl BarWidget for Network {
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
);
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
if ui
.add(
Label::new(layout_job)

View File

@@ -1,7 +1,5 @@
use crate::bar::Alignment;
use crate::config::Color32Ext;
use crate::widget::RenderConfig;
use crate::BACKGROUND_COLOR;
use crate::config::KomobarConfig;
use eframe::egui::Color32;
use eframe::egui::Frame;
use eframe::egui::InnerResponse;
@@ -13,7 +11,6 @@ use eframe::egui::Vec2;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use std::sync::atomic::Ordering;
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind")]
@@ -28,18 +25,49 @@ pub enum Grouping {
Widget(GroupingConfig),
}
impl Grouping {
#[derive(Copy, Clone)]
pub struct RenderConfig {
/// Spacing between widgets
pub spacing: f32,
/// Sets how widgets are grouped
pub grouping: Grouping,
/// Background color
pub background_color: Color32,
/// Alignment of the widgets
pub alignment: Option<Alignment>,
/// Remove spacing if true
pub no_spacing: Option<bool>,
}
pub trait RenderExt {
fn new_renderconfig(&self, background_color: Color32) -> RenderConfig;
}
impl RenderExt for &KomobarConfig {
fn new_renderconfig(&self, background_color: Color32) -> RenderConfig {
RenderConfig {
spacing: self.widget_spacing.unwrap_or(10.0),
grouping: self.grouping.unwrap_or(Grouping::None),
background_color,
alignment: None,
no_spacing: None,
}
}
}
impl RenderConfig {
pub fn apply_on_bar<R>(
&mut self,
ui: &mut Ui,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
match self {
Self::Bar(config) => Self::define_group(false, None, ui, add_contents, config),
Self::Alignment(_) => Self::no_group(ui, add_contents),
Self::Widget(_) => Self::no_group(ui, add_contents),
Self::None => Self::no_group(ui, add_contents),
self.alignment = None;
if let Grouping::Bar(config) = self.grouping {
return self.define_group(false, config, ui, add_contents);
}
Self::fallback_group(ui, add_contents)
}
pub fn apply_on_alignment<R>(
@@ -47,42 +75,65 @@ impl Grouping {
ui: &mut Ui,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
match self {
Self::Bar(_) => Self::no_group(ui, add_contents),
Self::Alignment(config) => Self::define_group(false, None, ui, add_contents, config),
Self::Widget(_) => Self::no_group(ui, add_contents),
Self::None => Self::no_group(ui, add_contents),
self.alignment = None;
if let Grouping::Alignment(config) = self.grouping {
return self.define_group(false, config, ui, add_contents);
}
Self::fallback_group(ui, add_contents)
}
pub fn apply_on_widget<R>(
&mut self,
use_spacing: bool,
render_config: RenderConfig,
// TODO: this should remove the margin on the last widget on the left side and the first widget on the right side
// This is complex, since the last/first widget can have multiple "sections", like komorebi, network, ...
// This and the same setting on RenderConfig needs to be combined.
//_first_or_last: Option<bool>,
ui: &mut Ui,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
match self {
Self::Bar(_) => Self::widget_group(use_spacing, render_config, ui, add_contents),
Self::Alignment(_) => Self::widget_group(use_spacing, render_config, ui, add_contents),
Self::Widget(config) => {
Self::define_group(use_spacing, Some(render_config), ui, add_contents, config)
}
Self::None => Self::widget_group(use_spacing, render_config, ui, add_contents),
if let Grouping::Widget(config) = self.grouping {
return self.define_group(use_spacing, config, ui, add_contents);
}
self.fallback_widget_group(use_spacing, ui, add_contents)
}
fn fallback_group<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
InnerResponse {
inner: add_contents(ui),
response: ui.response().clone(),
}
}
fn define_group<R>(
fn fallback_widget_group<R>(
&mut self,
use_spacing: bool,
render_config: Option<RenderConfig>,
ui: &mut Ui,
add_contents: impl FnOnce(&mut Ui) -> R,
config: &mut GroupingConfig,
) -> InnerResponse<R> {
Frame::none()
.outer_margin(Self::widget_outer_margin(render_config))
.outer_margin(self.widget_outer_margin())
.inner_margin(match use_spacing {
true => Margin::symmetric(5.0 + 3.0, 3.0),
true => Margin::symmetric(5.0, 0.0),
false => Margin::same(0.0),
})
.show(ui, add_contents)
}
fn define_group<R>(
&mut self,
use_spacing: bool,
config: GroupingConfig,
ui: &mut Ui,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
Frame::none()
.outer_margin(self.widget_outer_margin())
.inner_margin(match use_spacing {
true => Margin::symmetric(8.0, 3.0),
false => Margin::symmetric(3.0, 3.0),
})
.stroke(ui.style().visuals.widgets.noninteractive.bg_stroke)
@@ -91,7 +142,7 @@ impl Grouping {
None => ui.style().visuals.widgets.noninteractive.rounding,
})
.fill(
Color32::from_u32(BACKGROUND_COLOR.load(Ordering::SeqCst))
self.background_color
.try_apply_alpha(config.transparency_alpha),
)
.shadow(match config.style {
@@ -110,47 +161,31 @@ impl Grouping {
.show(ui, add_contents)
}
fn widget_group<R>(
use_spacing: bool,
render_config: RenderConfig,
ui: &mut Ui,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
Frame::none()
.outer_margin(Self::widget_outer_margin(Some(render_config)))
.inner_margin(match use_spacing {
true => Margin::symmetric(5.0, 0.0),
false => Margin::same(0.0),
})
.show(ui, add_contents)
}
fn no_group<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
InnerResponse {
inner: add_contents(ui),
response: ui.response().clone(),
}
}
fn widget_outer_margin(render_config: Option<RenderConfig>) -> Margin {
fn widget_outer_margin(&self) -> Margin {
Margin {
left: match render_config {
Some(config) => match config.alignment {
Some(align) => match align {
Alignment::Left => 0.0,
Alignment::Right => config.spacing,
},
None => 0.0,
left: match self.alignment {
Some(align) => match align {
Alignment::Left => 0.0,
Alignment::Right => {
if self.no_spacing.is_some_and(|v| v) {
0.0
} else {
self.spacing
}
}
},
None => 0.0,
},
right: match render_config {
Some(config) => match config.alignment {
Some(align) => match align {
Alignment::Left => config.spacing,
Alignment::Right => 0.0,
},
None => 0.0,
right: match self.alignment {
Some(align) => match align {
Alignment::Left => {
if self.no_spacing.is_some_and(|v| v) {
0.0
} else {
self.spacing
}
}
Alignment::Right => 0.0,
},
None => 0.0,
},
@@ -195,3 +230,18 @@ impl From<RoundingConfig> for Rounding {
}
}
}
pub trait Color32Ext {
fn try_apply_alpha(self, transparency_alpha: Option<u8>) -> Self;
}
impl Color32Ext for Color32 {
/// Tries to apply the alpha value to the Color32
fn try_apply_alpha(self, transparency_alpha: Option<u8>) -> Self {
if let Some(alpha) = transparency_alpha {
return Color32::from_rgba_unmultiplied(self.r(), self.g(), self.b(), alpha);
}
self
}
}

View File

@@ -1,6 +1,6 @@
use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::widget::BarWidget;
use crate::widget::RenderConfig;
use eframe::egui::text::LayoutJob;
use eframe::egui::Context;
use eframe::egui::FontId;
@@ -107,7 +107,7 @@ impl BarWidget for Storage {
TextFormat::simple(font_id.clone(), ctx.style().visuals.text_color()),
);
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
if ui
.add(
Label::new(layout_job)

View File

@@ -1,6 +1,6 @@
use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::widget::BarWidget;
use crate::widget::RenderConfig;
use eframe::egui::text::LayoutJob;
use eframe::egui::Context;
use eframe::egui::FontId;
@@ -110,7 +110,7 @@ impl BarWidget for Time {
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
);
config.grouping.apply_on_widget(true, config, ui, |ui| {
config.apply_on_widget(true, ui, |ui| {
if ui
.add(
Label::new(layout_job)

View File

@@ -1,11 +1,9 @@
use crate::bar::Alignment;
use crate::battery::Battery;
use crate::battery::BatteryConfig;
use crate::cpu::Cpu;
use crate::cpu::CpuConfig;
use crate::date::Date;
use crate::date::DateConfig;
use crate::group::Grouping;
use crate::komorebi::Komorebi;
use crate::komorebi::KomorebiConfig;
use crate::media::Media;
@@ -14,6 +12,7 @@ use crate::memory::Memory;
use crate::memory::MemoryConfig;
use crate::network::Network;
use crate::network::NetworkConfig;
use crate::render::RenderConfig;
use crate::storage::Storage;
use crate::storage::StorageConfig;
use crate::time::Time;
@@ -28,16 +27,6 @@ pub trait BarWidget {
fn render(&mut self, ctx: &Context, ui: &mut Ui, config: RenderConfig);
}
#[derive(Copy, Clone)]
pub struct RenderConfig {
/// Spacing between widgets
pub spacing: f32,
/// Sets how widgets are grouped
pub grouping: Grouping,
/// Alignment of the widgets
pub alignment: Option<Alignment>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub enum WidgetConfig {
Battery(BatteryConfig),