1 /* 2 Copyright (C) 2010-2014 Kristian Duske 3 4 This file is part of TrenchBroom. 5 6 TrenchBroom is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 TrenchBroom is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #ifndef TrenchBroom_CellLayout_h 21 #define TrenchBroom_CellLayout_h 22 23 #include "VecMath.h" 24 #include "Macros.h" 25 26 #include <algorithm> 27 #include <cassert> 28 #include <limits> 29 #include <memory> 30 #include <vector> 31 32 namespace TrenchBroom { 33 namespace View { 34 class LayoutBounds { 35 private: 36 float m_x; 37 float m_y; 38 float m_width; 39 float m_height; 40 public: LayoutBounds()41 LayoutBounds() : 42 m_x(0.0f), 43 m_y(0.0f), 44 m_width(0.0f), 45 m_height(0.0f) {} 46 LayoutBounds(const float x,const float y,const float width,const float height)47 LayoutBounds(const float x, const float y, const float width, const float height) : 48 m_x(x), 49 m_y(y), 50 m_width(width), 51 m_height(height) {} 52 left()53 float left() const { 54 return m_x; 55 } 56 top()57 float top() const { 58 return m_y; 59 } 60 right()61 float right() const { 62 return m_x + m_width; 63 } 64 bottom()65 float bottom() const { 66 return m_y + m_height; 67 } 68 midX()69 float midX() const { 70 return m_x + m_width / 2.0f; 71 } 72 midY()73 float midY() const { 74 return m_y + m_height / 2.0f; 75 } 76 width()77 float width() const { 78 return m_width; 79 } 80 height()81 float height() const { 82 return m_height; 83 } 84 containsPoint(const float x,const float y)85 bool containsPoint(const float x, const float y) const { 86 return x >= left() && x <= right() && y >= top() && y <= bottom(); 87 } 88 intersectsY(const float y,const float height)89 bool intersectsY(const float y, const float height) const { 90 return bottom() >= y && top() <= y + height ; 91 } 92 }; 93 94 template <typename CellType> 95 class LayoutCell { 96 public: 97 private: 98 CellType m_item; 99 float m_x; 100 float m_y; 101 float m_itemWidth; 102 float m_itemHeight; 103 float m_titleWidth; 104 float m_titleHeight; 105 float m_titleMargin; 106 float m_scale; 107 LayoutBounds m_cellBounds; 108 LayoutBounds m_itemBounds; 109 LayoutBounds m_titleBounds; 110 doLayout(const float maxUpScale,const float minWidth,const float maxWidth,const float minHeight,const float maxHeight)111 void doLayout(const float maxUpScale, 112 const float minWidth, const float maxWidth, 113 const float minHeight, const float maxHeight) { 114 assert(0.0f < minWidth); 115 assert(0.0f < minHeight); 116 assert(minWidth <= maxWidth); 117 assert(minHeight <= maxHeight); 118 119 m_scale = std::min(std::min(maxWidth / m_itemWidth, maxHeight / m_itemHeight), maxUpScale); 120 const float scaledItemWidth = m_scale * m_itemWidth; 121 const float scaledItemHeight = m_scale * m_itemHeight; 122 const float clippedTitleWidth = std::min(m_titleWidth, maxWidth); 123 const float cellWidth = std::max(minWidth, std::max(scaledItemWidth, clippedTitleWidth)); 124 const float cellHeight = std::max(minHeight, std::max(minHeight, scaledItemHeight) + m_titleHeight + m_titleMargin); 125 const float itemY = m_y + std::max(0.0f, cellHeight - m_titleHeight - scaledItemHeight - m_titleMargin); 126 127 m_cellBounds = LayoutBounds(m_x, 128 m_y, 129 cellWidth, 130 cellHeight); 131 m_itemBounds = LayoutBounds(m_x + (m_cellBounds.width() - scaledItemWidth) / 2.0f, 132 itemY, 133 scaledItemWidth, 134 scaledItemHeight); 135 m_titleBounds = LayoutBounds(m_x + (m_cellBounds.width() - clippedTitleWidth) / 2.0f, 136 m_itemBounds.bottom() + m_titleMargin, 137 clippedTitleWidth, 138 m_titleHeight); 139 } 140 public: LayoutCell(const CellType item,const float x,const float y,const float itemWidth,const float itemHeight,const float titleWidth,const float titleHeight,const float titleMargin,const float maxUpScale,const float minWidth,const float maxWidth,const float minHeight,const float maxHeight)141 LayoutCell(const CellType item, 142 const float x, const float y, 143 const float itemWidth, const float itemHeight, 144 const float titleWidth, const float titleHeight, 145 const float titleMargin, 146 const float maxUpScale, 147 const float minWidth, const float maxWidth, 148 const float minHeight, const float maxHeight) : 149 m_item(item), 150 m_x(x), 151 m_y(y), 152 m_itemWidth(itemWidth), 153 m_itemHeight(itemHeight), 154 m_titleWidth(titleWidth), 155 m_titleHeight(titleHeight), 156 m_titleMargin(titleMargin) { 157 doLayout(maxUpScale, minWidth, maxWidth, minHeight, maxHeight); 158 } 159 hitTest(const float x,const float y)160 bool hitTest(const float x, const float y) const { 161 return m_cellBounds.containsPoint(x, y) || m_titleBounds.containsPoint(x, y); 162 } 163 scale()164 float scale() const { 165 return m_scale; 166 } 167 cellBounds()168 const LayoutBounds& cellBounds() const { 169 return m_cellBounds; 170 } 171 titleBounds()172 const LayoutBounds& titleBounds() const { 173 return m_titleBounds; 174 } 175 itemBounds()176 const LayoutBounds& itemBounds() const { 177 return m_itemBounds; 178 } 179 updateLayout(const float maxUpScale,const float minWidth,const float maxWidth,const float minHeight,const float maxHeight)180 void updateLayout(const float maxUpScale, 181 const float minWidth, const float maxWidth, 182 const float minHeight, const float maxHeight) { 183 doLayout(maxUpScale, minWidth, maxWidth, minHeight, maxHeight); 184 } 185 item()186 CellType item() const { 187 return m_item; 188 } 189 190 }; 191 192 template <typename CellType> 193 class LayoutRow { 194 public: 195 typedef LayoutCell<CellType> Cell; 196 typedef std::vector<Cell> CellList; 197 private: 198 float m_cellMargin; 199 float m_titleMargin; 200 float m_maxWidth; 201 size_t m_maxCells; 202 float m_maxUpScale; 203 float m_minCellWidth; 204 float m_maxCellWidth; 205 float m_minCellHeight; 206 float m_maxCellHeight; 207 LayoutBounds m_bounds; 208 209 CellList m_cells; 210 readjustItems()211 void readjustItems() { 212 for (size_t i = 0; i < m_cells.size(); ++i) 213 m_cells[i].updateLayout(m_maxUpScale, m_minCellWidth, m_maxCellWidth, m_minCellHeight, m_maxCellHeight); 214 } 215 public: LayoutRow(const float x,const float y,const float cellMargin,const float titleMargin,const float maxWidth,const size_t maxCells,const float maxUpScale,const float minCellWidth,const float maxCellWidth,const float minCellHeight,const float maxCellHeight)216 LayoutRow(const float x, const float y, 217 const float cellMargin, 218 const float titleMargin, 219 const float maxWidth, 220 const size_t maxCells, 221 const float maxUpScale, 222 const float minCellWidth, const float maxCellWidth, 223 const float minCellHeight, const float maxCellHeight) : 224 m_cellMargin(cellMargin), 225 m_titleMargin(titleMargin), 226 m_maxWidth(maxWidth), 227 m_maxCells(maxCells), 228 m_maxUpScale(maxUpScale), 229 m_minCellWidth(minCellWidth), 230 m_maxCellWidth(maxCellWidth), 231 m_minCellHeight(minCellHeight), 232 m_maxCellHeight(maxCellHeight), 233 m_bounds(x, y, 0.0f, 0.0f) {} 234 235 const Cell& operator[] (const size_t index) const { 236 assert(index >= 0 && index < m_cells.size()); 237 return m_cells[index]; 238 } 239 addItem(CellType item,const float itemWidth,const float itemHeight,const float titleWidth,const float titleHeight)240 bool addItem(CellType item, 241 const float itemWidth, const float itemHeight, 242 const float titleWidth, const float titleHeight) { 243 float x = m_bounds.right(); 244 float width = m_bounds.width(); 245 if (!m_cells.empty()) { 246 x += m_cellMargin; 247 width += m_cellMargin; 248 } 249 250 Cell cell(item, x, m_bounds.top(), itemWidth, itemHeight, titleWidth, titleHeight, m_titleMargin, m_maxUpScale, m_minCellWidth, m_maxCellWidth, m_minCellHeight, m_maxCellHeight); 251 width += cell.cellBounds().width(); 252 253 if (m_maxCells == 0 && width > m_maxWidth && !m_cells.empty()) 254 return false; 255 if (m_maxCells > 0 && m_cells.size() >= m_maxCells - 1) 256 return false; 257 258 const float newItemRowHeight = cell.cellBounds().height() - cell.titleBounds().height() - m_titleMargin; 259 bool readjust = newItemRowHeight > m_minCellHeight; 260 if (readjust) { 261 m_minCellHeight = newItemRowHeight; 262 assert(m_minCellHeight <= m_maxCellHeight); 263 readjustItems(); 264 } 265 266 m_bounds = LayoutBounds(m_bounds.left(), m_bounds.top(), width, std::max(m_bounds.height(), cell.cellBounds().height())); 267 268 m_cells.push_back(cell); 269 return true; 270 } 271 272 cells()273 const CellList& cells() const { 274 return m_cells; 275 } 276 cellAt(const float x,const float y,const Cell ** result)277 bool cellAt(const float x, const float y, const Cell** result) const { 278 for (size_t i = 0; i < m_cells.size(); ++i) { 279 const Cell& cell = m_cells[i]; 280 const LayoutBounds& cellBounds = cell.cellBounds(); 281 if (x > cellBounds.right()) 282 continue; 283 else if (x < cellBounds.left()) 284 break; 285 if (cell.hitTest(x, y)) { 286 *result = &cell; 287 return true; 288 } 289 } 290 return false; 291 } 292 bounds()293 const LayoutBounds& bounds() const { 294 return m_bounds; 295 } 296 intersectsY(const float y,const float height)297 bool intersectsY(const float y, const float height) const { 298 return m_bounds.intersectsY(y, height); 299 } 300 size()301 size_t size() const { 302 return m_cells.size(); 303 } 304 }; 305 306 template <typename CellType, typename GroupType> 307 class LayoutGroup { 308 public: 309 typedef LayoutRow<CellType> Row; 310 typedef std::vector<Row> RowList; 311 private: 312 GroupType m_item; 313 float m_cellMargin; 314 float m_titleMargin; 315 float m_rowMargin; 316 size_t m_maxCellsPerRow; 317 float m_maxUpScale; 318 float m_minCellWidth; 319 float m_maxCellWidth; 320 float m_minCellHeight; 321 float m_maxCellHeight; 322 LayoutBounds m_titleBounds; 323 LayoutBounds m_contentBounds; 324 325 RowList m_rows; 326 public: 327 const Row& operator[] (const size_t index) const { 328 assert(index >= 0 && index < m_rows.size()); 329 return m_rows[index]; 330 } 331 LayoutGroup(GroupType item,const float x,const float y,const float cellMargin,const float titleMargin,const float rowMargin,const float titleHeight,const float width,const size_t maxCellsPerRow,const float maxUpScale,const float minCellWidth,const float maxCellWidth,const float minCellHeight,const float maxCellHeight)332 LayoutGroup(GroupType item, 333 const float x, const float y, 334 const float cellMargin, const float titleMargin, const float rowMargin, 335 const float titleHeight, 336 const float width, 337 const size_t maxCellsPerRow, 338 const float maxUpScale, 339 const float minCellWidth, const float maxCellWidth, 340 const float minCellHeight, const float maxCellHeight) : 341 m_item(item), 342 m_cellMargin(cellMargin), 343 m_titleMargin(titleMargin), 344 m_rowMargin(rowMargin), 345 m_maxCellsPerRow(maxCellsPerRow), 346 m_maxUpScale(maxUpScale), 347 m_minCellWidth(minCellWidth), 348 m_maxCellWidth(maxCellWidth), 349 m_minCellHeight(minCellHeight), 350 m_maxCellHeight(maxCellHeight), 351 m_titleBounds(0.0f, y, width + 2.0f * x, titleHeight), 352 m_contentBounds(x, y + titleHeight + m_rowMargin, width, 0.0f), 353 m_rows() {} 354 LayoutGroup(const float x,const float y,const float cellMargin,const float titleMargin,const float rowMargin,const float width,const size_t maxCellsPerRow,const float maxUpScale,const float minCellWidth,const float maxCellWidth,const float minCellHeight,const float maxCellHeight)355 LayoutGroup(const float x, const float y, 356 const float cellMargin, const float titleMargin, const float rowMargin, 357 const float width, 358 const size_t maxCellsPerRow, 359 const float maxUpScale, 360 const float minCellWidth, const float maxCellWidth, 361 const float minCellHeight, const float maxCellHeight) : 362 m_cellMargin(cellMargin), 363 m_titleMargin(titleMargin), 364 m_rowMargin(rowMargin), 365 m_maxCellsPerRow(maxCellsPerRow), 366 m_maxUpScale(maxUpScale), 367 m_minCellWidth(minCellWidth), 368 m_maxCellWidth(maxCellWidth), 369 m_minCellHeight(minCellHeight), 370 m_maxCellHeight(maxCellHeight), 371 m_titleBounds(x, y, width, 0.0f), 372 m_contentBounds(x, y, width, 0.0f), 373 m_rows() {} 374 addItem(CellType item,const float itemWidth,const float itemHeight,const float titleWidth,const float titleHeight)375 void addItem(CellType item, 376 const float itemWidth, const float itemHeight, 377 const float titleWidth, const float titleHeight) { 378 if (m_rows.empty()) { 379 const float y = m_contentBounds.top(); 380 m_rows.push_back(Row(m_contentBounds.left(), y, m_cellMargin, m_titleMargin, m_contentBounds.width(), m_maxCellsPerRow, m_maxUpScale, m_minCellWidth, m_maxCellWidth, m_minCellHeight, m_maxCellHeight)); 381 } 382 383 const LayoutBounds oldBounds = m_rows.back().bounds(); 384 const float oldRowHeight = m_rows.back().bounds().height(); 385 if (!m_rows.back().addItem(item, itemWidth, itemHeight, titleWidth, titleHeight)) { 386 const float y = oldBounds.bottom() + m_rowMargin; 387 m_rows.push_back(Row(m_contentBounds.left(), y, m_cellMargin, m_titleMargin, m_contentBounds.width(), m_maxCellsPerRow, m_maxUpScale, m_minCellWidth, m_maxCellWidth, m_minCellHeight, m_maxCellHeight)); 388 389 const bool added = (m_rows.back().addItem(item, itemWidth, itemHeight, titleWidth, titleHeight)); 390 assert(added); 391 unused(added); 392 393 const float newRowHeight = m_rows.back().bounds().height(); 394 m_contentBounds = LayoutBounds(m_contentBounds.left(), m_contentBounds.top(), m_contentBounds.width(), m_contentBounds.height() + newRowHeight + m_rowMargin); 395 } else { 396 const float newRowHeight = m_rows.back().bounds().height(); 397 m_contentBounds = LayoutBounds(m_contentBounds.left(), m_contentBounds.top(), m_contentBounds.width(), m_contentBounds.height() + (newRowHeight - oldRowHeight)); 398 } 399 } 400 indexOfRowAt(const float y)401 size_t indexOfRowAt(const float y) const { 402 for (size_t i = 0; i < m_rows.size(); ++i) { 403 const Row& row = m_rows[i]; 404 const LayoutBounds& rowBounds = row.bounds(); 405 if (y < rowBounds.bottom()) 406 return i; 407 } 408 409 return m_rows.size(); 410 } 411 rowAt(const float y,const Row ** result)412 bool rowAt(const float y, const Row** result) const { 413 size_t index = indexOfRowAt(y); 414 if (index == m_rows.size()) 415 return false; 416 417 *result = &m_rows[index]; 418 return true; 419 } 420 cellAt(const float x,const float y,const typename Row::Cell ** result)421 bool cellAt(const float x, const float y, const typename Row::Cell** result) const { 422 for (size_t i = 0; i < m_rows.size(); ++i) { 423 const Row& row = m_rows[i]; 424 const LayoutBounds& rowBounds = row.bounds(); 425 if (y > rowBounds.bottom()) 426 continue; 427 else if (y < rowBounds.top()) 428 break; 429 if (row.cellAt(x, y, result)) 430 return true; 431 } 432 433 return false; 434 } 435 hitTest(const float x,const float y)436 bool hitTest(const float x, const float y) const { 437 return bounds().containsPoint(x, y); 438 } 439 titleBounds()440 const LayoutBounds& titleBounds() const { 441 return m_titleBounds; 442 } 443 titleBoundsForVisibleRect(const float y,const float height,const float groupMargin)444 const LayoutBounds titleBoundsForVisibleRect(const float y, const float height, const float groupMargin) const { 445 if (intersectsY(y, height) && m_titleBounds.top() < y) { 446 if (y > m_contentBounds.bottom() - m_titleBounds.height() + groupMargin) 447 return LayoutBounds(m_titleBounds.left(), m_contentBounds.bottom() - m_titleBounds.height() + groupMargin, m_titleBounds.width(), m_titleBounds.height()); 448 return LayoutBounds(m_titleBounds.left(), y, m_titleBounds.width(), m_titleBounds.height()); 449 } 450 return m_titleBounds; 451 } 452 contentBounds()453 const LayoutBounds& contentBounds() const { 454 return m_contentBounds; 455 } 456 bounds()457 const LayoutBounds bounds() const { 458 return LayoutBounds(m_titleBounds.left(), m_titleBounds.top(), m_titleBounds.width(), m_contentBounds.bottom() - m_titleBounds.top()); 459 } 460 intersectsY(const float y,const float height)461 bool intersectsY(const float y, const float height) const { 462 return bounds().intersectsY(y, height); 463 } 464 item()465 GroupType item() const { 466 return m_item; 467 } 468 size()469 size_t size() const { 470 return m_rows.size(); 471 } 472 }; 473 474 template <typename CellType, typename GroupType> 475 class CellLayout { 476 public: 477 typedef LayoutGroup<CellType, GroupType> Group; 478 typedef std::vector<Group> GroupList; 479 private: 480 float m_width; 481 float m_cellMargin; 482 float m_titleMargin; 483 float m_rowMargin; 484 float m_groupMargin; 485 float m_outerMargin; 486 size_t m_maxCellsPerRow; 487 float m_maxUpScale; 488 float m_minCellWidth; 489 float m_maxCellWidth; 490 float m_minCellHeight; 491 float m_maxCellHeight; 492 493 GroupList m_groups; 494 bool m_valid; 495 float m_height; 496 validate()497 void validate() { 498 if (m_width <= 0.0f) 499 return; 500 501 m_height = 2.0f * m_outerMargin; 502 m_valid = true; 503 if (!m_groups.empty()) { 504 GroupList copy = m_groups; 505 m_groups.clear(); 506 507 for (size_t i = 0; i < copy.size(); ++i) { 508 Group& group = copy[i]; 509 addGroup(group.item(), group.titleBounds().height()); 510 for (size_t j = 0; j < group.size(); ++j) { 511 const typename Group::Row& row = group[j]; 512 for (size_t k = 0; k < row.size(); k++) { 513 const typename Group::Row::Cell& cell = row[k]; 514 const LayoutBounds& itemBounds = cell.itemBounds(); 515 const LayoutBounds& titleBounds = cell.titleBounds(); 516 float scale = cell.scale(); 517 float itemWidth = itemBounds.width() / scale; 518 float itemHeight = itemBounds.height() / scale; 519 addItem(cell.item(), itemWidth, itemHeight, titleBounds.width(), titleBounds.height()); 520 } 521 } 522 } 523 } 524 } 525 public: 526 const Group& operator[] (const size_t index) { 527 assert(index >= 0 && index < m_groups.size()); 528 if (!m_valid) 529 validate(); 530 return m_groups[index]; 531 } 532 533 CellLayout(const size_t maxCellsPerRow = 0) : 534 m_width(1.0f), 535 m_cellMargin(0.0f), 536 m_titleMargin(0.0f), 537 m_rowMargin(0.0f), 538 m_groupMargin(0.0f), 539 m_outerMargin(0.0f), 540 m_maxCellsPerRow(maxCellsPerRow), 541 m_maxUpScale(1.0f), 542 m_minCellWidth(100.0f), 543 m_maxCellWidth(100.0f), 544 m_minCellHeight(100.0f), 545 m_maxCellHeight(100.0f), 546 m_groups(), 547 m_valid(false), 548 m_height(0.0f) { 549 invalidate(); 550 } 551 setCellMargin(const float cellMargin)552 void setCellMargin(const float cellMargin) { 553 if (m_cellMargin == cellMargin) 554 return; 555 m_cellMargin = cellMargin; 556 invalidate(); 557 } 558 setTitleMargin(const float titleMargin)559 void setTitleMargin(const float titleMargin) { 560 if (m_titleMargin == titleMargin) 561 return; 562 m_titleMargin = titleMargin; 563 invalidate(); 564 } 565 setRowMargin(const float rowMargin)566 void setRowMargin(const float rowMargin) { 567 if (m_rowMargin == rowMargin) 568 return; 569 m_rowMargin = rowMargin; 570 invalidate(); 571 } 572 setGroupMargin(const float groupMargin)573 void setGroupMargin(const float groupMargin) { 574 if (m_groupMargin == groupMargin) 575 return; 576 m_groupMargin = groupMargin; 577 invalidate(); 578 } 579 setOuterMargin(const float outerMargin)580 void setOuterMargin(const float outerMargin) { 581 if (m_outerMargin == outerMargin) 582 return; 583 m_outerMargin = outerMargin; 584 invalidate(); 585 } 586 addGroup(const GroupType groupItem,const float titleHeight)587 void addGroup(const GroupType groupItem, const float titleHeight) { 588 if (!m_valid) 589 validate(); 590 591 float y = 0.0f; 592 if (!m_groups.empty()) { 593 y = m_groups.back().bounds().bottom() + m_groupMargin; 594 m_height += m_groupMargin; 595 } 596 597 m_groups.push_back(Group(groupItem, m_outerMargin, y, m_cellMargin, m_titleMargin, m_rowMargin, titleHeight, m_width - 2.0f * m_outerMargin, m_maxCellsPerRow, m_maxUpScale, m_minCellWidth, m_maxCellWidth, m_minCellHeight, m_maxCellHeight)); 598 m_height += m_groups.back().bounds().height(); 599 } 600 addItem(const CellType item,const float itemWidth,const float itemHeight,const float titleWidth,const float titleHeight)601 void addItem(const CellType item, 602 const float itemWidth, const float itemHeight, 603 const float titleWidth, const float titleHeight) { 604 if (!m_valid) 605 validate(); 606 607 if (m_groups.empty()) { 608 m_groups.push_back(Group(m_outerMargin, m_outerMargin, m_cellMargin, m_titleMargin, m_rowMargin, m_width - 2.0f * m_outerMargin, m_maxCellsPerRow, m_maxUpScale, m_minCellWidth, m_maxCellWidth, m_minCellHeight, m_maxCellHeight)); 609 m_height += titleHeight; 610 if (titleHeight > 0.0f) 611 m_height += m_rowMargin; 612 } 613 614 const float oldGroupHeight = m_groups.back().bounds().height(); 615 m_groups.back().addItem(item, itemWidth, itemHeight, titleWidth, titleHeight); 616 const float newGroupHeight = m_groups.back().bounds().height(); 617 618 m_height += (newGroupHeight - oldGroupHeight); 619 } 620 clear()621 void clear() { 622 m_groups.clear(); 623 invalidate(); 624 } 625 cellAt(const float x,const float y,const typename Group::Row::Cell ** result)626 bool cellAt(const float x, const float y, const typename Group::Row::Cell** result) { 627 if (!m_valid) 628 validate(); 629 630 for (size_t i = 0; i < m_groups.size(); ++i) { 631 const Group& group = m_groups[i]; 632 const LayoutBounds groupBounds = group.bounds(); 633 if (y > groupBounds.bottom()) 634 continue; 635 else if (y < groupBounds.top()) 636 break; 637 if (group.cellAt(x, y, result)) 638 return true; 639 } 640 641 return false; 642 } 643 groupAt(const float x,const float y,Group * result)644 bool groupAt(const float x, const float y, Group* result) { 645 if (!m_valid) 646 validate(); 647 648 for (size_t i = 0; i < m_groups.size(); ++i) { 649 Group* group = m_groups[i]; 650 const LayoutBounds groupBounds = group->bounds(); 651 if (y > groupBounds.bottom()) 652 continue; 653 else if (y < groupBounds.top()) 654 break; 655 if (group->hitTest(x, y)) { 656 result = group; 657 return true; 658 } 659 } 660 661 return false; 662 } 663 titleBoundsForVisibleRect(const Group & group,const float y,const float height)664 const LayoutBounds titleBoundsForVisibleRect(const Group& group, const float y, const float height) const { 665 return group.titleBoundsForVisibleRect(y, height, m_groupMargin); 666 } 667 rowPosition(const float y,const int offset)668 float rowPosition(const float y, const int offset) { 669 if (!m_valid) 670 validate(); 671 672 size_t groupIndex = m_groups.size(); 673 for (size_t i = 0; i < m_groups.size(); ++i) { 674 Group* candidate = &m_groups[i]; 675 const LayoutBounds groupBounds = candidate->bounds(); 676 if (y + m_rowMargin > groupBounds.bottom()) 677 continue; 678 groupIndex = i; 679 break; 680 } 681 682 if (groupIndex == m_groups.size()) 683 return y; 684 685 size_t rowIndex = m_groups[groupIndex].indexOfRowAt(y); 686 if (rowIndex == m_groups[groupIndex].size()) 687 return y; 688 689 if (offset == 0) 690 return y; 691 692 int newIndex = static_cast<int>(rowIndex) + offset; 693 if (newIndex < 0) { 694 while (newIndex < 0 && groupIndex > 0) 695 newIndex += m_groups[--groupIndex].size(); 696 } else if (newIndex >= static_cast<int>(m_groups[groupIndex].size())) { 697 while (newIndex >= static_cast<int>(m_groups[groupIndex].size()) && groupIndex < m_groups.size() - 1) 698 newIndex -= m_groups[groupIndex++].size(); 699 } 700 701 if (groupIndex < m_groups.size()) { 702 if (newIndex >= 0) { 703 rowIndex = static_cast<size_t>(newIndex); 704 if (rowIndex < m_groups[groupIndex].size()) { 705 return m_groups[groupIndex][rowIndex].bounds().top(); 706 } 707 } 708 } 709 710 711 return y; 712 } 713 size()714 size_t size() { 715 if (!m_valid) 716 validate(); 717 return m_groups.size(); 718 } 719 invalidate()720 void invalidate() { 721 m_valid = false; 722 } 723 setWidth(const float width)724 void setWidth(const float width) { 725 if (m_width == width) 726 return; 727 m_width = width; 728 invalidate(); 729 } 730 minCellWidth()731 float minCellWidth() const { 732 return m_minCellWidth; 733 } 734 maxCellWidth()735 float maxCellWidth() const { 736 return m_maxCellWidth; 737 } 738 setCellWidth(const float minCellWidth,const float maxCellWidth)739 void setCellWidth(const float minCellWidth, const float maxCellWidth) { 740 assert(0.0f < minCellWidth); 741 assert(minCellWidth <= maxCellWidth); 742 743 if (m_minCellWidth == minCellWidth && m_maxCellWidth == maxCellWidth) 744 return; 745 m_minCellWidth = minCellWidth; 746 m_maxCellWidth = maxCellWidth; 747 invalidate(); 748 } 749 minCellHeight()750 float minCellHeight() const { 751 return m_minCellHeight; 752 } 753 maxCellHeight()754 float maxCellHeight() const { 755 return m_maxCellHeight; 756 } 757 setCellHeight(const float minCellHeight,const float maxCellHeight)758 void setCellHeight(const float minCellHeight, const float maxCellHeight) { 759 assert(0.0f < minCellHeight); 760 assert(minCellHeight <= maxCellHeight); 761 762 if (m_minCellHeight == minCellHeight && m_maxCellHeight == maxCellHeight) 763 return; 764 m_minCellHeight = minCellHeight; 765 m_maxCellHeight = maxCellHeight; 766 invalidate(); 767 } 768 setMaxUpScale(const float maxUpScale)769 void setMaxUpScale(const float maxUpScale) { 770 if (m_maxUpScale == maxUpScale) 771 return; 772 m_maxUpScale = maxUpScale; 773 invalidate(); 774 } 775 width()776 float width() const { 777 return m_width; 778 } 779 height()780 float height() { 781 if (!m_valid) 782 validate(); 783 return m_height; 784 } 785 outerMargin()786 float outerMargin() const { 787 return m_outerMargin; 788 } 789 groupMargin()790 float groupMargin() const { 791 return m_groupMargin; 792 } 793 rowMargin()794 float rowMargin() const { 795 return m_rowMargin; 796 } 797 cellMargin()798 float cellMargin() const { 799 return m_cellMargin; 800 } 801 }; 802 } 803 } 804 805 #endif 806