Browse Source

Implement title widget

Thomas Dy 8 năm trước cách đây
mục cha
commit
89f2aba3af
6 tập tin đã thay đổi với 216 bổ sung65 xóa
  1. 12 0
      src/ui/font.rs
  2. 4 0
      src/ui/mod.rs
  3. 50 61
      src/ui/panel.rs
  4. 83 0
      src/ui/title.rs
  5. 5 2
      src/ui/tray.rs
  6. 62 2
      src/ui/widget.rs

+ 12 - 0
src/ui/font.rs

@@ -1,3 +1,4 @@
+use config::Config;
 use freetype as ft;
 use fontconfig_sys::*;
 use ui::ext;
@@ -144,6 +145,17 @@ impl FontLoader {
         }
     }
 
+    pub fn from_config(conn: Arc<xcb::Connection>, cfg: &Config) -> Self {
+        let val = cfg.lookup("bar.fonts").unwrap();
+        let fonts = val.as_slice().unwrap();
+        let fonts = fonts.iter().flat_map(|elem| elem.as_str());
+        let mut font_loader = Self::new(conn);
+        for font in fonts {
+            font_loader.load(font);
+        }
+        font_loader
+    }
+
     fn find_face(&self, name: &str) -> Option<ft::Face<'static>> {
         unsafe {
             let slice = CString::new(name.to_string()).unwrap();

+ 4 - 0
src/ui/mod.rs

@@ -1,6 +1,7 @@
 mod font;
 mod panel;
 mod ext;
+mod title;
 mod tray;
 mod util;
 mod widget;
@@ -31,6 +32,9 @@ pub fn ui_main(signal: chan::Receiver<chan_signal::Signal>, cfg: &Config) -> i32
 
         let (mut panel, panel_rx) = panel::Panel::new(conn, cfg);
 
+        let title = title::Title::new(panel.conn.clone(), panel.make_draw_context());
+        panel.add_left_widget(Box::new(title));
+
         let tray = tray::Tray::new(panel.tx.clone(), panel.conn.clone(), panel.window);
         panel.add_right_widget(Box::new(tray));
 

+ 50 - 61
src/ui/panel.rs

@@ -4,18 +4,23 @@ use xcb;
 use ui::ext;
 use ui::ext::ConnectionExt;
 use ui::font;
+use ui::widget;
 use ui::widget::Widget;
 use ui::x11;
 
+use std::rc::Rc;
 use std::sync::Arc;
+use std::iter;
+use std::slice;
 
 pub struct Panel {
     pub conn: Arc<x11::Connection>,
     pub window: xcb::Window,
     pub width: u16,
     pub tx: chan::Sender<()>,
+    left_widgets: Vec<Box<Widget>>,
     right_widgets: Vec<Box<Widget>>,
-    fonts: font::FontLoader,
+    fonts: Rc<font::FontLoader>,
     picture: xcb::render::Picture,
     finishing: bool
 }
@@ -26,41 +31,37 @@ impl Panel {
         let window = conn.generate_id();
         let picture = conn.generate_id();
 
-        let val = cfg.lookup("bar.fonts").unwrap();
-        let fonts = val.as_slice().unwrap();
-        let fonts = fonts.iter().flat_map(|elem| elem.as_str());
-        let mut font_loader = font::FontLoader::new(conn.clone_connection());
-        for font in fonts {
-            font_loader.load(font);
-        }
-
         let (tx, rx) = chan::sync(10);
         let width = conn.default_screen().width;
+        let font_loader = Rc::new(font::FontLoader::from_config(conn.clone_connection(), cfg));
 
         let panel = Panel {
             conn: conn,
             width: width,
             tx: tx,
             window: window,
-            fonts: font_loader,
+            left_widgets: vec![],
             right_widgets: vec![],
+            fonts: font_loader,
             picture: picture,
             finishing: false
         };
         (panel, rx)
     }
 
-    pub fn create(&self) {
-        let conn = &self.conn;
-        let screen = conn.default_screen();
+    pub fn create(&mut self) {
+        let (root, width) = {
+            let screen = self.conn.default_screen();
+            (screen.root, screen.width)
+        };
 
         xcb::create_window(
-            conn,
+            &self.conn,
             xcb::COPY_FROM_PARENT as u8,
             self.window,
-            screen.root,
+            root,
             0, 0,
-            screen.width, 20,
+            width, 20,
             0,
             xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
             xcb::COPY_FROM_PARENT,
@@ -70,17 +71,29 @@ impl Panel {
             ]
         );
         self.set_property(
-            conn.atom(x11::_NET_WM_WINDOW_TYPE),
+            self.conn.atom(x11::_NET_WM_WINDOW_TYPE),
             xcb::ATOM_ATOM,
             32,
-            &[conn.atom(x11::_NET_WM_WINDOW_TYPE_DOCK)]
+            &[self.conn.atom(x11::_NET_WM_WINDOW_TYPE_DOCK)]
         );
-        xcb::map_window(conn, self.window);
-        xcb::render::create_picture(&conn, self.picture, self.window, conn.get_pict_format(ext::PictFormat::RGB24), &[
+        xcb::map_window(&self.conn, self.window);
+        xcb::render::create_picture(&self.conn, self.picture, self.window, self.conn.get_pict_format(ext::PictFormat::RGB24), &[
             (xcb::render::CP_POLY_EDGE, xcb::render::POLY_EDGE_SMOOTH),
             (xcb::render::CP_POLY_MODE, xcb::render::POLY_MODE_IMPRECISE)
         ]);
-        conn.flush();
+        self.conn.flush();
+        for widget in self.widgets_iter() {
+            widget.init();
+        }
+        self.conn.flush();
+    }
+
+    pub fn make_draw_context(&self) -> widget::DrawContext {
+        widget::DrawContext::new(self.conn.clone_connection(), self.picture, self.fonts.clone())
+    }
+
+    pub fn widgets_iter(&mut self) -> iter::Chain<slice::IterMut<Box<Widget>>, slice::IterMut<Box<Widget>>>{
+        self.left_widgets.iter_mut().chain(self.right_widgets.iter_mut())
     }
 
     pub fn set_property<T>(&self, name: xcb::Atom, type_: xcb::Atom, format: u8, data: &[T]) {
@@ -96,15 +109,15 @@ impl Panel {
     }
 
     pub fn handle_event(&mut self, event: xcb::GenericEvent) -> bool {
+        let finishing = self.finishing;
         let mut should_exit = false;
-        for widget in self.right_widgets.iter_mut() {
-            should_exit |= widget.handle_event(&event, self.finishing);
+        for widget in self.widgets_iter() {
+            should_exit |= widget.handle_event(&event, finishing);
         }
         if event.response_type() == xcb::EXPOSE {
             self.relayout();
-            self.draw_text("Hello 世界!");
         }
-        if self.finishing && event.response_type() == xcb::DESTROY_NOTIFY {
+        if finishing && event.response_type() == xcb::DESTROY_NOTIFY {
             let event: &xcb::DestroyNotifyEvent = xcb::cast_event(&event);
             if event.window() == self.window {
                 return true
@@ -113,44 +126,15 @@ impl Panel {
         should_exit
     }
 
-    pub fn add_right_widget(&mut self, widget: Box<Widget>) {
-        self.right_widgets.push(widget);
-    }
-
-    pub fn create_pen(&self, r: u16, g: u16, b: u16, a: u16) -> xcb::render::Picture {
-        let color = xcb::render::Color::new(r, g, b, a);
-        let format = self.conn.get_pict_format(ext::PictFormat::ARGB32);
-        let pixmap = self.conn.generate_id();
-        xcb::create_pixmap(&self.conn, 32, pixmap, self.window, 1, 1);
-        let picture = self.conn.generate_id();
-        xcb::render::create_picture(
-            &self.conn,
-            picture,
-            pixmap,
-            format,
-            &[(xcb::render::CP_REPEAT, xcb::render::REPEAT_NORMAL)]
-        );
-        xcb::render::fill_rectangles(
-            &self.conn,
-            xcb::render::PICT_OP_OVER as u8,
-            picture,
-            color,
-            &[xcb::Rectangle::new(0, 0, 1, 1)]
-        );
-        xcb::free_pixmap(&self.conn, pixmap);
-        self.conn.flush();
-        picture
+    pub fn add_left_widget(&mut self, widget: Box<Widget>) {
+        self.left_widgets.push(widget);
     }
 
-    pub fn draw_text(&self, name: &str) {
-        let pen = self.create_pen(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
-        let text = self.fonts.create_renderable_text(name);
-        let baseline = 20 - self.fonts.default_offset(20);
-        text.render(&self.conn, pen, self.picture, 0, baseline as u16);
-        self.conn.flush();
+    pub fn add_right_widget(&mut self, widget: Box<Widget>) {
+        self.right_widgets.push(widget);
     }
 
-    pub fn relayout(&self) {
+    pub fn relayout(&mut self) {
         let color = xcb::render::Color::new(0, 0, 0, 0xFFFF);
         xcb::render::fill_rectangles(
             &self.conn,
@@ -159,8 +143,13 @@ impl Panel {
             color,
             &[xcb::Rectangle::new(0, 0, self.width, 20)]
         );
+        let mut left_pos = 0;
+        for widget in self.left_widgets.iter_mut() {
+            widget.render(left_pos);
+            left_pos += widget.width();
+        }
         let mut right_pos = self.width;
-        for widget in self.right_widgets.iter() {
+        for widget in self.right_widgets.iter_mut() {
             right_pos -= widget.width();
             widget.render(right_pos);
         }
@@ -169,7 +158,7 @@ impl Panel {
 
     pub fn finish(&mut self) {
         self.finishing = true;
-        for widget in self.right_widgets.iter_mut() {
+        for widget in self.widgets_iter() {
             widget.finish();
         }
         xcb::destroy_window(&self.conn, self.window);

+ 83 - 0
src/ui/title.rs

@@ -0,0 +1,83 @@
+use ui::widget;
+use ui::widget::Widget;
+use ui::x11;
+use xcb;
+
+use std::sync::Arc;
+
+pub struct Title {
+    conn: Arc<x11::Connection>,
+    context: widget::DrawContext,
+    title: String,
+    last_pos: u16,
+    last_width: u16,
+    last_win: xcb::Window
+}
+
+impl Title {
+    pub fn new(conn: Arc<x11::Connection>, context: widget::DrawContext) -> Title {
+        Title {
+            conn: conn,
+            context: context,
+            title: "".to_string(),
+            last_pos: 0,
+            last_width: 0,
+            last_win: 0
+        }
+    }
+
+    pub fn redraw(&mut self) {
+        self.context.draw_bg(self.last_pos, self.last_width);
+        self.context.draw_text(&self.title, self.last_pos);
+        self.last_width = self.context.measure_text(&self.title);
+        self.conn.flush();
+    }
+}
+
+impl Widget for Title {
+    fn init(&mut self) {
+        let screen = self.conn.default_screen();
+        self.last_win = screen.get_active_window();
+        self.title = self.conn.get_window_name(self.last_win);
+        self.conn.watch(screen.root, true);
+        self.conn.watch(self.last_win, true);
+    }
+
+    fn render(&mut self, x: u16) {
+        self.last_pos = x;
+        self.context.draw_text(&self.title, self.last_pos);
+        self.last_width = self.context.measure_text(&self.title);
+    }
+
+    fn width(&mut self) -> u16 {
+        0
+    }
+
+    fn handle_event(&mut self, event: &xcb::GenericEvent, _is_finishing: bool) -> bool {
+        if event.response_type() == xcb::PROPERTY_NOTIFY {
+            let event: &xcb::PropertyNotifyEvent = xcb::cast_event(&event);
+            if event.atom() == self.conn.atom(x11::_NET_ACTIVE_WINDOW) {
+                let new_win = {
+                    let screen = self.conn.default_screen();
+                    screen.get_active_window()
+                };
+                self.conn.watch(self.last_win, false);
+                self.conn.watch(new_win, true);
+                self.last_win = new_win;
+                self.title = self.conn.get_window_name(new_win);
+                self.redraw();
+            }
+            else if event.atom() == self.conn.atom(x11::_NET_WM_NAME) {
+                self.title = self.conn.get_window_name(self.last_win);
+                self.redraw();
+            }
+        }
+        false
+    }
+
+    fn finish(&mut self) {
+        let screen = self.conn.default_screen();
+        self.conn.watch(screen.root, false);
+        self.conn.watch(self.last_win, false);
+    }
+}

+ 5 - 2
src/ui/tray.rs

@@ -83,11 +83,14 @@ impl Tray {
 }
 
 impl widget::Widget for Tray {
-    fn width(&self) -> u16 {
+    fn init(&mut self) {
+    }
+
+    fn width(&mut self) -> u16 {
         (self.children.len() * 20) as u16
     }
 
-    fn render(&self, x: u16) {
+    fn render(&mut self, x: u16) {
         for (index, child) in self.children.iter().enumerate() {
             let window = *child;
             let xpos = x as u32 + index as u32 * 20;

+ 62 - 2
src/ui/widget.rs

@@ -1,8 +1,68 @@
 use xcb;
+use ui::font;
+
+use std::rc::Rc;
+use std::sync::Arc;
 
 pub trait Widget {
-    fn render(&self, x: u16);
-    fn width(&self) -> u16;
+    fn init(&mut self);
+    fn render(&mut self, x: u16);
+    fn width(&mut self) -> u16;
     fn handle_event(&mut self, event: &xcb::GenericEvent, is_finishing: bool) -> bool;
     fn finish(&mut self);
 }
+
+pub struct DrawContext {
+    conn: Arc<xcb::Connection>,
+    picture: xcb::render::Picture,
+    pen: xcb::render::Picture,
+    fonts: Rc<font::FontLoader>,
+    bg_color: xcb::render::Color
+}
+
+impl DrawContext {
+    pub fn new(conn: Arc<xcb::Connection>, picture: xcb::render::Picture, fonts: Rc<font::FontLoader>) -> DrawContext {
+        let pen = conn.generate_id();
+        let color = xcb::render::Color::new(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
+        xcb::render::create_solid_fill(
+            &conn,
+            pen,
+            color
+        );
+
+        DrawContext {
+            conn: conn,
+            picture: picture,
+            pen: pen,
+            fonts: fonts,
+            bg_color: xcb::render::Color::new(0, 0, 0, 0xFFFF)
+        }
+    }
+
+    pub fn draw_bg(&self, x: u16, width: u16) {
+        let color = xcb::render::Color::new(
+            self.bg_color.red(),
+            self.bg_color.green(),
+            self.bg_color.blue(),
+            self.bg_color.alpha()
+        );
+        xcb::render::fill_rectangles(
+            &self.conn,
+            xcb::render::PICT_OP_SRC as u8,
+            self.picture,
+            color,
+            &[xcb::Rectangle::new(x as i16, 0, width, 20)]
+        );
+    }
+
+    pub fn measure_text(&self, name: &str) -> u16 {
+        let text = self.fonts.create_renderable_text(name);
+        text.width
+    }
+
+    pub fn draw_text(&self, name: &str, x: u16) {
+        let text = self.fonts.create_renderable_text(name);
+        let baseline = 20 - self.fonts.default_offset(20);
+        text.render(&self.conn, self.pen, self.picture, x, baseline as u16);
+    }
+}