Dragon drop poof of conscepter

This commit is contained in:
Dustin Carlino 2021-06-10 10:07:38 -07:00
parent b397f97a12
commit 0d028d1bb7
4 changed files with 136 additions and 1 deletions

View File

@ -8,6 +8,7 @@
//! * [`Button`] - clickable buttons with keybindings and tooltips
//! * [`Toggle`] - checkboxes, switches, and other toggles
//! * [`CompareTimes`] - a scatter plot specialized for comparing times
//! * [`DragDrop`] - a reorderable row of draggable cards
//! * [`DrawWithTooltips`] - draw static geometry, with mouse tooltips in certain regions
//! * [`Dropdown`] - a button that expands into a menu
//! * [`FanChart`] - visualize a range of values over time
@ -54,6 +55,7 @@ pub use crate::widgets::autocomplete::Autocomplete;
pub(crate) use crate::widgets::button::Button;
pub use crate::widgets::button::{ButtonBuilder, MultiButton};
pub use crate::widgets::compare_times::CompareTimes;
pub use crate::widgets::drag_drop::DragDrop;
pub(crate) use crate::widgets::dropdown::Dropdown;
pub use crate::widgets::fan_chart::FanChart;
pub use crate::widgets::filler::Filler;

View File

@ -0,0 +1,119 @@
use crate::{
Drawable, EventCtx, GeomBatch, GeomBatchStack, GfxCtx, RewriteColor, ScreenDims, ScreenPt,
ScreenRectangle, Widget, WidgetImpl, WidgetOutput,
};
pub struct DragDrop<K: Clone> {
members: Vec<(K, GeomBatch, ScreenDims)>,
draw: Drawable,
hovering: Option<usize>,
dragging: Option<usize>,
dims: ScreenDims,
top_left: ScreenPt,
}
impl<K: 'static + Clone> DragDrop<K> {
pub fn new_widget(ctx: &EventCtx, members: Vec<(K, GeomBatch)>) -> Widget {
let mut dd = DragDrop {
members: members
.into_iter()
.map(|(key, batch)| {
let dims = batch.get_dims();
(key, batch, dims)
})
.collect(),
draw: Drawable::empty(ctx),
hovering: None,
dragging: None,
dims: ScreenDims::square(0.0),
top_left: ScreenPt::new(0.0, 0.0),
};
dd.recalc_draw(ctx);
Widget::new(Box::new(dd))
}
}
impl<K: 'static + Clone> DragDrop<K> {
fn recalc_draw(&mut self, ctx: &EventCtx) {
let mut stack = GeomBatchStack::horizontal(Vec::new());
for (idx, (_, batch, _)) in self.members.iter().enumerate() {
let mut batch = batch.clone();
if let Some(drag_idx) = self.dragging {
// If we're dragging, fade everything out except what we're dragging and where
// we're maybe going to drop
if idx == drag_idx {
// Leave it
} else if self.hovering == Some(idx) {
// Possible drop
batch = batch.color(RewriteColor::ChangeAlpha(0.8));
} else {
// Fade it out
batch = batch.color(RewriteColor::ChangeAlpha(0.5));
}
} else if self.hovering == Some(idx) {
// If we're not dragging, show what we're hovering on
batch = batch.color(RewriteColor::ChangeAlpha(0.5));
}
stack.push(batch);
}
let batch = stack.batch();
self.dims = batch.get_dims();
self.draw = batch.upload(ctx);
}
fn mouseover_card(&self, ctx: &EventCtx) -> Option<usize> {
let pt = ctx.canvas.get_cursor_in_screen_space()?;
let mut top_left = self.top_left;
for (idx, (_, _, dims)) in self.members.iter().enumerate() {
if ScreenRectangle::top_left(top_left, *dims).contains(pt) {
return Some(idx);
}
top_left.x += dims.width;
}
None
}
}
impl<K: 'static + Clone> WidgetImpl for DragDrop<K> {
fn get_dims(&self) -> ScreenDims {
self.dims
}
fn set_pos(&mut self, top_left: ScreenPt) {
self.top_left = top_left;
}
fn event(&mut self, ctx: &mut EventCtx, _: &mut WidgetOutput) {
if let Some(old_idx) = self.dragging {
if ctx.input.left_mouse_button_released() {
self.dragging = None;
if let Some(new_idx) = self.hovering {
if old_idx != new_idx {
// TODO Emit a Changed event, then the caller can go fetch the new ordering
self.members.swap(old_idx, new_idx);
self.recalc_draw(ctx);
}
}
}
}
if ctx.redo_mouseover() {
let old = self.hovering.take();
self.hovering = self.mouseover_card(ctx);
if old != self.hovering {
self.recalc_draw(ctx);
}
}
if let Some(idx) = self.hovering {
if ctx.input.left_mouse_button_pressed() {
self.dragging = Some(idx);
self.recalc_draw(ctx);
}
}
}
fn draw(&self, g: &mut GfxCtx) {
g.redraw_at(self.top_left, &self.draw);
}
}

View File

@ -21,6 +21,7 @@ pub mod autocomplete;
pub mod button;
pub mod compare_times;
pub mod containers;
pub mod drag_drop;
pub mod dropdown;
pub mod fan_chart;
pub mod filler;

View File

@ -5,7 +5,7 @@ use rand_xorshift::XorShiftRng;
use geom::{Angle, Duration, Percent, Polygon, Pt2D, Time};
use widgetry::{
lctrl, Choice, Color, ContentMode, Drawable, EventCtx, Fill, GeomBatch, GfxCtx,
lctrl, Choice, Color, ContentMode, DragDrop, Drawable, EventCtx, Fill, GeomBatch, GfxCtx,
HorizontalAlignment, Image, Key, Line, LinePlot, Outcome, Panel, PersistentSplit, PlotOptions,
ScreenDims, Series, Settings, SharedAppState, State, TabController, Text, TextExt, Texture,
Toggle, Transition, UpdateType, VerticalAlignment, Widget,
@ -315,12 +315,17 @@ fn setup_scrollable_canvas(ctx: &mut EventCtx) -> Drawable {
}
fn make_tabs(ctx: &mut EventCtx) -> TabController {
let draggable_cards = (0..5)
.map(|i| (i, make_draggable_card(ctx, i)))
.collect::<Vec<_>>();
let style = ctx.style();
let mut tabs = TabController::new("demo_tabs");
let gallery_bar_item = style.btn_tab.text("Component Gallery");
let gallery_content = Widget::col(vec![
"Reorder the cards below".text_widget(ctx),
DragDrop::new_widget(ctx, draggable_cards),
Text::from(Line("Text").big_heading_styled().size(18)).into_widget(ctx),
Text::from_all(vec![
Line("You can "),
@ -604,6 +609,14 @@ fn make_controls(ctx: &mut EventCtx, tabs: &mut TabController) -> Panel {
.build(ctx)
}
fn make_draggable_card(ctx: &mut EventCtx, num: usize) -> GeomBatch {
// TODO Kind of hardcoded. At least center the text or draw nice outlines?
let mut batch = GeomBatch::new();
batch.push(Color::RED, Polygon::rectangle(100.0, 150.0));
batch.append(Text::from(format!("Card {}", num)).render(ctx));
batch
}
// Boilerplate for web support
#[cfg(target_arch = "wasm32")]