Qwt User's Guide  6.2.0
qwt_dyngrid_layout.cpp
1 /******************************************************************************
2  * Qwt Widget Library
3  * Copyright (C) 1997 Josef Wilgen
4  * Copyright (C) 2002 Uwe Rathmann
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the Qwt License, Version 1.0
8  *****************************************************************************/
9 
10 #include "qwt_dyngrid_layout.h"
11 
12 #include <qvector.h>
13 #include <qlist.h>
14 
15 class QwtDynGridLayout::PrivateData
16 {
17  public:
18  PrivateData()
19  : isDirty( true )
20  {
21  }
22 
23  void updateLayoutCache();
24 
25  mutable QList< QLayoutItem* > itemList;
26 
27  uint maxColumns;
28  uint numRows;
29  uint numColumns;
30 
31  Qt::Orientations expanding;
32 
33  bool isDirty;
34  QVector< QSize > itemSizeHints;
35 };
36 
37 void QwtDynGridLayout::PrivateData::updateLayoutCache()
38 {
39  itemSizeHints.resize( itemList.count() );
40 
41  int index = 0;
42 
43  for ( QList< QLayoutItem* >::const_iterator it = itemList.constBegin();
44  it != itemList.constEnd(); ++it, index++ )
45  {
46  itemSizeHints[ index ] = ( *it )->sizeHint();
47  }
48 
49  isDirty = false;
50 }
51 
58 QwtDynGridLayout::QwtDynGridLayout( QWidget* parent, int margin, int spacing )
59  : QLayout( parent )
60 {
61  init();
62 
63  setSpacing( spacing );
64  setContentsMargins( margin, margin, margin, margin );
65 }
66 
72 {
73  init();
74  setSpacing( spacing );
75 }
76 
80 void QwtDynGridLayout::init()
81 {
82  m_data = new QwtDynGridLayout::PrivateData;
83  m_data->maxColumns = m_data->numRows = m_data->numColumns = 0;
84 }
85 
87 
89 {
90  qDeleteAll( m_data->itemList );
91  delete m_data;
92 }
93 
96 {
97  m_data->isDirty = true;
98  QLayout::invalidate();
99 }
100 
106 void QwtDynGridLayout::setMaxColumns( uint maxColumns )
107 {
108  m_data->maxColumns = maxColumns;
109 }
110 
120 {
121  return m_data->maxColumns;
122 }
123 
128 void QwtDynGridLayout::addItem( QLayoutItem* item )
129 {
130  m_data->itemList.append( item );
131  invalidate();
132 }
133 
138 {
139  return m_data->itemList.isEmpty();
140 }
141 
146 {
147  return m_data->itemList.count();
148 }
149 
157 QLayoutItem* QwtDynGridLayout::itemAt( int index ) const
158 {
159  if ( index < 0 || index >= m_data->itemList.count() )
160  return NULL;
161 
162  return m_data->itemList.at( index );
163 }
164 
172 QLayoutItem* QwtDynGridLayout::takeAt( int index )
173 {
174  if ( index < 0 || index >= m_data->itemList.count() )
175  return NULL;
176 
177  m_data->isDirty = true;
178  return m_data->itemList.takeAt( index );
179 }
180 
183 {
184  return m_data->itemList.count();
185 }
186 
196 void QwtDynGridLayout::setExpandingDirections( Qt::Orientations expanding )
197 {
198  m_data->expanding = expanding;
199 }
200 
212 {
213  return m_data->expanding;
214 }
215 
222 void QwtDynGridLayout::setGeometry( const QRect& rect )
223 {
224  QLayout::setGeometry( rect );
225 
226  if ( isEmpty() )
227  return;
228 
229  m_data->numColumns = columnsForWidth( rect.width() );
230  m_data->numRows = itemCount() / m_data->numColumns;
231  if ( itemCount() % m_data->numColumns )
232  m_data->numRows++;
233 
234  const QList< QRect > itemGeometries = layoutItems( rect, m_data->numColumns );
235 
236  int index = 0;
237  for ( QList< QLayoutItem* >::const_iterator it = m_data->itemList.constBegin();
238  it != m_data->itemList.constEnd(); ++it )
239  {
240  ( *it )->setGeometry( itemGeometries[index] );
241  index++;
242  }
243 }
244 
256 uint QwtDynGridLayout::columnsForWidth( int width ) const
257 {
258  if ( isEmpty() )
259  return 0;
260 
261  uint maxColumns = itemCount();
262  if ( m_data->maxColumns > 0 )
263  maxColumns = qMin( m_data->maxColumns, maxColumns );
264 
265  if ( maxRowWidth( maxColumns ) <= width )
266  return maxColumns;
267 
268  for ( uint numColumns = 2; numColumns <= maxColumns; numColumns++ )
269  {
270  const int rowWidth = maxRowWidth( numColumns );
271  if ( rowWidth > width )
272  return numColumns - 1;
273  }
274 
275  return 1; // At least 1 column
276 }
277 
285 int QwtDynGridLayout::maxRowWidth( int numColumns ) const
286 {
287  int col;
288 
289  QVector< int > colWidth( numColumns );
290  for ( col = 0; col < numColumns; col++ )
291  colWidth[col] = 0;
292 
293  if ( m_data->isDirty )
294  m_data->updateLayoutCache();
295 
296  for ( int index = 0;
297  index < m_data->itemSizeHints.count(); index++ )
298  {
299  col = index % numColumns;
300  colWidth[col] = qMax( colWidth[col],
301  m_data->itemSizeHints[index].width() );
302  }
303 
304  const QMargins m = contentsMargins();
305 
306  int rowWidth = m.left() + m.right() + ( numColumns - 1 ) * spacing();
307  for ( col = 0; col < numColumns; col++ )
308  rowWidth += colWidth[col];
309 
310  return rowWidth;
311 }
312 
317 {
318  if ( isEmpty() )
319  return 0;
320 
321  if ( m_data->isDirty )
322  m_data->updateLayoutCache();
323 
324  int w = 0;
325  for ( int i = 0; i < m_data->itemSizeHints.count(); i++ )
326  {
327  const int itemW = m_data->itemSizeHints[i].width();
328  if ( itemW > w )
329  w = itemW;
330  }
331 
332  return w;
333 }
334 
345  uint numColumns ) const
346 {
347  QList< QRect > itemGeometries;
348  if ( numColumns == 0 || isEmpty() )
349  return itemGeometries;
350 
351  uint numRows = itemCount() / numColumns;
352  if ( numColumns % itemCount() )
353  numRows++;
354 
355  if ( numRows == 0 )
356  return itemGeometries;
357 
358  QVector< int > rowHeight( numRows );
359  QVector< int > colWidth( numColumns );
360 
361  layoutGrid( numColumns, rowHeight, colWidth );
362 
363  bool expandH, expandV;
364  expandH = expandingDirections() & Qt::Horizontal;
365  expandV = expandingDirections() & Qt::Vertical;
366 
367  if ( expandH || expandV )
368  stretchGrid( rect, numColumns, rowHeight, colWidth );
369 
370  const int maxColumns = m_data->maxColumns;
371  m_data->maxColumns = numColumns;
372  const QRect alignedRect = alignmentRect( rect );
373  m_data->maxColumns = maxColumns;
374 
375  const int xOffset = expandH ? 0 : alignedRect.x();
376  const int yOffset = expandV ? 0 : alignedRect.y();
377 
378  QVector< int > colX( numColumns );
379  QVector< int > rowY( numRows );
380 
381  const int xySpace = spacing();
382 
383  const QMargins m = contentsMargins();
384 
385  rowY[0] = yOffset + m.top();
386  for ( uint r = 1; r < numRows; r++ )
387  rowY[r] = rowY[r - 1] + rowHeight[r - 1] + xySpace;
388 
389  colX[0] = xOffset + m.left();
390  for ( uint c = 1; c < numColumns; c++ )
391  colX[c] = colX[c - 1] + colWidth[c - 1] + xySpace;
392 
393  const int itemCount = m_data->itemList.size();
394  itemGeometries.reserve( itemCount );
395 
396  for ( int i = 0; i < itemCount; i++ )
397  {
398  const int row = i / numColumns;
399  const int col = i % numColumns;
400 
401  const QRect itemGeometry( colX[col], rowY[row],
402  colWidth[col], rowHeight[row] );
403  itemGeometries.append( itemGeometry );
404  }
405 
406  return itemGeometries;
407 }
408 
409 
419 void QwtDynGridLayout::layoutGrid( uint numColumns,
420  QVector< int >& rowHeight, QVector< int >& colWidth ) const
421 {
422  if ( numColumns <= 0 )
423  return;
424 
425  if ( m_data->isDirty )
426  m_data->updateLayoutCache();
427 
428  for ( int index = 0; index < m_data->itemSizeHints.count(); index++ )
429  {
430  const int row = index / numColumns;
431  const int col = index % numColumns;
432 
433  const QSize& size = m_data->itemSizeHints[index];
434 
435  rowHeight[row] = ( col == 0 )
436  ? size.height() : qMax( rowHeight[row], size.height() );
437  colWidth[col] = ( row == 0 )
438  ? size.width() : qMax( colWidth[col], size.width() );
439  }
440 }
441 
447 {
448  return true;
449 }
450 
455 int QwtDynGridLayout::heightForWidth( int width ) const
456 {
457  if ( isEmpty() )
458  return 0;
459 
460  const uint numColumns = columnsForWidth( width );
461  uint numRows = itemCount() / numColumns;
462  if ( itemCount() % numColumns )
463  numRows++;
464 
465  QVector< int > rowHeight( numRows );
466  QVector< int > colWidth( numColumns );
467 
468  layoutGrid( numColumns, rowHeight, colWidth );
469 
470  const QMargins m = contentsMargins();
471 
472  int h = m.top() + m.bottom() + ( numRows - 1 ) * spacing();
473  for ( uint row = 0; row < numRows; row++ )
474  h += rowHeight[row];
475 
476  return h;
477 }
478 
491 void QwtDynGridLayout::stretchGrid( const QRect& rect,
492  uint numColumns, QVector< int >& rowHeight, QVector< int >& colWidth ) const
493 {
494  if ( numColumns == 0 || isEmpty() )
495  return;
496 
497  bool expandH, expandV;
498  expandH = expandingDirections() & Qt::Horizontal;
499  expandV = expandingDirections() & Qt::Vertical;
500 
501  const QMargins m = contentsMargins();
502 
503  if ( expandH )
504  {
505  int xDelta = rect.width() - m.left() - m.right() - ( numColumns - 1 ) * spacing();
506  for ( uint col = 0; col < numColumns; col++ )
507  xDelta -= colWidth[col];
508 
509  if ( xDelta > 0 )
510  {
511  for ( uint col = 0; col < numColumns; col++ )
512  {
513  const int space = xDelta / ( numColumns - col );
514  colWidth[col] += space;
515  xDelta -= space;
516  }
517  }
518  }
519 
520  if ( expandV )
521  {
522  uint numRows = itemCount() / numColumns;
523  if ( itemCount() % numColumns )
524  numRows++;
525 
526  int yDelta = rect.height() - m.top() - m.bottom() - ( numRows - 1 ) * spacing();
527  for ( uint row = 0; row < numRows; row++ )
528  yDelta -= rowHeight[row];
529 
530  if ( yDelta > 0 )
531  {
532  for ( uint row = 0; row < numRows; row++ )
533  {
534  const int space = yDelta / ( numRows - row );
535  rowHeight[row] += space;
536  yDelta -= space;
537  }
538  }
539  }
540 }
541 
551 {
552  if ( isEmpty() )
553  return QSize();
554 
555  uint numColumns = itemCount();
556  if ( m_data->maxColumns > 0 )
557  numColumns = qMin( m_data->maxColumns, numColumns );
558 
559  uint numRows = itemCount() / numColumns;
560  if ( itemCount() % numColumns )
561  numRows++;
562 
563  QVector< int > rowHeight( numRows );
564  QVector< int > colWidth( numColumns );
565 
566  layoutGrid( numColumns, rowHeight, colWidth );
567 
568  const QMargins m = contentsMargins();
569 
570  int h = m.top() + m.bottom() + ( numRows - 1 ) * spacing();
571  for ( uint row = 0; row < numRows; row++ )
572  h += rowHeight[row];
573 
574  int w = m.left() + m.right() + ( numColumns - 1 ) * spacing();
575  for ( uint col = 0; col < numColumns; col++ )
576  w += colWidth[col];
577 
578  return QSize( w, h );
579 }
580 
587 {
588  return m_data->numRows;
589 }
590 
597 {
598  return m_data->numColumns;
599 }
600 
601 #if QWT_MOC_INCLUDE
602 #include "moc_qwt_dyngrid_layout.cpp"
603 #endif
void layoutGrid(uint numColumns, QVector< int > &rowHeight, QVector< int > &colWidth) const
virtual int heightForWidth(int) const override
virtual QLayoutItem * itemAt(int index) const override
virtual uint columnsForWidth(int width) const
Calculate the number of columns for a given width.
virtual bool isEmpty() const override
virtual QLayoutItem * takeAt(int index) override
virtual int maxItemWidth() const
void setExpandingDirections(Qt::Orientations)
virtual QSize sizeHint() const override
virtual Qt::Orientations expandingDirections() const override
Returns whether this layout can make use of more space than sizeHint().
uint maxColumns() const
Return the upper limit for the number of columns.
void stretchGrid(const QRect &rect, uint numColumns, QVector< int > &rowHeight, QVector< int > &colWidth) const
void setMaxColumns(uint maxColumns)
virtual void addItem(QLayoutItem *) override
Add an item to the next free position.
QList< QRect > layoutItems(const QRect &, uint numColumns) const
virtual void setGeometry(const QRect &) override
virtual ~QwtDynGridLayout()
Destructor.
QwtDynGridLayout(QWidget *, int margin=0, int spacing=-1)
virtual bool hasHeightForWidth() const override
virtual int count() const override
virtual void invalidate() override
Invalidate all internal caches.