panel.rs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. use config::Config;
  2. use xcb;
  3. use ui;
  4. use ui::color;
  5. use ui::draw_context::DrawContext;
  6. use ui::ext;
  7. use ui::ext::ConnectionExt;
  8. use ui::font;
  9. use ui::x11;
  10. use widgets::{Message, Update, Widget};
  11. use std::rc::Rc;
  12. use std::sync::Arc;
  13. use std::iter;
  14. use std::slice;
  15. #[derive(PartialEq)]
  16. enum WidgetStatus {
  17. Okay,
  18. PendingRender,
  19. PendingMeasure,
  20. Measured
  21. }
  22. struct WidgetState {
  23. widget: Box<Widget>,
  24. position: u16,
  25. width: u16,
  26. status: WidgetStatus
  27. }
  28. impl WidgetState {
  29. fn new(widget: Box<Widget>) -> Self {
  30. WidgetState {
  31. widget: widget,
  32. position: 0,
  33. width: 0,
  34. status: WidgetStatus::PendingMeasure
  35. }
  36. }
  37. fn should_render(&self) -> bool {
  38. match self.status {
  39. WidgetStatus::Measured => true,
  40. WidgetStatus::PendingRender => true,
  41. _ => false
  42. }
  43. }
  44. fn update(&mut self, status: WidgetStatus) {
  45. if self.status == WidgetStatus::PendingMeasure && status == WidgetStatus::PendingRender {
  46. }
  47. else {
  48. self.status = status;
  49. }
  50. }
  51. fn measure(&mut self) -> bool {
  52. if self.status == WidgetStatus::PendingMeasure {
  53. self.update(WidgetStatus::Measured);
  54. let old_width = self.width;
  55. self.width = self.widget.width();
  56. if old_width != self.width {
  57. return true;
  58. }
  59. }
  60. false
  61. }
  62. fn contains(&self, x: u16) -> bool {
  63. x >= self.position && x < self.position + self.width
  64. }
  65. }
  66. pub struct Panel {
  67. pub conn: Arc<x11::Connection>,
  68. pub window: xcb::Window,
  69. pub width: u16,
  70. left_widgets: Vec<WidgetState>,
  71. right_widgets: Vec<WidgetState>,
  72. fonts: Rc<font::FontLoader>,
  73. picture: xcb::render::Picture,
  74. finishing: bool
  75. }
  76. impl Panel {
  77. pub fn new(conn: x11::Connection, cfg: &Config) -> Panel {
  78. let conn = Arc::new(conn);
  79. let window = conn.generate_id();
  80. let picture = conn.generate_id();
  81. let width = conn.default_screen().width;
  82. let font_loader = Rc::new(font::FontLoader::from_config(conn.clone_connection(), cfg));
  83. Panel {
  84. conn: conn,
  85. width: width,
  86. window: window,
  87. left_widgets: vec![],
  88. right_widgets: vec![],
  89. fonts: font_loader,
  90. picture: picture,
  91. finishing: false
  92. }
  93. }
  94. pub fn create(&mut self) {
  95. let (root, width) = {
  96. let screen = self.conn.default_screen();
  97. (screen.root, screen.width)
  98. };
  99. xcb::create_window(
  100. &self.conn,
  101. xcb::COPY_FROM_PARENT as u8,
  102. self.window,
  103. root,
  104. 0, 0,
  105. width, ui::SIZE,
  106. 0,
  107. xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
  108. xcb::COPY_FROM_PARENT,
  109. &[
  110. (xcb::CW_BACK_PIXEL, 0xFF000000),
  111. (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_PROPERTY_CHANGE | xcb::EVENT_MASK_STRUCTURE_NOTIFY | xcb::EVENT_MASK_BUTTON_PRESS)
  112. ]
  113. );
  114. self.set_property(
  115. self.conn.atom(x11::_NET_WM_WINDOW_TYPE),
  116. xcb::ATOM_ATOM,
  117. 32,
  118. &[self.conn.atom(x11::_NET_WM_WINDOW_TYPE_DOCK)]
  119. );
  120. xcb::map_window(&self.conn, self.window);
  121. xcb::render::create_picture(&self.conn, self.picture, self.window, self.conn.get_pict_format(ext::PictFormat::RGB24), &[
  122. (xcb::render::CP_POLY_EDGE, xcb::render::POLY_EDGE_SMOOTH),
  123. (xcb::render::CP_POLY_MODE, xcb::render::POLY_MODE_IMPRECISE)
  124. ]);
  125. self.conn.flush();
  126. for state in self.widgets_iter() {
  127. state.widget.init();
  128. }
  129. self.conn.flush();
  130. }
  131. pub fn make_draw_context(&self) -> DrawContext {
  132. DrawContext::new(self.conn.clone_connection(), self.picture, self.fonts.clone())
  133. }
  134. fn widgets_iter(&mut self) -> iter::Chain<slice::IterMut<WidgetState>, slice::IterMut<WidgetState>>{
  135. self.left_widgets.iter_mut().chain(self.right_widgets.iter_mut())
  136. }
  137. pub fn set_property<T>(&self, name: xcb::Atom, type_: xcb::Atom, format: u8, data: &[T]) {
  138. xcb::change_property(
  139. &self.conn,
  140. xcb::PROP_MODE_REPLACE as u8,
  141. self.window,
  142. name,
  143. type_,
  144. format,
  145. data
  146. );
  147. }
  148. pub fn handle_event(&mut self, event: Message) -> bool {
  149. let finishing = self.finishing;
  150. let mut should_exit = false;
  151. let mut should_relayout = false;
  152. let mut press_location = None;
  153. if let Message::XcbEvent(ref event) = event {
  154. if finishing && event.response_type() == xcb::DESTROY_NOTIFY {
  155. let event: &xcb::DestroyNotifyEvent = unsafe { xcb::cast_event(event) };
  156. if event.window() == self.window {
  157. return true
  158. }
  159. }
  160. if event.response_type() == xcb::EXPOSE {
  161. should_relayout = true;
  162. }
  163. if event.response_type() == xcb::BUTTON_PRESS {
  164. let event: &xcb::ButtonPressEvent = unsafe { xcb::cast_event(event) };
  165. let x = event.root_x() as u16;
  166. press_location = Some(x);
  167. };
  168. }
  169. else if let Message::Quit = event {
  170. self.finish()
  171. }
  172. if !finishing {
  173. let mut delegate_event = |state: &mut WidgetState, event: &Message| {
  174. match state.widget.handle_event(event) {
  175. Update::Quit =>
  176. should_exit = true,
  177. Update::Relayout =>
  178. state.update(WidgetStatus::PendingMeasure),
  179. Update::Rerender =>
  180. state.update(WidgetStatus::PendingRender),
  181. Update::Nothing => ()
  182. };
  183. };
  184. if let Some(x) = press_location {
  185. for mut state in self.widgets_iter() {
  186. if state.contains(x) {
  187. let offset_x = x - state.position;
  188. let event = Message::MousePress(offset_x);
  189. delegate_event(&mut state, &event);
  190. }
  191. }
  192. }
  193. for mut state in self.widgets_iter() {
  194. delegate_event(&mut state, &event);
  195. }
  196. }
  197. // check widgets that requested relayout if widths have changed
  198. if self.widgets_iter().any(|state| state.measure()) {
  199. should_relayout = true;
  200. }
  201. if should_relayout {
  202. self.relayout();
  203. }
  204. else {
  205. self.rerender();
  206. }
  207. should_exit
  208. }
  209. pub fn add_left_widget(&mut self, widget: Box<Widget>) {
  210. self.left_widgets.push(WidgetState::new(widget));
  211. }
  212. pub fn add_right_widget(&mut self, widget: Box<Widget>) {
  213. self.right_widgets.push(WidgetState::new(widget));
  214. }
  215. pub fn relayout(&mut self) {
  216. xcb::render::fill_rectangles(
  217. &self.conn,
  218. xcb::render::PICT_OP_SRC as u8,
  219. self.picture,
  220. color::BLACK,
  221. &[xcb::Rectangle::new(0, 0, self.width, ui::SIZE)]
  222. );
  223. // measure
  224. let mut total_width = 0;
  225. for state in self.widgets_iter() {
  226. state.measure();
  227. if !state.widget.fit_width() {
  228. total_width += state.width;
  229. }
  230. }
  231. let margin = self.width.saturating_sub(total_width);
  232. for state in self.widgets_iter() {
  233. if state.widget.fit_width() {
  234. state.width = margin;
  235. }
  236. }
  237. // position
  238. let mut left_pos = 0;
  239. for state in self.left_widgets.iter_mut() {
  240. state.position = left_pos;
  241. left_pos += state.width;
  242. }
  243. let mut right_pos = self.width;
  244. for state in self.right_widgets.iter_mut() {
  245. right_pos -= state.width;
  246. state.position = right_pos;
  247. }
  248. // render
  249. for state in self.widgets_iter() {
  250. state.widget.render(state.position, state.width);
  251. state.update(WidgetStatus::Okay);
  252. }
  253. self.conn.flush();
  254. }
  255. pub fn rerender(&mut self) {
  256. let context = self.make_draw_context();
  257. for state in self.widgets_iter() {
  258. if state.should_render() {
  259. context.draw_bg(state.position, state.width);
  260. state.widget.render(state.position, state.width);
  261. state.update(WidgetStatus::Okay);
  262. }
  263. }
  264. self.conn.flush();
  265. }
  266. pub fn finish(&mut self) {
  267. self.finishing = true;
  268. for state in self.widgets_iter() {
  269. state.widget.finish();
  270. }
  271. xcb::destroy_window(&self.conn, self.window);
  272. self.conn.flush();
  273. }
  274. }