use gpui::{Bounds, Pixels, Point, px};
use crate::PANEL_MIN_SIZE;
use super::state::DockPlacement;
#[derive(Clone, Copy, Debug)]
pub struct DockSizing {
placement: DockPlacement,
area: Bounds<Pixels>,
opposite_dock_size: Pixels,
}
impl DockSizing {
pub fn new(placement: DockPlacement) -> Self {
Self {
placement,
area: Bounds::default(),
opposite_dock_size: px(0.),
}
}
pub fn with_area_bounds(mut self, area: Bounds<Pixels>) -> Self {
self.area = area;
self
}
pub fn with_area_width(mut self, width: Pixels) -> Self {
self.area.size.width = width;
self
}
pub fn with_area_height(mut self, height: Pixels) -> Self {
self.area.size.height = height;
self
}
pub fn with_opposite_dock_size(mut self, size: Pixels) -> Self {
self.opposite_dock_size = size;
self
}
pub fn size_from_pointer(&self, pointer: Point<Pixels>) -> Pixels {
match self.placement {
DockPlacement::Left => pointer.x - self.area.left(),
DockPlacement::Right => self.area.right() - pointer.x,
DockPlacement::Bottom => self.area.bottom() - pointer.y,
DockPlacement::Center => px(0.),
}
}
pub fn clamp(&self, size: Pixels) -> Pixels {
let max_size = match self.placement {
DockPlacement::Left | DockPlacement::Right => {
(self.area.size.width - PANEL_MIN_SIZE - self.opposite_dock_size)
.max(PANEL_MIN_SIZE)
}
DockPlacement::Bottom => (self.area.size.height - PANEL_MIN_SIZE).max(PANEL_MIN_SIZE),
DockPlacement::Center => return size,
};
size.clamp(PANEL_MIN_SIZE, max_size)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Dock {
open: bool,
collapsible: bool,
size: Pixels,
resizing: bool,
}
impl Dock {
pub fn new(size: Pixels) -> Self {
Self {
open: true,
collapsible: true,
size,
resizing: false,
}
}
pub fn is_open(&self) -> bool {
self.open
}
pub fn set_open(&mut self, open: bool) {
self.open = open;
}
pub fn is_collapsible(&self) -> bool {
self.collapsible
}
pub fn set_collapsible(&mut self, collapsible: bool) {
self.collapsible = collapsible;
}
pub fn size(&self) -> Pixels {
self.size
}
pub fn set_size(&mut self, size: Pixels) {
self.size = size.max(PANEL_MIN_SIZE);
}
pub fn is_resizing(&self) -> bool {
self.resizing
}
pub fn set_resizing(&mut self, resizing: bool) {
self.resizing = resizing;
}
}
#[cfg(test)]
mod dock_tests {
use super::*;
use gpui::{point, size};
#[test]
fn a_left_dock_cannot_squeeze_past_the_right_dock() {
let sizing = DockSizing::new(DockPlacement::Left)
.with_area_width(px(1000.))
.with_opposite_dock_size(px(300.));
assert_eq!(
sizing.clamp(px(900.)),
px(1000.) - PANEL_MIN_SIZE - px(300.)
);
}
#[test]
fn a_dock_never_clamps_below_the_minimum() {
let sizing = DockSizing::new(DockPlacement::Bottom).with_area_height(px(120.));
assert_eq!(sizing.clamp(px(1.)), PANEL_MIN_SIZE);
}
#[test]
fn a_bottom_dock_measures_from_the_area_bottom() {
let sizing = DockSizing::new(DockPlacement::Bottom).with_area_bounds(Bounds {
origin: point(px(0.), px(0.)),
size: size(px(800.), px(600.)),
});
assert_eq!(
sizing.size_from_pointer(point(px(400.), px(400.))),
px(200.)
);
}
#[test]
fn a_right_dock_measures_from_the_area_right_edge() {
let sizing = DockSizing::new(DockPlacement::Right).with_area_bounds(Bounds {
origin: point(px(0.), px(0.)),
size: size(px(800.), px(600.)),
});
assert_eq!(sizing.size_from_pointer(point(px(500.), px(0.))), px(300.));
}
#[test]
fn a_dock_never_shrinks_below_the_minimum() {
let mut dock = Dock::new(px(240.));
dock.set_size(px(1.));
assert_eq!(dock.size(), PANEL_MIN_SIZE);
dock.set_size(px(400.));
assert_eq!(dock.size(), px(400.), "a size above the floor is kept");
}
#[test]
fn the_center_placement_is_answered_rather_than_panicking() {
let sizing = DockSizing::new(DockPlacement::Center).with_area_bounds(Bounds {
origin: point(px(0.), px(0.)),
size: size(px(800.), px(600.)),
});
assert_eq!(sizing.clamp(px(7.)), px(7.), "the center clamps nothing");
assert_eq!(sizing.size_from_pointer(point(px(400.), px(300.))), px(0.));
}
#[test]
fn dock_state_defaults_to_open_and_collapsible() {
let dock = Dock::new(px(240.));
assert!(dock.is_open());
assert!(dock.is_collapsible());
assert_eq!(dock.size(), px(240.));
assert!(!dock.is_resizing());
}
}