Thomas Dy 7 жил өмнө
parent
commit
e25e45b093

+ 1 - 1
src/main.rs

@@ -61,7 +61,7 @@ fn main() {
         let mut right_widgets = vec![
             widgets::mpd(tx.clone(), panel.make_draw_context()),
             widgets::sensors(panel.make_draw_context(), &cfg),
-            widgets::tray(tx.clone(), panel.conn.clone(), panel.window)
+            widgets::tray(panel.conn.clone(), panel.window)
         ];
         right_widgets.reverse();
 

+ 0 - 4
src/ui/draw_context.rs

@@ -76,8 +76,4 @@ impl DrawContext {
             &[(xcb::render::CP_CLIP_MASK, xcb::render::PICTURE_NONE)]
         );
     }
-
-    pub fn flush(&self) {
-        self.conn.flush();
-    }
 }

+ 132 - 52
src/ui/panel.rs

@@ -7,20 +7,66 @@ use ui::ext;
 use ui::ext::ConnectionExt;
 use ui::font;
 use ui::x11;
-use widgets::{Message, Widget};
+use widgets::{Message, Update, Widget};
 
 use std::rc::Rc;
 use std::sync::Arc;
 use std::iter;
 use std::slice;
 
+#[derive(PartialEq)]
+enum WidgetStatus {
+    Okay,
+    PendingRender,
+    PendingMeasure,
+    Measured
+}
+
 struct WidgetState {
     widget: Box<Widget>,
     position: u16,
-    width: u16
+    width: u16,
+    status: WidgetStatus
 }
 
 impl WidgetState {
+    fn new(widget: Box<Widget>) -> Self {
+        WidgetState {
+            widget: widget,
+            position: 0,
+            width: 0,
+            status: WidgetStatus::PendingMeasure
+        }
+    }
+
+    fn should_render(&self) -> bool {
+        match self.status {
+            WidgetStatus::Measured => true,
+            WidgetStatus::PendingRender => true,
+            _ => false
+        }
+    }
+
+    fn update(&mut self, status: WidgetStatus) {
+        if self.status == WidgetStatus::PendingMeasure && status == WidgetStatus::PendingRender {
+        }
+        else {
+            self.status = status;
+        }
+    }
+
+    fn measure(&mut self) -> bool {
+        if self.status == WidgetStatus::PendingMeasure {
+            self.update(WidgetStatus::Measured);
+            let old_width = self.width;
+            self.width = self.widget.width();
+            if old_width != self.width {
+                return true;
+            }
+        }
+        false
+    }
+
     fn contains(&self, x: u16) -> bool {
         x >= self.position && x < self.position + self.width
     }
@@ -120,59 +166,75 @@ impl Panel {
     pub fn handle_event(&mut self, event: Message) -> bool {
         let finishing = self.finishing;
         let mut should_exit = false;
-        if !finishing {
-            for state in self.widgets_iter() {
-                should_exit |= state.widget.handle_event(&event);
+        let mut should_relayout = false;
+        let mut press_location = None;
+
+        if let Message::XcbEvent(ref event) = event {
+            if finishing && event.response_type() == xcb::DESTROY_NOTIFY {
+                let event: &xcb::DestroyNotifyEvent = unsafe { xcb::cast_event(event) };
+                if event.window() == self.window {
+                    return true
+                }
             }
+            if event.response_type() == xcb::EXPOSE {
+                should_relayout = true;
+            }
+            if event.response_type() == xcb::BUTTON_PRESS {
+                let event: &xcb::ButtonPressEvent = unsafe { xcb::cast_event(event) };
+                let x = event.root_x() as u16;
+                press_location = Some(x);
+            };
         }
-        match event {
-            Message::XcbEvent(event) => {
-                if event.response_type() == xcb::EXPOSE {
-                    self.relayout();
-                };
-                if event.response_type() == xcb::BUTTON_PRESS {
-                    let event: &xcb::ButtonPressEvent = unsafe { xcb::cast_event(&event) };
-                    let x = event.root_x() as u16;
-                    for state in self.widgets_iter() {
-                        if state.contains(x) {
-                            let offset_x = x - state.position;
-                            let event = Message::MousePress(offset_x);
-                            state.widget.handle_event(&event);
-                        }
-                    }
+        else if let Message::Quit = event {
+            self.finish()
+        }
+
+        if !finishing {
+            let mut delegate_event = |state: &mut WidgetState, event: &Message| {
+                match state.widget.handle_event(event) {
+                    Update::Quit =>
+                        should_exit = true,
+                    Update::Relayout =>
+                        state.update(WidgetStatus::PendingMeasure),
+                    Update::Rerender =>
+                        state.update(WidgetStatus::PendingRender),
+                    Update::Nothing => ()
                 };
-                if finishing && event.response_type() == xcb::DESTROY_NOTIFY {
-                    let event: &xcb::DestroyNotifyEvent = unsafe { xcb::cast_event(&event) };
-                    if event.window() == self.window {
-                        return true
+            };
+            if let Some(x) = press_location {
+                for mut state in self.widgets_iter() {
+                    if state.contains(x) {
+                        let offset_x = x - state.position;
+                        let event = Message::MousePress(offset_x);
+                        delegate_event(&mut state, &event);
                     }
                 }
-            },
-            Message::Update =>
-                self.relayout(),
-            Message::Relayout =>
-                self.relayout(),
-            Message::Quit =>
-                self.finish(),
-            _ => {}
+            }
+            for mut state in self.widgets_iter() {
+                delegate_event(&mut state, &event);
+            }
+        }
+
+        // check widgets that requested relayout if widths have changed
+        if self.widgets_iter().any(|state| state.measure()) {
+            should_relayout = true;
+        }
+
+        if should_relayout {
+            self.relayout();
+        }
+        else {
+            self.rerender();
         }
         should_exit
     }
 
     pub fn add_left_widget(&mut self, widget: Box<Widget>) {
-        self.left_widgets.push(WidgetState{
-            widget: widget,
-            position: 0,
-            width: 0
-        });
+        self.left_widgets.push(WidgetState::new(widget));
     }
 
     pub fn add_right_widget(&mut self, widget: Box<Widget>) {
-        self.right_widgets.push(WidgetState{
-            widget: widget,
-            position: 0,
-            width: 0
-        });
+        self.right_widgets.push(WidgetState::new(widget));
     }
 
     pub fn relayout(&mut self) {
@@ -185,30 +247,48 @@ impl Panel {
         );
 
         // measure
+        let mut total_width = 0;
+        for state in self.widgets_iter() {
+            state.measure();
+            if !state.widget.fit_width() {
+                total_width += state.width;
+            }
+        }
+        let margin = self.width.saturating_sub(total_width);
+        for state in self.widgets_iter() {
+            if state.widget.fit_width() {
+                state.width = margin;
+            }
+        }
+
+        // position
         let mut left_pos = 0;
         for state in self.left_widgets.iter_mut() {
             state.position = left_pos;
-            state.width = state.widget.width();
             left_pos += state.width;
         }
         let mut right_pos = self.width;
         for state in self.right_widgets.iter_mut() {
-            state.width = state.widget.width();
             right_pos -= state.width;
             state.position = right_pos;
         }
 
-        let margin = right_pos.saturating_sub(left_pos);
-
         // render
         for state in self.widgets_iter() {
-            let width = if state.widget.fit_width() {
-                margin
+            state.widget.render(state.position, state.width);
+            state.update(WidgetStatus::Okay);
+        }
+        self.conn.flush();
+    }
+
+    pub fn rerender(&mut self) {
+        let context = self.make_draw_context();
+        for state in self.widgets_iter() {
+            if state.should_render() {
+                context.draw_bg(state.position, state.width);
+                state.widget.render(state.position, state.width);
+                state.update(WidgetStatus::Okay);
             }
-            else {
-                state.width
-            };
-            state.widget.render(state.position, width);
         }
         self.conn.flush();
     }

+ 9 - 3
src/widgets/mod.rs

@@ -19,7 +19,6 @@ pub use self::wm::bspwm;
 pub type MessageSender = mpsc::Sender<Message>;
 
 pub enum Message {
-    Relayout,
     Update,
     Quit,
     MousePress(u16),
@@ -28,6 +27,13 @@ pub enum Message {
     XcbEvent(xcb::GenericEvent)
 }
 
+pub enum Update {
+    Nothing,
+    Rerender,
+    Relayout,
+    Quit
+}
+
 pub trait Widget {
     fn init(&mut self) {}
     fn render(&mut self, x: u16, width: u16);
@@ -35,8 +41,8 @@ pub trait Widget {
     fn fit_width(&self) -> bool {
         false
     }
-    fn handle_event(&mut self, _event: &Message) -> bool {
-        false
+    fn handle_event(&mut self, _event: &Message) -> Update {
+        Update::Nothing
     }
     fn finish(&mut self) {}
 }

+ 7 - 21
src/widgets/music.rs

@@ -7,7 +7,7 @@ use std::path::Path;
 use std::thread;
 use ui::color;
 use ui::draw_context::DrawContext;
-use widgets::{Message, MessageSender, Widget};
+use widgets::{Message, MessageSender, Update, Widget};
 
 const MARGIN: u16 = 7;
 const WIDTH: u16 = 250;
@@ -16,8 +16,7 @@ pub struct Mpd {
     context: DrawContext,
     tx: MessageSender,
     state: State,
-    song: Option<Song>,
-    last_pos: u16
+    song: Option<Song>
 }
 
 pub fn mpd(tx: MessageSender, context: DrawContext) -> Box<Widget> {
@@ -25,8 +24,7 @@ pub fn mpd(tx: MessageSender, context: DrawContext) -> Box<Widget> {
         context: context,
         tx: tx,
         state: State::Stop,
-        song: None,
-        last_pos: 0
+        song: None
     };
     Box::new(widget)
 }
@@ -54,15 +52,6 @@ impl Mpd {
             None => "mpd".to_string()
         }
     }
-
-    fn redraw(&mut self) {
-        let x = self.last_pos;
-        self.context.set_bg_color(color::BLACK);
-        self.context.draw_bg(x, WIDTH);
-        self.render(x, WIDTH);
-        self.context.flush();
-    }
-
 }
 
 impl Widget for Mpd {
@@ -72,7 +61,6 @@ impl Widget for Mpd {
     }
 
     fn render(&mut self, x: u16, _w: u16) {
-        self.last_pos = x;
         let icon_width = self.context.measure_text(self.icon());
 
         self.context.set_bg_color(color::GREY);
@@ -81,8 +69,6 @@ impl Widget for Mpd {
 
         let text = self.get_text();
         let text_width = WIDTH - MARGIN * 4 - icon_width;
-        self.context.set_bg_color(color::BLACK);
-        self.context.draw_bg(x + icon_width + MARGIN * 3, text_width);
         self.context.draw_text_with_clipping(&text, x + icon_width + MARGIN * 3, text_width);
     }
 
@@ -90,19 +76,19 @@ impl Widget for Mpd {
         WIDTH
     }
 
-    fn handle_event(&mut self, event: &Message) -> bool {
+    fn handle_event(&mut self, event: &Message) -> Update {
         match event {
             &Message::MpdEvent(ref state, ref song) => {
                 self.state = state.clone();
                 self.song = song.clone();
-                self.redraw();
+                Update::Rerender
             },
             &Message::MousePress(_x) => {
                 toggle();
+                Update::Nothing
             },
-            _ => {}
+            _ => Update::Nothing
         }
-        false
     }
 }
 

+ 7 - 7
src/widgets/sensors.rs

@@ -3,7 +3,7 @@ use super::super::sensors;
 use super::super::sensors::Sensor;
 use ui::color;
 use ui::draw_context::DrawContext;
-use widgets::{Message, Widget};
+use widgets::{Message, Update, Widget};
 
 const MARGIN: u16 = 7;
 
@@ -47,15 +47,15 @@ impl Widget for Sensors {
         sum
     }
 
-    fn handle_event(&mut self, event: &Message) -> bool {
+    fn handle_event(&mut self, event: &Message) -> Update {
         match event {
-            &Message::Update =>
+            &Message::Update => {
                 for ref mut sensor in self.sensors.iter_mut() {
                     sensor.process()
-                },
-            _ => {}
+                }
+                Update::Relayout
+            },
+            _ => Update::Nothing
         }
-
-        false
     }
 }

+ 13 - 23
src/widgets/title.rs

@@ -1,6 +1,6 @@
 use ui::draw_context::DrawContext;
 use ui::x11;
-use widgets::{Message, Widget};
+use widgets::{Message, Update, Widget};
 use xcb;
 
 use std::sync::Arc;
@@ -11,8 +11,6 @@ pub struct Title {
     conn: Arc<x11::Connection>,
     context: DrawContext,
     title: String,
-    last_pos: u16,
-    last_width: u16,
     last_win: xcb::Window
 }
 
@@ -21,23 +19,11 @@ pub fn title(conn: Arc<x11::Connection>, context: DrawContext) -> Box<Widget> {
         conn: conn,
         context: context,
         title: "".to_string(),
-        last_pos: 0,
-        last_width: 0,
         last_win: 0
     };
     Box::new(widget)
 }
 
-impl Title {
-    pub fn redraw(&mut self) {
-        self.context.draw_bg(self.last_pos, self.last_width);
-        let x = self.last_pos;
-        let w = self.last_width;
-        self.render(x, w);
-        self.conn.flush();
-    }
-}
-
 impl Widget for Title {
     fn init(&mut self) {
         let screen = self.conn.default_screen();
@@ -48,9 +34,8 @@ impl Widget for Title {
     }
 
     fn render(&mut self, x: u16, w: u16) {
-        self.last_pos = x;
-        self.last_width = w;
-        self.context.draw_text_with_clipping(&self.title, self.last_pos + MARGIN, w - MARGIN * 2);
+        let width = w.saturating_sub(MARGIN * 2);
+        self.context.draw_text_with_clipping(&self.title, x + MARGIN, width);
     }
 
     fn width(&mut self) -> u16 {
@@ -61,7 +46,7 @@ impl Widget for Title {
         true
     }
 
-    fn handle_event(&mut self, event: &Message) -> bool {
+    fn handle_event(&mut self, event: &Message) -> Update {
         match event {
             &Message::XcbEvent(ref event) =>
                 if event.response_type() == xcb::PROPERTY_NOTIFY {
@@ -75,16 +60,21 @@ impl Widget for Title {
                         self.conn.watch(new_win, true);
                         self.last_win = new_win;
                         self.title = self.conn.get_window_name(new_win);
-                        self.redraw();
+                        Update::Rerender
                     }
                     else if event.atom() == self.conn.atom(x11::_NET_WM_NAME) {
                         self.title = self.conn.get_window_name(self.last_win);
-                        self.redraw();
+                        Update::Rerender
+                    }
+                    else {
+                        Update::Nothing
                     }
+                }
+                else {
+                    Update::Nothing
                 },
-            _ => {}
+            _ => Update::Nothing
         }
-        false
     }
 
     fn finish(&mut self) {

+ 8 - 13
src/widgets/tray.rs

@@ -1,6 +1,6 @@
 use ui;
 use ui::x11;
-use widgets::{Message, MessageSender, Widget};
+use widgets::{Message, Update, Widget};
 use xcb;
 
 use std::sync::Arc;
@@ -8,17 +8,15 @@ use std::sync::Arc;
 const CLIENT_MESSAGE: u8 = xcb::CLIENT_MESSAGE | 0x80; // 0x80 flag for client messages
 
 pub struct Tray {
-    tx: MessageSender,
     conn: Arc<x11::Connection>,
     window: xcb::Window,
     children: Vec<xcb::Window>,
     timestamp: xcb::Timestamp,
 }
 
-pub fn tray(tx: MessageSender, conn: Arc<x11::Connection>, window: xcb::Window) -> Box<Widget> {
+pub fn tray(conn: Arc<x11::Connection>, window: xcb::Window) -> Box<Widget> {
     let widget = Tray {
         conn: conn,
-        tx: tx,
         window: window,
         children: vec![],
         timestamp: 0
@@ -58,16 +56,10 @@ impl Tray {
         self.force_size(window, None);
         conn.flush();
         self.children.push(window);
-        self.relayout();
     }
 
     pub fn forget(&mut self, window: xcb::Window) {
         self.children.retain(|child| *child != window);
-        self.relayout();
-    }
-
-    fn relayout(&self) {
-        self.tx.send(Message::Relayout).expect("Failed to send relayout");
     }
 
     pub fn force_size(&self, window: xcb::Window, dimensions: Option<(u16, u16)>) {
@@ -101,7 +93,7 @@ impl Widget for Tray {
         }
     }
 
-    fn handle_event(&mut self, event: &Message) -> bool {
+    fn handle_event(&mut self, event: &Message) -> Update {
         match event {
             &Message::XcbEvent(ref event) =>
                 match event.response_type() {
@@ -109,7 +101,7 @@ impl Widget for Tray {
                         let event: &xcb::PropertyNotifyEvent = unsafe { xcb::cast_event(&event) };
                         if !self.take_selection(event.time()) {
                             println!("Could not take ownership of tray selection. Maybe another tray is also running?");
-                            return true
+                            return Update::Quit
                         }
                     },
                     CLIENT_MESSAGE => {
@@ -120,6 +112,7 @@ impl Widget for Tray {
                             let window = data[2];
                             if opcode == 0 {
                                 self.adopt(window);
+                                return Update::Relayout
                             }
                         }
                     },
@@ -127,11 +120,13 @@ impl Widget for Tray {
                         let event: &xcb::ReparentNotifyEvent = unsafe { xcb::cast_event(&event) };
                         if event.parent() != self.window {
                             self.forget(event.window());
+                            return Update::Relayout
                         }
                     },
                     xcb::DESTROY_NOTIFY => {
                         let event: &xcb::DestroyNotifyEvent = unsafe { xcb::cast_event(&event) };
                         self.forget(event.window());
+                        return Update::Relayout
                     },
                     xcb::CONFIGURE_NOTIFY => {
                         let event: &xcb::ConfigureNotifyEvent = unsafe { xcb::cast_event(&event) };
@@ -147,7 +142,7 @@ impl Widget for Tray {
                 },
                 _ => {}
         }
-        false
+        Update::Nothing
     }
 
     fn finish(&mut self) {

+ 5 - 5
src/widgets/wm.rs

@@ -5,7 +5,7 @@ use std::thread;
 
 use ui::draw_context::DrawContext;
 use ui::color;
-use widgets::{Message, MessageSender, Widget};
+use widgets::{Message, MessageSender, Update, Widget};
 
 const MARGIN: u16 = 7;
 
@@ -112,11 +112,11 @@ impl Widget for Bspwm {
         sum
     }
 
-    fn handle_event(&mut self, event: &Message) -> bool {
+    fn handle_event(&mut self, event: &Message) -> Update {
         match event {
             &Message::BspwmEvent(ref line) => {
                 self.parse_bspwm(line);
-                self.tx.send(Message::Relayout).expect("Failed to send relayout");
+                Update::Relayout
             },
             &Message::MousePress(x) => {
                 for desktop in self.desktops.iter() {
@@ -124,10 +124,10 @@ impl Widget for Bspwm {
                         switch_desktop(&desktop.name);
                     }
                 }
+                Update::Nothing
             },
-            _ => {}
+            _ => Update::Nothing
         }
-        false
     }
 }