1use std::{
2 ops::BitAnd,
3 pin::Pin,
4 rc::Rc,
5 sync::{Arc, Mutex},
6};
7
8use anyhow::{anyhow, Result};
9use csscolorparser::Color;
10use derive_debug::Dbg;
11use lazy_static::lazy_static;
12use lazybar_types::EventResponse;
13use regex::Regex;
14use tokio::{
15 net::UnixStream,
16 sync::{mpsc::UnboundedSender, OnceCell},
17 task::JoinSet,
18};
19use tokio_stream::{Stream, StreamMap};
20use x11rb::{
21 connection::Connection,
22 protocol::{
23 self,
24 xproto::{ConnectionExt, Visualtype, Window},
25 },
26 xcb_ffi::XCBConnection,
27};
28
29use crate::{
30 create_surface, create_window,
31 ipc::{self, ChannelEndpoint},
32 set_wm_properties, Alignment, IpcStream, Margins, PanelDrawFn, PanelHideFn,
33 PanelShowFn, PanelShutdownFn, PanelStream, Position,
34};
35#[cfg(feature = "cursor")]
36use crate::{x::set_cursor, CursorFn};
37
38lazy_static! {
39 static ref REGEX: Regex =
40 Regex::new(r"(?<region>[lcr])(?<idx>\d+).(?<message>.+)").unwrap();
41 #[allow(missing_docs)]
42 pub static ref BAR_INFO: OnceCell<BarInfo> = OnceCell::new();
43}
44
45#[derive(Debug, Clone)]
47pub struct BarInfo {
48 pub window: Window,
50 pub visual: Visualtype,
52 pub width: u16,
54 pub height: u16,
56 pub transparent: bool,
58 pub bg: Color,
60 #[cfg(feature = "cursor")]
62 pub cursors: Cursors,
63}
64
65#[cfg(feature = "cursor")]
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68pub struct Cursors {
69 pub default: &'static str,
71 pub click: &'static str,
73 pub scroll: &'static str,
75}
76
77#[cfg(feature = "cursor")]
82#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
83pub enum Cursor {
84 #[default]
86 Default,
87 Click,
89 Scroll,
91}
92
93#[cfg(feature = "cursor")]
94impl From<Cursor> for &str {
95 fn from(value: Cursor) -> Self {
96 match value {
97 Cursor::Default => BAR_INFO.get().unwrap().cursors.default,
98 Cursor::Click => BAR_INFO.get().unwrap().cursors.click,
99 Cursor::Scroll => BAR_INFO.get().unwrap().cursors.scroll,
100 }
101 }
102}
103
104#[derive(PartialEq, Eq, Debug)]
105enum CenterState {
106 Center,
107 Left,
108 Right,
109 Unknown,
110}
111
112#[derive(Debug)]
113enum Region {
114 Left,
115 CenterRight,
116 Right,
117 All,
118 Custom { start_x: f64, end_x: f64 },
119}
120
121#[derive(Debug)]
122struct Extents {
123 left: f64,
124 center: (f64, f64),
125 right: f64,
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
133pub enum Dependence {
134 #[default]
136 None,
137 Left,
139 Right,
141 Both,
143}
144
145#[cfg(feature = "cursor")]
147#[derive(Dbg)]
148pub enum CursorInfo {
149 Static(Cursor),
152 #[dbg(skip)]
156 Dynamic(CursorFn),
157}
158
159#[cfg(feature = "cursor")]
160impl CursorInfo {
161 pub fn get(&self, event: MouseEvent) -> Result<Cursor> {
166 match self {
167 Self::Static(s) => Ok(*s),
168 Self::Dynamic(f) => f(event),
169 }
170 }
171}
172
173#[derive(Dbg)]
175pub struct PanelDrawInfo {
176 pub width: i32,
178 pub height: i32,
180 pub dependence: Dependence,
182 #[dbg(placeholder = "..")]
186 pub draw_fn: PanelDrawFn,
187 #[dbg(formatter = "fmt_option")]
189 pub show_fn: Option<PanelShowFn>,
190 #[dbg(formatter = "fmt_option")]
192 pub hide_fn: Option<PanelHideFn>,
193 #[dbg(formatter = "fmt_option")]
197 pub shutdown: Option<PanelShutdownFn>,
198 #[cfg(feature = "cursor")]
200 pub cursor_info: CursorInfo,
201 pub dump: String,
203}
204
205fn fmt_option<T>(value: &Option<T>) -> &'static str {
206 match value {
207 Some(_) => "Some(..)",
208 None => "None",
209 }
210}
211
212impl PanelDrawInfo {
213 #[must_use]
215 pub const fn new(
216 dims: (i32, i32),
217 dependence: Dependence,
218 draw_fn: PanelDrawFn,
219 show_fn: Option<PanelShowFn>,
220 hide_fn: Option<PanelHideFn>,
221 shutdown: Option<PanelShutdownFn>,
222 #[cfg(feature = "cursor")] cursor_info: CursorInfo,
223 dump: String,
224 ) -> Self {
225 Self {
226 width: dims.0,
227 height: dims.1,
228 dependence,
229 draw_fn,
230 show_fn,
231 hide_fn,
232 shutdown,
233 #[cfg(feature = "cursor")]
234 cursor_info,
235 dump,
236 }
237 }
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
241enum PanelStatus {
242 Shown,
243 ZeroWidth,
244 Dependent(Dependence),
245}
246
247impl BitAnd for PanelStatus {
248 type Output = Self;
249
250 fn bitand(self, rhs: Self) -> Self::Output {
251 if self == Self::Shown && rhs == Self::Shown {
252 Self::Shown
253 } else {
254 Self::ZeroWidth
255 }
256 }
257}
258
259impl From<&Panel> for PanelStatus {
260 fn from(value: &Panel) -> Self {
261 if value.visible {
262 value.draw_info.as_ref().map_or(Self::ZeroWidth, |d| {
263 match (d.dependence, d.width) {
264 (Dependence::None, 0) => Self::ZeroWidth,
265 (Dependence::None, _) => Self::Shown,
266 (dep, _) => Self::Dependent(dep),
267 }
268 })
269 } else {
270 Self::ZeroWidth
271 }
272 }
273}
274
275#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
279pub enum MouseButton {
280 #[default]
282 Left,
283 Middle,
285 Right,
287 ScrollUp,
289 ScrollDown,
291}
292
293impl MouseButton {
294 fn try_parse(value: u8, reverse: bool) -> Result<Self> {
295 match value {
296 1 => Ok(Self::Left),
297 2 => Ok(Self::Middle),
298 3 => Ok(Self::Right),
299 4 => {
300 if reverse {
301 Ok(Self::ScrollUp)
302 } else {
303 Ok(Self::ScrollDown)
304 }
305 }
306 5 => {
307 if reverse {
308 Ok(Self::ScrollDown)
309 } else {
310 Ok(Self::ScrollUp)
311 }
312 }
313 _ => Err(anyhow!("X server provided invalid button")),
314 }
315 }
316}
317
318#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
320pub struct MouseEvent {
321 pub button: MouseButton,
323 pub x: i16,
325 pub y: i16,
327}
328
329#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
331pub enum Event {
332 Mouse(MouseEvent),
334 Action(Option<String>),
336}
337
338#[derive(Debug)]
340pub struct Panel {
341 pub draw_info: Option<PanelDrawInfo>,
343 pub x: f64,
345 pub name: &'static str,
348 pub visible: bool,
351 endpoint: Option<Arc<Mutex<ChannelEndpoint<Event, EventResponse>>>>,
352}
353
354impl Panel {
355 #[must_use]
357 pub fn new(
358 draw_info: Option<PanelDrawInfo>,
359 name: &'static str,
360 endpoint: Option<ChannelEndpoint<Event, EventResponse>>,
361 visible: bool,
362 ) -> Self {
363 Self {
364 draw_info,
365 x: 0.0,
366 name,
367 visible,
368 endpoint: endpoint.map(|e| Arc::new(Mutex::new(e))),
369 }
370 }
371}
372
373#[allow(dead_code)]
377#[derive(Dbg)]
378pub struct Bar {
379 pub(crate) name: String,
380 position: Position,
381 pub(crate) conn: Arc<XCBConnection>,
382 screen: usize,
383 window: Window,
384 surface: cairo::XCBSurface,
385 pub(crate) cr: Rc<cairo::Context>,
386 width: i32,
387 height: u16,
388 bg: Color,
389 margins: Margins,
390 extents: Extents,
391 reverse_scroll: bool,
392 pub(crate) left_panels: Vec<Panel>,
393 pub(crate) center_panels: Vec<Panel>,
394 pub(crate) right_panels: Vec<Panel>,
395 #[dbg(placeholder = "..")]
396 pub(crate) streams: StreamMap<Alignment, StreamMap<usize, PanelStream>>,
397 pub(crate) ipc: bool,
398 mapped: bool,
399 center_state: CenterState,
400}
401
402impl Bar {
403 pub fn new(
406 name: &str,
407 position: Position,
408 height: u16,
409 transparent: bool,
410 bg: Color,
411 margins: Margins,
412 reverse_scroll: bool,
413 ipc: bool,
414 monitor: Option<String>,
415 #[cfg(feature = "cursor")] cursors: Cursors,
416 ) -> Result<(Self, IpcStream)> {
417 let (conn, screen, window, width, visual, mon) =
418 create_window(position, height, transparent, &bg, monitor)?;
419
420 BAR_INFO
421 .set(BarInfo {
422 window,
423 visual,
424 width,
425 height,
426 transparent,
427 bg: bg.clone(),
428 #[cfg(feature = "cursor")]
429 cursors,
430 })
431 .unwrap();
432
433 let (result, name) = ipc::init(ipc, name);
434 let ipc_stream: Pin<
435 Box<
436 dyn Stream<
437 Item = std::result::Result<UnixStream, std::io::Error>,
438 >,
439 >,
440 > = match result {
441 Ok(stream) => {
442 log::info!("IPC initialized");
443 stream
444 }
445 Err(e) => {
446 log::info!("IPC disabled due to an error: {e}");
447 Box::pin(tokio_stream::pending())
448 }
449 };
450
451 set_wm_properties(
452 &conn,
453 window,
454 position,
455 width.into(),
456 height.into(),
457 name.as_str(),
458 &mon,
459 );
460 conn.map_window(window)?;
461 let surface =
462 create_surface(window, visual, width.into(), height.into(), &conn)?;
463 let cr = cairo::Context::new(&surface)?;
464 surface.flush();
465 conn.flush()?;
466
467 Ok((
468 Self {
469 name,
470 position,
471 conn: Arc::new(conn),
472 screen,
473 window,
474 surface,
475 cr: Rc::new(cr),
476 width: width.into(),
477 height,
478 bg,
479 margins,
480 extents: Extents {
481 left: 0.0,
482 center: ((width / 2).into(), (width / 2).into()),
483 right: width.into(),
484 },
485 reverse_scroll,
486 left_panels: Vec::new(),
487 center_panels: Vec::new(),
488 right_panels: Vec::new(),
489 streams: StreamMap::new(),
490 ipc,
491 mapped: true,
492 center_state: CenterState::Center,
493 },
494 ipc_stream,
495 ))
496 }
497
498 pub fn shutdown(self) {
500 self.left_panels
501 .into_iter()
502 .chain(self.center_panels)
503 .chain(self.right_panels)
504 .filter_map(|panel| panel.draw_info)
505 .filter_map(|draw_info| draw_info.shutdown)
506 .for_each(|shutdown| shutdown());
507 }
508
509 fn apply_dependence(panels: &[Panel]) -> Vec<PanelStatus> {
510 (0..panels.len())
511 .map(|idx| match PanelStatus::from(&panels[idx]) {
512 PanelStatus::Shown => PanelStatus::Shown,
513 PanelStatus::ZeroWidth => PanelStatus::ZeroWidth,
514 PanelStatus::Dependent(Dependence::Left) => panels
515 .get(idx - 1)
516 .map_or(PanelStatus::ZeroWidth, PanelStatus::from),
517 PanelStatus::Dependent(Dependence::Right) => panels
518 .get(idx + 1)
519 .map_or(PanelStatus::ZeroWidth, PanelStatus::from),
520 PanelStatus::Dependent(Dependence::Both) => {
521 panels
522 .get(idx - 1)
523 .map_or(PanelStatus::ZeroWidth, PanelStatus::from)
524 & panels
525 .get(idx + 1)
526 .map_or(PanelStatus::ZeroWidth, PanelStatus::from)
527 }
528 PanelStatus::Dependent(Dependence::None) => unreachable!(),
529 })
530 .collect()
531 }
532
533 fn show_panels(&self) {
534 self.left_panels
535 .iter()
536 .chain(self.center_panels.iter())
537 .chain(self.right_panels.iter())
538 .filter_map(|p| p.draw_info.as_ref())
539 .filter_map(|d| d.show_fn.as_ref())
540 .for_each(|f| {
541 if let Err(e) = f() {
542 log::warn!("showing panel produced an error: {e}");
543 }
544 });
545 }
546
547 fn hide_panels(&self) {
548 self.left_panels
549 .iter()
550 .chain(self.center_panels.iter())
551 .chain(self.right_panels.iter())
552 .filter_map(|p| p.draw_info.as_ref())
553 .filter_map(|d| d.hide_fn.as_ref())
554 .for_each(|f| {
555 if let Err(e) = f() {
556 log::warn!("hiding panel produced an error: {e}");
557 }
558 });
559 }
560
561 pub fn process_event(&mut self, event: &protocol::Event) -> Result<()> {
563 match event {
564 protocol::Event::Expose(_) => {
565 log::info!(
566 "Received expose event from X server; redrawing entire bar"
567 );
568 self.redraw_bar()
569 }
570 protocol::Event::ButtonPress(event) => match event.detail {
571 button @ 1..=5 => {
572 let (x, y) = if event.same_screen {
573 (event.event_x, event.event_y)
574 } else {
575 (event.root_x, event.root_y)
576 };
577
578 let panel = self
579 .left_panels
580 .iter()
581 .chain(self.center_panels.iter())
582 .chain(self.right_panels.iter())
583 .filter(|p| p.draw_info.is_some())
584 .find(|p| {
585 p.x <= x as f64
586 && p.x
587 + p.draw_info.as_ref().unwrap().width as f64
588 >= x as f64
589 });
590
591 if let Some(p) = panel {
592 if let Some(e) = &p.endpoint {
593 let e = e.lock().unwrap();
594 e.send.send(Event::Mouse(MouseEvent {
595 button: MouseButton::try_parse(
596 button,
597 self.reverse_scroll,
598 )
599 .unwrap(),
601 x: x - p.x as i16,
602 y,
603 }))?;
604 }
605 }
606 Ok(())
607 }
608 _ => Ok(()),
609 },
610 #[cfg(feature = "cursor")]
611 protocol::Event::MotionNotify(event) => {
612 let (x, y) = if event.same_screen {
613 (event.event_x, event.event_y)
614 } else {
615 (event.root_x, event.root_y)
616 };
617
618 let panel = self
619 .left_panels
620 .iter()
621 .chain(self.center_panels.iter())
622 .chain(self.right_panels.iter())
623 .filter(|p| p.draw_info.is_some())
624 .find(|p| {
625 p.x <= x as f64
626 && p.x + p.draw_info.as_ref().unwrap().width as f64
627 >= x as f64
628 });
629
630 if let Some(panel) = panel {
631 if let Some(ref draw_info) = panel.draw_info {
632 if let Ok(cursor) =
633 draw_info.cursor_info.get(MouseEvent {
634 button: MouseButton::Left,
635 x: x - panel.x as i16,
636 y,
637 })
638 {
639 set_cursor(
640 self.conn.as_ref(),
641 self.screen,
642 cursor,
643 self.window,
644 )?;
645 } else {
646 set_cursor(
647 self.conn.as_ref(),
648 self.screen,
649 Cursor::Default,
650 self.window,
651 )?;
652 }
653 } else {
654 set_cursor(
655 self.conn.as_ref(),
656 self.screen,
657 Cursor::Default,
658 self.window,
659 )?;
660 }
661 } else {
662 set_cursor(
663 self.conn.as_ref(),
664 self.screen,
665 Cursor::Default,
666 self.window,
667 )?;
668 }
669
670 Ok(())
671 }
672 _ => Ok(()),
673 }
674 }
675
676 fn handle_ipc_event(&mut self, message: &str) -> Result<bool> {
677 match message {
678 "quit" => Ok(true),
679 "show" => {
680 self.mapped = true;
681 self.conn.map_window(self.window)?;
682 self.show_panels();
683 Ok(false)
684 }
685 "hide" => {
686 self.mapped = true;
687 self.conn.unmap_window(self.window)?;
688 self.hide_panels();
689 Ok(false)
690 }
691 "toggle" => {
692 if self.mapped {
693 self.handle_ipc_event("hide")
694 } else {
695 self.handle_ipc_event("show")
696 }
697 }
698 _ => Ok(false),
699 }
700 }
701
702 fn handle_panel_event(
703 &mut self,
704 message: &str,
705 ) -> Result<(bool, Option<String>)> {
706 if let Some(caps) = REGEX.captures(message) {
707 let region = &caps["region"];
708 let idx = caps["idx"].parse::<usize>()?;
709
710 if let Some(target) = match region {
711 "l" => self.left_panels.get_mut(idx),
712 "c" => self.center_panels.get_mut(idx),
713 "r" => self.right_panels.get_mut(idx),
714 _ => unreachable!(),
715 } {
716 match &caps["message"] {
717 "show" | "toggle" if !target.visible => {
718 if let Some(ref draw_info) = target.draw_info {
719 if let Some(ref f) = draw_info.show_fn {
720 f()?;
721 }
722 }
723 target.visible = true;
724 }
725 "hide" | "toggle" if target.visible => {
726 if let Some(ref draw_info) = target.draw_info {
727 if let Some(ref f) = draw_info.hide_fn {
728 f()?;
729 }
730 }
731 target.visible = false;
732 }
733 "dump" => {
734 if let Some(ref draw_info) = target.draw_info {
735 return Ok((false, Some(draw_info.dump.clone())));
736 }
737 }
738 message => {
739 return Err(anyhow!(
740 "Unknown or invalid message {message}"
741 ))
742 }
743 }
744
745 match region {
746 "l" => self.redraw_left(),
747 "c" => self.redraw_center_right(true),
748 "r" => self.redraw_right(true, None),
749 _ => unreachable!(),
750 }?;
751 }
752 }
753 Ok((false, None))
754 }
755
756 pub fn send_message(
758 &mut self,
759 message: &str,
760 ipc_set: &mut JoinSet<Result<()>>,
761 ipc_send: UnboundedSender<EventResponse>,
762 ) -> Result<bool> {
763 if let Some(stripped) = message.strip_prefix('#') {
764 let (exit, message) = self.handle_panel_event(stripped)?;
765 ipc_send.send(EventResponse::Ok(message))?;
766 return Ok(exit);
767 }
768
769 let (dest, message) = match message.split_once('.') {
770 Some((panel, message)) => (Some(panel), message),
771 None => (None, message),
772 };
773
774 if let Some(panel) = dest {
775 let mut panels = self
776 .left_panels
777 .iter()
778 .chain(&self.center_panels)
779 .chain(&self.right_panels)
780 .filter(|p| p.name == panel);
781
782 let target = panels.next();
783 let (endpoint, message) = match if target.is_none() {
784 Err(anyhow!("No panel with name {panel} was found"))
785 } else if panels.next().is_some() {
786 Err(anyhow!(
787 "This panel has multiple instances and cannot be messaged"
788 ))
789 } else if let Some(ref endpoint) = target.unwrap().endpoint {
790 Ok((endpoint.clone(), message.to_string()))
791 } else {
792 Err(anyhow!(
793 "The target panel has no associated sender and cannot be \
794 messaged"
795 ))
796 } {
797 Ok(r) => r,
798 Err(e) => {
799 let err = e.to_string();
800 ipc_set.spawn_blocking(move || {
801 Ok(ipc_send.send(EventResponse::Err(err))?)
802 });
803 return Err(e);
804 }
805 };
806
807 ipc_set.spawn_blocking(move || {
808 let send = endpoint.lock().unwrap().send.clone();
809 let response =
810 if let Err(e) = send.send(Event::Action(Some(message))) {
811 EventResponse::Err(e.to_string())
812 } else {
813 endpoint
814 .lock()
815 .unwrap()
816 .recv
817 .blocking_recv()
818 .unwrap_or(EventResponse::Ok(None))
819 };
820 log::trace!("response received");
821
822 ipc_send.send(response)?;
823 log::trace!("response sent");
824
825 Ok(())
826 });
827
828 log::trace!("task spawned");
829
830 Ok(false)
831 } else {
832 self.handle_ipc_event(message)
833 }
834 }
835
836 fn redraw_background(&self, scope: &Region) -> Result<()> {
837 self.cr.save()?;
838 self.cr.set_operator(cairo::Operator::Source);
839 self.cr.set_source_rgba(
840 self.bg.r.into(),
841 self.bg.g.into(),
842 self.bg.b.into(),
843 self.bg.a.into(),
844 );
845 match scope {
846 Region::Left => self.cr.rectangle(
847 0.0,
848 0.0,
849 self.extents.left + self.margins.internal,
850 f64::from(self.height),
851 ),
852 Region::CenterRight => self.cr.rectangle(
853 self.extents.center.0 - self.margins.internal,
854 0.0,
855 f64::from(self.width)
856 - (self.extents.center.0 - self.margins.internal),
857 f64::from(self.height),
858 ),
859 Region::Right => self.cr.rectangle(
860 self.extents.right - self.margins.internal,
861 0.0,
862 f64::from(self.width)
863 - (self.extents.right - self.margins.internal),
864 f64::from(self.height),
865 ),
866 Region::All => {
867 self.cr.rectangle(
868 0.0,
869 0.0,
870 f64::from(self.width),
871 f64::from(self.height),
872 );
873 }
874 Region::Custom { start_x, end_x } => {
875 self.cr.rectangle(
876 *start_x,
877 0.0,
878 end_x - start_x,
879 f64::from(self.height),
880 );
881 }
882 }
883 self.cr.fill()?;
884 self.cr.restore()?;
885
886 Ok(())
887 }
888
889 pub fn update_panel(
891 &mut self,
892 alignment: Alignment,
893 idx: usize,
894 draw_info: PanelDrawInfo,
895 ) -> Result<()> {
896 let new_width = f64::from(draw_info.width);
897 match alignment {
898 Alignment::Left => {
899 let cur_width = f64::from(
900 self.left_panels
901 .get(idx)
902 .expect("one or more panels have vanished")
903 .draw_info
904 .as_ref()
905 .map_or(0, |i| i.width),
906 );
907
908 self.left_panels
909 .get_mut(idx)
910 .expect("one or more panels have vanished")
911 .draw_info = Some(draw_info);
912
913 if (new_width - cur_width).abs() < f64::EPSILON {
914 self.redraw_one(alignment, idx)?;
915 } else if new_width - cur_width
916 + self.extents.left
917 + self.margins.internal
918 < self.extents.center.0
919 && (self.center_state == CenterState::Center
920 || self.center_state == CenterState::Left)
921 {
922 self.redraw_left()?;
923 } else {
924 self.redraw_bar()?;
925 }
926
927 Ok(())
928 }
929 Alignment::Center => {
930 let cur_width = f64::from(
931 self.center_panels
932 .get(idx)
933 .expect("one or more panels have vanished")
934 .draw_info
935 .as_ref()
936 .map_or(0, |i| i.width),
937 );
938
939 self.center_panels
940 .get_mut(idx)
941 .expect("one or more panels have vanished")
942 .draw_info = Some(draw_info);
943
944 if (new_width - cur_width).abs() < f64::EPSILON {
945 self.redraw_one(alignment, idx)?;
946 } else {
947 self.redraw_bar()?;
948 }
949
950 Ok(())
951 }
952 Alignment::Right => {
953 let cur_width = f64::from(
954 self.right_panels
955 .get(idx)
956 .expect("one or more panels have vanished")
957 .draw_info
958 .as_ref()
959 .map_or(0, |i| i.width),
960 );
961
962 self.right_panels
963 .get_mut(idx)
964 .expect("one or more panels have vanished")
965 .draw_info = Some(draw_info);
966
967 if (new_width - cur_width).abs() < f64::EPSILON {
968 self.redraw_one(alignment, idx)?;
969 } else if self.extents.right
970 - new_width
971 - cur_width
972 - self.margins.internal
973 > self.extents.center.1
974 {
975 self.redraw_right(true, None)?;
976 } else if (self.extents.right
977 - self.extents.center.1
978 - self.margins.internal)
979 + (self.extents.center.0
980 - self.extents.left
981 - self.margins.internal)
982 > new_width - cur_width
983 {
984 self.extents.right += new_width - cur_width;
985 self.redraw_center_right(true)?;
986 } else {
987 self.redraw_bar()?;
988 }
989
990 self.surface.flush();
991 self.conn.flush()?;
992
993 Ok(())
994 }
995 }
996 }
997
998 fn redraw_one(&self, alignment: Alignment, idx: usize) -> Result<()> {
999 match alignment {
1000 Alignment::Left => {
1001 self.cr.save()?;
1002
1003 let panel = self
1004 .left_panels
1005 .get(idx)
1006 .expect("one or more panels have vanished");
1007 if let Some(draw_info) = &panel.draw_info {
1008 self.redraw_background(&Region::Custom {
1009 start_x: panel.x,
1010 end_x: panel.x + f64::from(draw_info.width),
1011 })?;
1012 self.cr.translate(panel.x, 0.0);
1013 (draw_info.draw_fn)(&self.cr, panel.x)?;
1014 }
1015
1016 self.surface.flush();
1017 self.conn.flush()?;
1018 self.cr.restore()?;
1019
1020 Ok(())
1021 }
1022 Alignment::Center => {
1023 self.cr.save()?;
1024 let panel = self
1025 .center_panels
1026 .get(idx)
1027 .expect("one or more panels have vanished");
1028
1029 if let Some(draw_info) = &self
1030 .center_panels
1031 .get(idx)
1032 .expect("one or more panels have vanished")
1033 .draw_info
1034 {
1035 self.redraw_background(&Region::Custom {
1036 start_x: panel.x,
1037 end_x: panel.x + f64::from(draw_info.width),
1038 })?;
1039 self.cr.translate(panel.x, 0.0);
1040 (draw_info.draw_fn)(&self.cr, panel.x)?;
1041 }
1042
1043 self.surface.flush();
1044 self.conn.flush()?;
1045 self.cr.restore()?;
1046
1047 Ok(())
1048 }
1049 Alignment::Right => {
1050 self.cr.save()?;
1051 let panel = self
1052 .right_panels
1053 .get(idx)
1054 .expect("one or more panels have vanished");
1055
1056 if let Some(draw_info) = &self
1057 .right_panels
1058 .get(idx)
1059 .expect("one or more panels have vanished")
1060 .draw_info
1061 {
1062 self.redraw_background(&Region::Custom {
1063 start_x: panel.x,
1064 end_x: panel.x + f64::from(draw_info.width),
1065 })?;
1066 self.cr.translate(panel.x, 0.0);
1067 (draw_info.draw_fn)(&self.cr, panel.x)?;
1068 }
1069
1070 self.surface.flush();
1071 self.conn.flush()?;
1072 self.cr.restore()?;
1073
1074 Ok(())
1075 }
1076 }
1077 }
1078
1079 pub fn redraw_bar(&mut self) -> Result<()> {
1087 log::info!("Redrawing entire bar");
1088
1089 self.redraw_background(&Region::All)?;
1090
1091 self.redraw_left()?;
1092 self.redraw_center_right(false)?;
1093
1094 Ok(())
1095 }
1096
1097 fn redraw_left(&mut self) -> Result<()> {
1098 log::info!("Redrawing left");
1099
1100 self.redraw_background(&Region::Left)?;
1101
1102 self.extents.left = self.margins.left;
1103
1104 let statuses = Self::apply_dependence(self.left_panels.as_slice());
1105
1106 for panel in self
1107 .left_panels
1108 .iter_mut()
1109 .enumerate()
1110 .filter(|(idx, _)| {
1111 statuses.get(*idx).unwrap() == &PanelStatus::Shown
1112 })
1113 .map(|(_, panel)| panel)
1114 {
1115 if let Some(draw_info) = &panel.draw_info {
1116 self.cr.save()?;
1117 let x = self.extents.left;
1118 panel.x = x;
1119 self.cr.translate(x, 0.0);
1120 (draw_info.draw_fn)(&self.cr, x)?;
1121 self.extents.left += f64::from(draw_info.width);
1122 self.cr.restore()?;
1123 }
1124 }
1125
1126 self.surface.flush();
1127 self.conn.flush()?;
1128
1129 Ok(())
1130 }
1131
1132 fn redraw_center_right(&mut self, standalone: bool) -> Result<()> {
1133 log::info!("Redrawing center panels");
1134 if standalone {
1135 self.redraw_background(&Region::CenterRight)?;
1136 }
1137
1138 let center_statuses =
1139 Self::apply_dependence(self.center_panels.as_slice());
1140
1141 let center_panels = self
1142 .center_panels
1143 .iter_mut()
1144 .enumerate()
1145 .filter(|(idx, _)| {
1146 center_statuses.get(*idx).unwrap() == &PanelStatus::Shown
1147 })
1148 .map(|(_, panel)| panel)
1149 .collect::<Vec<_>>();
1150
1151 let right_statuses =
1152 Self::apply_dependence(self.right_panels.as_slice());
1153
1154 let right_panels = self
1155 .right_panels
1156 .iter()
1157 .enumerate()
1158 .filter(|(idx, _)| {
1159 right_statuses.get(*idx).unwrap() == &PanelStatus::Shown
1160 })
1161 .map(|(_, panel)| panel);
1162
1163 let center_width = f64::from(
1164 center_panels
1165 .iter()
1166 .filter_map(|p| p.draw_info.as_ref().map(|i| i.width))
1167 .sum::<i32>(),
1168 );
1169
1170 self.extents.right = f64::from(
1171 self.width
1172 - right_panels
1173 .filter_map(|p| p.draw_info.as_ref().map(|i| i.width))
1174 .sum::<i32>(),
1175 ) - self.margins.internal;
1176
1177 if center_width
1178 > 2.0f64.mul_add(
1179 -self.margins.internal,
1180 self.extents.right - self.extents.left,
1181 )
1182 {
1183 self.extents.center.0 = self.margins.internal + self.extents.left;
1184 self.extents.center.1 = self.margins.internal + self.extents.left;
1185 self.center_state = CenterState::Unknown;
1186 } else if center_width / 2.0
1187 > self.extents.right
1188 - f64::from(self.width / 2)
1189 - self.margins.internal
1190 {
1191 self.extents.center.0 =
1192 self.extents.right - center_width - self.margins.internal;
1193 self.extents.center.1 =
1194 self.extents.right - center_width - self.margins.internal;
1195 self.center_state = CenterState::Left;
1196 } else if center_width / 2.0
1197 > f64::from(self.width / 2)
1198 - self.extents.left
1199 - self.margins.internal
1200 {
1201 self.extents.center.0 = self.extents.left + self.margins.internal;
1202 self.extents.center.1 = self.extents.left + self.margins.internal;
1203 self.center_state = CenterState::Right;
1204 } else {
1205 self.extents.center.0 =
1206 f64::from(self.width / 2) - center_width / 2.0;
1207 self.extents.center.1 =
1208 f64::from(self.width / 2) - center_width / 2.0;
1209 self.center_state = CenterState::Center;
1210 }
1211
1212 for panel in center_panels {
1213 if let Some(draw_info) = &panel.draw_info {
1214 self.cr.save()?;
1215 let x = self.extents.center.1;
1216 panel.x = x;
1217 self.cr.translate(x, 0.0);
1218 (draw_info.draw_fn)(&self.cr, x)?;
1219 self.extents.center.1 += f64::from(draw_info.width);
1220 self.cr.restore()?;
1221 }
1222 }
1223
1224 self.redraw_right(standalone, Some(right_statuses))?;
1225
1226 self.surface.flush();
1227 self.conn.flush()?;
1228
1229 Ok(())
1230 }
1231
1232 fn redraw_right(
1233 &mut self,
1234 standalone: bool,
1235 statuses: Option<Vec<PanelStatus>>,
1236 ) -> Result<()> {
1237 log::info!("Redrawing right panels");
1238
1239 if standalone {
1240 self.redraw_background(&Region::Right)?;
1241 }
1242
1243 let statuses = statuses.unwrap_or_else(|| {
1244 Self::apply_dependence(self.right_panels.as_slice())
1245 });
1246
1247 let total_width = f64::from(
1248 self.right_panels
1249 .iter()
1250 .enumerate()
1251 .filter(|(idx, _)| {
1252 statuses.get(*idx).unwrap() == &PanelStatus::Shown
1253 })
1254 .map(|(_, panel)| panel)
1255 .filter_map(|p| p.draw_info.as_ref().map(|i| i.width))
1256 .sum::<i32>(),
1257 ) + self.margins.right;
1258
1259 if total_width > f64::from(self.width) - self.extents.center.1 {
1260 self.extents.right = self.extents.center.1 + self.margins.internal;
1261 } else {
1262 self.extents.right = f64::from(self.width) - total_width;
1263 }
1264
1265 let mut temp = self.extents.right;
1266
1267 for panel in self
1268 .right_panels
1269 .iter_mut()
1270 .enumerate()
1271 .filter(|(idx, _)| {
1272 statuses.get(*idx).unwrap() == &PanelStatus::Shown
1273 })
1274 .map(|(_, panel)| panel)
1275 {
1276 if let Some(draw_info) = &panel.draw_info {
1277 self.cr.save()?;
1278 let x = temp;
1279 panel.x = x;
1280 self.cr.translate(x, 0.0);
1281 (draw_info.draw_fn)(&self.cr, x)?;
1282 temp += f64::from(draw_info.width);
1283 self.cr.restore()?;
1284 }
1285 }
1286
1287 self.surface.flush();
1288 self.conn.flush()?;
1289
1290 Ok(())
1291 }
1292}