1 /**************************************************************************** 2 ** 3 ** Copyright (C) 2016 The Qt Company Ltd. 4 ** Contact: https://www.qt.io/licensing/ 5 ** 6 ** This file is part of the QtGui module of the Qt Toolkit. 7 ** 8 ** $QT_BEGIN_LICENSE:LGPL$ 9 ** Commercial License Usage 10 ** Licensees holding valid commercial Qt licenses may use this file in 11 ** accordance with the commercial license agreement provided with the 12 ** Software or, alternatively, in accordance with the terms contained in 13 ** a written agreement between you and The Qt Company. For licensing terms 14 ** and conditions see https://www.qt.io/terms-conditions. For further 15 ** information use the contact form at https://www.qt.io/contact-us. 16 ** 17 ** GNU Lesser General Public License Usage 18 ** Alternatively, this file may be used under the terms of the GNU Lesser 19 ** General Public License version 3 as published by the Free Software 20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the 21 ** packaging of this file. Please review the following information to 22 ** ensure the GNU Lesser General Public License version 3 requirements 23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24 ** 25 ** GNU General Public License Usage 26 ** Alternatively, this file may be used under the terms of the GNU 27 ** General Public License version 2.0 or (at your option) the GNU General 28 ** Public license version 3 or any later version approved by the KDE Free 29 ** Qt Foundation. The licenses are as published by the Free Software 30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31 ** included in the packaging of this file. Please review the following 32 ** information to ensure the GNU General Public License requirements will 33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34 ** https://www.gnu.org/licenses/gpl-3.0.html. 35 ** 36 ** $QT_END_LICENSE$ 37 ** 38 ****************************************************************************/ 39 40 #ifndef QGRIDLAYOUTENGINE_P_H 41 #define QGRIDLAYOUTENGINE_P_H 42 43 // 44 // W A R N I N G 45 // ------------- 46 // 47 // This file is not part of the Qt API. It exists for the convenience 48 // of the graphics view layout classes. This header 49 // file may change from version to version without notice, or even be removed. 50 // 51 // We mean it. 52 // 53 54 #include <QtGui/private/qtguiglobal_p.h> 55 #include "qalgorithms.h" 56 #include "qbitarray.h" 57 #include "qlist.h" 58 #include "qmap.h" 59 #include "qpair.h" 60 #include <QtCore/qvector.h> 61 #include <QtCore/qsize.h> 62 #include <QtCore/qrect.h> 63 #include <float.h> 64 #include "qlayoutpolicy_p.h" 65 #include "qabstractlayoutstyleinfo_p.h" 66 67 // #define QGRIDLAYOUTENGINE_DEBUG 68 69 QT_BEGIN_NAMESPACE 70 71 class QStyle; 72 class QWidget; 73 74 // ### deal with Descent in a similar way 75 enum { 76 MinimumSize = Qt::MinimumSize, 77 PreferredSize = Qt::PreferredSize, 78 MaximumSize = Qt::MaximumSize, 79 NSizes 80 }; 81 82 // do not reorder 83 enum { 84 Hor, 85 Ver, 86 NOrientations 87 }; 88 89 // do not reorder 90 enum LayoutSide { 91 Left, 92 Top, 93 Right, 94 Bottom 95 }; 96 97 enum { 98 NoConstraint, 99 HorizontalConstraint, // Width depends on the height 100 VerticalConstraint, // Height depends on the width 101 UnknownConstraint, // need to update cache 102 UnfeasibleConstraint // not feasible, it be has some items with Vertical and others with Horizontal constraints 103 }; 104 105 template <typename T> 106 class QLayoutParameter 107 { 108 public: 109 enum State { Default, User, Cached }; 110 QLayoutParameter()111 inline QLayoutParameter() : q_value(T()), q_state(Default) {} q_value(value)112 inline QLayoutParameter(T value, State state = Default) : q_value(value), q_state(state) {} 113 setUserValue(T value)114 inline void setUserValue(T value) { 115 q_value = value; 116 q_state = User; 117 } setCachedValue(T value)118 inline void setCachedValue(T value) const { 119 if (q_state != User) { 120 q_value = value; 121 q_state = Cached; 122 } 123 } value()124 inline T value() const { return q_value; } value(T defaultValue)125 inline T value(T defaultValue) const { return isUser() ? q_value : defaultValue; } isDefault()126 inline bool isDefault() const { return q_state == Default; } isUser()127 inline bool isUser() const { return q_state == User; } isCached()128 inline bool isCached() const { return q_state == Cached; } 129 130 private: 131 mutable T q_value; 132 mutable State q_state; 133 }; 134 135 class QStretchParameter : public QLayoutParameter<int> 136 { 137 public: QStretchParameter()138 QStretchParameter() : QLayoutParameter<int>(-1) {} 139 140 }; 141 142 class Q_GUI_EXPORT QGridLayoutBox 143 { 144 public: QGridLayoutBox()145 inline QGridLayoutBox() 146 : q_minimumSize(0), q_preferredSize(0), q_maximumSize(FLT_MAX), 147 q_minimumDescent(-1), q_minimumAscent(-1) {} 148 149 void add(const QGridLayoutBox &other, int stretch, qreal spacing); 150 void combine(const QGridLayoutBox &other); 151 void normalize(); 152 153 #ifdef QGRIDLAYOUTENGINE_DEBUG 154 void dump(int indent = 0) const; 155 #endif 156 // This code could use the union-struct-array trick, but a compiler 157 // bug prevents this from working. 158 qreal q_minimumSize; 159 qreal q_preferredSize; 160 qreal q_maximumSize; 161 qreal q_minimumDescent; 162 qreal q_minimumAscent; q_sizes(int which)163 inline qreal &q_sizes(int which) 164 { 165 qreal *t; 166 switch (which) { 167 case Qt::MinimumSize: 168 t = &q_minimumSize; 169 break; 170 case Qt::PreferredSize: 171 t = &q_preferredSize; 172 break; 173 case Qt::MaximumSize: 174 t = &q_maximumSize; 175 break; 176 case Qt::MinimumDescent: 177 t = &q_minimumDescent; 178 break; 179 case (Qt::MinimumDescent + 1): 180 t = &q_minimumAscent; 181 break; 182 default: 183 t = nullptr; 184 break; 185 } 186 return *t; 187 } q_sizes(int which)188 inline const qreal &q_sizes(int which) const 189 { 190 const qreal *t; 191 switch (which) { 192 case Qt::MinimumSize: 193 t = &q_minimumSize; 194 break; 195 case Qt::PreferredSize: 196 t = &q_preferredSize; 197 break; 198 case Qt::MaximumSize: 199 t = &q_maximumSize; 200 break; 201 case Qt::MinimumDescent: 202 t = &q_minimumDescent; 203 break; 204 case (Qt::MinimumDescent + 1): 205 t = &q_minimumAscent; 206 break; 207 default: 208 t = nullptr; 209 break; 210 } 211 return *t; 212 } 213 }; 214 Q_DECLARE_TYPEINFO(QGridLayoutBox, Q_MOVABLE_TYPE); // cannot be Q_PRIMITIVE_TYPE, as q_maximumSize, say, is != 0 215 216 bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2); 217 inline bool operator!=(const QGridLayoutBox &box1, const QGridLayoutBox &box2) 218 { return !operator==(box1, box2); } 219 220 class QGridLayoutMultiCellData 221 { 222 public: QGridLayoutMultiCellData()223 inline QGridLayoutMultiCellData() : q_stretch(-1) {} 224 225 QGridLayoutBox q_box; 226 int q_stretch; 227 }; 228 229 typedef QMap<QPair<int, int>, QGridLayoutMultiCellData> MultiCellMap; 230 231 class QGridLayoutRowInfo; 232 233 class QGridLayoutRowData 234 { 235 public: 236 void reset(int count); 237 void distributeMultiCells(const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid); 238 void calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes, 239 qreal *descents, const QGridLayoutBox &totalBox, 240 const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid); 241 QGridLayoutBox totalBox(int start, int end) const; 242 void stealBox(int start, int end, int which, qreal *positions, qreal *sizes); 243 244 #ifdef QGRIDLAYOUTENGINE_DEBUG 245 void dump(int indent = 0) const; 246 #endif 247 248 QBitArray ignore; // ### rename q_ 249 QVector<QGridLayoutBox> boxes; 250 MultiCellMap multiCellMap; 251 QVector<int> stretches; 252 QVector<qreal> spacings; 253 bool hasIgnoreFlag; 254 }; 255 256 class QGridLayoutRowInfo 257 { 258 public: QGridLayoutRowInfo()259 inline QGridLayoutRowInfo() : count(0) {} 260 261 void insertOrRemoveRows(int row, int delta); 262 263 #ifdef QGRIDLAYOUTENGINE_DEBUG 264 void dump(int indent = 0) const; 265 #endif 266 267 int count; 268 QVector<QStretchParameter> stretches; 269 QVector<QLayoutParameter<qreal> > spacings; 270 QVector<Qt::Alignment> alignments; 271 QVector<QGridLayoutBox> boxes; 272 }; 273 274 275 class Q_GUI_EXPORT QGridLayoutItem 276 { 277 public: 278 QGridLayoutItem(int row, int column, int rowSpan = 1, int columnSpan = 1, 279 Qt::Alignment alignment = { }); ~QGridLayoutItem()280 virtual ~QGridLayoutItem() {} 281 firstRow()282 inline int firstRow() const { return q_firstRows[Ver]; } firstColumn()283 inline int firstColumn() const { return q_firstRows[Hor]; } rowSpan()284 inline int rowSpan() const { return q_rowSpans[Ver]; } columnSpan()285 inline int columnSpan() const { return q_rowSpans[Hor]; } lastRow()286 inline int lastRow() const { return firstRow() + rowSpan() - 1; } lastColumn()287 inline int lastColumn() const { return firstColumn() + columnSpan() - 1; } 288 289 int firstRow(Qt::Orientation orientation) const; 290 int firstColumn(Qt::Orientation orientation) const; 291 int lastRow(Qt::Orientation orientation) const; 292 int lastColumn(Qt::Orientation orientation) const; 293 int rowSpan(Qt::Orientation orientation) const; 294 int columnSpan(Qt::Orientation orientation) const; 295 void setFirstRow(int row, Qt::Orientation orientation = Qt::Vertical); 296 void setRowSpan(int rowSpan, Qt::Orientation orientation = Qt::Vertical); 297 298 int stretchFactor(Qt::Orientation orientation) const; 299 void setStretchFactor(int stretch, Qt::Orientation orientation); 300 alignment()301 inline Qt::Alignment alignment() const { return q_alignment; } setAlignment(Qt::Alignment alignment)302 inline void setAlignment(Qt::Alignment alignment) { q_alignment = alignment; } 303 304 virtual QLayoutPolicy::Policy sizePolicy(Qt::Orientation orientation) const = 0; 305 virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const = 0; isIgnored()306 virtual bool isIgnored() const { return false; } 307 308 virtual void setGeometry(const QRectF &rect) = 0; 309 /* 310 returns true if the size policy returns true for either hasHeightForWidth() 311 or hasWidthForHeight() 312 */ hasDynamicConstraint()313 virtual bool hasDynamicConstraint() const { return false; } dynamicConstraintOrientation()314 virtual Qt::Orientation dynamicConstraintOrientation() const { return Qt::Horizontal; } 315 316 317 virtual QLayoutPolicy::ControlTypes controlTypes(LayoutSide side) const; 318 319 QRectF geometryWithin(qreal x, qreal y, qreal width, qreal height, qreal rowDescent, Qt::Alignment align, bool snapToPixelGrid) const; 320 QGridLayoutBox box(Qt::Orientation orientation, bool snapToPixelGrid, qreal constraint = -1.0) const; 321 322 323 void transpose(); 324 void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical); 325 QSizeF effectiveMaxSize(const QSizeF &constraint) const; 326 327 #ifdef QGRIDLAYOUTENGINE_DEBUG 328 void dump(int indent = 0) const; 329 #endif 330 331 private: 332 int q_firstRows[NOrientations]; 333 int q_rowSpans[NOrientations]; 334 int q_stretches[NOrientations]; 335 Qt::Alignment q_alignment; 336 337 }; 338 339 class Q_GUI_EXPORT QGridLayoutEngine 340 { 341 public: 342 QGridLayoutEngine(Qt::Alignment defaultAlignment = { }, bool snapToPixelGrid = false); ~QGridLayoutEngine()343 inline ~QGridLayoutEngine() { qDeleteAll(q_items); } 344 345 int rowCount(Qt::Orientation orientation) const; 346 int columnCount(Qt::Orientation orientation) const; rowCount()347 inline int rowCount() const { return q_infos[Ver].count; } columnCount()348 inline int columnCount() const { return q_infos[Hor].count; } 349 // returns the number of items inserted, which may be less than (rowCount * columnCount) 350 int itemCount() const; 351 QGridLayoutItem *itemAt(int index) const; 352 353 int effectiveFirstRow(Qt::Orientation orientation = Qt::Vertical) const; 354 int effectiveLastRow(Qt::Orientation orientation = Qt::Vertical) const; 355 356 void setSpacing(qreal spacing, Qt::Orientations orientations); 357 qreal spacing(Qt::Orientation orientation, const QAbstractLayoutStyleInfo *styleInfo) const; 358 // ### setSpacingAfterRow(), spacingAfterRow() 359 void setRowSpacing(int row, qreal spacing, Qt::Orientation orientation = Qt::Vertical); 360 qreal rowSpacing(int row, Qt::Orientation orientation = Qt::Vertical) const; 361 362 void setRowStretchFactor(int row, int stretch, Qt::Orientation orientation = Qt::Vertical); 363 int rowStretchFactor(int row, Qt::Orientation orientation = Qt::Vertical) const; 364 365 void setRowSizeHint(Qt::SizeHint which, int row, qreal size, 366 Qt::Orientation orientation = Qt::Vertical); 367 qreal rowSizeHint(Qt::SizeHint which, int row, 368 Qt::Orientation orientation = Qt::Vertical) const; 369 370 void setRowAlignment(int row, Qt::Alignment alignment, Qt::Orientation orientation); 371 Qt::Alignment rowAlignment(int row, Qt::Orientation orientation) const; 372 373 Qt::Alignment effectiveAlignment(const QGridLayoutItem *layoutItem) const; 374 375 376 void insertItem(QGridLayoutItem *item, int index); 377 void addItem(QGridLayoutItem *item); 378 void removeItem(QGridLayoutItem *item); deleteItems()379 void deleteItems() 380 { 381 const QList<QGridLayoutItem *> oldItems = q_items; 382 q_items.clear(); // q_items are used as input when the grid is regenerated in removeRows 383 // The following calls to removeRows are suboptimal 384 int rows = rowCount(Qt::Vertical); 385 removeRows(0, rows, Qt::Vertical); 386 rows = rowCount(Qt::Horizontal); 387 removeRows(0, rows, Qt::Horizontal); 388 qDeleteAll(oldItems); 389 } 390 391 QGridLayoutItem *itemAt(int row, int column, Qt::Orientation orientation = Qt::Vertical) const; 392 inline void insertRow(int row, Qt::Orientation orientation = Qt::Vertical) 393 { insertOrRemoveRows(row, +1, orientation); } removeRows(int row,int count,Qt::Orientation orientation)394 inline void removeRows(int row, int count, Qt::Orientation orientation) 395 { insertOrRemoveRows(row, -count, orientation); } 396 397 void invalidate(); 398 void setGeometries(const QRectF &contentsGeometry, const QAbstractLayoutStyleInfo *styleInfo); 399 QRectF cellRect(const QRectF &contentsGeometry, int row, int column, int rowSpan, int columnSpan, 400 const QAbstractLayoutStyleInfo *styleInfo) const; 401 QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint, 402 const QAbstractLayoutStyleInfo *styleInfo) const; 403 404 // heightForWidth / widthForHeight support 405 QSizeF dynamicallyConstrainedSizeHint(Qt::SizeHint which, const QSizeF &constraint) const; 406 bool ensureDynamicConstraint() const; 407 bool hasDynamicConstraint() const; 408 Qt::Orientation constraintOrientation() const; 409 410 411 QLayoutPolicy::ControlTypes controlTypes(LayoutSide side) const; 412 void transpose(); 413 void setVisualDirection(Qt::LayoutDirection direction); 414 Qt::LayoutDirection visualDirection() const; 415 #ifdef QGRIDLAYOUTENGINE_DEBUG 416 void dump(int indent = 0) const; 417 #endif 418 419 private: grossRoundUp(int n)420 static int grossRoundUp(int n) { return ((n + 2) | 0x3) - 2; } 421 422 void maybeExpandGrid(int row, int column, Qt::Orientation orientation = Qt::Vertical); 423 void regenerateGrid(); internalGridRowCount()424 inline int internalGridRowCount() const { return grossRoundUp(rowCount()); } internalGridColumnCount()425 inline int internalGridColumnCount() const { return grossRoundUp(columnCount()); } 426 void setItemAt(int row, int column, QGridLayoutItem *item); 427 void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical); 428 void fillRowData(QGridLayoutRowData *rowData, 429 const qreal *colPositions, const qreal *colSizes, 430 Qt::Orientation orientation, 431 const QAbstractLayoutStyleInfo *styleInfo) const; 432 void ensureEffectiveFirstAndLastRows() const; 433 void ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox, 434 const qreal *colPositions, const qreal *colSizes, 435 Qt::Orientation orientation, 436 const QAbstractLayoutStyleInfo *styleInfo) const; 437 438 void ensureGeometries(const QSizeF &size, const QAbstractLayoutStyleInfo *styleInfo) const; 439 protected: 440 QList<QGridLayoutItem *> q_items; 441 private: 442 // User input 443 QVector<QGridLayoutItem *> q_grid; 444 QLayoutParameter<qreal> q_defaultSpacings[NOrientations]; 445 QGridLayoutRowInfo q_infos[NOrientations]; 446 Qt::LayoutDirection m_visualDirection; 447 448 // Configuration 449 Qt::Alignment m_defaultAlignment; 450 unsigned m_snapToPixelGrid : 1; 451 452 // Lazily computed from the above user input 453 mutable int q_cachedEffectiveFirstRows[NOrientations]; 454 mutable int q_cachedEffectiveLastRows[NOrientations]; 455 mutable quint8 q_cachedConstraintOrientation : 3; 456 457 // this is useful to cache 458 mutable QGridLayoutBox q_totalBoxes[NOrientations]; 459 enum { 460 NotCached = -2, // Cache is empty. Happens when the engine is invalidated. 461 CachedWithNoConstraint = -1 // cache has a totalBox without any HFW/WFH constraints. 462 // >= 0 // cache has a totalBox with this specific constraint. 463 }; 464 mutable qreal q_totalBoxCachedConstraints[NOrientations]; // holds the constraint used for the cached totalBox 465 466 // Layout item input 467 mutable QGridLayoutRowData q_columnData; 468 mutable QGridLayoutRowData q_rowData; 469 470 // Output 471 mutable QSizeF q_cachedSize; 472 mutable QVector<qreal> q_xx; 473 mutable QVector<qreal> q_yy; 474 mutable QVector<qreal> q_widths; 475 mutable QVector<qreal> q_heights; 476 mutable QVector<qreal> q_descents; 477 478 friend class QGridLayoutItem; 479 }; 480 481 QT_END_NAMESPACE 482 483 #endif 484