Home | History | Annotate | Download | only in controls
      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 #include "ui/views/controls/single_split_view.h"
      6 
      7 #include "base/logging.h"
      8 #include "testing/gtest/include/gtest/gtest.h"
      9 #include "ui/views/controls/single_split_view_listener.h"
     10 
     11 namespace {
     12 
     13 static void VerifySplitViewLayout(const views::SingleSplitView& split) {
     14   ASSERT_EQ(2, split.child_count());
     15 
     16   const views::View* leading = split.child_at(0);
     17   const views::View* trailing = split.child_at(1);
     18 
     19   if (split.bounds().IsEmpty()) {
     20     EXPECT_TRUE(leading->bounds().IsEmpty());
     21     EXPECT_TRUE(trailing->bounds().IsEmpty());
     22     return;
     23   }
     24 
     25   EXPECT_FALSE(leading->bounds().IsEmpty());
     26   EXPECT_FALSE(trailing->bounds().IsEmpty());
     27   EXPECT_FALSE(leading->bounds().Intersects(trailing->bounds()));
     28 
     29   if (split.orientation() == views::SingleSplitView::HORIZONTAL_SPLIT) {
     30     EXPECT_EQ(leading->bounds().height(), split.bounds().height());
     31     EXPECT_EQ(trailing->bounds().height(), split.bounds().height());
     32     EXPECT_LT(leading->bounds().width() + trailing->bounds().width(),
     33               split.bounds().width());
     34   } else if (split.orientation() == views::SingleSplitView::VERTICAL_SPLIT) {
     35     EXPECT_EQ(leading->bounds().width(), split.bounds().width());
     36     EXPECT_EQ(trailing->bounds().width(), split.bounds().width());
     37     EXPECT_LT(leading->bounds().height() + trailing->bounds().height(),
     38               split.bounds().height());
     39   } else {
     40     NOTREACHED();
     41   }
     42 }
     43 
     44 class SingleSplitViewListenerImpl : public views::SingleSplitViewListener {
     45  public:
     46   SingleSplitViewListenerImpl() : count_(0) {}
     47 
     48   virtual bool SplitHandleMoved(views::SingleSplitView* sender) OVERRIDE {
     49     ++count_;
     50     return false;
     51   }
     52 
     53   int count() const { return count_; }
     54 
     55  private:
     56   int count_;
     57 
     58   DISALLOW_COPY_AND_ASSIGN(SingleSplitViewListenerImpl);
     59 };
     60 
     61 class MinimumSizedView: public views::View {
     62  public:
     63   MinimumSizedView(gfx::Size min_size) : min_size_(min_size) {}
     64 
     65  private:
     66   gfx::Size min_size_;
     67   virtual gfx::Size GetMinimumSize() const OVERRIDE;
     68 };
     69 
     70 gfx::Size MinimumSizedView::GetMinimumSize() const {
     71   return min_size_;
     72 }
     73 
     74 }  // namespace
     75 
     76 namespace views {
     77 
     78 TEST(SingleSplitViewTest, Resize) {
     79   // Test cases to iterate through for horizontal and vertical split views.
     80   struct TestCase {
     81     // Split view resize policy for this test case.
     82     bool resize_leading_on_bounds_change;
     83     // Split view size to set.
     84     int primary_axis_size;
     85     int secondary_axis_size;
     86     // Expected divider offset.
     87     int divider_offset;
     88   } test_cases[] = {
     89     // The initial split size is 100x100, divider at 33.
     90     { true, 100, 100, 33 },
     91     // Grow the split view, leading view should grow.
     92     { true, 1000, 100, 933 },
     93     // Shrink the split view, leading view should shrink.
     94     { true, 200, 100, 133 },
     95     // Minimize the split view, divider should not move.
     96     { true, 0, 0, 133 },
     97     // Restore the split view, divider should not move.
     98     { false, 500, 100, 133 },
     99     // Resize the split view by secondary axis, divider should not move.
    100     { false,  500, 600, 133 }
    101   };
    102 
    103   SingleSplitView::Orientation orientations[] = {
    104     SingleSplitView::HORIZONTAL_SPLIT,
    105     SingleSplitView::VERTICAL_SPLIT
    106   };
    107 
    108   for (size_t orientation = 0; orientation < arraysize(orientations);
    109        ++orientation) {
    110     // Create a split view.
    111     SingleSplitView split(
    112         new View(), new View(), orientations[orientation], NULL);
    113 
    114     // Set initial size and divider offset.
    115     EXPECT_EQ(test_cases[0].primary_axis_size,
    116               test_cases[0].secondary_axis_size);
    117     split.SetBounds(0, 0, test_cases[0].primary_axis_size,
    118                     test_cases[0].secondary_axis_size);
    119     split.set_divider_offset(test_cases[0].divider_offset);
    120     split.Layout();
    121 
    122     // Run all test cases.
    123     for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
    124       split.set_resize_leading_on_bounds_change(
    125           test_cases[i].resize_leading_on_bounds_change);
    126       if (split.orientation() == SingleSplitView::HORIZONTAL_SPLIT) {
    127         split.SetBounds(0, 0, test_cases[i].primary_axis_size,
    128                         test_cases[i].secondary_axis_size);
    129       } else {
    130         split.SetBounds(0, 0, test_cases[i].secondary_axis_size,
    131                         test_cases[i].primary_axis_size);
    132       }
    133 
    134       EXPECT_EQ(test_cases[i].divider_offset, split.divider_offset());
    135       VerifySplitViewLayout(split);
    136     }
    137 
    138     // Special cases, one of the child views is hidden.
    139     split.child_at(0)->SetVisible(false);
    140     split.Layout();
    141 
    142     EXPECT_EQ(split.size(), split.child_at(1)->size());
    143 
    144     split.child_at(0)->SetVisible(true);
    145     split.child_at(1)->SetVisible(false);
    146     split.Layout();
    147 
    148     EXPECT_EQ(split.size(), split.child_at(0)->size());
    149   }
    150 }
    151 
    152 TEST(SingleSplitViewTest, MouseDrag) {
    153   const int kMinimumChildSize = 25;
    154   MinimumSizedView *child0 =
    155       new MinimumSizedView(gfx::Size(5, kMinimumChildSize));
    156   MinimumSizedView *child1 =
    157       new MinimumSizedView(gfx::Size(5, kMinimumChildSize));
    158   SingleSplitViewListenerImpl listener;
    159   SingleSplitView split(
    160       child0, child1, SingleSplitView::VERTICAL_SPLIT, &listener);
    161 
    162   const int kTotalSplitSize = 100;
    163   split.SetBounds(0, 0, 10, kTotalSplitSize);
    164   const int kInitialDividerOffset = 33;
    165   const int kMouseOffset = 2;  // Mouse offset in the divider.
    166   const int kMouseMoveDelta = 7;
    167   split.set_divider_offset(kInitialDividerOffset);
    168   split.Layout();
    169 
    170   gfx::Point press_point(7, kInitialDividerOffset + kMouseOffset);
    171   ui::MouseEvent mouse_pressed(
    172       ui::ET_MOUSE_PRESSED, press_point, press_point, 0, 0);
    173   ASSERT_TRUE(split.OnMousePressed(mouse_pressed));
    174   EXPECT_EQ(kInitialDividerOffset, split.divider_offset());
    175   EXPECT_EQ(0, listener.count());
    176 
    177   // Drag divider to the bottom.
    178   gfx::Point drag_1_point(
    179       5, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta);
    180   ui::MouseEvent mouse_dragged_1(
    181       ui::ET_MOUSE_DRAGGED, drag_1_point, drag_1_point, 0, 0);
    182   ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_1));
    183   EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta, split.divider_offset());
    184   EXPECT_EQ(1, listener.count());
    185 
    186   // Drag divider to the top, beyond first child minimum size.
    187   gfx::Point drag_2_point(
    188       7, kMinimumChildSize - 5);
    189   ui::MouseEvent mouse_dragged_2(
    190       ui::ET_MOUSE_DRAGGED, drag_2_point, drag_2_point, 0,0 );
    191   ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_2));
    192   EXPECT_EQ(kMinimumChildSize, split.divider_offset());
    193   EXPECT_EQ(2, listener.count());
    194 
    195   // Drag divider to the bottom, beyond second child minimum size.
    196   gfx::Point drag_3_point(
    197       7, kTotalSplitSize - kMinimumChildSize + 5);
    198   ui::MouseEvent mouse_dragged_3(
    199       ui::ET_MOUSE_DRAGGED, drag_3_point, drag_3_point, 0, 0);
    200   ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_3));
    201   EXPECT_EQ(kTotalSplitSize - kMinimumChildSize - split.GetDividerSize(),
    202             split.divider_offset());
    203   EXPECT_EQ(3, listener.count());
    204 
    205   // Drag divider between childs' minimum sizes.
    206   gfx::Point drag_4_point(
    207       6, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta * 2);
    208   ui::MouseEvent mouse_dragged_4(
    209       ui::ET_MOUSE_DRAGGED, drag_4_point, drag_4_point, 0, 0);
    210   ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_4));
    211   EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta * 2,
    212             split.divider_offset());
    213   EXPECT_EQ(4, listener.count());
    214 
    215   gfx::Point release_point(
    216       7, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta * 2);
    217   ui::MouseEvent mouse_released(
    218       ui::ET_MOUSE_RELEASED, release_point, release_point, 0, 0);
    219   split.OnMouseReleased(mouse_released);
    220   EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta * 2,
    221             split.divider_offset());
    222 
    223   // Expect intial offset after a system/user gesture cancels the drag.
    224   // This shouldn't occur after mouse release, but it's sufficient for testing.
    225   split.OnMouseCaptureLost();
    226   EXPECT_EQ(kInitialDividerOffset, split.divider_offset());
    227   EXPECT_EQ(5, listener.count());
    228 }
    229 
    230 }  // namespace views
    231