adding a scrollable log buffer widget

This commit is contained in:
Dustin Carlino 2018-09-21 13:32:47 -07:00
parent f6ddd8aeaa
commit f9e0b6facc
6 changed files with 168 additions and 0 deletions

View File

@ -87,3 +87,13 @@ Wait, length of car affects parking pretty critically. A bunch of things plumb
around the precomputed front of the spot, used for drawing and for cars to line around the precomputed front of the spot, used for drawing and for cars to line
up their front in the sim. I think we need to plumb the true start of the spot up their front in the sim. I think we need to plumb the true start of the spot
and have a method to interpolate and pick the true front. and have a method to interpolate and pick the true front.
## Logging
Libraries will do it too -- that's fine
nice UI features:
- highlight what general area publishes a message
- filter by area
- remember where the log scroller is, even when hidden
- jump to end or beginning quickly

View File

@ -0,0 +1,55 @@
use ezgui::{Canvas, GfxCtx, LogScroller, UserInput};
use objects::ROOT_MENU;
use piston::input::Key;
use plugins::Colorizer;
// TODO This is all total boilerplate!
pub enum DisplayLogs {
Inactive,
Active(LogScroller),
}
impl DisplayLogs {
pub fn new() -> DisplayLogs {
DisplayLogs::Inactive
}
pub fn event(&mut self, input: &mut UserInput) -> bool {
let mut new_state: Option<DisplayLogs> = None;
match self {
DisplayLogs::Inactive => {
if input.unimportant_key_pressed(
Key::Comma,
ROOT_MENU,
"show logs",
) {
let mut scroller = LogScroller::new_with_capacity(100);
for i in 0..150 {
scroller.add_line(&format!("Sup line {}", i));
}
new_state = Some(DisplayLogs::Active(scroller));
}
}
DisplayLogs::Active(ref mut scroller) => {
if scroller.event(input) {
new_state = Some(DisplayLogs::Inactive);
}
}
}
if let Some(s) = new_state {
*self = s;
}
match self {
DisplayLogs::Inactive => false,
_ => true,
}
}
pub fn draw(&self, g: &mut GfxCtx, canvas: &Canvas) {
if let DisplayLogs::Active(scroller) = self {
scroller.draw(g, canvas);
}
}
}
impl Colorizer for DisplayLogs {}

View File

@ -6,6 +6,7 @@ pub mod floodfill;
pub mod follow; pub mod follow;
pub mod geom_validation; pub mod geom_validation;
pub mod hider; pub mod hider;
pub mod logs;
pub mod road_editor; pub mod road_editor;
pub mod search; pub mod search;
pub mod show_route; pub mod show_route;

View File

@ -17,6 +17,7 @@ use piston::input::{Key, MouseCursorEvent};
use piston::window::Size; use piston::window::Size;
use plugins::classification::OsmClassifier; use plugins::classification::OsmClassifier;
use plugins::color_picker::ColorPicker; use plugins::color_picker::ColorPicker;
use plugins::logs::DisplayLogs;
use plugins::debug_objects::DebugObjectsState; use plugins::debug_objects::DebugObjectsState;
use plugins::draw_polygon::DrawPolygonState; use plugins::draw_polygon::DrawPolygonState;
use plugins::floodfill::Floodfiller; use plugins::floodfill::Floodfiller;
@ -125,6 +126,7 @@ impl UIWrapper {
turn_cycler: TurnCyclerState::new(), turn_cycler: TurnCyclerState::new(),
draw_polygon: DrawPolygonState::new(), draw_polygon: DrawPolygonState::new(),
wizard_sample: WizardSample::new(), wizard_sample: WizardSample::new(),
logs: DisplayLogs::new(),
active_plugin: None, active_plugin: None,
@ -246,6 +248,7 @@ impl UIWrapper {
Box::new(|ui, input, _osd| ui.turn_cycler.event(input, ui.current_selection)), Box::new(|ui, input, _osd| ui.turn_cycler.event(input, ui.current_selection)),
Box::new(|ui, input, osd| ui.draw_polygon.event(input, &ui.canvas, &ui.map, osd)), Box::new(|ui, input, osd| ui.draw_polygon.event(input, &ui.canvas, &ui.map, osd)),
Box::new(|ui, input, _osd| ui.wizard_sample.event(input, &ui.map)), Box::new(|ui, input, _osd| ui.wizard_sample.event(input, &ui.map)),
Box::new(|ui, input, _osd| ui.logs.event(input)),
], ],
} }
} }
@ -279,6 +282,7 @@ struct UI {
turn_cycler: TurnCyclerState, turn_cycler: TurnCyclerState,
draw_polygon: DrawPolygonState, draw_polygon: DrawPolygonState,
wizard_sample: WizardSample, wizard_sample: WizardSample,
logs: DisplayLogs,
// An index into UIWrapper.plugins. // An index into UIWrapper.plugins.
active_plugin: Option<usize>, active_plugin: Option<usize>,
@ -462,6 +466,7 @@ impl UI {
self.color_picker.draw(&self.canvas, g); self.color_picker.draw(&self.canvas, g);
self.draw_polygon.draw(g, &self.canvas); self.draw_polygon.draw(g, &self.canvas);
self.wizard_sample.draw(g, &self.canvas); self.wizard_sample.draw(g, &self.canvas);
self.logs.draw(g, &self.canvas);
self.search_state.draw(g, &self.canvas); self.search_state.draw(g, &self.canvas);
self.warp.draw(g, &self.canvas); self.warp.draw(g, &self.canvas);
@ -515,6 +520,7 @@ impl UI {
15 => Some(Box::new(&self.turn_cycler)), 15 => Some(Box::new(&self.turn_cycler)),
16 => Some(Box::new(&self.draw_polygon)), 16 => Some(Box::new(&self.draw_polygon)),
17 => Some(Box::new(&self.wizard_sample)), 17 => Some(Box::new(&self.wizard_sample)),
18 => Some(Box::new(&self.logs)),
_ => panic!("Active plugin {} is too high", idx), _ => panic!("Active plugin {} is too high", idx),
} }
} }

View File

@ -11,6 +11,7 @@ extern crate piston;
mod canvas; mod canvas;
mod input; mod input;
mod keys; mod keys;
mod log_scroller;
mod menu; mod menu;
mod runner; mod runner;
mod text; mod text;
@ -23,6 +24,7 @@ use graphics::types::Color;
pub use input::UserInput; pub use input::UserInput;
pub use menu::Menu; pub use menu::Menu;
use opengl_graphics::{GlGraphics, Texture}; use opengl_graphics::{GlGraphics, Texture};
pub use log_scroller::LogScroller;
use piston::input::Key; use piston::input::Key;
pub use runner::{run, EventLoopMode, GUI}; pub use runner::{run, EventLoopMode, GUI};
pub use text::TextOSD; pub use text::TextOSD;

94
ezgui/src/log_scroller.rs Normal file
View File

@ -0,0 +1,94 @@
use piston::input::{Button, Key, PressEvent};
use std::collections::VecDeque;
use {text, Canvas, GfxCtx, TextOSD, UserInput};
pub struct LogScroller {
// TODO store SpanText or similar
lines: VecDeque<String>,
capacity: usize,
y_offset: usize,
}
impl LogScroller {
pub fn new_with_capacity(capacity: usize) -> LogScroller {
LogScroller {
lines: VecDeque::with_capacity(capacity),
// Store separately, since VecDeque might internally choose a bigger capacity
capacity,
y_offset: 0,
}
}
// TODO take and store styled text
pub fn add_line(&mut self, line: &str) {
if self.lines.len() == self.capacity {
self.lines.pop_front();
}
self.lines.push_back(line.to_string());
}
// True if done
pub fn event(&mut self, input: &mut UserInput) -> bool {
let ev = input.use_event_directly().clone();
input.consume_event();
if let Some(Button::Keyboard(Key::Escape)) = ev.press_args() {
return true;
}
if let Some(Button::Keyboard(Key::Up)) = ev.press_args() {
if self.y_offset > 0 {
self.y_offset -= 1;
}
}
if let Some(Button::Keyboard(Key::Down)) = ev.press_args() {
self.y_offset += 1;
}
false
}
// TODO overlapping logic with Menu
pub fn draw(&self, g: &mut GfxCtx, canvas: &Canvas) {
let mut osd = TextOSD::new();
// TODO Force padding of everything to a fixed 80% of the screen or so
osd.add_styled_line(
"Logs".to_string(),
text::TEXT_FG_COLOR,
Some(text::TEXT_QUERY_COLOR),
);
// How many lines can we fit on the screen?
let can_fit = {
// Subtract 1 for the title, and an additional TODO hacky
// few to avoid the bottom OSD and stuff.
let n =
(f64::from(canvas.window_size.height) / text::LINE_HEIGHT).floor() as isize - 1 - 6;
if n <= 0 {
0
} else {
n as usize
}
};
// TODO argh, we want to do this clamping in event() or something; otherwise we can
// accumulate a bunch of invisible silly y_offsetness
let mut low_idx = self.y_offset;
if low_idx + can_fit > self.lines.len() {
if can_fit <= self.lines.len() {
low_idx = self.lines.len() - can_fit;
}
}
let high_idx = (low_idx + can_fit).min(self.lines.len());
// Slice syntax doesn't seem to work for no elements?
if !self.lines.is_empty() {
// TODO VecDeque can't be sliced, argh
let copy: Vec<&String> = self.lines.iter().collect();
for line in &copy[low_idx .. high_idx] {
osd.add_line(line.to_string());
}
}
canvas.draw_centered_text(g, osd);
}
}