extern crate input;
extern crate vecmath;
use input::{Button, GenericEvent, MouseButton};
use self::math::{is_inside, inside_pos, Matrix2d, Rectangle};
mod math;
const LEFT: u8 = 0x1;
const RIGHT: u8 = 0x2;
const TOP: u8 = 0x4;
const BOTTOM: u8 = 0x8;
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct SplitLayoutSettings {
pub border: f64,
pub center_min_size: [f64; 2],
pub left_value: f64,
pub left_min_value: f64,
pub right_value: f64,
pub right_min_value: f64,
pub top_value: f64,
pub top_min_value: f64,
pub bottom_value: f64,
pub bottom_min_value: f64,
pub lock_left: bool,
pub lock_right: bool,
pub lock_top: bool,
pub lock_bottom: bool,
}
impl SplitLayoutSettings {
pub fn new(border: f64, min_value: f64) -> SplitLayoutSettings {
SplitLayoutSettings {
border: border,
center_min_size: [1.0; 2],
left_value: min_value,
left_min_value: min_value,
right_value: min_value,
right_min_value: min_value,
top_value: min_value,
top_min_value: min_value,
bottom_value: min_value,
bottom_min_value: min_value,
lock_left: false,
lock_right: false,
lock_top: false,
lock_bottom: false,
}
}
pub fn left(mut self, value: f64, min_value: f64) -> SplitLayoutSettings {
self.left_value = value;
self.left_min_value = min_value;
self
}
pub fn right(mut self, value: f64, min_value: f64) -> SplitLayoutSettings {
self.right_value = value;
self.right_min_value = min_value;
self
}
pub fn top(mut self, value: f64, min_value: f64) -> SplitLayoutSettings {
self.top_value = value;
self.top_min_value = min_value;
self
}
pub fn bottom(mut self, value: f64, min_value: f64) -> SplitLayoutSettings {
self.bottom_value = value;
self.bottom_min_value = min_value;
self
}
pub fn lock_left(mut self, value: f64) -> SplitLayoutSettings {
self.lock_left = true;
self.left_value = value;
self.left_min_value = value;
self
}
pub fn lock_right(mut self, value: f64) -> SplitLayoutSettings {
self.lock_right = true;
self.right_value = value;
self.right_min_value = value;
self
}
pub fn lock_top(mut self, value: f64) -> SplitLayoutSettings {
self.lock_top = true;
self.top_value = value;
self.top_min_value = value;
self
}
pub fn lock_bottom(mut self, value: f64) -> SplitLayoutSettings {
self.lock_bottom = true;
self.bottom_value = value;
self.bottom_min_value = value;
self
}
}
pub struct SplitLayoutController {
pub left: SplitController,
pub right: SplitController,
pub top: SplitController,
pub bottom: SplitController,
center_min_size: [f64; 2],
drag_splits: u8,
lock_splits: u8,
}
impl SplitLayoutController {
pub fn new(settings: &SplitLayoutSettings) -> SplitLayoutController {
SplitLayoutController {
left: SplitController::new(settings.left_value, settings.left_min_value,
settings.border, SplitOrientation::Left),
right: SplitController::new(settings.right_value, settings.right_min_value,
settings.border, SplitOrientation::Right),
top: SplitController::new(settings.top_value, settings.top_min_value,
settings.border, SplitOrientation::Top),
bottom: SplitController::new(settings.bottom_value, settings.bottom_min_value,
settings.border, SplitOrientation::Bottom),
center_min_size: settings.center_min_size,
drag_splits: 0,
lock_splits: if settings.lock_left {LEFT} else {0} |
if settings.lock_right {RIGHT} else {0} |
if settings.lock_top {TOP} else {0} |
if settings.lock_bottom {BOTTOM} else {0},
}
}
pub fn event<E: GenericEvent>(&mut self, rect: Rectangle, transform: Matrix2d, e: &E) {
let bounds = self.bounds(rect);
if (self.lock_splits & TOP) != TOP {
if self.drag_splits == 0 || (self.drag_splits & TOP) == TOP {
let layout = self.top_bottom_layout();
let max_value = bounds[3] - self.bottom.value - self.center_min_size[1] -
self.top.border - self.bottom.border;
self.top.event(layout, max_value, bounds, transform, e);
}
}
if (self.lock_splits & BOTTOM) != BOTTOM {
if self.drag_splits == 0 || (self.drag_splits & BOTTOM) == BOTTOM {
let layout = self.top_bottom_layout();
let max_value = bounds[3] - self.top.value - self.center_min_size[1] -
self.bottom.border - self.top.border;
self.bottom.event(layout, max_value, bounds, transform, e);
}
}
if (self.lock_splits & LEFT) != LEFT {
if self.drag_splits == 0 || (self.drag_splits & LEFT) == LEFT {
let layout = self.left_right_layout(SplitLayoutPurpose::Event);
let max_value = bounds[2] - self.right.value - self.center_min_size[0] -
self.left.border - self.right.border;
self.left.event(layout, max_value, bounds, transform, e);
}
}
if (self.lock_splits & RIGHT) != RIGHT {
if self.drag_splits == 0 || (self.drag_splits & RIGHT) == RIGHT {
let layout = self.left_right_layout(SplitLayoutPurpose::Event);
let max_value = bounds[2] - self.left.value - self.center_min_size[0] -
self.right.border - self.left.border;
self.right.event(layout, max_value, bounds, transform, e);
}
}
self.drag_splits = if self.top.is_dragging() {TOP} else {0} |
if self.bottom.is_dragging() {BOTTOM} else {0} |
if self.left.is_dragging() {LEFT} else {0} |
if self.right.is_dragging() {RIGHT} else {0};
}
pub fn left_right_layout(&self, purpose: SplitLayoutPurpose) -> SplitLayout {
let sign = purpose.sign();
SplitLayout {
start: self.top.value + sign * self.top.border,
end: self.bottom.value + sign * self.bottom.border
}
}
pub fn top_bottom_layout(&self) -> SplitLayout {
SplitLayout {start: 0.0, end: 0.0}
}
pub fn rectangles(&self, rect: Rectangle) -> [Rectangle; 4] {
let bounds = self.bounds(rect);
let top_bottom_layout = self.top_bottom_layout();
let left_right_layout = self.left_right_layout(SplitLayoutPurpose::Draw);
[
self.left.line_rect(left_right_layout, bounds),
self.right.line_rect(left_right_layout, bounds),
self.top.line_rect(top_bottom_layout, bounds),
self.bottom.line_rect(top_bottom_layout, bounds),
]
}
pub fn states(&self) -> [SplitState; 4] {
[self.left.state(), self.right.state(), self.top.state(), self.bottom.state()]
}
pub fn panel_rectangles(&self, rect: Rectangle) -> [Rectangle; 5] {
let bounds = self.bounds(rect);
let left_right_y = bounds[1] + self.top.value + self.top.border;
let left_right_h = bounds[3] - self.top.value - self.top.border -
self.bottom.value - self.bottom.border;
[
[bounds[0], left_right_y, self.left.value, left_right_h],
[bounds[0] + bounds[2] - self.right.value, left_right_y,
self.right.value, left_right_h],
[bounds[0], bounds[1], bounds[2], self.top.value],
[bounds[0], bounds[1] + bounds[3] - self.bottom.value, bounds[2], self.bottom.value],
[bounds[0] + self.left.value + self.left.border, left_right_y,
bounds[2] - self.right.value - self.right.border -
self.left.value - self.left.border, left_right_h],
]
}
pub fn min_size(&self) -> [f64; 2] {
[
self.left.value + self.left.border + self.right.value + self.right.border +
self.center_min_size[0],
self.top.value + self.top.border + self.bottom.value + self.bottom.border +
self.center_min_size[1]
]
}
pub fn bounds(&self, rect: Rectangle) -> Rectangle {
let min_size = self.min_size();
[rect[0], rect[1], rect[2].max(min_size[0]), rect[3].max(min_size[1])]
}
}
pub struct SplitController {
mouse_hover: bool,
dragging: bool,
pub value: f64,
pub min_value: f64,
pub border: f64,
pub orientation: SplitOrientation,
}
impl SplitController {
pub fn new(
value: f64,
min_value: f64,
border: f64,
orientation: SplitOrientation
) -> SplitController {
SplitController {
mouse_hover: false,
dragging: false,
value: value,
min_value: min_value,
border: border,
orientation: orientation,
}
}
pub fn is_dragging(&self) -> bool {self.dragging}
pub fn event<E: GenericEvent>(
&mut self,
layout: SplitLayout,
max_value: f64,
rect: Rectangle,
transform: Matrix2d,
e: &E
) {
if let Some(pos) = e.mouse_cursor_args() {
let pos = inside_pos(pos, transform);
if self.dragging {
match self.orientation {
SplitOrientation::Left => {
self.value = (pos[0] - rect[0] - 0.5 * self.border)
.max(self.min_value)
.min(max_value);
}
SplitOrientation::Right => {
self.value = (rect[2] - pos[0] + rect[0] - 0.5 * self.border)
.max(self.min_value)
.min(max_value);
}
SplitOrientation::Top => {
self.value = (pos[1] - rect[1] - 0.5 * self.border)
.max(self.min_value)
.min(max_value);
}
SplitOrientation::Bottom => {
self.value = (rect[1] + rect[3] - pos[1] - 0.5 * self.border)
.max(self.min_value)
.min(max_value);
}
}
}
let line_rect = self.line_rect(layout, rect);
self.mouse_hover = is_inside(pos, line_rect);
}
if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() {
if self.mouse_hover {
self.dragging = true;
}
}
if let Some(Button::Mouse(MouseButton::Left)) = e.release_args() {
self.dragging = false;
}
}
pub fn state(&self) -> SplitState {
match (self.mouse_hover, self.dragging) {
(false, false) => SplitState::Inactive,
(true, false) => SplitState::Hover,
(true, true) => SplitState::Drag,
(false, true) => SplitState::DragNotFollowing,
}
}
pub fn line_rect(&self, layout: SplitLayout, rect: Rectangle) -> Rectangle {
match self.orientation {
SplitOrientation::Left => {
[rect[0] + self.value, rect[1] + layout.start,
self.border, rect[3] - layout.start - layout.end]
}
SplitOrientation::Right => {
[rect[0] + rect[2] - self.value - self.border, rect[1] + layout.start,
self.border, rect[3] - layout.start - layout.end]
}
SplitOrientation::Top => {
[rect[0] + layout.start, rect[1] + self.value,
rect[2] - layout.start - layout.end, self.border]
}
SplitOrientation::Bottom => {
[rect[0] + layout.start, rect[1] + rect[3] - self.value - self.border,
rect[2] - layout.start - layout.end, self.border]
}
}
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct SplitLayout {
pub start: f64,
pub end: f64,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum SplitLayoutPurpose {
Draw,
Event,
}
impl SplitLayoutPurpose {
fn sign(self) -> f64 {if let SplitLayoutPurpose::Draw = self {1.0} else {0.0}}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum SplitOrientation {
Left,
Right,
Top,
Bottom,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum SplitState {
Inactive,
Hover,
Drag,
DragNotFollowing,
}