1// Copyright (c) 2011 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#ifndef UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_
6#define UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_
7
8#include "base/gtest_prod_util.h"
9#include "ui/views/view.h"
10
11namespace views {
12
13class SingleSplitViewListener;
14
15// SingleSplitView lays out two views next to each other, either horizontally
16// or vertically. A splitter exists between the two views that the user can
17// drag around to resize the views.
18// SingleSplitViewListener's SplitHandleMoved notification helps to monitor user
19// initiated layout changes.
20class VIEWS_EXPORT SingleSplitView : public View {
21 public:
22  enum Orientation {
23    HORIZONTAL_SPLIT,
24    VERTICAL_SPLIT
25  };
26
27  static const char kViewClassName[];
28
29  SingleSplitView(View* leading,
30                  View* trailing,
31                  Orientation orientation,
32                  SingleSplitViewListener* listener);
33
34  virtual void Layout() OVERRIDE;
35  virtual const char* GetClassName() const OVERRIDE;
36
37  virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE;
38
39  // SingleSplitView's preferred size is the sum of the preferred widths
40  // and the max of the heights.
41  virtual gfx::Size GetPreferredSize() const OVERRIDE;
42
43  // Overriden to return a resize cursor when over the divider.
44  virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) OVERRIDE;
45
46  Orientation orientation() const {
47    return is_horizontal_ ? HORIZONTAL_SPLIT : VERTICAL_SPLIT;
48  }
49
50  void set_orientation(Orientation orientation) {
51    is_horizontal_ = orientation == HORIZONTAL_SPLIT;
52  }
53
54  void set_divider_offset(int divider_offset) {
55    divider_offset_ = divider_offset;
56  }
57  int divider_offset() const { return divider_offset_; }
58
59  int GetDividerSize() const;
60
61  void set_resize_disabled(bool resize_disabled) {
62    resize_disabled_ = resize_disabled;
63  }
64  bool is_resize_disabled() const { return resize_disabled_; }
65
66  // Sets whether the leading component is resized when the split views size
67  // changes. The default is true. A value of false results in the trailing
68  // component resizing on a bounds change.
69  void set_resize_leading_on_bounds_change(bool resize) {
70    resize_leading_on_bounds_change_ = resize;
71  }
72
73  // Calculates ideal leading and trailing view bounds according to the given
74  // split view |bounds|, current divider offset and children visiblity.
75  // Does not change children view bounds.
76  void CalculateChildrenBounds(const gfx::Rect& bounds,
77                               gfx::Rect* leading_bounds,
78                               gfx::Rect* trailing_bounds) const;
79
80  void SetAccessibleName(const base::string16& name);
81
82 protected:
83  // View overrides.
84  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
85  virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
86  virtual void OnMouseCaptureLost() OVERRIDE;
87  virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;
88
89 private:
90  // This test calls OnMouse* functions.
91  FRIEND_TEST_ALL_PREFIXES(SingleSplitViewTest, MouseDrag);
92
93  // Returns true if |x| or |y| is over the divider.
94  bool IsPointInDivider(const gfx::Point& p);
95
96  // Calculates the new |divider_offset| based on the changes of split view
97  // bounds.
98  int CalculateDividerOffset(int divider_offset,
99                             const gfx::Rect& previous_bounds,
100                             const gfx::Rect& new_bounds) const;
101
102  // Returns divider offset within primary axis size range for given split
103  // view |bounds|.
104  int NormalizeDividerOffset(int divider_offset, const gfx::Rect& bounds) const;
105
106  // Returns width in case of horizontal split and height otherwise.
107  int GetPrimaryAxisSize() const {
108    return GetPrimaryAxisSize(width(), height());
109  }
110
111  int GetPrimaryAxisSize(int h, int v) const {
112    return is_horizontal_ ? h : v;
113  }
114
115  // Used to track drag info.
116  struct DragInfo {
117    // The initial coordinate of the mouse when the user started the drag.
118    int initial_mouse_offset;
119    // The initial position of the divider when the user started the drag.
120    int initial_divider_offset;
121  };
122
123  DragInfo drag_info_;
124
125  // Orientation of the split view.
126  bool is_horizontal_;
127
128  // Position of the divider.
129  int divider_offset_;
130
131  bool resize_leading_on_bounds_change_;
132
133  // Whether resizing is disabled.
134  bool resize_disabled_;
135
136  // Listener to notify about user initiated handle movements. Not owned.
137  SingleSplitViewListener* listener_;
138
139  // The accessible name of this view.
140  base::string16 accessible_name_;
141
142  DISALLOW_COPY_AND_ASSIGN(SingleSplitView);
143};
144
145}  // namespace views
146
147#endif  // UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_
148