mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 12:12:00 +03:00
ModalMenus can be mostly hidden
This commit is contained in:
parent
6e2689f68c
commit
b42bba799d
@ -33,7 +33,7 @@ impl TurnCyclerState {
|
||||
self.state = State::ShowLane(id);
|
||||
} else if ctx
|
||||
.input
|
||||
.key_pressed(Key::Tab, "cycle through this lane's turns")
|
||||
.key_pressed(Key::Z, "cycle through this lane's turns")
|
||||
{
|
||||
self.state = State::CycleTurns(id, idx + 1);
|
||||
}
|
||||
@ -42,7 +42,7 @@ impl TurnCyclerState {
|
||||
if !ui.primary.map.get_turns_from_lane(id).is_empty()
|
||||
&& ctx
|
||||
.input
|
||||
.key_pressed(Key::Tab, "cycle through this lane's turns")
|
||||
.key_pressed(Key::Z, "cycle through this lane's turns")
|
||||
{
|
||||
self.state = State::CycleTurns(id, 0);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ impl TrafficSignalEditor {
|
||||
false,
|
||||
);
|
||||
|
||||
ctx.input.set_mode_with_prompt(
|
||||
self.diagram_top_left = ctx.input.set_mode_with_prompt(
|
||||
"Traffic Signal Editor",
|
||||
format!("Traffic Signal Editor for {}", self.i),
|
||||
&ctx.canvas,
|
||||
|
@ -8,8 +8,8 @@ use crate::tutorial::TutorialMode;
|
||||
use crate::ui::{EditorState, Flags, ShowEverything, UI};
|
||||
use abstutil::elapsed_seconds;
|
||||
use ezgui::{
|
||||
Canvas, EventCtx, EventLoopMode, GfxCtx, Key, LogScroller, ModalMenu, Prerender, TopMenu,
|
||||
UserInput, Wizard, GUI,
|
||||
Canvas, EventCtx, EventLoopMode, GfxCtx, Key, LogScroller, ModalMenu, Prerender, UserInput,
|
||||
Wizard, GUI,
|
||||
};
|
||||
use geom::{Duration, Line, Pt2D, Speed};
|
||||
use map_model::Map;
|
||||
@ -69,10 +69,6 @@ impl GameState {
|
||||
}
|
||||
|
||||
impl GUI for GameState {
|
||||
fn top_menu(&self, _: &Canvas) -> Option<TopMenu> {
|
||||
None
|
||||
}
|
||||
|
||||
fn modal_menus(&self) -> Vec<ModalMenu> {
|
||||
vec![
|
||||
ModalMenu::new(
|
||||
|
@ -182,7 +182,7 @@ impl SandboxMode {
|
||||
if ctx.input.modal_action("speed up sim") {
|
||||
mode.desired_speed += ADJUST_SPEED;
|
||||
}
|
||||
if ctx.input.modal_action("reset sim") {
|
||||
if !state.ui.primary.sim.is_empty() && ctx.input.modal_action("reset sim") {
|
||||
// TODO savestate_every gets lost
|
||||
state.ui.primary.sim = Sim::new(
|
||||
&state.ui.primary.map,
|
||||
|
@ -48,6 +48,7 @@ impl ContextMenu {
|
||||
.map(|(hotkey, action)| (Some(hotkey), action, hotkey))
|
||||
.collect(),
|
||||
false,
|
||||
false,
|
||||
Position::TopLeftAt(origin),
|
||||
canvas,
|
||||
))
|
||||
@ -254,7 +255,7 @@ impl UserInput {
|
||||
if let Some(ref mut menu) = self.modal_state.mut_active_mode(mode) {
|
||||
menu.mark_all_inactive();
|
||||
menu.change_prompt(prompt);
|
||||
menu.get_bottom_left()
|
||||
menu.get_bottom_left(canvas)
|
||||
} else {
|
||||
if let Some(ref m) = self.modal_state.modes.get(mode) {
|
||||
let mut menu = Menu::new(
|
||||
@ -264,11 +265,12 @@ impl UserInput {
|
||||
.map(|(key, action)| (Some(*key), action.to_string(), *key))
|
||||
.collect(),
|
||||
false,
|
||||
true,
|
||||
Position::TopRightOfScreen(extra_width),
|
||||
canvas,
|
||||
);
|
||||
menu.mark_all_inactive();
|
||||
let corner = menu.get_bottom_left();
|
||||
let corner = menu.get_bottom_left(canvas);
|
||||
self.modal_state.active.push((mode.to_string(), menu));
|
||||
corner
|
||||
} else {
|
||||
|
@ -9,11 +9,14 @@ pub struct Menu<T: Clone> {
|
||||
current_idx: Option<usize>,
|
||||
mouse_in_bounds: bool,
|
||||
keys_enabled: bool,
|
||||
hideable: bool,
|
||||
hidden: bool,
|
||||
pos: Position,
|
||||
|
||||
row_height: f64,
|
||||
top_left: ScreenPt,
|
||||
first_choice_row: ScreenRectangle,
|
||||
// TODO Could store this in hidden and non-hidden modes, maybe simpler that way.
|
||||
total_height: f64,
|
||||
}
|
||||
|
||||
@ -30,6 +33,7 @@ impl<T: Clone> Menu<T> {
|
||||
prompt: Option<Text>,
|
||||
choices: Vec<(Option<Key>, String, T)>,
|
||||
keys_enabled: bool,
|
||||
hideable: bool,
|
||||
pos: Position,
|
||||
canvas: &Canvas,
|
||||
) -> Menu<T> {
|
||||
@ -81,6 +85,8 @@ impl<T: Clone> Menu<T> {
|
||||
// TODO Bit of a hack, but eh.
|
||||
mouse_in_bounds: !keys_enabled,
|
||||
pos,
|
||||
hideable,
|
||||
hidden: false,
|
||||
|
||||
row_height,
|
||||
top_left,
|
||||
@ -95,59 +101,72 @@ impl<T: Clone> Menu<T> {
|
||||
}
|
||||
|
||||
pub fn event(&mut self, ev: Event, canvas: &Canvas) -> InputResult<T> {
|
||||
// Handle the mouse
|
||||
if ev == Event::LeftMouseButtonDown {
|
||||
if let Some(i) = self.current_idx {
|
||||
let (_, choice, active, data) = self.choices[i].clone();
|
||||
if active && self.mouse_in_bounds {
|
||||
return InputResult::Done(choice, data);
|
||||
} else {
|
||||
return InputResult::StillActive;
|
||||
}
|
||||
} else {
|
||||
return InputResult::Canceled;
|
||||
}
|
||||
} else if ev == Event::RightMouseButtonDown {
|
||||
return InputResult::Canceled;
|
||||
} else if let Event::MouseMovedTo(pt) = ev {
|
||||
if !canvas.is_dragging() {
|
||||
for i in 0..self.choices.len() {
|
||||
if self.choices[i].2
|
||||
&& self
|
||||
.first_choice_row
|
||||
.translate(0.0, (i as f64) * self.row_height)
|
||||
.contains(pt)
|
||||
{
|
||||
self.current_idx = Some(i);
|
||||
self.mouse_in_bounds = true;
|
||||
if !self.hidden {
|
||||
// Handle the mouse
|
||||
if ev == Event::LeftMouseButtonDown {
|
||||
if let Some(i) = self.current_idx {
|
||||
let (_, choice, active, data) = self.choices[i].clone();
|
||||
if active && self.mouse_in_bounds {
|
||||
return InputResult::Done(choice, data);
|
||||
} else {
|
||||
return InputResult::StillActive;
|
||||
}
|
||||
} else {
|
||||
return InputResult::Canceled;
|
||||
}
|
||||
self.mouse_in_bounds = false;
|
||||
if !self.keys_enabled {
|
||||
self.current_idx = None;
|
||||
} else if ev == Event::RightMouseButtonDown {
|
||||
return InputResult::Canceled;
|
||||
} else if let Event::MouseMovedTo(pt) = ev {
|
||||
if !canvas.is_dragging() {
|
||||
for i in 0..self.choices.len() {
|
||||
if self.choices[i].2
|
||||
&& self
|
||||
.first_choice_row
|
||||
.translate(0.0, (i as f64) * self.row_height)
|
||||
.contains(pt)
|
||||
{
|
||||
self.current_idx = Some(i);
|
||||
self.mouse_in_bounds = true;
|
||||
return InputResult::StillActive;
|
||||
}
|
||||
}
|
||||
self.mouse_in_bounds = false;
|
||||
if !self.keys_enabled {
|
||||
self.current_idx = None;
|
||||
}
|
||||
return InputResult::StillActive;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle keys
|
||||
if self.keys_enabled {
|
||||
let idx = self.current_idx.unwrap();
|
||||
if ev == Event::KeyPress(Key::Enter) {
|
||||
let (_, name, active, data) = self.choices[idx].clone();
|
||||
if active {
|
||||
return InputResult::Done(name, data);
|
||||
} else {
|
||||
return InputResult::StillActive;
|
||||
}
|
||||
} else if ev == Event::KeyPress(Key::UpArrow) {
|
||||
if idx > 0 {
|
||||
self.current_idx = Some(idx - 1);
|
||||
}
|
||||
} else if ev == Event::KeyPress(Key::DownArrow) {
|
||||
if idx < self.choices.len() - 1 {
|
||||
self.current_idx = Some(idx + 1);
|
||||
}
|
||||
}
|
||||
return InputResult::StillActive;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle keys
|
||||
if self.keys_enabled {
|
||||
let idx = self.current_idx.unwrap();
|
||||
if ev == Event::KeyPress(Key::Enter) {
|
||||
let (_, name, active, data) = self.choices[idx].clone();
|
||||
if active {
|
||||
return InputResult::Done(name, data);
|
||||
if self.hideable {
|
||||
if ev == Event::KeyPress(Key::Tab) {
|
||||
if self.hidden {
|
||||
self.hidden = false;
|
||||
} else {
|
||||
return InputResult::StillActive;
|
||||
}
|
||||
} else if ev == Event::KeyPress(Key::UpArrow) {
|
||||
if idx > 0 {
|
||||
self.current_idx = Some(idx - 1);
|
||||
}
|
||||
} else if ev == Event::KeyPress(Key::DownArrow) {
|
||||
if idx < self.choices.len() - 1 {
|
||||
self.current_idx = Some(idx + 1);
|
||||
self.hidden = true;
|
||||
self.current_idx = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,6 +193,7 @@ impl<T: Clone> Menu<T> {
|
||||
.map(|(key, choice, _, data)| (*key, choice.to_string(), data.clone()))
|
||||
.collect(),
|
||||
self.keys_enabled,
|
||||
self.hideable,
|
||||
self.pos.clone(),
|
||||
canvas,
|
||||
);
|
||||
@ -187,42 +207,45 @@ impl<T: Clone> Menu<T> {
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
let mut txt = self.prompt.clone().unwrap_or_else(Text::new);
|
||||
for (idx, (hotkey, choice, active, _)) in self.choices.iter().enumerate() {
|
||||
let bg = if Some(idx) == self.current_idx {
|
||||
Some(text::SELECTED_COLOR)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if *active {
|
||||
if let Some(key) = hotkey {
|
||||
txt.add_styled_line(key.describe(), Some(text::HOTKEY_COLOR), bg, None);
|
||||
txt.append(format!(" - {}", choice), None);
|
||||
if !self.hidden {
|
||||
for (idx, (hotkey, choice, active, _)) in self.choices.iter().enumerate() {
|
||||
let bg = if Some(idx) == self.current_idx {
|
||||
Some(text::SELECTED_COLOR)
|
||||
} else {
|
||||
txt.add_styled_line(choice.to_string(), None, bg, None);
|
||||
}
|
||||
} else {
|
||||
if let Some(key) = hotkey {
|
||||
txt.add_styled_line(
|
||||
format!("{} - {}", key.describe(), choice),
|
||||
Some(text::INACTIVE_CHOICE_COLOR),
|
||||
bg,
|
||||
None,
|
||||
);
|
||||
None
|
||||
};
|
||||
if *active {
|
||||
if let Some(key) = hotkey {
|
||||
txt.add_styled_line(key.describe(), Some(text::HOTKEY_COLOR), bg, None);
|
||||
txt.append(format!(" - {}", choice), None);
|
||||
} else {
|
||||
txt.add_styled_line(choice.to_string(), None, bg, None);
|
||||
}
|
||||
} else {
|
||||
txt.add_styled_line(
|
||||
choice.to_string(),
|
||||
Some(text::INACTIVE_CHOICE_COLOR),
|
||||
bg,
|
||||
None,
|
||||
);
|
||||
if let Some(key) = hotkey {
|
||||
txt.add_styled_line(
|
||||
format!("{} - {}", key.describe(), choice),
|
||||
Some(text::INACTIVE_CHOICE_COLOR),
|
||||
bg,
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
txt.add_styled_line(
|
||||
choice.to_string(),
|
||||
Some(text::INACTIVE_CHOICE_COLOR),
|
||||
bg,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let (_, total_height) = g.canvas.text_dims(&txt);
|
||||
g.canvas.mark_covered_area(ScreenRectangle {
|
||||
x1: self.top_left.x,
|
||||
y1: self.top_left.y,
|
||||
x2: self.first_choice_row.x2,
|
||||
y2: self.top_left.y + self.total_height,
|
||||
y2: self.top_left.y + total_height,
|
||||
});
|
||||
g.draw_text_at_screenspace_topleft(&txt, self.top_left);
|
||||
}
|
||||
@ -258,7 +281,12 @@ impl<T: Clone> Menu<T> {
|
||||
// TODO Actually just recalculate geometry when this happens...
|
||||
}
|
||||
|
||||
pub fn get_bottom_left(&self) -> ScreenPt {
|
||||
ScreenPt::new(self.top_left.x, self.top_left.y + self.total_height)
|
||||
pub fn get_bottom_left(&self, canvas: &Canvas) -> ScreenPt {
|
||||
let total_height = if self.hidden {
|
||||
canvas.text_dims(&self.prompt.as_ref().unwrap()).1
|
||||
} else {
|
||||
self.total_height
|
||||
};
|
||||
ScreenPt::new(self.top_left.x, self.top_left.y + total_height)
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ impl TopMenu {
|
||||
.map(|(maybe_key, action)| (*maybe_key, action.to_string(), ()))
|
||||
.collect(),
|
||||
false,
|
||||
false,
|
||||
Position::TopLeftAt(ScreenPt::new(f.rectangle.x1, f.rectangle.y2)),
|
||||
canvas,
|
||||
);
|
||||
|
@ -220,6 +220,7 @@ impl<'a> WrappedWizard<'a> {
|
||||
)),
|
||||
boxed_choices,
|
||||
true,
|
||||
false,
|
||||
Position::ScreenCenter,
|
||||
self.canvas,
|
||||
));
|
||||
|
Loading…
Reference in New Issue
Block a user