summaryrefslogtreecommitdiffstats
path: root/views/controls/single_split_view.h
blob: dc69c6729639e7c34ccfe2530c3e0c3d867b9af1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_
#define VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_
#pragma once

#include "base/gtest_prod_util.h"
#include "views/view.h"

namespace views {

// SingleSplitView lays out two views next to each other, either horizontally
// or vertically. A splitter exists between the two views that the user can
// drag around to resize the views.
// Observer's SplitHandleMoved notification helps to monitor user initiated
// layout changes.
class SingleSplitView : public views::View {
 public:
  enum Orientation {
    HORIZONTAL_SPLIT,
    VERTICAL_SPLIT
  };

  class Observer {
   public:
    // Invoked when split handle is moved by the user. |source|'s divider_offset
    // is already set to the new value, but Layout has not happened yet.
    // Returns false if the layout has been handled by the observer, returns
    // true if |source| should do it by itself.
    virtual bool SplitHandleMoved(SingleSplitView* source) = 0;
   protected:
    virtual ~Observer() {}
  };

  SingleSplitView(View* leading,
                  View* trailing,
                  Orientation orientation,
                  Observer* observer);

  virtual void OnBoundsChanged();
  virtual void Layout();

  virtual AccessibilityTypes::Role GetAccessibleRole();

  // SingleSplitView's preferred size is the sum of the preferred widths
  // and the max of the heights.
  virtual gfx::Size GetPreferredSize();

  // Overriden to return a resize cursor when over the divider.
  virtual gfx::NativeCursor GetCursorForPoint(ui::EventType event_type,
                                              const gfx::Point& p);

  Orientation orientation() const {
    return is_horizontal_ ? HORIZONTAL_SPLIT : VERTICAL_SPLIT;
  }

  void set_divider_offset(int divider_offset) {
    divider_offset_ = divider_offset;
  }
  int divider_offset() const { return divider_offset_; }

  // Sets whether the leading component is resized when the split views size
  // changes. The default is true. A value of false results in the trailing
  // component resizing on a bounds change.
  void set_resize_leading_on_bounds_change(bool resize) {
    resize_leading_on_bounds_change_ = resize;
  }

  // Calculates ideal leading and trailing view bounds according to the given
  // split view |bounds|, current divider offset and children visiblity.
  // Does not change children view bounds.
  void CalculateChildrenBounds(const gfx::Rect& bounds,
                               gfx::Rect* leading_bounds,
                               gfx::Rect* trailing_bounds) const;

 protected:
  virtual bool OnMousePressed(const MouseEvent& event);
  virtual bool OnMouseDragged(const MouseEvent& event);
  virtual void OnMouseReleased(const MouseEvent& event, bool canceled);

 private:
  // This test calls OnMouse* functions.
  FRIEND_TEST_ALL_PREFIXES(SingleSplitViewTest, MouseDrag);

  // Returns true if |x| or |y| is over the divider.
  bool IsPointInDivider(const gfx::Point& p);

  // Calculates the new |divider_offset| based on the changes of split view
  // bounds.
  int CalculateDividerOffset(
      int divider_offset,
      const gfx::Rect& previous_bounds,
      const gfx::Rect& new_bounds) const;

  // Returns divider offset within primary axis size range for given split
  // view |bounds|.
  int NormalizeDividerOffset(int divider_offset, const gfx::Rect& bounds) const;

  // Returns width in case of horizontal split and height otherwise.
  int GetPrimaryAxisSize() const {
    return GetPrimaryAxisSize(width(), height());
  }

  int GetPrimaryAxisSize(int h, int v) const {
    return is_horizontal_ ? h : v;
  }

  // Used to track drag info.
  struct DragInfo {
    // The initial coordinate of the mouse when the user started the drag.
    int initial_mouse_offset;
    // The initial position of the divider when the user started the drag.
    int initial_divider_offset;
  };

  DragInfo drag_info_;

  // Orientation of the split view.
  bool is_horizontal_;

  // Position of the divider.
  int divider_offset_;

  // The bounds of the SingleSplitView as a result of the last resize. Used to
  // determine the divider position when a subsequent resize occurs.
  gfx::Rect previous_bounds_;

  bool resize_leading_on_bounds_change_;

  // Observer to notify about user initiated handle movements. Not own by us.
  Observer* observer_;

  DISALLOW_COPY_AND_ASSIGN(SingleSplitView);
};

}  // namespace views

#endif  // VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_