1extern crate input;
4extern crate vecmath;
5
6use input::{Button, GenericEvent, MouseButton};
7
8use self::math::{is_inside, inside_pos, Matrix2d, Rectangle};
9
10mod math;
11
12const LEFT: u8 = 0x1;
13const RIGHT: u8 = 0x2;
14const TOP: u8 = 0x4;
15const BOTTOM: u8 = 0x8;
16
17#[derive(Copy, Clone, PartialEq, Debug)]
19pub struct SplitLayoutSettings {
20 pub border: f64,
22 pub center_min_size: [f64; 2],
24 pub left_value: f64,
26 pub left_min_value: f64,
28 pub right_value: f64,
30 pub right_min_value: f64,
32 pub top_value: f64,
34 pub top_min_value: f64,
36 pub bottom_value: f64,
38 pub bottom_min_value: f64,
40 pub lock_left: bool,
42 pub lock_right: bool,
44 pub lock_top: bool,
46 pub lock_bottom: bool,
48}
49
50impl SplitLayoutSettings {
51 pub fn new(border: f64, min_value: f64) -> SplitLayoutSettings {
55 SplitLayoutSettings {
56 border: border,
57 center_min_size: [1.0; 2],
58 left_value: min_value,
59 left_min_value: min_value,
60 right_value: min_value,
61 right_min_value: min_value,
62 top_value: min_value,
63 top_min_value: min_value,
64 bottom_value: min_value,
65 bottom_min_value: min_value,
66 lock_left: false,
67 lock_right: false,
68 lock_top: false,
69 lock_bottom: false,
70 }
71 }
72
73 pub fn left(mut self, value: f64, min_value: f64) -> SplitLayoutSettings {
75 self.left_value = value;
76 self.left_min_value = min_value;
77 self
78 }
79
80 pub fn right(mut self, value: f64, min_value: f64) -> SplitLayoutSettings {
82 self.right_value = value;
83 self.right_min_value = min_value;
84 self
85 }
86
87 pub fn top(mut self, value: f64, min_value: f64) -> SplitLayoutSettings {
89 self.top_value = value;
90 self.top_min_value = min_value;
91 self
92 }
93
94 pub fn bottom(mut self, value: f64, min_value: f64) -> SplitLayoutSettings {
96 self.bottom_value = value;
97 self.bottom_min_value = min_value;
98 self
99 }
100
101 pub fn lock_left(mut self, value: f64) -> SplitLayoutSettings {
103 self.lock_left = true;
104 self.left_value = value;
105 self.left_min_value = value;
106 self
107 }
108
109 pub fn lock_right(mut self, value: f64) -> SplitLayoutSettings {
111 self.lock_right = true;
112 self.right_value = value;
113 self.right_min_value = value;
114 self
115 }
116
117 pub fn lock_top(mut self, value: f64) -> SplitLayoutSettings {
119 self.lock_top = true;
120 self.top_value = value;
121 self.top_min_value = value;
122 self
123 }
124
125 pub fn lock_bottom(mut self, value: f64) -> SplitLayoutSettings {
127 self.lock_bottom = true;
128 self.bottom_value = value;
129 self.bottom_min_value = value;
130 self
131 }
132}
133
134pub struct SplitLayoutController {
138 pub left: SplitController,
140 pub right: SplitController,
142 pub top: SplitController,
144 pub bottom: SplitController,
146 center_min_size: [f64; 2],
148 drag_splits: u8,
150 lock_splits: u8,
152}
153
154impl SplitLayoutController {
155 pub fn new(settings: &SplitLayoutSettings) -> SplitLayoutController {
157 SplitLayoutController {
158 left: SplitController::new(settings.left_value, settings.left_min_value,
159 settings.border, SplitOrientation::Left),
160 right: SplitController::new(settings.right_value, settings.right_min_value,
161 settings.border, SplitOrientation::Right),
162 top: SplitController::new(settings.top_value, settings.top_min_value,
163 settings.border, SplitOrientation::Top),
164 bottom: SplitController::new(settings.bottom_value, settings.bottom_min_value,
165 settings.border, SplitOrientation::Bottom),
166 center_min_size: settings.center_min_size,
167 drag_splits: 0,
168 lock_splits: if settings.lock_left {LEFT} else {0} |
169 if settings.lock_right {RIGHT} else {0} |
170 if settings.lock_top {TOP} else {0} |
171 if settings.lock_bottom {BOTTOM} else {0},
172 }
173 }
174
175 pub fn event<E: GenericEvent>(&mut self, rect: Rectangle, transform: Matrix2d, e: &E) {
177 let bounds = self.bounds(rect);
178
179 if (self.lock_splits & TOP) != TOP {
180 if self.drag_splits == 0 || (self.drag_splits & TOP) == TOP {
181 let layout = self.top_bottom_layout();
182 let max_value = bounds[3] - self.bottom.value - self.center_min_size[1] -
183 self.top.border - self.bottom.border;
184 self.top.event(layout, max_value, bounds, transform, e);
185 }
186 }
187 if (self.lock_splits & BOTTOM) != BOTTOM {
188 if self.drag_splits == 0 || (self.drag_splits & BOTTOM) == BOTTOM {
189 let layout = self.top_bottom_layout();
190 let max_value = bounds[3] - self.top.value - self.center_min_size[1] -
191 self.bottom.border - self.top.border;
192 self.bottom.event(layout, max_value, bounds, transform, e);
193 }
194 }
195 if (self.lock_splits & LEFT) != LEFT {
196 if self.drag_splits == 0 || (self.drag_splits & LEFT) == LEFT {
197 let layout = self.left_right_layout(SplitLayoutPurpose::Event);
198 let max_value = bounds[2] - self.right.value - self.center_min_size[0] -
199 self.left.border - self.right.border;
200 self.left.event(layout, max_value, bounds, transform, e);
201 }
202 }
203 if (self.lock_splits & RIGHT) != RIGHT {
204 if self.drag_splits == 0 || (self.drag_splits & RIGHT) == RIGHT {
205 let layout = self.left_right_layout(SplitLayoutPurpose::Event);
206 let max_value = bounds[2] - self.left.value - self.center_min_size[0] -
207 self.right.border - self.left.border;
208 self.right.event(layout, max_value, bounds, transform, e);
209 }
210 }
211
212 self.drag_splits = if self.top.is_dragging() {TOP} else {0} |
213 if self.bottom.is_dragging() {BOTTOM} else {0} |
214 if self.left.is_dragging() {LEFT} else {0} |
215 if self.right.is_dragging() {RIGHT} else {0};
216 }
217
218 pub fn left_right_layout(&self, purpose: SplitLayoutPurpose) -> SplitLayout {
223 let sign = purpose.sign();
224 SplitLayout {
225 start: self.top.value + sign * self.top.border,
226 end: self.bottom.value + sign * self.bottom.border
227 }
228 }
229
230 pub fn top_bottom_layout(&self) -> SplitLayout {
232 SplitLayout {start: 0.0, end: 0.0}
233 }
234
235 pub fn rectangles(&self, rect: Rectangle) -> [Rectangle; 4] {
237 let bounds = self.bounds(rect);
238 let top_bottom_layout = self.top_bottom_layout();
239 let left_right_layout = self.left_right_layout(SplitLayoutPurpose::Draw);
240 [
241 self.left.line_rect(left_right_layout, bounds),
242 self.right.line_rect(left_right_layout, bounds),
243 self.top.line_rect(top_bottom_layout, bounds),
244 self.bottom.line_rect(top_bottom_layout, bounds),
245 ]
246 }
247
248 pub fn states(&self) -> [SplitState; 4] {
250 [self.left.state(), self.right.state(), self.top.state(), self.bottom.state()]
251 }
252
253 pub fn panel_rectangles(&self, rect: Rectangle) -> [Rectangle; 5] {
255 let bounds = self.bounds(rect);
256 let left_right_y = bounds[1] + self.top.value + self.top.border;
257 let left_right_h = bounds[3] - self.top.value - self.top.border -
258 self.bottom.value - self.bottom.border;
259 [
260 [bounds[0], left_right_y, self.left.value, left_right_h],
261 [bounds[0] + bounds[2] - self.right.value, left_right_y,
262 self.right.value, left_right_h],
263 [bounds[0], bounds[1], bounds[2], self.top.value],
264 [bounds[0], bounds[1] + bounds[3] - self.bottom.value, bounds[2], self.bottom.value],
265 [bounds[0] + self.left.value + self.left.border, left_right_y,
266 bounds[2] - self.right.value - self.right.border -
267 self.left.value - self.left.border, left_right_h],
268 ]
269 }
270
271 pub fn min_size(&self) -> [f64; 2] {
276 [
277 self.left.value + self.left.border + self.right.value + self.right.border +
278 self.center_min_size[0],
279 self.top.value + self.top.border + self.bottom.value + self.bottom.border +
280 self.center_min_size[1]
281 ]
282 }
283
284 pub fn bounds(&self, rect: Rectangle) -> Rectangle {
288 let min_size = self.min_size();
289 [rect[0], rect[1], rect[2].max(min_size[0]), rect[3].max(min_size[1])]
290 }
291}
292
293pub struct SplitController {
295 mouse_hover: bool,
297 dragging: bool,
299 pub value: f64,
301 pub min_value: f64,
303 pub border: f64,
305 pub orientation: SplitOrientation,
307}
308
309impl SplitController {
310 pub fn new(
312 value: f64,
313 min_value: f64,
314 border: f64,
315 orientation: SplitOrientation
316 ) -> SplitController {
317 SplitController {
318 mouse_hover: false,
319 dragging: false,
320 value: value,
321 min_value: min_value,
322 border: border,
323 orientation: orientation,
324 }
325 }
326
327 pub fn is_dragging(&self) -> bool {self.dragging}
329
330 pub fn event<E: GenericEvent>(
332 &mut self,
333 layout: SplitLayout,
334 max_value: f64,
335 rect: Rectangle,
336 transform: Matrix2d,
337 e: &E
338 ) {
339 if let Some(pos) = e.mouse_cursor_args() {
340 let pos = inside_pos(pos, transform);
341 if self.dragging {
342 match self.orientation {
343 SplitOrientation::Left => {
344 self.value = (pos[0] - rect[0] - 0.5 * self.border)
345 .max(self.min_value)
346 .min(max_value);
347 }
348 SplitOrientation::Right => {
349 self.value = (rect[2] - pos[0] + rect[0] - 0.5 * self.border)
350 .max(self.min_value)
351 .min(max_value);
352 }
353 SplitOrientation::Top => {
354 self.value = (pos[1] - rect[1] - 0.5 * self.border)
355 .max(self.min_value)
356 .min(max_value);
357 }
358 SplitOrientation::Bottom => {
359 self.value = (rect[1] + rect[3] - pos[1] - 0.5 * self.border)
360 .max(self.min_value)
361 .min(max_value);
362 }
363 }
364 }
365 let line_rect = self.line_rect(layout, rect);
366 self.mouse_hover = is_inside(pos, line_rect);
367 }
368
369 if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() {
370 if self.mouse_hover {
371 self.dragging = true;
372 }
373 }
374
375 if let Some(Button::Mouse(MouseButton::Left)) = e.release_args() {
376 self.dragging = false;
377 }
378 }
379
380 pub fn state(&self) -> SplitState {
382 match (self.mouse_hover, self.dragging) {
383 (false, false) => SplitState::Inactive,
384 (true, false) => SplitState::Hover,
385 (true, true) => SplitState::Drag,
386 (false, true) => SplitState::DragNotFollowing,
387 }
388 }
389
390 pub fn line_rect(&self, layout: SplitLayout, rect: Rectangle) -> Rectangle {
392 match self.orientation {
393 SplitOrientation::Left => {
394 [rect[0] + self.value, rect[1] + layout.start,
395 self.border, rect[3] - layout.start - layout.end]
396 }
397 SplitOrientation::Right => {
398 [rect[0] + rect[2] - self.value - self.border, rect[1] + layout.start,
399 self.border, rect[3] - layout.start - layout.end]
400 }
401 SplitOrientation::Top => {
402 [rect[0] + layout.start, rect[1] + self.value,
403 rect[2] - layout.start - layout.end, self.border]
404 }
405 SplitOrientation::Bottom => {
406 [rect[0] + layout.start, rect[1] + rect[3] - self.value - self.border,
407 rect[2] - layout.start - layout.end, self.border]
408 }
409 }
410 }
411}
412
413#[derive(Copy, Clone, PartialEq, Debug)]
415pub struct SplitLayout {
416 pub start: f64,
418 pub end: f64,
420}
421
422#[derive(Copy, Clone, PartialEq, Debug)]
424pub enum SplitLayoutPurpose {
425 Draw,
427 Event,
429}
430
431impl SplitLayoutPurpose {
432 fn sign(self) -> f64 {if let SplitLayoutPurpose::Draw = self {1.0} else {0.0}}
433}
434
435#[derive(Copy, Clone, PartialEq, Eq, Debug)]
437pub enum SplitOrientation {
438 Left,
440 Right,
442 Top,
444 Bottom,
446}
447
448#[derive(Copy, Clone, PartialEq, Eq, Debug)]
450pub enum SplitState {
451 Inactive,
453 Hover,
455 Drag,
457 DragNotFollowing,
459}