mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
trait-ify Button
This commit is contained in:
parent
10d3c0aa60
commit
1682b6c28f
@ -22,7 +22,6 @@ pub struct Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum WidgetType {
|
enum WidgetType {
|
||||||
Btn(Button),
|
|
||||||
Row(Vec<Widget>),
|
Row(Vec<Widget>),
|
||||||
Column(Vec<Widget>),
|
Column(Vec<Widget>),
|
||||||
Nothing,
|
Nothing,
|
||||||
@ -247,7 +246,7 @@ impl Widget {
|
|||||||
|
|
||||||
pub(crate) fn btn(btn: Button) -> Widget {
|
pub(crate) fn btn(btn: Button) -> Widget {
|
||||||
let action = btn.action.clone();
|
let action = btn.action.clone();
|
||||||
Widget::new(WidgetType::Btn(btn)).named(action)
|
Widget::new(WidgetType::Generic(Box::new(btn))).named(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slider(slider: Slider) -> Widget {
|
pub fn slider(slider: Slider) -> Widget {
|
||||||
@ -352,12 +351,6 @@ impl Widget {
|
|||||||
impl Widget {
|
impl Widget {
|
||||||
fn event(&mut self, ctx: &mut EventCtx, redo_layout: &mut bool) -> Option<Outcome> {
|
fn event(&mut self, ctx: &mut EventCtx, redo_layout: &mut bool) -> Option<Outcome> {
|
||||||
match self.widget {
|
match self.widget {
|
||||||
WidgetType::Btn(ref mut btn) => {
|
|
||||||
btn.event(ctx);
|
|
||||||
if btn.clicked() {
|
|
||||||
return Some(Outcome::Clicked(btn.action.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WidgetType::Row(ref mut widgets) | WidgetType::Column(ref mut widgets) => {
|
WidgetType::Row(ref mut widgets) | WidgetType::Column(ref mut widgets) => {
|
||||||
for w in widgets {
|
for w in widgets {
|
||||||
if let Some(o) = w.event(ctx, redo_layout) {
|
if let Some(o) = w.event(ctx, redo_layout) {
|
||||||
@ -388,7 +381,6 @@ impl Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match self.widget {
|
match self.widget {
|
||||||
WidgetType::Btn(ref btn) => btn.draw(g),
|
|
||||||
WidgetType::Row(ref widgets) | WidgetType::Column(ref widgets) => {
|
WidgetType::Row(ref widgets) | WidgetType::Column(ref widgets) => {
|
||||||
for w in widgets {
|
for w in widgets {
|
||||||
w.draw(g);
|
w.draw(g);
|
||||||
@ -402,7 +394,6 @@ impl Widget {
|
|||||||
// Populate a flattened list of Nodes, matching the traversal order
|
// Populate a flattened list of Nodes, matching the traversal order
|
||||||
fn get_flexbox(&self, parent: Node, stretch: &mut Stretch, nodes: &mut Vec<Node>) {
|
fn get_flexbox(&self, parent: Node, stretch: &mut Stretch, nodes: &mut Vec<Node>) {
|
||||||
let dims = match self.widget {
|
let dims = match self.widget {
|
||||||
WidgetType::Btn(ref widget) => widget.get_dims(),
|
|
||||||
WidgetType::Row(ref widgets) => {
|
WidgetType::Row(ref widgets) => {
|
||||||
let mut style = Style {
|
let mut style = Style {
|
||||||
flex_direction: FlexDirection::Row,
|
flex_direction: FlexDirection::Row,
|
||||||
@ -491,9 +482,6 @@ impl Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match self.widget {
|
match self.widget {
|
||||||
WidgetType::Btn(ref mut widget) => {
|
|
||||||
widget.set_pos(top_left);
|
|
||||||
}
|
|
||||||
WidgetType::Row(ref mut widgets) => {
|
WidgetType::Row(ref mut widgets) => {
|
||||||
// layout() doesn't return absolute position; it's relative to the container.
|
// layout() doesn't return absolute position; it's relative to the container.
|
||||||
for widget in widgets {
|
for widget in widgets {
|
||||||
@ -530,31 +518,23 @@ impl Widget {
|
|||||||
|
|
||||||
fn get_all_click_actions(&self, actions: &mut HashSet<String>) {
|
fn get_all_click_actions(&self, actions: &mut HashSet<String>) {
|
||||||
match self.widget {
|
match self.widget {
|
||||||
WidgetType::Btn(ref btn) => {
|
|
||||||
if actions.contains(&btn.action) {
|
|
||||||
panic!(
|
|
||||||
"Two buttons in one Composite both use action {}",
|
|
||||||
btn.action
|
|
||||||
);
|
|
||||||
}
|
|
||||||
actions.insert(btn.action.clone());
|
|
||||||
}
|
|
||||||
WidgetType::Row(ref widgets) | WidgetType::Column(ref widgets) => {
|
WidgetType::Row(ref widgets) | WidgetType::Column(ref widgets) => {
|
||||||
for w in widgets {
|
for w in widgets {
|
||||||
w.get_all_click_actions(actions);
|
w.get_all_click_actions(actions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WidgetType::Nothing => unreachable!(),
|
WidgetType::Nothing => unreachable!(),
|
||||||
// TODO Will need something
|
WidgetType::Generic(ref w) => w.get_all_click_actions(actions),
|
||||||
WidgetType::Generic(_) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_btn(&self, name: &str) -> bool {
|
pub fn is_btn(&self, name: &str) -> bool {
|
||||||
if let WidgetType::Btn(ref btn) = self.widget {
|
match self.widget {
|
||||||
btn.action == name
|
WidgetType::Generic(ref w) => w
|
||||||
} else {
|
.downcast_ref::<Button>()
|
||||||
false
|
.map(|btn| btn.action == name)
|
||||||
|
.unwrap_or(false),
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -597,7 +577,7 @@ impl Widget {
|
|||||||
|
|
||||||
pub(crate) fn take_btn(self) -> Button {
|
pub(crate) fn take_btn(self) -> Button {
|
||||||
match self.widget {
|
match self.widget {
|
||||||
WidgetType::Btn(btn) => btn,
|
WidgetType::Generic(w) => *w.downcast::<Button>().ok().unwrap(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
text, Color, Drawable, EventCtx, GeomBatch, GfxCtx, JustDraw, Line, MultiKey, RewriteColor,
|
text, Color, Drawable, EventCtx, GeomBatch, GfxCtx, JustDraw, Line, MultiKey, Outcome,
|
||||||
ScreenDims, ScreenPt, Text, Widget, WidgetImpl,
|
RewriteColor, ScreenDims, ScreenPt, ScreenRectangle, Text, Widget, WidgetImpl,
|
||||||
};
|
};
|
||||||
use geom::Polygon;
|
use geom::Polygon;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
pub struct Button {
|
pub struct Button {
|
||||||
pub action: String,
|
pub action: String,
|
||||||
@ -18,7 +19,6 @@ pub struct Button {
|
|||||||
hitbox: Polygon,
|
hitbox: Polygon,
|
||||||
|
|
||||||
hovering: bool,
|
hovering: bool,
|
||||||
clicked: bool,
|
|
||||||
|
|
||||||
pub(crate) top_left: ScreenPt,
|
pub(crate) top_left: ScreenPt,
|
||||||
dims: ScreenDims,
|
dims: ScreenDims,
|
||||||
@ -53,58 +53,11 @@ impl Button {
|
|||||||
hitbox,
|
hitbox,
|
||||||
|
|
||||||
hovering: false,
|
hovering: false,
|
||||||
clicked: false,
|
|
||||||
|
|
||||||
top_left: ScreenPt::new(0.0, 0.0),
|
top_left: ScreenPt::new(0.0, 0.0),
|
||||||
dims,
|
dims,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn event(&mut self, ctx: &mut EventCtx) {
|
|
||||||
if self.clicked {
|
|
||||||
panic!("Caller didn't consume button click");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.redo_mouseover() {
|
|
||||||
if let Some(pt) = ctx.canvas.get_cursor_in_screen_space() {
|
|
||||||
self.hovering = self
|
|
||||||
.hitbox
|
|
||||||
.translate(self.top_left.x, self.top_left.y)
|
|
||||||
.contains_pt(pt.to_pt());
|
|
||||||
} else {
|
|
||||||
self.hovering = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.hovering && ctx.normal_left_click() {
|
|
||||||
self.clicked = true;
|
|
||||||
self.hovering = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref hotkey) = self.hotkey {
|
|
||||||
if ctx.input.new_was_pressed(hotkey) {
|
|
||||||
self.clicked = true;
|
|
||||||
self.hovering = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn clicked(&mut self) -> bool {
|
|
||||||
if self.clicked {
|
|
||||||
self.clicked = false;
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn draw(&self, g: &mut GfxCtx) {
|
|
||||||
if self.hovering {
|
|
||||||
g.redraw_at(self.top_left, &self.draw_hovered);
|
|
||||||
g.draw_mouse_tooltip(self.tooltip.clone());
|
|
||||||
} else {
|
|
||||||
g.redraw_at(self.top_left, &self.draw_normal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WidgetImpl for Button {
|
impl WidgetImpl for Button {
|
||||||
@ -115,6 +68,56 @@ impl WidgetImpl for Button {
|
|||||||
fn set_pos(&mut self, top_left: ScreenPt) {
|
fn set_pos(&mut self, top_left: ScreenPt) {
|
||||||
self.top_left = top_left;
|
self.top_left = top_left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn event(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut EventCtx,
|
||||||
|
_rect: &ScreenRectangle,
|
||||||
|
_redo_layout: &mut bool,
|
||||||
|
) -> Option<Outcome> {
|
||||||
|
if ctx.redo_mouseover() {
|
||||||
|
if let Some(pt) = ctx.canvas.get_cursor_in_screen_space() {
|
||||||
|
self.hovering = self
|
||||||
|
.hitbox
|
||||||
|
.translate(self.top_left.x, self.top_left.y)
|
||||||
|
.contains_pt(pt.to_pt());
|
||||||
|
} else {
|
||||||
|
self.hovering = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.hovering && ctx.normal_left_click() {
|
||||||
|
self.hovering = false;
|
||||||
|
return Some(Outcome::Clicked(self.action.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref hotkey) = self.hotkey {
|
||||||
|
if ctx.input.new_was_pressed(hotkey) {
|
||||||
|
self.hovering = false;
|
||||||
|
return Some(Outcome::Clicked(self.action.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, g: &mut GfxCtx) {
|
||||||
|
if self.hovering {
|
||||||
|
g.redraw_at(self.top_left, &self.draw_hovered);
|
||||||
|
g.draw_mouse_tooltip(self.tooltip.clone());
|
||||||
|
} else {
|
||||||
|
g.redraw_at(self.top_left, &self.draw_normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_click_actions(&self, actions: &mut HashSet<String>) {
|
||||||
|
if actions.contains(&self.action) {
|
||||||
|
panic!(
|
||||||
|
"Two buttons in one Composite both use action {}",
|
||||||
|
self.action
|
||||||
|
);
|
||||||
|
}
|
||||||
|
actions.insert(self.action.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Btn {}
|
pub struct Btn {}
|
||||||
|
@ -36,11 +36,11 @@ impl WidgetImpl for Checkbox {
|
|||||||
fn event(
|
fn event(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &mut EventCtx,
|
ctx: &mut EventCtx,
|
||||||
_rect: &ScreenRectangle,
|
rect: &ScreenRectangle,
|
||||||
redo_layout: &mut bool,
|
redo_layout: &mut bool,
|
||||||
) -> Option<Outcome> {
|
) -> Option<Outcome> {
|
||||||
self.btn.event(ctx);
|
// TODO Lying about the rectangle
|
||||||
if self.btn.clicked() {
|
if self.btn.event(ctx, rect, redo_layout).is_some() {
|
||||||
std::mem::swap(&mut self.btn, &mut self.other_btn);
|
std::mem::swap(&mut self.btn, &mut self.other_btn);
|
||||||
self.btn.set_pos(self.other_btn.top_left);
|
self.btn.set_pos(self.other_btn.top_left);
|
||||||
self.enabled = !self.enabled;
|
self.enabled = !self.enabled;
|
||||||
|
@ -75,8 +75,8 @@ impl<T: 'static + Clone> WidgetImpl for Dropdown<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.btn.event(ctx);
|
// TODO Again lying about the rectangle
|
||||||
if self.btn.clicked() {
|
if self.btn.event(ctx, rect, redo_layout).is_some() {
|
||||||
// TODO set current idx in menu
|
// TODO set current idx in menu
|
||||||
// TODO Choice::map_value?
|
// TODO Choice::map_value?
|
||||||
let mut menu = PopupMenu::new(
|
let mut menu = PopupMenu::new(
|
||||||
|
@ -14,6 +14,7 @@ pub mod wizard;
|
|||||||
|
|
||||||
use crate::{EventCtx, GfxCtx, Outcome, ScreenDims, ScreenPt, ScreenRectangle};
|
use crate::{EventCtx, GfxCtx, Outcome, ScreenDims, ScreenPt, ScreenRectangle};
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
pub trait WidgetImpl: downcast_rs::Downcast {
|
pub trait WidgetImpl: downcast_rs::Downcast {
|
||||||
fn get_dims(&self) -> ScreenDims;
|
fn get_dims(&self) -> ScreenDims;
|
||||||
@ -29,6 +30,8 @@ pub trait WidgetImpl: downcast_rs::Downcast {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn draw(&self, _g: &mut GfxCtx) {}
|
fn draw(&self, _g: &mut GfxCtx) {}
|
||||||
|
|
||||||
|
fn get_all_click_actions(&self, _actions: &mut HashSet<String>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
downcast_rs::impl_downcast!(WidgetImpl);
|
downcast_rs::impl_downcast!(WidgetImpl);
|
||||||
|
Loading…
Reference in New Issue
Block a user