panel.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. use config::Config;
  2. use xcb;
  3. use ui::draw_context::DrawContext;
  4. use ui::ext;
  5. use ui::ext::ConnectionExt;
  6. use ui::font;
  7. use ui::x11;
  8. use widgets::{Message, Widget};
  9. use std::rc::Rc;
  10. use std::sync::Arc;
  11. use std::iter;
  12. use std::slice;
  13. struct WidgetState {
  14. widget: Box<Widget>,
  15. position: u16,
  16. width: u16
  17. }
  18. impl WidgetState {
  19. fn contains(&self, x: u16) -> bool {
  20. x >= self.position && x < self.position + self.width
  21. }
  22. }
  23. pub struct Panel {
  24. pub conn: Arc<x11::Connection>,
  25. pub window: xcb::Window,
  26. pub width: u16,
  27. left_widgets: Vec<WidgetState>,
  28. right_widgets: Vec<WidgetState>,
  29. fonts: Rc<font::FontLoader>,
  30. picture: xcb::render::Picture,
  31. finishing: bool
  32. }
  33. impl Panel {
  34. pub fn new(conn: x11::Connection, cfg: &Config) -> Panel {
  35. let conn = Arc::new(conn);
  36. let window = conn.generate_id();
  37. let picture = conn.generate_id();
  38. let width = conn.default_screen().width;
  39. let font_loader = Rc::new(font::FontLoader::from_config(conn.clone_connection(), cfg));
  40. Panel {
  41. conn: conn,
  42. width: width,
  43. window: window,
  44. left_widgets: vec![],
  45. right_widgets: vec![],
  46. fonts: font_loader,
  47. picture: picture,
  48. finishing: false
  49. }
  50. }
  51. pub fn create(&mut self) {
  52. let (root, width) = {
  53. let screen = self.conn.default_screen();
  54. (screen.root, screen.width)
  55. };
  56. xcb::create_window(
  57. &self.conn,
  58. xcb::COPY_FROM_PARENT as u8,
  59. self.window,
  60. root,
  61. 0, 0,
  62. width, 20,
  63. 0,
  64. xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
  65. xcb::COPY_FROM_PARENT,
  66. &[
  67. (xcb::CW_BACK_PIXEL, 0xFF000000),
  68. (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_PROPERTY_CHANGE | xcb::EVENT_MASK_STRUCTURE_NOTIFY | xcb::EVENT_MASK_BUTTON_PRESS)
  69. ]
  70. );
  71. self.set_property(
  72. self.conn.atom(x11::_NET_WM_WINDOW_TYPE),
  73. xcb::ATOM_ATOM,
  74. 32,
  75. &[self.conn.atom(x11::_NET_WM_WINDOW_TYPE_DOCK)]
  76. );
  77. xcb::map_window(&self.conn, self.window);
  78. xcb::render::create_picture(&self.conn, self.picture, self.window, self.conn.get_pict_format(ext::PictFormat::RGB24), &[
  79. (xcb::render::CP_POLY_EDGE, xcb::render::POLY_EDGE_SMOOTH),
  80. (xcb::render::CP_POLY_MODE, xcb::render::POLY_MODE_IMPRECISE)
  81. ]);
  82. self.conn.flush();
  83. for state in self.widgets_iter() {
  84. state.widget.init();
  85. }
  86. self.conn.flush();
  87. }
  88. pub fn make_draw_context(&self) -> DrawContext {
  89. DrawContext::new(self.conn.clone_connection(), self.picture, self.fonts.clone())
  90. }
  91. fn widgets_iter(&mut self) -> iter::Chain<slice::IterMut<WidgetState>, slice::IterMut<WidgetState>>{
  92. self.left_widgets.iter_mut().chain(self.right_widgets.iter_mut())
  93. }
  94. pub fn set_property<T>(&self, name: xcb::Atom, type_: xcb::Atom, format: u8, data: &[T]) {
  95. xcb::change_property(
  96. &self.conn,
  97. xcb::PROP_MODE_REPLACE as u8,
  98. self.window,
  99. name,
  100. type_,
  101. format,
  102. data
  103. );
  104. }
  105. pub fn handle_event(&mut self, event: Message) -> bool {
  106. let finishing = self.finishing;
  107. let mut should_exit = false;
  108. if !finishing {
  109. for state in self.widgets_iter() {
  110. should_exit |= state.widget.handle_event(&event);
  111. }
  112. }
  113. match event {
  114. Message::XcbEvent(event) => {
  115. if event.response_type() == xcb::EXPOSE {
  116. self.relayout();
  117. };
  118. if event.response_type() == xcb::BUTTON_PRESS {
  119. let event: &xcb::ButtonPressEvent = unsafe { xcb::cast_event(&event) };
  120. let x = event.root_x() as u16;
  121. for state in self.widgets_iter() {
  122. if state.contains(x) {
  123. let offset_x = x - state.position;
  124. let event = Message::MousePress(offset_x);
  125. state.widget.handle_event(&event);
  126. }
  127. }
  128. };
  129. if finishing && event.response_type() == xcb::DESTROY_NOTIFY {
  130. let event: &xcb::DestroyNotifyEvent = unsafe { xcb::cast_event(&event) };
  131. if event.window() == self.window {
  132. return true
  133. }
  134. }
  135. },
  136. Message::Update =>
  137. self.relayout(),
  138. Message::Relayout =>
  139. self.relayout(),
  140. Message::Quit =>
  141. self.finish(),
  142. _ => {}
  143. }
  144. should_exit
  145. }
  146. pub fn add_left_widget(&mut self, widget: Box<Widget>) {
  147. self.left_widgets.push(WidgetState{
  148. widget: widget,
  149. position: 0,
  150. width: 0
  151. });
  152. }
  153. pub fn add_right_widget(&mut self, widget: Box<Widget>) {
  154. self.right_widgets.push(WidgetState{
  155. widget: widget,
  156. position: 0,
  157. width: 0
  158. });
  159. }
  160. pub fn relayout(&mut self) {
  161. let color = xcb::render::Color::new(0, 0, 0, 0xFFFF);
  162. xcb::render::fill_rectangles(
  163. &self.conn,
  164. xcb::render::PICT_OP_SRC as u8,
  165. self.picture,
  166. color,
  167. &[xcb::Rectangle::new(0, 0, self.width, 20)]
  168. );
  169. // measure
  170. let mut left_pos = 0;
  171. for state in self.left_widgets.iter_mut() {
  172. state.position = left_pos;
  173. state.width = state.widget.width();
  174. left_pos += state.width;
  175. }
  176. let mut right_pos = self.width;
  177. for state in self.right_widgets.iter_mut() {
  178. state.width = state.widget.width();
  179. right_pos -= state.width;
  180. state.position = right_pos;
  181. }
  182. let margin = right_pos.saturating_sub(left_pos);
  183. // render
  184. for state in self.widgets_iter() {
  185. let width = if state.widget.fit_width() {
  186. margin
  187. }
  188. else {
  189. state.width
  190. };
  191. state.widget.render(state.position, width);
  192. }
  193. self.conn.flush();
  194. }
  195. pub fn finish(&mut self) {
  196. self.finishing = true;
  197. for state in self.widgets_iter() {
  198. state.widget.finish();
  199. }
  200. xcb::destroy_window(&self.conn, self.window);
  201. self.conn.flush();
  202. }
  203. }