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
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.
## 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 geom_validation;
pub mod hider;
pub mod logs;
pub mod road_editor;
pub mod search;
pub mod show_route;

View File

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

View File

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