use std::collections::HashMap;
use std::time::Instant;
use gpui::MouseButton;
use crate::axis::AxisLayoutCache;
use crate::datasource::DecimationScratch;
use crate::geom::{ScreenPoint, ScreenRect};
use crate::interaction::{HitRegion, Pin, PlotRegions};
use crate::render::RenderCacheKey;
use crate::series::SeriesId;
use crate::transform::Transform;
use crate::view::{Range, Viewport};
use super::geometry::rect_contains;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum DragMode {
Pan,
ZoomRect,
ZoomX,
ZoomY,
}
#[derive(Debug, Clone)]
pub(crate) struct DragState {
pub(crate) mode: DragMode,
pub(crate) start: ScreenPoint,
pub(crate) last: ScreenPoint,
pub(crate) active: bool,
}
impl DragState {
pub(crate) fn new(mode: DragMode, start: ScreenPoint, active: bool) -> Self {
Self {
mode,
start,
last: start,
active,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct ClickState {
pub(crate) region: HitRegion,
pub(crate) button: MouseButton,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct PinToggle {
pub(crate) pin: Pin,
pub(crate) added: bool,
pub(crate) at: Instant,
pub(crate) screen_pos: ScreenPoint,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct HoverTarget {
pub(crate) pin: Pin,
pub(crate) screen: ScreenPoint,
pub(crate) is_pinned: bool,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct SeriesCache {
pub(crate) key: Option<RenderCacheKey>,
pub(crate) points: Vec<crate::geom::Point>,
}
#[derive(Debug, Clone)]
pub(crate) struct LegendEntry {
pub(crate) series_id: SeriesId,
pub(crate) row_rect: ScreenRect,
}
#[derive(Debug, Clone)]
pub(crate) struct LegendLayout {
pub(crate) rect: ScreenRect,
pub(crate) entries: Vec<LegendEntry>,
}
#[derive(Debug, Clone)]
pub(crate) struct PlotUiState {
pub(crate) x_layout: AxisLayoutCache,
pub(crate) y_layout: AxisLayoutCache,
pub(crate) regions: PlotRegions,
pub(crate) plot_rect: Option<ScreenRect>,
pub(crate) transform: Option<Transform>,
pub(crate) viewport: Option<Viewport>,
pub(crate) drag: Option<DragState>,
pub(crate) pending_click: Option<ClickState>,
pub(crate) last_pin_toggle: Option<PinToggle>,
pub(crate) hover_target: Option<HoverTarget>,
pub(crate) selection_rect: Option<ScreenRect>,
pub(crate) hover: Option<ScreenPoint>,
pub(crate) last_cursor: Option<ScreenPoint>,
pub(crate) linked_cursor_x: Option<f64>,
pub(crate) linked_brush_x: Option<Range>,
pub(crate) link_view_seq: u64,
pub(crate) link_cursor_seq: u64,
pub(crate) link_brush_seq: u64,
pub(crate) decimation_scratch: DecimationScratch,
pub(crate) series_cache: HashMap<SeriesId, SeriesCache>,
pub(crate) legend_layout: Option<LegendLayout>,
}
impl Default for PlotUiState {
fn default() -> Self {
Self {
x_layout: AxisLayoutCache::default(),
y_layout: AxisLayoutCache::default(),
regions: PlotRegions {
plot: ScreenRect::new(ScreenPoint::new(0.0, 0.0), ScreenPoint::new(0.0, 0.0)),
x_axis: ScreenRect::new(ScreenPoint::new(0.0, 0.0), ScreenPoint::new(0.0, 0.0)),
y_axis: ScreenRect::new(ScreenPoint::new(0.0, 0.0), ScreenPoint::new(0.0, 0.0)),
},
plot_rect: None,
transform: None,
viewport: None,
drag: None,
pending_click: None,
last_pin_toggle: None,
hover_target: None,
selection_rect: None,
hover: None,
last_cursor: None,
linked_cursor_x: None,
linked_brush_x: None,
link_view_seq: 0,
link_cursor_seq: 0,
link_brush_seq: 0,
decimation_scratch: DecimationScratch::new(),
series_cache: HashMap::new(),
legend_layout: None,
}
}
}
impl PlotUiState {
pub(crate) fn clear_interaction(&mut self) {
self.drag = None;
self.pending_click = None;
self.selection_rect = None;
}
pub(crate) fn legend_hit(&self, point: ScreenPoint) -> Option<SeriesId> {
let layout = self.legend_layout.as_ref()?;
if !rect_contains(layout.rect, point) {
return None;
}
for entry in &layout.entries {
if rect_contains(entry.row_rect, point) {
return Some(entry.series_id);
}
}
None
}
}