1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * This view implements a vertically split display with a draggable divider.
7 *
8 *                  <<-- sizer -->>
9 *
10 *  +----------------------++----------------+
11 *  |                      ||                |
12 *  |                      ||                |
13 *  |                      ||                |
14 *  |                      ||                |
15 *  |       leftView       ||   rightView    |
16 *  |                      ||                |
17 *  |                      ||                |
18 *  |                      ||                |
19 *  |                      ||                |
20 *  |                      ||                |
21 *  +----------------------++----------------+
22 *
23 * @param {!View} leftView The widget to position on the left.
24 * @param {!View} rightView The widget to position on the right.
25 * @param {!DivView} sizerView The widget that will serve as draggable divider.
26 * @constructor
27 */
28function ResizableVerticalSplitView(leftView, rightView, sizerView) {
29  View.call(this);
30
31  this.leftView_ = leftView;
32  this.rightView_ = rightView;
33  this.sizerView_ = sizerView;
34
35  // Setup the "sizer" so it can be dragged left/right to reposition the
36  // vertical split.
37  sizerView.getNode().addEventListener(
38      'mousedown', this.onDragSizerStart_.bind(this), true);
39}
40
41inherits(ResizableVerticalSplitView, View);
42
43// Minimum width to size panels to, in pixels.
44ResizableVerticalSplitView.MIN_PANEL_WIDTH = 50;
45
46/**
47 * Repositions all of the elements to fit the window.
48 */
49ResizableVerticalSplitView.prototype.setGeometry = function(
50    left, top, width, height) {
51  ResizableVerticalSplitView.superClass_.setGeometry.call(
52      this, left, top, width, height);
53
54  // If this is the first setGeometry(), initialize the split point at 50%.
55  if (!this.leftSplit_)
56    this.leftSplit_ = parseInt((width / 2).toFixed(0));
57
58  // Calculate the horizontal split points.
59  var leftboxWidth = this.leftSplit_;
60  var sizerWidth = this.sizerView_.getWidth();
61  var rightboxWidth = width - (leftboxWidth + sizerWidth);
62
63  // Don't let the right pane get too small.
64  if (rightboxWidth < ResizableVerticalSplitView.MIN_PANEL_WIDTH) {
65    rightboxWidth = ResizableVerticalSplitView.MIN_PANEL_WIDTH;
66    leftboxWidth = width - (sizerWidth + rightboxWidth);
67  }
68
69  // Position the boxes using calculated split points.
70  this.leftView_.setGeometry(left, top, leftboxWidth, height);
71  this.sizerView_.setGeometry(this.leftView_.getRight(), top,
72                              sizerWidth, height);
73  this.rightView_.setGeometry(this.sizerView_.getRight(), top,
74                              rightboxWidth, height);
75};
76
77ResizableVerticalSplitView.prototype.show = function(isVisible) {
78  ResizableVerticalSplitView.superClass_.show.call(this, isVisible);
79  this.leftView_.show(isVisible);
80  this.sizerView_.show(isVisible);
81  this.rightView_.show(isVisible);
82};
83
84/**
85 * Called once we have clicked into the sizer. Starts capturing the mouse
86 * position to implement dragging.
87 */
88ResizableVerticalSplitView.prototype.onDragSizerStart_ = function(event) {
89  this.sizerMouseMoveListener_ = this.onDragSizer.bind(this);
90  this.sizerMouseUpListener_ = this.onDragSizerEnd.bind(this);
91
92  window.addEventListener('mousemove', this.sizerMouseMoveListener_, true);
93  window.addEventListener('mouseup', this.sizerMouseUpListener_, true);
94
95  event.preventDefault();
96};
97
98/**
99 * Called when the mouse has moved after dragging started.
100 */
101ResizableVerticalSplitView.prototype.onDragSizer = function(event) {
102  // Convert from page coordinates, to view coordinates.
103  this.leftSplit_ = (event.pageX - this.getLeft());
104
105  // Avoid shrinking the left box too much.
106  this.leftSplit_ = Math.max(
107      this.leftSplit_, ResizableVerticalSplitView.MIN_PANEL_WIDTH);
108  // Avoid shrinking the right box too much.
109  this.leftSplit_ = Math.min(
110      this.leftSplit_,
111      this.getWidth() - ResizableVerticalSplitView.MIN_PANEL_WIDTH);
112
113  // Force a layout with the new |leftSplit_|.
114  this.setGeometry(
115      this.getLeft(), this.getTop(), this.getWidth(), this.getHeight());
116};
117
118/**
119 * Called once the mouse has been released, and the dragging is over.
120 */
121ResizableVerticalSplitView.prototype.onDragSizerEnd = function(event) {
122  window.removeEventListener('mousemove', this.sizerMouseMoveListener_, true);
123  window.removeEventListener('mouseup', this.sizerMouseUpListener_, true);
124
125  this.sizerMouseMoveListener_ = null;
126  this.sizerMouseUpListener_ = null;
127
128  event.preventDefault();
129};
130