mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
move Scroller to ezgui lib
This commit is contained in:
parent
43365e919a
commit
1412a888d6
@ -1,8 +1,5 @@
|
||||
use crate::render::{DrawCtx, DrawTurn};
|
||||
use ezgui::{
|
||||
Canvas, Color, EventCtx, GeomBatch, GfxCtx, ModalMenu, ScreenDims, ScreenPt, ScreenRectangle,
|
||||
Text,
|
||||
};
|
||||
use ezgui::{Color, EventCtx, GeomBatch, GfxCtx, ModalMenu, ScreenDims, ScreenPt, Scroller, Text};
|
||||
use geom::{Circle, Distance, Duration, PolyLine, Polygon, Pt2D};
|
||||
use map_model::{Cycle, IntersectionID, Map, TurnPriority, TurnType, LANE_THICKNESS};
|
||||
use ordered_float::NotNan;
|
||||
@ -224,11 +221,11 @@ impl TrafficSignalDiagram {
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, menu: &mut ModalMenu) {
|
||||
self.scroller.event(ctx);
|
||||
|
||||
if self.scroller.current_selection != 0 && menu.action("select previous cycle") {
|
||||
if self.scroller.current_idx() != 0 && menu.action("select previous cycle") {
|
||||
self.scroller.select_previous();
|
||||
return;
|
||||
}
|
||||
if self.scroller.current_selection != self.scroller.items.len() - 3
|
||||
if self.scroller.current_idx() != self.scroller.num_items() - 1
|
||||
&& menu.action("select next cycle")
|
||||
{
|
||||
self.scroller.select_next(ctx.canvas);
|
||||
@ -236,10 +233,8 @@ impl TrafficSignalDiagram {
|
||||
}
|
||||
}
|
||||
|
||||
//*self = TrafficSignalDiagram::new(self.i, self.scroller.current_selection, &ui.primary.map, ctx);
|
||||
|
||||
pub fn current_cycle(&self) -> usize {
|
||||
self.scroller.current_selection
|
||||
self.scroller.current_idx()
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, ctx: &DrawCtx) {
|
||||
@ -261,263 +256,3 @@ impl TrafficSignalDiagram {
|
||||
g.unfork();
|
||||
}
|
||||
}
|
||||
|
||||
enum Item<T: Clone + Copy> {
|
||||
UpButton,
|
||||
DownButton,
|
||||
ActualItem(T),
|
||||
}
|
||||
|
||||
// TODO Unify with Menu?
|
||||
struct Scroller<T: Clone + Copy> {
|
||||
// TODO Maybe the height of each thing; insist that the width is the same for all?
|
||||
items: Vec<(Item<T>, ScreenDims)>,
|
||||
|
||||
master_topleft: ScreenPt,
|
||||
hovering_on: Option<usize>,
|
||||
bg_color: Color,
|
||||
hovering_color: Color,
|
||||
current_selection_color: Color,
|
||||
|
||||
// Does NOT include buttons!
|
||||
top_idx: usize,
|
||||
current_selection: usize,
|
||||
}
|
||||
|
||||
impl<T: Clone + Copy> Scroller<T> {
|
||||
fn new(
|
||||
master_topleft: ScreenPt,
|
||||
actual_items: Vec<(T, ScreenDims)>,
|
||||
current_selection: usize,
|
||||
ctx: &EventCtx,
|
||||
) -> Scroller<T> {
|
||||
let max_width = actual_items
|
||||
.iter()
|
||||
.map(|(_, dims)| dims.width)
|
||||
.max_by_key(|w| NotNan::new(*w).unwrap())
|
||||
.unwrap();
|
||||
let (_, button_height) = ctx.canvas.text_dims(&Text::from_line("dummy".to_string()));
|
||||
let mut items = vec![(Item::UpButton, ScreenDims::new(max_width, button_height))];
|
||||
for (item, dims) in actual_items {
|
||||
items.push((Item::ActualItem(item), dims));
|
||||
}
|
||||
items.push((Item::DownButton, ScreenDims::new(max_width, button_height)));
|
||||
|
||||
let top_idx = current_selection;
|
||||
// TODO Try to start with current_selection centered, ideally. Or at least start a bit up
|
||||
// in this case. :\
|
||||
|
||||
Scroller {
|
||||
items,
|
||||
master_topleft,
|
||||
hovering_on: None,
|
||||
// TODO ctx.cs
|
||||
bg_color: Color::BLACK.alpha(0.95),
|
||||
hovering_color: Color::RED.alpha(0.95),
|
||||
current_selection_color: Color::BLUE.alpha(0.95),
|
||||
top_idx,
|
||||
current_selection,
|
||||
}
|
||||
}
|
||||
|
||||
// Includes buttons!
|
||||
fn get_visible_items(&self, canvas: &Canvas) -> Vec<(usize, ScreenRectangle)> {
|
||||
// Up button
|
||||
let mut visible = vec![(
|
||||
0,
|
||||
ScreenRectangle {
|
||||
x1: self.master_topleft.x,
|
||||
y1: self.master_topleft.y,
|
||||
x2: self.master_topleft.x + self.items[0].1.width,
|
||||
y2: self.master_topleft.y + self.items[0].1.height,
|
||||
},
|
||||
)];
|
||||
|
||||
// Include the two buttons here
|
||||
let mut space_left = canvas.window_height - (2.0 * self.items[0].1.height);
|
||||
let mut y1 = visible[0].1.y2;
|
||||
|
||||
for idx in 1 + self.top_idx..self.items.len() - 1 {
|
||||
if self.items[idx].1.height > space_left {
|
||||
break;
|
||||
}
|
||||
visible.push((
|
||||
idx,
|
||||
ScreenRectangle {
|
||||
x1: self.master_topleft.x,
|
||||
y1,
|
||||
x2: self.master_topleft.x + self.items[idx].1.width,
|
||||
y2: y1 + self.items[idx].1.height,
|
||||
},
|
||||
));
|
||||
y1 += self.items[idx].1.height;
|
||||
space_left -= self.items[idx].1.height;
|
||||
}
|
||||
|
||||
// Down button
|
||||
visible.push((
|
||||
self.items.len() - 1,
|
||||
ScreenRectangle {
|
||||
x1: self.master_topleft.x,
|
||||
y1,
|
||||
x2: self.master_topleft.x + self.items[0].1.width,
|
||||
y2: y1 + self.items[0].1.height,
|
||||
},
|
||||
));
|
||||
|
||||
visible
|
||||
}
|
||||
|
||||
fn num_items_hidden_below(&self, canvas: &Canvas) -> usize {
|
||||
let visible = self.get_visible_items(canvas);
|
||||
// Ignore the down button
|
||||
let last_idx = visible[visible.len() - 2].0;
|
||||
self.items.len() - 2 - last_idx
|
||||
}
|
||||
|
||||
// Returns the item selected, if it changes
|
||||
fn event(&mut self, ctx: &mut EventCtx) -> Option<T> {
|
||||
if ctx.redo_mouseover() {
|
||||
let cursor = ctx.canvas.get_cursor_in_screen_space();
|
||||
self.hovering_on = None;
|
||||
for (idx, rect) in self.get_visible_items(ctx.canvas) {
|
||||
if rect.contains(cursor) {
|
||||
self.hovering_on = Some(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(idx) = self.hovering_on {
|
||||
if ctx.input.left_mouse_button_pressed() {
|
||||
match self.items[idx].0 {
|
||||
Item::UpButton => {
|
||||
if self.top_idx != 0 {
|
||||
self.top_idx -= 1;
|
||||
}
|
||||
}
|
||||
Item::DownButton => {
|
||||
if self.num_items_hidden_below(ctx.canvas) != 0 {
|
||||
self.top_idx += 1;
|
||||
}
|
||||
}
|
||||
Item::ActualItem(item) => {
|
||||
self.current_selection = idx - 1;
|
||||
return Some(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// Returns the items to draw and the space they occupy.
|
||||
fn draw(&self, g: &mut GfxCtx) -> Vec<(T, ScreenRectangle)> {
|
||||
let visible = self.get_visible_items(g.canvas);
|
||||
// We know buttons have the max_width.
|
||||
let max_width = visible[0].1.width();
|
||||
let mut total_height = 0.0;
|
||||
for (_, rect) in &visible {
|
||||
total_height += rect.height();
|
||||
}
|
||||
|
||||
g.fork_screenspace();
|
||||
g.draw_polygon(
|
||||
self.bg_color,
|
||||
&Polygon::rectangle_topleft(
|
||||
Pt2D::new(self.master_topleft.x, self.master_topleft.y),
|
||||
Distance::meters(max_width),
|
||||
Distance::meters(total_height),
|
||||
),
|
||||
);
|
||||
g.canvas.mark_covered_area(ScreenRectangle::top_left(
|
||||
self.master_topleft,
|
||||
ScreenDims::new(max_width, total_height),
|
||||
));
|
||||
|
||||
let mut items = Vec::new();
|
||||
for (idx, rect) in visible {
|
||||
if Some(idx) == self.hovering_on || idx == self.current_selection + 1 {
|
||||
// Drawing text keeps reseting this. :(
|
||||
g.fork_screenspace();
|
||||
g.draw_polygon(
|
||||
if Some(idx) == self.hovering_on {
|
||||
self.hovering_color
|
||||
} else {
|
||||
self.current_selection_color
|
||||
},
|
||||
&Polygon::rectangle_topleft(
|
||||
Pt2D::new(rect.x1, rect.y1),
|
||||
Distance::meters(rect.width()),
|
||||
Distance::meters(rect.height()),
|
||||
),
|
||||
);
|
||||
}
|
||||
match self.items[idx].0 {
|
||||
Item::UpButton => {
|
||||
// TODO center the text inside the rectangle. and actually, g should have a
|
||||
// method for that.
|
||||
let mut txt = Text::with_bg_color(None);
|
||||
if self.top_idx == 0 {
|
||||
// TODO text::INACTIVE_CHOICE_COLOR
|
||||
txt.add_styled_line(
|
||||
"scroll up".to_string(),
|
||||
Some(Color::grey(0.4)),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
txt.add_line(format!("scroll up ({} more items)", self.top_idx));
|
||||
}
|
||||
g.draw_text_at_screenspace_topleft(&txt, ScreenPt::new(rect.x1, rect.y1));
|
||||
}
|
||||
Item::DownButton => {
|
||||
let mut txt = Text::with_bg_color(None);
|
||||
let num_items = self.num_items_hidden_below(g.canvas);
|
||||
if num_items == 0 {
|
||||
txt.add_styled_line(
|
||||
"scroll down".to_string(),
|
||||
Some(Color::grey(0.4)),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
txt.add_line(format!("scroll down ({} more items)", num_items));
|
||||
}
|
||||
g.draw_text_at_screenspace_topleft(&txt, ScreenPt::new(rect.x1, rect.y1));
|
||||
}
|
||||
Item::ActualItem(item) => {
|
||||
items.push((item, rect));
|
||||
}
|
||||
}
|
||||
}
|
||||
g.unfork();
|
||||
|
||||
items
|
||||
}
|
||||
|
||||
fn select_previous(&mut self) {
|
||||
assert!(self.current_selection != 0);
|
||||
self.current_selection -= 1;
|
||||
// TODO This and the case below aren't right; we might scroll far past the current
|
||||
// selection. Need similar logic for initializing Scroller and make sure the new
|
||||
// current_selection is "centered", but also retain consistency.
|
||||
if self.current_selection < self.top_idx {
|
||||
self.top_idx -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn select_next(&mut self, canvas: &Canvas) {
|
||||
assert!(self.current_selection != self.items.len() - 2);
|
||||
self.current_selection += 1;
|
||||
// Remember, the indices include buttons. :(
|
||||
if self
|
||||
.get_visible_items(canvas)
|
||||
.into_iter()
|
||||
.find(|(idx, _)| self.current_selection + 1 == *idx)
|
||||
.is_none()
|
||||
{
|
||||
self.top_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ glium = "0.25.1"
|
||||
glium-glyph = "0.5.0"
|
||||
glutin = "0.21.0"
|
||||
nom = "4.2.3"
|
||||
ordered-float = "1.0.1"
|
||||
serde = "1.0.89"
|
||||
serde_derive = "1.0.89"
|
||||
simsearch = "0.1.4"
|
||||
|
@ -16,11 +16,11 @@ pub use crate::event::{hotkey, lctrl, Event, Key, MultiKey};
|
||||
pub use crate::event_ctx::{Drawable, EventCtx, Prerender};
|
||||
pub use crate::input::UserInput;
|
||||
pub use crate::runner::{run, EventLoopMode, GUI};
|
||||
pub use crate::screen_geom::{ScreenDims, ScreenPt, ScreenRectangle};
|
||||
pub use crate::screen_geom::{ScreenDims, ScreenPt};
|
||||
pub use crate::text::{Text, HOTKEY_COLOR};
|
||||
pub use crate::widgets::{
|
||||
Autocomplete, ItemSlider, LogScroller, ModalMenu, ScrollingMenu, Slider, SliderWithTextBox,
|
||||
TextBox, Warper, WarpingItemSlider, Wizard, WrappedWizard,
|
||||
Autocomplete, ItemSlider, LogScroller, ModalMenu, Scroller, ScrollingMenu, Slider,
|
||||
SliderWithTextBox, TextBox, Warper, WarpingItemSlider, Wizard, WrappedWizard,
|
||||
};
|
||||
|
||||
pub enum InputResult<T: Clone> {
|
||||
|
@ -3,6 +3,7 @@ mod log_scroller;
|
||||
mod menu;
|
||||
mod modal_menu;
|
||||
mod screenshot;
|
||||
mod scroller;
|
||||
mod scrolling_menu;
|
||||
mod slider;
|
||||
mod text_box;
|
||||
@ -14,6 +15,7 @@ pub use self::log_scroller::LogScroller;
|
||||
pub use self::menu::{Menu, Position};
|
||||
pub use self::modal_menu::ModalMenu;
|
||||
pub(crate) use self::screenshot::{screenshot_current, screenshot_everything};
|
||||
pub use self::scroller::Scroller;
|
||||
pub use self::scrolling_menu::ScrollingMenu;
|
||||
pub use self::slider::{ItemSlider, Slider, SliderWithTextBox, WarpingItemSlider};
|
||||
pub use self::text_box::TextBox;
|
||||
|
274
ezgui/src/widgets/scroller.rs
Normal file
274
ezgui/src/widgets/scroller.rs
Normal file
@ -0,0 +1,274 @@
|
||||
use crate::screen_geom::ScreenRectangle;
|
||||
use crate::{Canvas, Color, EventCtx, GfxCtx, ScreenDims, ScreenPt, Text};
|
||||
use geom::{Distance, Polygon, Pt2D};
|
||||
use ordered_float::NotNan;
|
||||
|
||||
enum Item<T: Clone + Copy> {
|
||||
UpButton,
|
||||
DownButton,
|
||||
ActualItem(T),
|
||||
}
|
||||
|
||||
// TODO Unify with Menu?
|
||||
// TODO Handle window resizing generally
|
||||
// TODO Hide scrolling buttons if not needed... or maybe that's an inconsistent UX
|
||||
pub struct Scroller<T: Clone + Copy> {
|
||||
// TODO Maybe the height of each thing; insist that the width is the same for all?
|
||||
items: Vec<(Item<T>, ScreenDims)>,
|
||||
|
||||
master_topleft: ScreenPt,
|
||||
hovering_on: Option<usize>,
|
||||
bg_color: Color,
|
||||
hovering_color: Color,
|
||||
current_selection_color: Color,
|
||||
|
||||
// Does NOT include buttons!
|
||||
top_idx: usize,
|
||||
current_selection: usize,
|
||||
}
|
||||
|
||||
impl<T: Clone + Copy> Scroller<T> {
|
||||
pub fn new(
|
||||
master_topleft: ScreenPt,
|
||||
actual_items: Vec<(T, ScreenDims)>,
|
||||
current_selection: usize,
|
||||
ctx: &EventCtx,
|
||||
) -> Scroller<T> {
|
||||
let max_width = actual_items
|
||||
.iter()
|
||||
.map(|(_, dims)| dims.width)
|
||||
.max_by_key(|w| NotNan::new(*w).unwrap())
|
||||
.unwrap();
|
||||
let (_, button_height) = ctx.canvas.text_dims(&Text::from_line("dummy".to_string()));
|
||||
let mut items = vec![(Item::UpButton, ScreenDims::new(max_width, button_height))];
|
||||
for (item, dims) in actual_items {
|
||||
items.push((Item::ActualItem(item), dims));
|
||||
}
|
||||
items.push((Item::DownButton, ScreenDims::new(max_width, button_height)));
|
||||
|
||||
let top_idx = current_selection;
|
||||
// TODO Try to start with current_selection centered, ideally. Or at least start a bit up
|
||||
// in this case. :\
|
||||
|
||||
Scroller {
|
||||
items,
|
||||
master_topleft,
|
||||
hovering_on: None,
|
||||
// TODO ctx.cs
|
||||
bg_color: Color::BLACK.alpha(0.95),
|
||||
hovering_color: Color::RED.alpha(0.95),
|
||||
current_selection_color: Color::BLUE.alpha(0.95),
|
||||
top_idx,
|
||||
current_selection,
|
||||
}
|
||||
}
|
||||
|
||||
// Includes buttons!
|
||||
fn get_visible_items(&self, canvas: &Canvas) -> Vec<(usize, ScreenRectangle)> {
|
||||
// Up button
|
||||
let mut visible = vec![(
|
||||
0,
|
||||
ScreenRectangle {
|
||||
x1: self.master_topleft.x,
|
||||
y1: self.master_topleft.y,
|
||||
x2: self.master_topleft.x + self.items[0].1.width,
|
||||
y2: self.master_topleft.y + self.items[0].1.height,
|
||||
},
|
||||
)];
|
||||
|
||||
// Include the two buttons here
|
||||
let mut space_left = canvas.window_height - (2.0 * self.items[0].1.height);
|
||||
let mut y1 = visible[0].1.y2;
|
||||
|
||||
for idx in 1 + self.top_idx..self.items.len() - 1 {
|
||||
if self.items[idx].1.height > space_left {
|
||||
break;
|
||||
}
|
||||
visible.push((
|
||||
idx,
|
||||
ScreenRectangle {
|
||||
x1: self.master_topleft.x,
|
||||
y1,
|
||||
x2: self.master_topleft.x + self.items[idx].1.width,
|
||||
y2: y1 + self.items[idx].1.height,
|
||||
},
|
||||
));
|
||||
y1 += self.items[idx].1.height;
|
||||
space_left -= self.items[idx].1.height;
|
||||
}
|
||||
|
||||
// Down button
|
||||
visible.push((
|
||||
self.items.len() - 1,
|
||||
ScreenRectangle {
|
||||
x1: self.master_topleft.x,
|
||||
y1,
|
||||
x2: self.master_topleft.x + self.items[0].1.width,
|
||||
y2: y1 + self.items[0].1.height,
|
||||
},
|
||||
));
|
||||
|
||||
visible
|
||||
}
|
||||
|
||||
fn num_items_hidden_below(&self, canvas: &Canvas) -> usize {
|
||||
let visible = self.get_visible_items(canvas);
|
||||
// Ignore the down button
|
||||
let last_idx = visible[visible.len() - 2].0;
|
||||
self.items.len() - 2 - last_idx
|
||||
}
|
||||
|
||||
// Returns the item selected, if it changes
|
||||
pub fn event(&mut self, ctx: &mut EventCtx) -> Option<T> {
|
||||
if ctx.redo_mouseover() {
|
||||
let cursor = ctx.canvas.get_cursor_in_screen_space();
|
||||
self.hovering_on = None;
|
||||
for (idx, rect) in self.get_visible_items(ctx.canvas) {
|
||||
if rect.contains(cursor) {
|
||||
self.hovering_on = Some(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(idx) = self.hovering_on {
|
||||
if ctx.input.left_mouse_button_pressed() {
|
||||
match self.items[idx].0 {
|
||||
Item::UpButton => {
|
||||
if self.top_idx != 0 {
|
||||
self.top_idx -= 1;
|
||||
}
|
||||
}
|
||||
Item::DownButton => {
|
||||
if self.num_items_hidden_below(ctx.canvas) != 0 {
|
||||
self.top_idx += 1;
|
||||
}
|
||||
}
|
||||
Item::ActualItem(item) => {
|
||||
self.current_selection = idx - 1;
|
||||
return Some(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// Returns the items to draw and the space they occupy.
|
||||
pub fn draw(&self, g: &mut GfxCtx) -> Vec<(T, ScreenRectangle)> {
|
||||
let visible = self.get_visible_items(g.canvas);
|
||||
// We know buttons have the max_width.
|
||||
let max_width = visible[0].1.width();
|
||||
let mut total_height = 0.0;
|
||||
for (_, rect) in &visible {
|
||||
total_height += rect.height();
|
||||
}
|
||||
|
||||
g.fork_screenspace();
|
||||
g.draw_polygon(
|
||||
self.bg_color,
|
||||
&Polygon::rectangle_topleft(
|
||||
Pt2D::new(self.master_topleft.x, self.master_topleft.y),
|
||||
Distance::meters(max_width),
|
||||
Distance::meters(total_height),
|
||||
),
|
||||
);
|
||||
g.canvas.mark_covered_area(ScreenRectangle::top_left(
|
||||
self.master_topleft,
|
||||
ScreenDims::new(max_width, total_height),
|
||||
));
|
||||
|
||||
let mut items = Vec::new();
|
||||
for (idx, rect) in visible {
|
||||
if Some(idx) == self.hovering_on || idx == self.current_selection + 1 {
|
||||
// Drawing text keeps reseting this. :(
|
||||
g.fork_screenspace();
|
||||
g.draw_polygon(
|
||||
if Some(idx) == self.hovering_on {
|
||||
self.hovering_color
|
||||
} else {
|
||||
self.current_selection_color
|
||||
},
|
||||
&Polygon::rectangle_topleft(
|
||||
Pt2D::new(rect.x1, rect.y1),
|
||||
Distance::meters(rect.width()),
|
||||
Distance::meters(rect.height()),
|
||||
),
|
||||
);
|
||||
}
|
||||
match self.items[idx].0 {
|
||||
Item::UpButton => {
|
||||
// TODO center the text inside the rectangle. and actually, g should have a
|
||||
// method for that.
|
||||
let mut txt = Text::with_bg_color(None);
|
||||
if self.top_idx == 0 {
|
||||
// TODO text::INACTIVE_CHOICE_COLOR
|
||||
txt.add_styled_line(
|
||||
"scroll up".to_string(),
|
||||
Some(Color::grey(0.4)),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
txt.add_line(format!("scroll up ({} more items)", self.top_idx));
|
||||
}
|
||||
g.draw_text_at_screenspace_topleft(&txt, ScreenPt::new(rect.x1, rect.y1));
|
||||
}
|
||||
Item::DownButton => {
|
||||
let mut txt = Text::with_bg_color(None);
|
||||
let num_items = self.num_items_hidden_below(g.canvas);
|
||||
if num_items == 0 {
|
||||
txt.add_styled_line(
|
||||
"scroll down".to_string(),
|
||||
Some(Color::grey(0.4)),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
txt.add_line(format!("scroll down ({} more items)", num_items));
|
||||
}
|
||||
g.draw_text_at_screenspace_topleft(&txt, ScreenPt::new(rect.x1, rect.y1));
|
||||
}
|
||||
Item::ActualItem(item) => {
|
||||
items.push((item, rect));
|
||||
}
|
||||
}
|
||||
}
|
||||
g.unfork();
|
||||
|
||||
items
|
||||
}
|
||||
|
||||
pub fn select_previous(&mut self) {
|
||||
assert!(self.current_selection != 0);
|
||||
self.current_selection -= 1;
|
||||
// TODO This and the case below aren't right; we might scroll far past the current
|
||||
// selection. Need similar logic for initializing Scroller and make sure the new
|
||||
// current_selection is "centered", but also retain consistency.
|
||||
if self.current_selection < self.top_idx {
|
||||
self.top_idx -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_next(&mut self, canvas: &Canvas) {
|
||||
assert!(self.current_selection != self.items.len() - 2);
|
||||
self.current_selection += 1;
|
||||
// Remember, the indices include buttons. :(
|
||||
if self
|
||||
.get_visible_items(canvas)
|
||||
.into_iter()
|
||||
.find(|(idx, _)| self.current_selection + 1 == *idx)
|
||||
.is_none()
|
||||
{
|
||||
self.top_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_idx(&self) -> usize {
|
||||
self.current_selection
|
||||
}
|
||||
|
||||
pub fn num_items(&self) -> usize {
|
||||
self.items.len() - 2
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user