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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
|
// Copyright (c) 2013 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.
#include "chrome/browser/ui/cocoa/autofill/simple_grid_layout.h"
#include <stddef.h>
#include <algorithm>
#include "base/logging.h"
#include "base/stl_util.h"
namespace {
const int kAutoColumnIdStart = 1000000; // Starting ID for autogeneration.
}
// Encapsulates state for a single NSView in the layout
class ViewState {
public:
ViewState(NSView* view, ColumnSet* column_set, int row, int column);
// Gets the current width of the column associated with this view.
float GetColumnWidth();
// Get the preferred height for specified width.
float GetHeightForWidth(float with);
Column* GetColumn() const { return column_set_->GetColumn(column_); }
int row_index() { return row_; }
NSView* view() { return view_; }
float preferred_height() { return pref_height_; }
void set_preferred_height(float height) { pref_height_ = height; }
private:
NSView* view_;
ColumnSet* column_set_;
int row_;
int column_;
float pref_height_;
};
class LayoutElement {
public:
LayoutElement(float resize_percent, int fixed_width);
virtual ~LayoutElement() {}
template <class T>
static void ResetSizes(ScopedVector<T>* elements) {
// Reset the layout width of each column.
for (typename std::vector<T*>::iterator i = elements->begin();
i != elements->end(); ++i) {
(*i)->ResetSize();
}
}
template <class T>
static void CalculateLocationsFromSize(ScopedVector<T>* elements) {
// Reset the layout width of each column.
int location = 0;
for (typename std::vector<T*>::iterator i = elements->begin();
i != elements->end(); ++i) {
(*i)->SetLocation(location);
location += (*i)->Size();
}
}
float Size() { return size_; }
void ResetSize() {
size_ = fixed_width_;
}
void SetSize(float size) {
size_ = size;
}
float Location() const {
return location_;
}
// Adjusts the size of this LayoutElement to be the max of the current size
// and the specified size.
virtual void AdjustSize(float size) {
size_ = std::max(size_, size);
}
void SetLocation(float location) {
location_ = location;
}
bool IsResizable() {
return resize_percent_ > 0.0f;
}
float ResizePercent() {
return resize_percent_;
}
private:
float resize_percent_;
int fixed_width_;
float size_;
float location_;
};
LayoutElement::LayoutElement(float resize_percent, int fixed_width)
: resize_percent_(resize_percent),
fixed_width_(fixed_width),
size_(0),
location_(0) {
}
class Column : public LayoutElement {
public:
Column(float resize_percent, int fixed_width, bool is_padding);
bool is_padding() { return is_padding_; }
private:
const bool is_padding_;
};
Column::Column(float resize_percent, int fixed_width, bool is_padding)
: LayoutElement(resize_percent, fixed_width),
is_padding_(is_padding) {
}
class Row : public LayoutElement {
public:
Row(float resize_percent, int fixed_height, ColumnSet* column_set);
ColumnSet* column_set() { return column_set_; }
private:
ColumnSet* column_set_;
};
Row::Row(float resize_percent, int fixed_height, ColumnSet* column_set)
: LayoutElement(resize_percent, fixed_height),
column_set_(column_set) {
}
ViewState::ViewState(NSView* view, ColumnSet* column_set, int row, int column)
: view_(view),
column_set_(column_set),
row_(row),
column_(column) {}
float ViewState::GetColumnWidth() {
return column_set_->GetColumnWidth(column_);
}
float ViewState::GetHeightForWidth(float width) {
// NSView doesn't have any way to make height fit size, get frame height.
return NSHeight([view_ frame]);
}
ColumnSet::ColumnSet(int id) : id_(id) {
}
ColumnSet::~ColumnSet() {
}
void ColumnSet::AddPaddingColumn(int fixed_width) {
columns_.push_back(new Column(0.0f, fixed_width, true));
}
void ColumnSet::AddColumn(float resize_percent) {
columns_.push_back(new Column(resize_percent, 0, false));
}
void ColumnSet::CalculateSize(float width) {
// Reset column widths
LayoutElement::ResetSizes(&columns_);
width = CalculateRemainingWidth(width);
DistributeRemainingWidth(width);
}
void ColumnSet::ResetColumnXCoordinates() {
LayoutElement::CalculateLocationsFromSize(&columns_);
}
float ColumnSet::CalculateRemainingWidth(float width) {
for (size_t i = 0; i < columns_.size(); ++i)
width -= columns_[i]->Size();
return width;
}
void ColumnSet::DistributeRemainingWidth(float width) {
float total_resize = 0.0f;
int resizable_columns = 0.0;
for (size_t i = 0; i < columns_.size(); ++i) {
if (columns_[i]->IsResizable()) {
total_resize += columns_[i]->ResizePercent();
resizable_columns++;
}
}
float remaining_width = width;
for (size_t i = 0; i < columns_.size(); ++i) {
if (columns_[i]->IsResizable()) {
float delta = (resizable_columns == 0) ? remaining_width :
(width * columns_[i]->ResizePercent() / total_resize);
remaining_width -= delta;
columns_[i]->SetSize(columns_[i]->Size() + delta);
resizable_columns--;
}
}
}
float ColumnSet::GetColumnWidth(int column_index) {
if (column_index < 0 || column_index >= num_columns())
return 0.0;
return columns_[column_index]->Size();
}
float ColumnSet::ColumnLocation(int column_index) {
if (column_index < 0 || column_index >= num_columns())
return 0.0;
return columns_[column_index]->Location();
}
SimpleGridLayout::SimpleGridLayout(NSView* host)
: next_column_(0),
current_auto_id_(kAutoColumnIdStart),
host_(host) {
[host_ frame];
}
SimpleGridLayout::~SimpleGridLayout() {
}
ColumnSet* SimpleGridLayout::AddColumnSet(int id) {
DCHECK(GetColumnSet(id) == NULL);
ColumnSet* column_set = new ColumnSet(id);
column_sets_.push_back(column_set);
return column_set;
}
ColumnSet* SimpleGridLayout::GetColumnSet(int id) {
for (ScopedVector<ColumnSet>::const_iterator i = column_sets_.begin();
i != column_sets_.end(); ++i) {
if ((*i)->id() == id) {
return *i;
}
}
return NULL;
}
void SimpleGridLayout::AddPaddingRow(int fixed_height) {
AddRow(new Row(0.0f, fixed_height, NULL));
}
void SimpleGridLayout::StartRow(float vertical_resize, int column_set_id) {
ColumnSet* column_set = GetColumnSet(column_set_id);
DCHECK(column_set);
AddRow(new Row(vertical_resize, 0, column_set));
}
ColumnSet* SimpleGridLayout::AddRow() {
AddRow(new Row(0, 0, AddColumnSet(current_auto_id_++)));
return column_sets_.back();
}
void SimpleGridLayout::SkipColumns(int col_count) {
DCHECK(col_count > 0);
next_column_ += col_count;
ColumnSet* current_row_col_set_ = GetLastValidColumnSet();
DCHECK(current_row_col_set_ &&
next_column_ <= current_row_col_set_->num_columns());
SkipPaddingColumns();
}
void SimpleGridLayout::AddView(NSView* view) {
[host_ addSubview:view];
DCHECK(next_column_ < GetLastValidColumnSet()->num_columns());
view_states_.push_back(
new ViewState(view,
GetLastValidColumnSet(),
rows_.size() - 1,
next_column_++));
SkipPaddingColumns();
}
// Sizes elements to fit into the superViews bounds, according to constraints.
void SimpleGridLayout::Layout(NSView* superView) {
SizeRowsAndColumns(NSWidth([superView bounds]));
for (std::vector<ViewState*>::iterator i = view_states_.begin();
i != view_states_.end(); ++i) {
ViewState* view_state = *i;
NSView* view = view_state->view();
NSRect frame = NSMakeRect(view_state->GetColumn()->Location(),
rows_[view_state->row_index()]->Location(),
view_state->GetColumn()->Size(),
rows_[view_state->row_index()]->Size());
[view setFrame:NSIntegralRect(frame)];
}
}
void SimpleGridLayout::SizeRowsAndColumns(float width) {
// Size all columns first.
for (ScopedVector<ColumnSet>::iterator i = column_sets_.begin();
i != column_sets_.end(); ++i) {
(*i)->CalculateSize(width);
(*i)->ResetColumnXCoordinates();
}
// Reset the height of each row.
LayoutElement::ResetSizes(&rows_);
// For each ViewState, obtain the preferred height
for (std::vector<ViewState*>::iterator i= view_states_.begin();
i != view_states_.end() ; ++i) {
ViewState* view_state = *i;
// The view is resizable. As the pref height may vary with the width,
// ask for the pref again.
int actual_width = view_state->GetColumnWidth();
// The width this view will get differs from its preferred. Some Views
// pref height varies with its width; ask for the preferred again.
view_state->set_preferred_height(
view_state->GetHeightForWidth(actual_width));
}
// Make sure each row can accommodate all contained ViewStates.
std::vector<ViewState*>::iterator view_states_iterator = view_states_.begin();
for (; view_states_iterator != view_states_.end(); ++view_states_iterator) {
ViewState* view_state = *view_states_iterator;
Row* row = rows_[view_state->row_index()];
row->AdjustSize(view_state->preferred_height());
}
// Update the location of each of the rows.
LayoutElement::CalculateLocationsFromSize(&rows_);
}
void SimpleGridLayout::SkipPaddingColumns() {
ColumnSet* current_row_col_set_ = GetLastValidColumnSet();
while (next_column_ < current_row_col_set_->num_columns() &&
current_row_col_set_->GetColumn(next_column_)->is_padding()) {
next_column_++;
}
}
ColumnSet* SimpleGridLayout::GetLastValidColumnSet() {
for (int i = num_rows() - 1; i >= 0; --i) {
if (rows_[i]->column_set())
return rows_[i]->column_set();
}
return NULL;
}
float SimpleGridLayout::GetRowHeight(int row_index) {
if (row_index < 0 || row_index >= num_rows())
return 0.0;
return rows_[row_index]->Size();
}
float SimpleGridLayout::GetRowLocation(int row_index) const {
if (row_index < 0 || row_index >= num_rows())
return 0.0;
return rows_[row_index]->Location();
}
float SimpleGridLayout::GetPreferredHeightForWidth(float width) {
if (rows_.empty())
return 0.0f;
SizeRowsAndColumns(width);
return rows_.back()->Location() + rows_.back()->Size();
}
void SimpleGridLayout::AddRow(Row* row) {
next_column_ = 0;
rows_.push_back(row);
}
|