trait-ify Button

This commit is contained in:
Dustin Carlino 2020-03-22 15:20:46 -07:00
parent 10d3c0aa60
commit 1682b6c28f
5 changed files with 70 additions and 84 deletions

View File

@ -22,7 +22,6 @@ pub struct Widget {
}
enum WidgetType {
Btn(Button),
Row(Vec<Widget>),
Column(Vec<Widget>),
Nothing,
@ -247,7 +246,7 @@ impl Widget {
pub(crate) fn btn(btn: Button) -> Widget {
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 {
@ -352,12 +351,6 @@ impl Widget {
impl Widget {
fn event(&mut self, ctx: &mut EventCtx, redo_layout: &mut bool) -> Option<Outcome> {
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) => {
for w in widgets {
if let Some(o) = w.event(ctx, redo_layout) {
@ -388,7 +381,6 @@ impl Widget {
}
match self.widget {
WidgetType::Btn(ref btn) => btn.draw(g),
WidgetType::Row(ref widgets) | WidgetType::Column(ref widgets) => {
for w in widgets {
w.draw(g);
@ -402,7 +394,6 @@ impl Widget {
// Populate a flattened list of Nodes, matching the traversal order
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::Row(ref widgets) => {
let mut style = Style {
flex_direction: FlexDirection::Row,
@ -491,9 +482,6 @@ impl Widget {
}
match self.widget {
WidgetType::Btn(ref mut widget) => {
widget.set_pos(top_left);
}
WidgetType::Row(ref mut widgets) => {
// layout() doesn't return absolute position; it's relative to the container.
for widget in widgets {
@ -530,31 +518,23 @@ impl Widget {
fn get_all_click_actions(&self, actions: &mut HashSet<String>) {
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) => {
for w in widgets {
w.get_all_click_actions(actions);
}
}
WidgetType::Nothing => unreachable!(),
// TODO Will need something
WidgetType::Generic(_) => {}
WidgetType::Generic(ref w) => w.get_all_click_actions(actions),
}
}
pub fn is_btn(&self, name: &str) -> bool {
if let WidgetType::Btn(ref btn) = self.widget {
btn.action == name
} else {
false
match self.widget {
WidgetType::Generic(ref w) => w
.downcast_ref::<Button>()
.map(|btn| btn.action == name)
.unwrap_or(false),
_ => false,
}
}
@ -597,7 +577,7 @@ impl Widget {
pub(crate) fn take_btn(self) -> Button {
match self.widget {
WidgetType::Btn(btn) => btn,
WidgetType::Generic(w) => *w.downcast::<Button>().ok().unwrap(),
_ => unreachable!(),
}
}

View File

@ -1,8 +1,9 @@
use crate::{
text, Color, Drawable, EventCtx, GeomBatch, GfxCtx, JustDraw, Line, MultiKey, RewriteColor,
ScreenDims, ScreenPt, Text, Widget, WidgetImpl,
text, Color, Drawable, EventCtx, GeomBatch, GfxCtx, JustDraw, Line, MultiKey, Outcome,
RewriteColor, ScreenDims, ScreenPt, ScreenRectangle, Text, Widget, WidgetImpl,
};
use geom::Polygon;
use std::collections::HashSet;
pub struct Button {
pub action: String,
@ -18,7 +19,6 @@ pub struct Button {
hitbox: Polygon,
hovering: bool,
clicked: bool,
pub(crate) top_left: ScreenPt,
dims: ScreenDims,
@ -53,58 +53,11 @@ impl Button {
hitbox,
hovering: false,
clicked: false,
top_left: ScreenPt::new(0.0, 0.0),
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 {
@ -115,6 +68,56 @@ impl WidgetImpl for Button {
fn set_pos(&mut self, top_left: ScreenPt) {
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 {}

View File

@ -36,11 +36,11 @@ impl WidgetImpl for Checkbox {
fn event(
&mut self,
ctx: &mut EventCtx,
_rect: &ScreenRectangle,
rect: &ScreenRectangle,
redo_layout: &mut bool,
) -> Option<Outcome> {
self.btn.event(ctx);
if self.btn.clicked() {
// TODO Lying about the rectangle
if self.btn.event(ctx, rect, redo_layout).is_some() {
std::mem::swap(&mut self.btn, &mut self.other_btn);
self.btn.set_pos(self.other_btn.top_left);
self.enabled = !self.enabled;

View File

@ -75,8 +75,8 @@ impl<T: 'static + Clone> WidgetImpl for Dropdown<T> {
}
}
} else {
self.btn.event(ctx);
if self.btn.clicked() {
// TODO Again lying about the rectangle
if self.btn.event(ctx, rect, redo_layout).is_some() {
// TODO set current idx in menu
// TODO Choice::map_value?
let mut menu = PopupMenu::new(

View File

@ -14,6 +14,7 @@ pub mod wizard;
use crate::{EventCtx, GfxCtx, Outcome, ScreenDims, ScreenPt, ScreenRectangle};
use ordered_float::NotNan;
use std::collections::HashSet;
pub trait WidgetImpl: downcast_rs::Downcast {
fn get_dims(&self) -> ScreenDims;
@ -29,6 +30,8 @@ pub trait WidgetImpl: downcast_rs::Downcast {
None
}
fn draw(&self, _g: &mut GfxCtx) {}
fn get_all_click_actions(&self, _actions: &mut HashSet<String>) {}
}
downcast_rs::impl_downcast!(WidgetImpl);