diff options
Diffstat (limited to 'src/bar.rs')
| -rw-r--r-- | src/bar.rs | 192 |
1 files changed, 189 insertions, 3 deletions
@@ -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); |
