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