|
@@ -7,20 +7,66 @@ use ui::ext;
|
|
|
use ui::ext::ConnectionExt;
|
|
|
use ui::font;
|
|
|
use ui::x11;
|
|
|
-use widgets::{Message, Widget};
|
|
|
+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
|
|
|
+ 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
|
|
|
}
|
|
@@ -120,59 +166,75 @@ impl Panel {
|
|
|
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);
|
|
|
+ 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);
|
|
|
+ };
|
|
|
}
|
|
|
- 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);
|
|
|
- }
|
|
|
- }
|
|
|
+ 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 finishing && event.response_type() == xcb::DESTROY_NOTIFY {
|
|
|
- let event: &xcb::DestroyNotifyEvent = unsafe { xcb::cast_event(&event) };
|
|
|
- if event.window() == self.window {
|
|
|
- return true
|
|
|
+ };
|
|
|
+ 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);
|
|
|
}
|
|
|
}
|
|
|
- },
|
|
|
- Message::Update =>
|
|
|
- self.relayout(),
|
|
|
- Message::Relayout =>
|
|
|
- self.relayout(),
|
|
|
- Message::Quit =>
|
|
|
- self.finish(),
|
|
|
- _ => {}
|
|
|
+ }
|
|
|
+ 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{
|
|
|
- widget: widget,
|
|
|
- position: 0,
|
|
|
- width: 0
|
|
|
- });
|
|
|
+ self.left_widgets.push(WidgetState::new(widget));
|
|
|
}
|
|
|
|
|
|
pub fn add_right_widget(&mut self, widget: Box<Widget>) {
|
|
|
- self.right_widgets.push(WidgetState{
|
|
|
- widget: widget,
|
|
|
- position: 0,
|
|
|
- width: 0
|
|
|
- });
|
|
|
+ self.right_widgets.push(WidgetState::new(widget));
|
|
|
}
|
|
|
|
|
|
pub fn relayout(&mut self) {
|
|
@@ -185,30 +247,48 @@ impl Panel {
|
|
|
);
|
|
|
|
|
|
// 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;
|
|
|
- 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
|
|
|
+ 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);
|
|
|
}
|
|
|
- else {
|
|
|
- state.width
|
|
|
- };
|
|
|
- state.widget.render(state.position, width);
|
|
|
}
|
|
|
self.conn.flush();
|
|
|
}
|