Home | History | Annotate | Download | only in gtk
      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 "chrome/browser/ui/gtk/gtk_chrome_shrinkable_hbox.h"
      6 
      7 #include <vector>
      8 
      9 #include "testing/gtest/include/gtest/gtest.h"
     10 
     11 namespace {
     12 
     13 const int kSpacing = 3;
     14 const int kBorderWidth = 5;
     15 
     16 }  // namespace
     17 
     18 class GtkChromeShrinkableHBoxTest : public testing::Test {
     19  protected:
     20   GtkChromeShrinkableHBoxTest()
     21       : window_(gtk_window_new(GTK_WINDOW_TOPLEVEL)),
     22         box_(gtk_chrome_shrinkable_hbox_new(FALSE, FALSE, kSpacing)) {
     23     gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR);
     24     gtk_window_set_default_size(GTK_WINDOW(window_), 200, 200);
     25     gtk_container_add(GTK_CONTAINER(window_), box_);
     26     gtk_container_set_border_width(GTK_CONTAINER(box_), kBorderWidth);
     27   }
     28 
     29   ~GtkChromeShrinkableHBoxTest() {
     30     gtk_widget_destroy(window_);
     31   }
     32 
     33   // Add some children widgets with arbitrary width and padding.
     34   void AddChildren(bool pack_start) {
     35     static struct {
     36       int width;
     37       int padding;
     38     } kChildrenData[] = {
     39       { 60, 2 },
     40       { 70, 3 },
     41       { 80, 5 },
     42       { 50, 7 },
     43       { 40, 11 },
     44       { 60, 0 },
     45       { 0, 0 }
     46     };
     47 
     48     for (size_t i = 0; kChildrenData[i].width; ++i) {
     49       GtkWidget* child = gtk_fixed_new();
     50       gtk_widget_set_size_request(child, kChildrenData[i].width, -1);
     51       if (pack_start) {
     52         gtk_chrome_shrinkable_hbox_pack_start(
     53             GTK_CHROME_SHRINKABLE_HBOX(box_), child, kChildrenData[i].padding);
     54       } else {
     55         gtk_chrome_shrinkable_hbox_pack_end(
     56             GTK_CHROME_SHRINKABLE_HBOX(box_), child, kChildrenData[i].padding);
     57       }
     58     }
     59   }
     60 
     61   // Check if all children's size allocation are inside the |box_|'s boundary.
     62   void Validate(bool pack_start) {
     63     std::vector<ChildData> children_data;
     64     gtk_container_foreach(GTK_CONTAINER(box_), CollectChildData,
     65                           &children_data);
     66 
     67     size_t children_count = children_data.size();
     68     size_t visible_children_count = 0;
     69     for (size_t i = 0; i < children_count; ++i) {
     70       if (children_data[i].visible)
     71         ++visible_children_count;
     72     }
     73 
     74     if (visible_children_count == 0)
     75       return;
     76 
     77     int border_width = gtk_container_get_border_width(GTK_CONTAINER(box_));
     78     int x = box_->allocation.x + border_width;
     79     int width = box_->allocation.width - border_width * 2;
     80     int spacing = gtk_box_get_spacing(GTK_BOX(box_));
     81     bool homogeneous = gtk_box_get_homogeneous(GTK_BOX(box_));
     82 
     83     if (homogeneous) {
     84       // If the |box_| is in homogeneous mode, then check if the visible
     85       // children are not overlapped with each other.
     86       int homogeneous_child_width =
     87           (width - (visible_children_count - 1) * spacing) /
     88           visible_children_count;
     89 
     90       for (size_t i = 0; i < children_count; ++i) {
     91         SCOPED_TRACE(testing::Message() << "Validate homogeneous child " << i
     92                      << " visible: " << children_data[i].visible
     93                      << " padding: " << children_data[i].padding
     94                      << " x: " << children_data[i].x
     95                      << " width: " << children_data[i].width);
     96 
     97         if (children_data[i].visible)
     98           ASSERT_LE(children_data[i].width, homogeneous_child_width);
     99       }
    100     } else {
    101       // If the |box_| is not in homogeneous mode, then just check if all
    102       // visible children are inside the |box_|'s boundary. And for those
    103       // hidden children which are out of the boundary, they should only
    104       // be hidden one by one from the end of the |box_|.
    105       bool last_visible = pack_start;
    106       bool visibility_changed = false;
    107       for (size_t i = 0; i < children_count; ++i) {
    108         SCOPED_TRACE(testing::Message() << "Validate child " << i
    109                      << " visible: " << children_data[i].visible
    110                      << " padding: " << children_data[i].padding
    111                      << " x: " << children_data[i].x
    112                      << " width: " << children_data[i].width);
    113 
    114         if (last_visible != children_data[i].visible) {
    115           ASSERT_FALSE(visibility_changed);
    116           visibility_changed = true;
    117           last_visible = children_data[i].visible;
    118         }
    119         if (children_data[i].visible) {
    120           ASSERT_GE(children_data[i].x,
    121                     x + children_data[i].padding);
    122           ASSERT_LE(children_data[i].x + children_data[i].width,
    123                     x + width - children_data[i].padding);
    124         }
    125       }
    126     }
    127   }
    128 
    129   void Test(bool pack_start) {
    130     gtk_widget_show_all(window_);
    131     GtkAllocation allocation = { 0, 0, 0, 200 };
    132     gtk_chrome_shrinkable_hbox_set_hide_child_directly(
    133         GTK_CHROME_SHRINKABLE_HBOX(box_), FALSE);
    134     for (int width = 500; width > kBorderWidth * 2; --width) {
    135       SCOPED_TRACE(testing::Message() << "Shrink hide_child_directly = FALSE,"
    136                    << " width = " << width);
    137 
    138       allocation.width = width;
    139       // Reducing the width may cause some children to be hidden, which will
    140       // cause queue resize, so it's necessary to do another size allocation to
    141       // emulate the queue resize.
    142       gtk_widget_size_allocate(box_, &allocation);
    143       gtk_widget_size_allocate(box_, &allocation);
    144       ASSERT_NO_FATAL_FAILURE(Validate(pack_start)) << "width = " << width;
    145     }
    146 
    147     for (int width = kBorderWidth * 2; width <= 500; ++width) {
    148       SCOPED_TRACE(testing::Message() << "Expand hide_child_directly = FALSE,"
    149                    << " width = " << width);
    150 
    151       allocation.width = width;
    152       // Expanding the width may cause some invisible children to be shown,
    153       // which will cause queue resize, so it's necessary to do another size
    154       // allocation to emulate the queue resize.
    155       gtk_widget_size_allocate(box_, &allocation);
    156       gtk_widget_size_allocate(box_, &allocation);
    157       ASSERT_NO_FATAL_FAILURE(Validate(pack_start));
    158     }
    159 
    160     gtk_chrome_shrinkable_hbox_set_hide_child_directly(
    161         GTK_CHROME_SHRINKABLE_HBOX(box_), TRUE);
    162     for (int width = 500; width > kBorderWidth * 2; --width) {
    163       SCOPED_TRACE(testing::Message() << "Shrink hide_child_directly = TRUE,"
    164                    << " width = " << width);
    165 
    166       allocation.width = width;
    167       gtk_widget_size_allocate(box_, &allocation);
    168       gtk_widget_size_allocate(box_, &allocation);
    169       ASSERT_NO_FATAL_FAILURE(Validate(pack_start));
    170     }
    171 
    172     for (int width = kBorderWidth * 2; width <= 500; ++width) {
    173       SCOPED_TRACE(testing::Message() << "Expand hide_child_directly = TRUE,"
    174                    << " width = " << width);
    175 
    176       allocation.width = width;
    177       gtk_widget_size_allocate(box_, &allocation);
    178       gtk_widget_size_allocate(box_, &allocation);
    179       ASSERT_NO_FATAL_FAILURE(Validate(pack_start));
    180     }
    181   }
    182 
    183  protected:
    184   GtkWidget* window_;
    185   GtkWidget* box_;
    186 
    187  private:
    188   struct ChildData {
    189     bool visible;
    190     int padding;
    191     int x;
    192     int width;
    193   };
    194 
    195   static void CollectChildData(GtkWidget* child, gpointer userdata) {
    196     guint padding;
    197     gtk_box_query_child_packing(GTK_BOX(gtk_widget_get_parent(child)), child,
    198                                 NULL, NULL, &padding, NULL);
    199 
    200     ChildData data;
    201     data.visible = GTK_WIDGET_VISIBLE(child);
    202     data.padding = padding;
    203     data.x = child->allocation.x;
    204     data.width = child->allocation.width;
    205 
    206     reinterpret_cast<std::vector<ChildData>*>(userdata)->push_back(data);
    207   }
    208 };
    209 
    210 TEST_F(GtkChromeShrinkableHBoxTest, PackStart) {
    211   AddChildren(true);
    212 
    213   {
    214     SCOPED_TRACE("Test LTR");
    215     gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR);
    216     EXPECT_NO_FATAL_FAILURE(Test(true));
    217   }
    218   {
    219     SCOPED_TRACE("Test RTL");
    220     gtk_widget_set_direction(box_, GTK_TEXT_DIR_RTL);
    221     EXPECT_NO_FATAL_FAILURE(Test(true));
    222   }
    223 }
    224 
    225 TEST_F(GtkChromeShrinkableHBoxTest, PackEnd) {
    226   AddChildren(false);
    227 
    228   {
    229     SCOPED_TRACE("Test LTR");
    230     gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR);
    231     EXPECT_NO_FATAL_FAILURE(Test(false));
    232   }
    233   {
    234     SCOPED_TRACE("Test RTL");
    235     gtk_widget_set_direction(box_, GTK_TEXT_DIR_RTL);
    236     EXPECT_NO_FATAL_FAILURE(Test(false));
    237   }
    238 }
    239 
    240 TEST_F(GtkChromeShrinkableHBoxTest, Homogeneous) {
    241   AddChildren(true);
    242   gtk_box_set_homogeneous(GTK_BOX(box_), true);
    243 
    244   {
    245     SCOPED_TRACE("Test LTR");
    246     gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR);
    247     EXPECT_NO_FATAL_FAILURE(Test(true));
    248   }
    249   {
    250     SCOPED_TRACE("Test RTL");
    251     gtk_widget_set_direction(box_, GTK_TEXT_DIR_RTL);
    252     EXPECT_NO_FATAL_FAILURE(Test(true));
    253   }
    254 }
    255