summaryrefslogtreecommitdiff
path: root/src/bar.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bar.rs')
-rw-r--r--src/bar.rs192
1 files changed, 189 insertions, 3 deletions
diff --git a/src/bar.rs b/src/bar.rs
index 102978d..84bbecc 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -14,6 +14,7 @@ use crate::protocol::*;
use crate::shared_state::SharedState;
use crate::state::State;
use crate::text::{self, ComputedText, RenderOptions};
+use crate::tray::TrayCommand;
use crate::wm_info_provider::{FocusedWindow, Tag};
pub struct Bar {
@@ -39,6 +40,14 @@ pub struct Bar {
layout_name_computed: Option<ComputedText>,
mode_computed: Option<ComputedText>,
focused_window_computed: Option<ComputedText>,
+ tray_btns: ButtonManager<TrayBtn>,
+ tray_toggle_computed: Option<ComputedText>,
+}
+
+#[derive(Clone)]
+enum TrayBtn {
+ Toggle,
+ Item(String),
}
#[derive(Debug, PartialEq)]
@@ -60,7 +69,7 @@ impl Bar {
surface,
Some(output.wl),
state.shared_state.config.layer.into(),
- c"i3bar-river".into(),
+ c"i3bar-mango".into(),
layer_surface_cb,
);
@@ -87,6 +96,8 @@ impl Bar {
layout_name_computed: None,
mode_computed: None,
focused_window_computed: None,
+ tray_btns: ButtonManager::default(),
+ tray_toggle_computed: None,
}
}
@@ -128,7 +139,7 @@ impl Bar {
button: PointerBtn,
seat: WlSeat,
x: f64,
- _y: f64,
+ y: f64,
) -> anyhow::Result<()> {
if let Some(tag_id) = self.tags_btns.click(x) {
ss.wm_info_provider
@@ -136,6 +147,31 @@ impl Bar {
} else if self.tags_btns.is_between(x) {
ss.wm_info_provider
.click_on_tag(conn, &self.output, seat, None, button);
+ } else if let Some(tray_btn) = self.tray_btns.click(x).cloned() {
+ match tray_btn {
+ TrayBtn::Toggle => {
+ ss.tray_expanded = !ss.tray_expanded;
+ self.tray_toggle_computed = None;
+ self.frame(conn, ss);
+ }
+ TrayBtn::Item(key) => {
+ if let Some(tray) = &ss.tray {
+ let cmd = match button {
+ PointerBtn::Left => Some(TrayCommand::Activate { key }),
+ PointerBtn::Middle => Some(TrayCommand::SecondaryActivate { key }),
+ PointerBtn::Right => Some(TrayCommand::ContextMenu {
+ key,
+ x: x as i32,
+ y: y as i32,
+ }),
+ _ => None,
+ };
+ if let Some(cmd) = cmd {
+ tray.send(cmd);
+ }
+ }
+ }
+ }
} else if let Some((name, instance)) = self.blocks_btns.click(x) {
if let Some(cmd) = &mut ss.status_cmd {
cmd.send_click_event(&i3bar_protocol::Event {
@@ -381,6 +417,14 @@ impl Bar {
}
}
+ // Display the tray
+ let tray_width = if ss.config.tray.enabled {
+ self.render_tray(&cairo_ctx, ss, width_f, height_f)
+ } else {
+ self.tray_btns.clear();
+ 0.0
+ };
+
// Display the blocks
render_blocks(
&cairo_ctx,
@@ -388,7 +432,7 @@ impl Bar {
ss.blocks_cache.get_computed(),
&mut self.blocks_btns,
offset_left,
- width_f,
+ width_f - tray_width,
height_f,
);
@@ -417,6 +461,148 @@ impl Bar {
self.surface.commit(conn);
}
+ fn render_tray(
+ &mut self,
+ context: &cairo::Context,
+ ss: &mut SharedState,
+ full_width: f64,
+ full_height: f64,
+ ) -> f64 {
+ self.tray_btns.clear();
+ if ss.tray_items.is_empty() {
+ return 0.0;
+ }
+
+ let cfg = ss.config.tray;
+ let expanded = ss.tray_expanded;
+ let icon_size = full_height - 4.0;
+ let spacing = 8.0;
+ let bar_height = self.height;
+
+ let toggle = self.tray_toggle_computed.get_or_insert_with(|| {
+ ComputedText::new(
+ if expanded { "\u{25BE}" } else { "\u{25B8}" },
+ text::Attributes {
+ font: &ss.config.font,
+ padding_left: 8.0,
+ padding_right: 8.0,
+ min_width: None,
+ align: Default::default(),
+ markup: false,
+ },
+ )
+ });
+
+ struct Rendered {
+ key: String,
+ icon: Option<cairo::ImageSurface>,
+ text: Option<ComputedText>,
+ width: f64,
+ }
+
+ let mut rendered = Vec::new();
+ if expanded {
+ for item in &ss.tray_items {
+ let label = match (cfg.show_name, cfg.show_description && !item.tooltip.is_empty()) {
+ (true, true) if item.tooltip != item.title => {
+ format!("{} \u{2014} {}", item.title, item.tooltip)
+ }
+ (true, _) => item.title.clone(),
+ (false, true) => item.tooltip.clone(),
+ (false, false) => String::new(),
+ };
+ let text = if label.is_empty() {
+ None
+ } else {
+ Some(ComputedText::new(
+ &label,
+ text::Attributes {
+ font: &ss.config.font,
+ padding_left: 4.0,
+ padding_right: 4.0,
+ min_width: None,
+ align: Default::default(),
+ markup: false,
+ },
+ ))
+ };
+ let icon = if cfg.show_icon {
+ item.pixmap.clone().or_else(|| {
+ ss.icon_cache
+ .get_tray_icon(&item.icon_name, &item.icon_theme_path, bar_height)
+ .cloned()
+ })
+ } else {
+ None
+ };
+ let mut width = text.as_ref().map_or(0.0, |t| t.width);
+ if icon.is_some() {
+ width += icon_size + 4.0;
+ }
+ if width > 0.0 {
+ rendered.push(Rendered {
+ key: item.key.clone(),
+ icon,
+ text,
+ width,
+ });
+ }
+ }
+ }
+
+ let items_width: f64 = rendered.iter().map(|r| r.width + spacing).sum();
+ let total_width = items_width + toggle.width;
+ let mut x = full_width - total_width;
+
+ for r in rendered {
+ let mut item_x = x;
+ if let Some(icon) = &r.icon {
+ let scale = icon_size / (icon.width() as f64).max(icon.height() as f64);
+ let icon_w = icon.width() as f64 * scale;
+ let icon_h = icon.height() as f64 * scale;
+ context.save().unwrap();
+ context.translate(item_x + (icon_size - icon_w) / 2.0, (full_height - icon_h) / 2.0);
+ context.scale(scale, scale);
+ context.set_source_surface(icon, 0.0, 0.0).unwrap();
+ context.paint().unwrap();
+ context.restore().unwrap();
+ item_x += icon_size + 4.0;
+ }
+ if let Some(text) = &r.text {
+ text.render(
+ context,
+ RenderOptions {
+ x_offset: item_x,
+ bar_height: full_height,
+ fg_color: ss.config.color,
+ bg_color: None,
+ r_left: 0.0,
+ r_right: 0.0,
+ overlap: 0.0,
+ },
+ );
+ }
+ self.tray_btns.push(x, r.width, TrayBtn::Item(r.key));
+ x += r.width + spacing;
+ }
+
+ toggle.render(
+ context,
+ RenderOptions {
+ x_offset: x,
+ bar_height: full_height,
+ fg_color: ss.config.color,
+ bg_color: None,
+ r_left: 0.0,
+ r_right: 0.0,
+ overlap: 0.0,
+ },
+ );
+ self.tray_btns.push(x, toggle.width, TrayBtn::Toggle);
+
+ total_width
+ }
+
pub fn show(&mut self, conn: &mut Connection<State>, shared_state: &SharedState) {
assert!(!self.mapped);