KD Reports API Documentation  2.2
KDReportsTableLayout.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** This file is part of the KD Reports library.
4 **
5 ** SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6 **
7 ** SPDX-License-Identifier: MIT
8 **
9 ****************************************************************************/
10 
11 #include "KDReportsLayoutHelper_p.h" // mmToPixels
12 #include "KDReportsTableLayout_p.h"
13 #include <QAbstractItemModel>
14 
15 #include <QDebug>
16 #include <QFontMetrics>
17 
18 using namespace KDReports;
19 
21  : m_model(nullptr)
22  , m_cellFont()
23  , m_horizontalHeaderFont()
24  , m_verticalHeaderFont()
25  , m_horizontalHeaderVisible(true)
26  , m_verticalHeaderVisible(true)
27  , m_cellPadding(KDReports::mmToPixels(0.5))
28  , m_fixedRowHeight(0)
29  , m_iconSize(32, 32)
30  , m_rowHeight(0)
31  , m_vHeaderWidth(0)
32  , m_hHeaderHeight(0)
33  , m_cellFontScaler(m_cellFont)
34  , m_horizontalHeaderFontScaler(m_horizontalHeaderFont)
35  , m_verticalHeaderFontScaler(m_verticalHeaderFont)
36 {
37 }
38 
39 void TableLayout::setInitialFontScalingFactor(qreal fontScalingFactor)
40 {
41  m_cellFontScaler.setFontAndScalingFactor(m_cellFont, fontScalingFactor);
43  m_horizontalHeaderFontScaler.setFontAndScalingFactor(m_horizontalHeaderFont, fontScalingFactor);
44  }
46  m_verticalHeaderFontScaler.setFontAndScalingFactor(m_verticalHeaderFont, fontScalingFactor);
47  }
48  updateRowHeight();
49 #ifdef DEBUG_LAYOUT
50  qDebug() << "initial m_rowHeight=" << m_rowHeight;
51 #endif
52 }
53 
54 void TableLayout::updateRowHeight()
55 {
56  if (m_fixedRowHeight > 0) {
57  m_rowHeight = m_fixedRowHeight;
58  return;
59  }
60  m_rowHeight = m_cellFontScaler.fontMetrics().height() + 2.0 * scaledCellPadding();
62  m_hHeaderHeight = m_horizontalHeaderFontScaler.fontMetrics().height() + 2.0 * scaledCellPadding();
63  }
65  qreal vHeaderHeight = m_verticalHeaderFontScaler.fontMetrics().height() + 2.0 * scaledCellPadding();
66 #ifdef DEBUG_LAYOUT
67  qDebug() << "cells say rowHeight=" << m_rowHeight << "vHeader says height=" << vHeaderHeight;
68 #endif
69  m_rowHeight = qMax(m_rowHeight, vHeaderHeight);
70  }
71 }
72 
74 {
75  if (!m_model) {
76  return;
77  }
78 
79  const QFontMetricsF fm = m_cellFontScaler.fontMetrics();
80  m_hHeaderHeight = 0;
81  const int colCount = m_model->columnCount();
82  // qDebug() << "Starting layout of table" << colCount << "columns" << m_model->rowCount() << "rows";
83  m_columnWidths.resize(colCount);
84  m_widestTextPerColumn.resize(colCount);
85  const int rowCount = m_model->rowCount();
86  for (int col = 0; col < colCount; ++col) {
87  m_columnWidths[col] = 0;
89  const QString cellText = m_model->headerData(col, Qt::Horizontal).toString();
90  const qreal textWidth = m_horizontalHeaderFontScaler.textWidth(cellText);
91  const qreal width = addIconWidth(textWidth, m_model->headerData(col, Qt::Horizontal, Qt::DecorationRole));
92  m_columnWidths[col] = qMax(m_columnWidths[col], width);
93  m_widestTextPerColumn[col] = cellText;
94  m_hHeaderHeight = qMax(m_hHeaderHeight, m_horizontalHeaderFontScaler.fontMetrics().height());
95  }
96  for (int row = 0; row < rowCount; ++row) {
97  const QModelIndex index = m_model->index(row, col);
98  if (m_model->span(index).width() > 1) {
99  // Ignore spanned cells. Not ideal of course, but we'll have to assume
100  // the other cells determine width, and this one just has to fit in.
101  // I guess a two-pass algorithm is needed otherwise, checking every spanned cell
102  // after the initial column width distribution? Urgh.
103  continue;
104  }
105  qreal width;
106  const QString cellText = m_model->data(index, Qt::DisplayRole).toString();
107  const QSizeF cellSize = m_model->data(index, Qt::SizeHintRole).toSizeF();
108  if (cellSize.isValid()) {
109  width = mmToPixels(cellSize.width());
110  } else {
111  const qreal textWidth = fm.size(0 /*flags*/, cellText).width();
112  width = addIconWidth(textWidth, m_model->data(index, Qt::DecorationRole));
113  }
114  if (width > m_columnWidths[col]) {
115  m_columnWidths[col] = width;
116  m_widestTextPerColumn[col] = cellText;
117  }
118  }
119  // qDebug() << "Column" << col << "width" << m_columnWidths[col] << "+padding=" << m_columnWidths[col]+2*scaledCellPadding();
120  m_columnWidths[col] += 2 * scaledCellPadding();
121  }
122 
123  m_vHeaderWidth = 0;
125  for (int row = 0; row < rowCount; ++row) {
126  const QString cellText = m_model->headerData(row, Qt::Vertical).toString();
127  const qreal textWidth = m_verticalHeaderFontScaler.textWidth(cellText);
128  const qreal width = addIconWidth(textWidth, m_model->headerData(row, Qt::Vertical, Qt::DecorationRole));
129  m_vHeaderWidth = qMax(m_vHeaderWidth, width);
130  }
131  m_vHeaderWidth += 2 * scaledCellPadding();
132 #ifdef DEBUG_LAYOUT
133  // qDebug() << "m_vHeaderWidth=" << m_vHeaderWidth;
134 #endif
135  }
137  m_hHeaderHeight += 2 * scaledCellPadding();
138  }
139 }
140 
141 #if 0
142 void TableLayout::updateColumnWidthsByFactor( qreal factor )
143 {
144  const int colCount = m_model->columnCount();
145  for ( int col = 0; col < colCount; ++col )
146  {
147  m_columnWidths[col] *= factor;
148  }
149  m_vHeaderWidth *= factor;
150 #ifdef DEBUG_LAYOUT
151  qDebug() << "updateColumnWidthsByFactor: after factor" << factor << "m_vHeaderWidth=" << m_vHeaderWidth;
152 #endif
153 
154 #ifdef DEBUG_LAYOUT
155  qDebug() << "final scaling factors:" << m_cellFontScaler.scalingFactor()
156  << m_horizontalHeaderFontScaler.scalingFactor()
157  << m_verticalHeaderFontScaler.scalingFactor();
158 #endif
159 }
160 #endif
161 
163 {
164 #ifdef DEBUG_LAYOUT
165  qDebug() << "TableLayout::ensureScalingFactorForWidth" << factor;
166 #endif
167  // ## fonts are definitely not proportional. QFontMetricsF::width for "Hello world"
168  // jumps from 40 to 32 just by changing the font size from 6.6 to 6.3...
169  // So instead of saying "applying this scaling factor", we should iterate over all
170  // columns, and resize the font down until the column has the desired width, then choose
171  // the min font size over all columns? Well, this might add up rounding problems, but
172  // at least it will always fit...
173 
174  const int colCount = m_model->columnCount();
175  QString textForScaling;
176  for (int col = 0; col < colCount; ++col) {
177  // Which column should we use as 'reference' for the scaling calculation?
178  // The widest or the narrowest one? Chose narrowest, more rounding problems there.
179  if (col == 0 || m_widestTextPerColumn[col].length() < textForScaling.length())
180  textForScaling = m_widestTextPerColumn[col];
181  }
182 
183  m_cellFontScaler.setFactorForWidth(factor, textForScaling);
184  m_horizontalHeaderFontScaler.setFactorForWidth(factor, textForScaling);
185  m_verticalHeaderFontScaler.setFactorForWidth(factor, textForScaling);
186  updateRowHeight();
187 }
188 
190 {
191  const qreal wantedRowHeightFactor = maxRowHeight / m_rowHeight;
192  // Apply _final_ padding when determining the wanted text height, obviously.
193  // Testcase with numbers: initial: 6+2*2=10. Wanted row height 5, so wanted factor 0.5
194  // so wanted text height = 5 - 2*1 = 3.
195  const qreal wantedTextHeight = maxRowHeight - 2.0 * wantedRowHeightFactor * scaledCellPadding();
196 #ifdef DEBUG_LAYOUT
197  qDebug() << " ensureScalingFactorForHeight: wantedRowHeightFactor=" << wantedRowHeightFactor << "wantedTextHeight=" << wantedTextHeight << "after removing padding ( unscaled" << m_cellPadding
198  << "current scaling" << scaledCellPadding() << "wanted" << wantedRowHeightFactor << "*" << m_cellPadding << "*" << scalingFactor() << "=" << wantedRowHeightFactor * scaledCellPadding()
199  << ")";
200 #endif
201  qreal additionalFactor = 0;
202 
203  // Let's see if the height is constrained by the cell font or by the vHeader font,
204  // that's the one that has to determine the scaling factor
205  if (m_cellFontScaler.fontMetrics().height() >= m_verticalHeaderFontScaler.fontMetrics().height()) {
206 #ifdef DEBUG_LAYOUT
207  qDebug() << " Reducing the cell font to be wantedTextHeight=" << wantedTextHeight;
208 #endif
209  const qreal initialCellFactor = m_cellFontScaler.scalingFactor();
210  m_cellFontScaler.setFactorForHeight(wantedTextHeight);
211  additionalFactor = m_cellFontScaler.scalingFactor() / initialCellFactor;
212 #ifdef DEBUG_LAYOUT
213  // qDebug() << " applying factor to m_verticalHeaderFontScaler";
214 #endif
215  m_verticalHeaderFontScaler.applyAdditionalScalingFactor(additionalFactor);
216  } else {
217  // bigger font in the vertical header
218 #ifdef DEBUG_LAYOUT
219  qDebug() << "Reducing the vertical header font to be wantedTextHeight=" << wantedTextHeight;
220 #endif
221  const qreal initialVerticHeaderFactor = m_verticalHeaderFontScaler.scalingFactor();
222  m_verticalHeaderFontScaler.setFactorForHeight(wantedTextHeight);
223  additionalFactor = m_verticalHeaderFontScaler.scalingFactor() / initialVerticHeaderFactor;
224 #ifdef DEBUG_LAYOUT
225  // qDebug() << " applying factor to m_cellFontScaler";
226 #endif
227  m_cellFontScaler.applyAdditionalScalingFactor(additionalFactor);
228  }
229 
230 #ifdef DEBUG_LAYOUT
231  // qDebug() << " Now we have cellFontScaler:" << m_cellFontScaler.fontMetrics().height()
232  // << "verticalheaderfontscaler:" << m_verticalHeaderFontScaler.fontMetrics().height();
233 #endif
234 
235  m_horizontalHeaderFontScaler.applyAdditionalScalingFactor(additionalFactor);
236  updateRowHeight();
237  // With very small fonts, we can't get less than 3 pixels high for the text.
238  m_rowHeight = qMin(maxRowHeight, m_rowHeight);
239 
240 #ifdef DEBUG_LAYOUT
241  qDebug() << " ensureScalingFactorForHeight: applied additional factor" << additionalFactor << "row height is now" << m_rowHeight;
242 #endif
243 }
244 
245 QSize KDReports::TableLayout::decorationSize(const QVariant &cellDecoration) const
246 {
247  QImage img = qvariant_cast<QImage>(cellDecoration);
248  if (!img.isNull()) {
249  return img.size();
250  }
251  QPixmap pix = qvariant_cast<QPixmap>(cellDecoration);
252  if (!pix.isNull()) {
253  return pix.size();
254  }
255  return m_iconSize;
256 }
257 
258 qreal KDReports::TableLayout::addIconWidth(qreal textWidth, const QVariant &cellDecoration) const
259 {
260  qreal width = textWidth;
261  if (!cellDecoration.isNull())
262  width += decorationSize(cellDecoration).width() + 2 /*see textRect adjustments in SpreadsheetReportLayout::paintPageContent*/;
263  return width;
264 }
qreal textWidth(const QString &text) const
void applyAdditionalScalingFactor(qreal factor)
void setFactorForWidth(qreal wantedFactor, const QString &sampleText)
QFontMetricsF fontMetrics() const
void setFactorForHeight(qreal wantedHeight)
void setFontAndScalingFactor(const QFont &font, qreal scalingFactor)
QAbstractItemModel * m_model
QSize decorationSize(const QVariant &cellDecoration) const
void ensureScalingFactorForHeight(qreal maxRowHeight)
void setInitialFontScalingFactor(qreal fontScalingFactor)
QVector< QString > m_widestTextPerColumn
void ensureScalingFactorForWidth(qreal scalingFactor)
KDREPORTS_EXPORT qreal mmToPixels(qreal mm)

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-reports/
Generated by doxygen 1.9.1