mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
trait-ify dropdowns (generic type plumbed in the right place now)
This commit is contained in:
parent
2b2b30a6bb
commit
67b21a334c
@ -23,7 +23,6 @@ pub struct Widget {
|
||||
|
||||
enum WidgetType {
|
||||
Btn(Button),
|
||||
Dropdown(Dropdown),
|
||||
Slider(Slider),
|
||||
Row(Vec<Widget>),
|
||||
Column(Vec<Widget>),
|
||||
@ -297,18 +296,18 @@ impl Widget {
|
||||
))))
|
||||
}
|
||||
|
||||
pub fn dropdown<T: 'static + PartialEq>(
|
||||
pub fn dropdown<T: 'static + PartialEq + Clone>(
|
||||
ctx: &EventCtx,
|
||||
label: &str,
|
||||
default_value: T,
|
||||
choices: Vec<Choice<T>>,
|
||||
) -> Widget {
|
||||
Widget::new(WidgetType::Dropdown(Dropdown::new(
|
||||
Widget::new(WidgetType::Generic(Box::new(Dropdown::new(
|
||||
ctx,
|
||||
label,
|
||||
default_value,
|
||||
choices,
|
||||
)))
|
||||
))))
|
||||
.named(label)
|
||||
.outline(2.0, Color::WHITE)
|
||||
}
|
||||
@ -360,11 +359,6 @@ impl Widget {
|
||||
return Some(Outcome::Clicked(btn.action.clone()));
|
||||
}
|
||||
}
|
||||
WidgetType::Dropdown(ref mut dropdown) => {
|
||||
if dropdown.event(ctx, &self.rect) {
|
||||
*redo_layout = true;
|
||||
}
|
||||
}
|
||||
WidgetType::Slider(ref mut slider) => {
|
||||
slider.event(ctx);
|
||||
}
|
||||
@ -392,7 +386,6 @@ impl Widget {
|
||||
|
||||
match self.widget {
|
||||
WidgetType::Btn(ref btn) => btn.draw(g),
|
||||
WidgetType::Dropdown(ref dropdown) => dropdown.draw(g),
|
||||
WidgetType::Slider(ref slider) => {
|
||||
if self.id != Some("horiz scrollbar".to_string())
|
||||
&& self.id != Some("vert scrollbar".to_string())
|
||||
@ -414,7 +407,6 @@ impl Widget {
|
||||
fn get_flexbox(&self, parent: Node, stretch: &mut Stretch, nodes: &mut Vec<Node>) {
|
||||
let dims = match self.widget {
|
||||
WidgetType::Btn(ref widget) => widget.get_dims(),
|
||||
WidgetType::Dropdown(ref widget) => widget.get_dims(),
|
||||
WidgetType::Slider(ref widget) => widget.get_dims(),
|
||||
WidgetType::Row(ref widgets) => {
|
||||
let mut style = Style {
|
||||
@ -507,9 +499,6 @@ impl Widget {
|
||||
WidgetType::Btn(ref mut widget) => {
|
||||
widget.set_pos(top_left);
|
||||
}
|
||||
WidgetType::Dropdown(ref mut widget) => {
|
||||
widget.set_pos(top_left);
|
||||
}
|
||||
WidgetType::Slider(ref mut widget) => {
|
||||
widget.set_pos(top_left);
|
||||
}
|
||||
@ -549,7 +538,7 @@ impl Widget {
|
||||
|
||||
fn get_all_click_actions(&self, actions: &mut HashSet<String>) {
|
||||
match self.widget {
|
||||
WidgetType::Slider(_) | WidgetType::Dropdown(_) => {}
|
||||
WidgetType::Slider(_) => {}
|
||||
WidgetType::Btn(ref btn) => {
|
||||
if actions.contains(&btn.action) {
|
||||
panic!(
|
||||
@ -915,15 +904,14 @@ impl Composite {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dropdown_value<T: 'static + Clone>(&mut self, name: &str) -> T {
|
||||
match self.find_mut(name).widget {
|
||||
WidgetType::Dropdown(ref mut dropdown) => {
|
||||
// Amusing little pattern here.
|
||||
// TODO I think this entire hack goes away when WidgetImpl is just a trait.
|
||||
let choice: Choice<T> = dropdown.take_value();
|
||||
let value = choice.data.clone();
|
||||
dropdown.return_value(choice);
|
||||
value
|
||||
pub fn dropdown_value<T: 'static + PartialEq + Clone>(&mut self, name: &str) -> T {
|
||||
match self.find(name).widget {
|
||||
WidgetType::Generic(ref w) => {
|
||||
if let Some(dropdown) = w.downcast_ref::<Dropdown<T>>() {
|
||||
dropdown.current_value()
|
||||
} else {
|
||||
panic!("{} isn't a dropdown", name);
|
||||
}
|
||||
}
|
||||
_ => panic!("{} isn't a dropdown", name),
|
||||
}
|
||||
|
@ -1,26 +1,25 @@
|
||||
use crate::{
|
||||
Btn, Button, Choice, Color, EventCtx, GfxCtx, InputResult, PopupMenu, ScreenDims, ScreenPt,
|
||||
ScreenRectangle, WidgetImpl,
|
||||
Btn, Button, Choice, Color, EventCtx, GfxCtx, InputResult, Outcome, PopupMenu, ScreenDims,
|
||||
ScreenPt, ScreenRectangle, WidgetImpl,
|
||||
};
|
||||
use geom::{Polygon, Pt2D};
|
||||
use std::any::Any;
|
||||
|
||||
pub struct Dropdown {
|
||||
pub struct Dropdown<T: Clone> {
|
||||
current_idx: usize,
|
||||
btn: Button,
|
||||
menu: Option<PopupMenu<usize>>,
|
||||
label: String,
|
||||
|
||||
choices: Vec<Choice<Box<dyn Any>>>,
|
||||
choices: Vec<Choice<T>>,
|
||||
}
|
||||
|
||||
impl Dropdown {
|
||||
pub fn new<T: 'static + PartialEq>(
|
||||
impl<T: 'static + PartialEq + Clone> Dropdown<T> {
|
||||
pub fn new(
|
||||
ctx: &EventCtx,
|
||||
label: &str,
|
||||
default_value: T,
|
||||
choices: Vec<Choice<T>>,
|
||||
) -> Dropdown {
|
||||
) -> Dropdown<T> {
|
||||
let current_idx = choices
|
||||
.iter()
|
||||
.position(|c| c.data == default_value)
|
||||
@ -31,30 +30,34 @@ impl Dropdown {
|
||||
btn: make_btn(ctx, &choices[current_idx].label, label),
|
||||
menu: None,
|
||||
label: label.to_string(),
|
||||
|
||||
choices: choices
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
// TODO Can't use with_value here :(
|
||||
let data: Box<dyn Any> = Box::new(c.data);
|
||||
Choice {
|
||||
label: c.label,
|
||||
data,
|
||||
hotkey: c.hotkey,
|
||||
active: c.active,
|
||||
tooltip: c.tooltip,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
choices,
|
||||
}
|
||||
}
|
||||
|
||||
// If true, widgets should be recomputed.
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, our_rect: &ScreenRectangle) -> bool {
|
||||
pub fn current_value(&self) -> T {
|
||||
self.choices[self.current_idx].data.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + Clone> WidgetImpl for Dropdown<T> {
|
||||
fn get_dims(&self) -> ScreenDims {
|
||||
self.btn.get_dims()
|
||||
}
|
||||
|
||||
fn set_pos(&mut self, top_left: ScreenPt) {
|
||||
self.btn.set_pos(top_left);
|
||||
}
|
||||
|
||||
fn event(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
rect: &ScreenRectangle,
|
||||
redo_layout: &mut bool,
|
||||
) -> Option<Outcome> {
|
||||
if let Some(ref mut m) = self.menu {
|
||||
// TODO wraaaaaaaaaaaawng
|
||||
let mut redo_layout = false;
|
||||
m.event(ctx, our_rect, &mut redo_layout);
|
||||
// TODO Pass in the dropdown's rectangle, not the menu's. This is a lie! But the menu
|
||||
// doesn't use it, so fine?
|
||||
m.event(ctx, rect, redo_layout);
|
||||
match m.state {
|
||||
InputResult::StillActive => {}
|
||||
InputResult::Canceled => {
|
||||
@ -68,7 +71,7 @@ impl Dropdown {
|
||||
// change
|
||||
self.btn = make_btn(ctx, &self.choices[self.current_idx].label, &self.label);
|
||||
self.btn.set_pos(top_left);
|
||||
return true;
|
||||
*redo_layout = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -84,15 +87,15 @@ impl Dropdown {
|
||||
.map(|(idx, c)| c.with_value(idx))
|
||||
.collect(),
|
||||
);
|
||||
menu.set_pos(ScreenPt::new(our_rect.x1, our_rect.y2 + 15.0));
|
||||
menu.set_pos(ScreenPt::new(rect.x1, rect.y2 + 15.0));
|
||||
self.menu = Some(menu);
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
None
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
self.btn.draw(g);
|
||||
if let Some(ref m) = self.menu {
|
||||
// We need a background too!
|
||||
@ -106,43 +109,6 @@ impl Dropdown {
|
||||
m.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO This invalidates the entire widget! Have to call return_value
|
||||
pub fn take_value<T: 'static>(&mut self) -> Choice<T> {
|
||||
let c = self.choices.remove(self.current_idx);
|
||||
let data: Box<dyn Any> = c.data;
|
||||
let boxed: Box<T> = data.downcast().unwrap();
|
||||
Choice {
|
||||
label: c.label,
|
||||
data: *boxed,
|
||||
hotkey: c.hotkey,
|
||||
active: c.active,
|
||||
tooltip: c.tooltip,
|
||||
}
|
||||
}
|
||||
pub fn return_value<T: 'static>(&mut self, c: Choice<T>) {
|
||||
let data: Box<dyn Any> = Box::new(c.data);
|
||||
self.choices.insert(
|
||||
self.current_idx,
|
||||
Choice {
|
||||
label: c.label,
|
||||
data,
|
||||
hotkey: c.hotkey,
|
||||
active: c.active,
|
||||
tooltip: c.tooltip,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for Dropdown {
|
||||
fn get_dims(&self) -> ScreenDims {
|
||||
self.btn.get_dims()
|
||||
}
|
||||
|
||||
fn set_pos(&mut self, top_left: ScreenPt) {
|
||||
self.btn.set_pos(top_left);
|
||||
}
|
||||
}
|
||||
|
||||
fn make_btn(ctx: &EventCtx, name: &str, label: &str) -> Button {
|
||||
|
Loading…
Reference in New Issue
Block a user