123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- use config::Config;
- use xcb;
- use ui;
- use ui::color;
- use ui::draw_context::DrawContext;
- use ui::ext;
- use ui::ext::ConnectionExt;
- use ui::font;
- use ui::x11;
- use widgets::{Message, Update, Widget};
- use std::rc::Rc;
- use std::sync::Arc;
- use std::iter;
- use std::slice;
- #[derive(PartialEq)]
- enum WidgetStatus {
- Okay,
- PendingRender,
- PendingMeasure,
- Measured
- }
- struct WidgetState {
- widget: Box<Widget>,
- position: u16,
- width: u16,
- status: WidgetStatus
- }
- impl WidgetState {
- fn new(widget: Box<Widget>) -> Self {
- WidgetState {
- widget: widget,
- position: 0,
- width: 0,
- status: WidgetStatus::PendingMeasure
- }
- }
- fn should_render(&self) -> bool {
- match self.status {
- WidgetStatus::Measured => true,
- WidgetStatus::PendingRender => true,
- _ => false
- }
- }
- fn update(&mut self, status: WidgetStatus) {
- if self.status == WidgetStatus::PendingMeasure && status == WidgetStatus::PendingRender {
- }
- else {
- self.status = status;
- }
- }
- fn measure(&mut self) -> bool {
- if self.status == WidgetStatus::PendingMeasure {
- self.update(WidgetStatus::Measured);
- let old_width = self.width;
- self.width = self.widget.width();
- if old_width != self.width {
- return true;
- }
- }
- false
- }
- 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, ui::SIZE,
- 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;
- let mut should_relayout = false;
- let mut press_location = None;
- if let Message::XcbEvent(ref 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
- }
- }
- if event.response_type() == xcb::EXPOSE {
- should_relayout = true;
- }
- if event.response_type() == xcb::BUTTON_PRESS {
- let event: &xcb::ButtonPressEvent = unsafe { xcb::cast_event(event) };
- let x = event.root_x() as u16;
- press_location = Some(x);
- };
- }
- else if let Message::Quit = event {
- self.finish()
- }
- if !finishing {
- let mut delegate_event = |state: &mut WidgetState, event: &Message| {
- match state.widget.handle_event(event) {
- Update::Quit =>
- should_exit = true,
- Update::Relayout =>
- state.update(WidgetStatus::PendingMeasure),
- Update::Rerender =>
- state.update(WidgetStatus::PendingRender),
- Update::Nothing => ()
- };
- };
- if let Some(x) = press_location {
- for mut state in self.widgets_iter() {
- if state.contains(x) {
- let offset_x = x - state.position;
- let event = Message::MousePress(offset_x);
- delegate_event(&mut state, &event);
- }
- }
- }
- for mut state in self.widgets_iter() {
- delegate_event(&mut state, &event);
- }
- }
- // check widgets that requested relayout if widths have changed
- if self.widgets_iter().any(|state| state.measure()) {
- should_relayout = true;
- }
- if should_relayout {
- self.relayout();
- }
- else {
- self.rerender();
- }
- should_exit
- }
- pub fn add_left_widget(&mut self, widget: Box<Widget>) {
- self.left_widgets.push(WidgetState::new(widget));
- }
- pub fn add_right_widget(&mut self, widget: Box<Widget>) {
- self.right_widgets.push(WidgetState::new(widget));
- }
- pub fn relayout(&mut self) {
- xcb::render::fill_rectangles(
- &self.conn,
- xcb::render::PICT_OP_SRC as u8,
- self.picture,
- color::BLACK,
- &[xcb::Rectangle::new(0, 0, self.width, ui::SIZE)]
- );
- // measure
- let mut total_width = 0;
- for state in self.widgets_iter() {
- state.measure();
- if !state.widget.fit_width() {
- total_width += state.width;
- }
- }
- let margin = self.width.saturating_sub(total_width);
- for state in self.widgets_iter() {
- if state.widget.fit_width() {
- state.width = margin;
- }
- }
- // position
- let mut left_pos = 0;
- for state in self.left_widgets.iter_mut() {
- state.position = left_pos;
- left_pos += state.width;
- }
- let mut right_pos = self.width;
- for state in self.right_widgets.iter_mut() {
- right_pos -= state.width;
- state.position = right_pos;
- }
- // render
- for state in self.widgets_iter() {
- state.widget.render(state.position, state.width);
- state.update(WidgetStatus::Okay);
- }
- self.conn.flush();
- }
- pub fn rerender(&mut self) {
- let context = self.make_draw_context();
- for state in self.widgets_iter() {
- if state.should_render() {
- context.draw_bg(state.position, state.width);
- state.widget.render(state.position, state.width);
- state.update(WidgetStatus::Okay);
- }
- }
- 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();
- }
- }
|