فهرست منبع

Implement mouse handling

Thomas Dy 7 سال پیش
والد
کامیت
c1d534cb3d
4فایلهای تغییر یافته به همراه131 افزوده شده و 57 حذف شده
  1. 63 39
      src/ui/bspwm.rs
  2. 9 0
      src/ui/music.rs
  3. 58 18
      src/ui/panel.rs
  4. 1 0
      src/ui/widget.rs

+ 63 - 39
src/ui/bspwm.rs

@@ -9,7 +9,15 @@ const MARGIN: u16 = 7;
 struct Desktop {
     name: String,
     selected: bool,
-    urgent: bool
+    urgent: bool,
+    position: u16,
+    width: u16
+}
+
+impl Desktop {
+    fn contains(&self, x: u16) -> bool {
+        x >= self.position && x < self.position + self.width
+    }
 }
 
 pub struct Bspwm {
@@ -26,6 +34,43 @@ impl Bspwm {
             desktops: vec![]
         }
     }
+
+    fn parse_bspwm(&mut self, line: &str) {
+        let mut desktops = vec![];
+        let elems = line.split(':');
+
+        let mut pos = 0;
+
+        for elem in elems {
+            let mut chars = elem.chars();
+            let kind = chars.next().unwrap();
+            let name = chars.collect::<String>();
+
+            if kind == 'M' || kind == 'm' {}
+            else if kind == 'L' {}
+            else if kind == 'G' {}
+            else if kind == 'T' {}
+            else {
+                let empty = kind == 'f';
+                let urgent = kind == 'U' || kind == 'u';
+                let selected = kind.is_uppercase();
+
+                if !empty {
+                    let width = self.context.measure_text(&name) + MARGIN * 2;
+                    desktops.push(Desktop {
+                        name: name,
+                        selected: selected,
+                        urgent: urgent,
+                        position: pos,
+                        width: width
+                    });
+                    pos += width;
+                }
+            }
+        }
+
+        self.desktops = desktops;
+    }
 }
 
 impl widget::Widget for Bspwm {
@@ -35,10 +80,7 @@ impl widget::Widget for Bspwm {
     }
 
     fn render(&mut self, x: u16) {
-        let mut offset = x;
         for desktop in self.desktops.iter() {
-            let text_width = self.context.measure_text(&desktop.name);
-
             if desktop.selected {
                 self.context.set_bg_color(0x6666, 0xCCCC, 0x6666, 0xFFFF);
             }
@@ -48,10 +90,8 @@ impl widget::Widget for Bspwm {
             else {
                 self.context.set_bg_color(0x0, 0x0, 0x0, 0xFFFF);
             }
-            self.context.draw_bg(offset, text_width + MARGIN * 2);
-            self.context.draw_text(&desktop.name, offset + MARGIN);
-
-            offset += text_width + MARGIN * 2;
+            self.context.draw_bg(x + desktop.position, desktop.width);
+            self.context.draw_text(&desktop.name, x + desktop.position + MARGIN);
         }
     }
 
@@ -66,9 +106,16 @@ impl widget::Widget for Bspwm {
     fn handle_event(&mut self, event: &widget::Message) -> bool {
         match event {
             &widget::Message::BspwmEvent(ref line) => {
-                self.desktops = parse_bspwm(line);
+                self.parse_bspwm(line);
                 self.tx.send(widget::Message::Relayout).expect("Failed to send relayout");
             },
+            &widget::Message::MousePress(x) => {
+                for desktop in self.desktops.iter() {
+                    if desktop.contains(x) {
+                        switch_desktop(&desktop.name);
+                    }
+                }
+            },
             _ => {}
         }
         false
@@ -104,34 +151,11 @@ fn monitor_thread(tx: widget::MessageSender) {
     }
 }
 
-fn parse_bspwm(line: &str) -> Vec<Desktop> {
-    let mut desktops = vec![];
-    let elems = line.split(':');
-
-    for elem in elems {
-        let mut chars = elem.chars();
-        let kind = chars.next().unwrap();
-        let name = chars.collect::<String>();
-
-        if kind == 'M' || kind == 'm' {}
-        else if kind == 'L' {}
-        else if kind == 'G' {}
-        else if kind == 'T' {}
-        else {
-            let empty = kind == 'f';
-            let urgent = kind == 'U' || kind == 'u';
-            let selected = kind.is_uppercase();
-
-            if !empty {
-                desktops.push(Desktop {
-                    name: name,
-                    selected: selected,
-                    urgent: urgent
-                })
-            }
-        }
-    }
-
-    desktops
+fn switch_desktop(name: &str) {
+    Command::new("bspc")
+        .arg("desktop")
+        .arg("-f")
+        .arg(name)
+        .output()
+        .ok();
 }
-

+ 9 - 0
src/ui/music.rs

@@ -45,6 +45,7 @@ impl Music {
         self.render(x);
         self.context.flush();
     }
+
 }
 
 impl widget::Widget for Music {
@@ -86,6 +87,9 @@ impl widget::Widget for Music {
                 self.title = title.clone();
                 self.redraw();
             },
+            &widget::Message::MousePress(_x) => {
+                toggle();
+            },
             _ => {}
         }
         false
@@ -105,3 +109,8 @@ fn monitor_thread(tx: widget::MessageSender) {
         conn.wait(&[idle::Subsystem::Player]).ok();
     }
 }
+
+fn toggle() {
+    let mut conn = mpd::client::Client::connect("127.0.0.1:6600").unwrap();
+    conn.toggle_pause().expect("Failed to pause");
+}

+ 58 - 18
src/ui/panel.rs

@@ -12,12 +12,24 @@ use std::sync::Arc;
 use std::iter;
 use std::slice;
 
+struct WidgetState {
+    widget: Box<Widget>,
+    position: u16,
+    width: u16
+}
+
+impl WidgetState {
+    fn contains(&self, x: u16) -> bool {
+        x >= self.position && x < self.position + self.width
+    }
+}
+
 pub struct Panel {
     pub conn: Arc<x11::Connection>,
     pub window: xcb::Window,
     pub width: u16,
-    left_widgets: Vec<Box<Widget>>,
-    right_widgets: Vec<Box<Widget>>,
+    left_widgets: Vec<WidgetState>,
+    right_widgets: Vec<WidgetState>,
     fonts: Rc<font::FontLoader>,
     picture: xcb::render::Picture,
     finishing: bool
@@ -62,7 +74,7 @@ impl Panel {
             xcb::COPY_FROM_PARENT,
             &[
                 (xcb::CW_BACK_PIXEL, 0xFF000000),
-                (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_PROPERTY_CHANGE | xcb::EVENT_MASK_STRUCTURE_NOTIFY)
+                (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_PROPERTY_CHANGE | xcb::EVENT_MASK_STRUCTURE_NOTIFY | xcb::EVENT_MASK_BUTTON_PRESS)
             ]
         );
         self.set_property(
@@ -77,8 +89,8 @@ impl Panel {
             (xcb::render::CP_POLY_MODE, xcb::render::POLY_MODE_IMPRECISE)
         ]);
         self.conn.flush();
-        for widget in self.widgets_iter() {
-            widget.init();
+        for state in self.widgets_iter() {
+            state.widget.init();
         }
         self.conn.flush();
     }
@@ -87,7 +99,7 @@ impl Panel {
         widget::DrawContext::new(self.conn.clone_connection(), self.window, self.picture, self.fonts.clone())
     }
 
-    pub fn widgets_iter(&mut self) -> iter::Chain<slice::IterMut<Box<Widget>>, slice::IterMut<Box<Widget>>>{
+    fn widgets_iter(&mut self) -> iter::Chain<slice::IterMut<WidgetState>, slice::IterMut<WidgetState>>{
         self.left_widgets.iter_mut().chain(self.right_widgets.iter_mut())
     }
 
@@ -107,8 +119,8 @@ impl Panel {
         let finishing = self.finishing;
         let mut should_exit = false;
         if !finishing {
-            for widget in self.widgets_iter() {
-                should_exit |= widget.handle_event(&event);
+            for state in self.widgets_iter() {
+                should_exit |= state.widget.handle_event(&event);
             }
         }
         match event {
@@ -116,6 +128,17 @@ impl Panel {
                 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 = widget::Message::MousePress(offset_x);
+                            state.widget.handle_event(&event);
+                        }
+                    }
+                };
                 if finishing && event.response_type() == xcb::DESTROY_NOTIFY {
                     let event: &xcb::DestroyNotifyEvent = unsafe { xcb::cast_event(&event) };
                     if event.window() == self.window {
@@ -135,11 +158,19 @@ impl Panel {
     }
 
     pub fn add_left_widget(&mut self, widget: Box<Widget>) {
-        self.left_widgets.push(widget);
+        self.left_widgets.push(WidgetState{
+            widget: widget,
+            position: 0,
+            width: 0
+        });
     }
 
     pub fn add_right_widget(&mut self, widget: Box<Widget>) {
-        self.right_widgets.push(widget);
+        self.right_widgets.push(WidgetState{
+            widget: widget,
+            position: 0,
+            width: 0
+        });
     }
 
     pub fn relayout(&mut self) {
@@ -151,23 +182,32 @@ impl Panel {
             color,
             &[xcb::Rectangle::new(0, 0, self.width, 20)]
         );
+
+        // measure
         let mut left_pos = 0;
-        for widget in self.left_widgets.iter_mut() {
-            widget.render(left_pos);
-            left_pos += widget.width();
+        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 widget in self.right_widgets.iter_mut() {
-            right_pos -= widget.width();
-            widget.render(right_pos);
+        for state in self.right_widgets.iter_mut() {
+            state.width = state.widget.width();
+            right_pos -= state.width;
+            state.position = right_pos;
+        }
+
+        // render
+        for state in self.widgets_iter() {
+            state.widget.render(state.position);
         }
         self.conn.flush();
     }
 
     pub fn finish(&mut self) {
         self.finishing = true;
-        for widget in self.widgets_iter() {
-            widget.finish();
+        for state in self.widgets_iter() {
+            state.widget.finish();
         }
         xcb::destroy_window(&self.conn, self.window);
         self.conn.flush();

+ 1 - 0
src/ui/widget.rs

@@ -15,6 +15,7 @@ pub enum Message {
     Relayout,
     Update,
     Quit,
+    MousePress(u16),
     BspwmEvent(String),
     MpdEvent(mpd::status::State, String, String),
     XcbEvent(xcb::GenericEvent)