123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- use config::Config;
- use xcb;
- use ui::draw_context::DrawContext;
- use ui::ext;
- use ui::ext::ConnectionExt;
- use ui::font;
- use ui::x11;
- use widgets::{Message, Widget};
- use std::rc::Rc;
- 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<WidgetState>,
- right_widgets: Vec<WidgetState>,
- fonts: Rc<font::FontLoader>,
- picture: xcb::render::Picture,
- finishing: bool
- }
- impl Panel {
- pub fn new(conn: x11::Connection, cfg: &Config) -> Panel {
- let conn = Arc::new(conn);
- let window = conn.generate_id();
- let picture = conn.generate_id();
- let width = conn.default_screen().width;
- let font_loader = Rc::new(font::FontLoader::from_config(conn.clone_connection(), cfg));
- Panel {
- conn: conn,
- width: width,
- window: window,
- left_widgets: vec![],
- right_widgets: vec![],
- fonts: font_loader,
- picture: picture,
- finishing: false
- }
- }
- pub fn create(&mut self) {
- let (root, width) = {
- let screen = self.conn.default_screen();
- (screen.root, screen.width)
- };
- xcb::create_window(
- &self.conn,
- xcb::COPY_FROM_PARENT as u8,
- self.window,
- root,
- 0, 0,
- width, 20,
- 0,
- xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
- 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::EVENT_MASK_BUTTON_PRESS)
- ]
- );
- self.set_property(
- self.conn.atom(x11::_NET_WM_WINDOW_TYPE),
- xcb::ATOM_ATOM,
- 32,
- &[self.conn.atom(x11::_NET_WM_WINDOW_TYPE_DOCK)]
- );
- 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)
- ]);
- self.conn.flush();
- for state in self.widgets_iter() {
- state.widget.init();
- }
- self.conn.flush();
- }
- pub fn make_draw_context(&self) -> DrawContext {
- DrawContext::new(self.conn.clone_connection(), self.picture, self.fonts.clone())
- }
- fn widgets_iter(&mut self) -> iter::Chain<slice::IterMut<WidgetState>, slice::IterMut<WidgetState>>{
- 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]) {
- xcb::change_property(
- &self.conn,
- xcb::PROP_MODE_REPLACE as u8,
- self.window,
- name,
- type_,
- format,
- data
- );
- }
- 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);
- }
- }
- 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);
- }
- }
- };
- if finishing && event.response_type() == xcb::DESTROY_NOTIFY {
- let event: &xcb::DestroyNotifyEvent = unsafe { xcb::cast_event(&event) };
- if event.window() == self.window {
- return true
- }
- }
- },
- Message::Update =>
- self.relayout(),
- Message::Relayout =>
- self.relayout(),
- Message::Quit =>
- self.finish(),
- _ => {}
- }
- should_exit
- }
- pub fn add_left_widget(&mut self, widget: Box<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(WidgetState{
- widget: widget,
- position: 0,
- width: 0
- });
- }
- pub fn relayout(&mut self) {
- let color = xcb::render::Color::new(0, 0, 0, 0xFFFF);
- xcb::render::fill_rectangles(
- &self.conn,
- xcb::render::PICT_OP_SRC as u8,
- self.picture,
- color,
- &[xcb::Rectangle::new(0, 0, self.width, 20)]
- );
- // measure
- 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
- }
- else {
- state.width
- };
- state.widget.render(state.position, width);
- }
- self.conn.flush();
- }
- pub fn finish(&mut self) {
- self.finishing = true;
- for state in self.widgets_iter() {
- state.widget.finish();
- }
- xcb::destroy_window(&self.conn, self.window);
- self.conn.flush();
- }
- }
|