File indexing completed on 2024-05-26 05:56:35
0001 /* 0002 This file is part of the Okteta Kasten module, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2003, 2008 Friedrich W. H. Kossebau <kossebau@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 */ 0008 0009 #include "abstractcolumnframerenderer.hpp" 0010 0011 // Okteta gui 0012 #include <Okteta/LineRange> 0013 #include <Okteta/AbstractColumnRenderer> 0014 // Qt 0015 #include <QPainter> 0016 #include <QVector> 0017 // Std 0018 #include <utility> 0019 0020 class AbstractColumnFrameRendererPrivate 0021 { 0022 public: 0023 AbstractColumnFrameRendererPrivate(); 0024 ~AbstractColumnFrameRendererPrivate(); 0025 0026 public: 0027 void updateWidths(); 0028 0029 public: // calculated 0030 /** collection of all the columns. All columns will be autodeleted. */ 0031 QVector<Okteta::AbstractColumnRenderer*> mColumns; 0032 /** the number of lines which the columnRenderer view has */ 0033 Okteta::LineSize mNoOfLines = 0; 0034 /** the height of each line in pixels */ 0035 Okteta::PixelY mLineHeight = 1; 0036 /** the width of all visible columns together */ 0037 Okteta::PixelX mColumnsWidth = 0; 0038 }; 0039 0040 AbstractColumnFrameRendererPrivate::AbstractColumnFrameRendererPrivate() = default; 0041 0042 AbstractColumnFrameRendererPrivate::~AbstractColumnFrameRendererPrivate() 0043 { 0044 qDeleteAll(mColumns); 0045 } 0046 0047 void AbstractColumnFrameRendererPrivate::updateWidths() 0048 { 0049 mColumnsWidth = 0; 0050 for (auto* columnRenderer : std::as_const(mColumns)) { 0051 columnRenderer->setX(mColumnsWidth); 0052 mColumnsWidth += columnRenderer->visibleWidth(); 0053 } 0054 } 0055 0056 AbstractColumnFrameRenderer::AbstractColumnFrameRenderer() 0057 : d(new AbstractColumnFrameRendererPrivate()) 0058 { 0059 } 0060 0061 AbstractColumnFrameRenderer::~AbstractColumnFrameRenderer() = default; 0062 0063 Okteta::LineSize AbstractColumnFrameRenderer::noOfLines() const { return d->mNoOfLines; } 0064 Okteta::PixelY AbstractColumnFrameRenderer::lineHeight() const { return d->mLineHeight; } 0065 0066 Okteta::PixelY AbstractColumnFrameRenderer::columnsHeight() const { return d->mNoOfLines * d->mLineHeight; } 0067 Okteta::PixelX AbstractColumnFrameRenderer::columnsWidth() const { return d->mColumnsWidth; } 0068 0069 void AbstractColumnFrameRenderer::setNoOfLines(Okteta::LineSize noOfLines) 0070 { 0071 if (d->mNoOfLines == noOfLines) { 0072 return; 0073 } 0074 0075 d->mNoOfLines = noOfLines; 0076 } 0077 0078 void AbstractColumnFrameRenderer::setLineHeight(Okteta::PixelY newLineHeight) 0079 { 0080 if (newLineHeight == d->mLineHeight) { 0081 return; 0082 } 0083 0084 if (newLineHeight < 1) { 0085 newLineHeight = 1; 0086 } 0087 d->mLineHeight = newLineHeight; 0088 0089 for (auto* columnRenderer : std::as_const(d->mColumns)) { 0090 columnRenderer->setLineHeight(d->mLineHeight); 0091 } 0092 } 0093 0094 void AbstractColumnFrameRenderer::updateWidths() 0095 { 0096 d->updateWidths(); 0097 } 0098 0099 Okteta::LineSize AbstractColumnFrameRenderer::noOfLinesPerFrame() const 0100 { 0101 // TODO: the right reaction? 0102 if (d->mLineHeight < 1) { 0103 return 1; 0104 } 0105 0106 Okteta::LineSize noOfLinesPerFrame = height() / d->mLineHeight; 0107 0108 if (noOfLinesPerFrame < 1) { 0109 // ensure at least one line, so there aren't endless frames TODO: think about 0110 noOfLinesPerFrame = 1; 0111 } 0112 0113 return noOfLinesPerFrame; 0114 } 0115 0116 void AbstractColumnFrameRenderer::addColumn(Okteta::AbstractColumnRenderer* columnRenderer) 0117 { 0118 d->mColumns.append(columnRenderer); 0119 0120 updateWidths(); 0121 } 0122 0123 void AbstractColumnFrameRenderer::removeColumn(Okteta::AbstractColumnRenderer* columnRenderer) 0124 { 0125 const int columnPos = d->mColumns.indexOf(columnRenderer); 0126 if (columnPos != -1) { 0127 d->mColumns.removeAt(columnPos); 0128 } 0129 0130 delete columnRenderer; 0131 0132 updateWidths(); 0133 } 0134 0135 void AbstractColumnFrameRenderer::renderFrame(QPainter* painter, int frameIndex) 0136 { 0137 Okteta::PixelXRange renderedXs = Okteta::PixelXRange::fromWidth(0, width()); 0138 0139 // content to be shown? 0140 if (renderedXs.startsBefore(d->mColumnsWidth)) { 0141 // collect affected columns 0142 QVector<Okteta::AbstractColumnRenderer*> columnRenderers; 0143 columnRenderers.reserve(d->mColumns.size()); 0144 for (auto* columnRenderer : std::as_const(d->mColumns)) { 0145 if (columnRenderer->isVisible() && columnRenderer->overlaps(renderedXs)) { 0146 columnRenderers.append(columnRenderer); 0147 } 0148 } 0149 0150 // calculate affected lines 0151 const Okteta::Line baseLine = frameIndex * noOfLinesPerFrame(); 0152 Okteta::LineRange renderedLines = Okteta::LineRange::fromWidth(baseLine, noOfLinesPerFrame()); 0153 renderedLines.restrictEndTo(noOfLines() - 1); 0154 0155 Okteta::PixelYRange renderedYs = Okteta::PixelYRange::fromWidth(0, renderedLines.width() * d->mLineHeight); 0156 0157 // any lines of any columns to be drawn? 0158 if (renderedLines.isValid()) { 0159 // paint full columns 0160 for (auto* columnRenderer : std::as_const(columnRenderers)) { 0161 columnRenderer->renderColumn(painter, renderedXs, renderedYs); 0162 } 0163 0164 Okteta::PixelY cy = 0; 0165 // starting painting with the first line 0166 Okteta::Line line = renderedLines.start(); 0167 auto it = columnRenderers.constBegin(); 0168 Okteta::AbstractColumnRenderer* columnRenderer = *it; 0169 painter->translate(columnRenderer->x(), cy); 0170 0171 while (true) { 0172 columnRenderer->renderFirstLine(painter, renderedXs, line); 0173 ++it; 0174 if (it == columnRenderers.constEnd()) { 0175 break; 0176 } 0177 painter->translate(columnRenderer->width(), 0); 0178 columnRenderer = *it; 0179 } 0180 painter->translate(-columnRenderer->x(), 0); 0181 0182 // Go through the other lines 0183 while (true) { 0184 ++line; 0185 0186 if (line > renderedLines.end()) { 0187 break; 0188 } 0189 0190 auto it = columnRenderers.constBegin(); 0191 columnRenderer = *it; 0192 painter->translate(columnRenderer->x(), d->mLineHeight); 0193 0194 while (true) { 0195 columnRenderer->renderNextLine(painter); 0196 ++it; 0197 if (it == columnRenderers.constEnd()) { 0198 break; 0199 } 0200 painter->translate(columnRenderer->width(), 0); 0201 columnRenderer = *it; 0202 } 0203 painter->translate(-columnRenderer->x(), 0); 0204 } 0205 cy = (renderedLines.width() - 1) * d->mLineHeight; 0206 0207 painter->translate(0, -cy); 0208 } 0209 0210 // draw empty columns? 0211 renderedYs.set(renderedYs.nextBehindEnd(), height() - 1); 0212 if (renderedYs.isValid()) { 0213 for (auto* columnRenderer : std::as_const(columnRenderers)) { 0214 columnRenderer->renderEmptyColumn(painter, renderedXs, renderedYs); 0215 } 0216 } 0217 } 0218 0219 // painter empty rects 0220 renderedXs.setStart(d->mColumnsWidth); 0221 if (renderedXs.isValid()) { 0222 drawEmptyArea(painter, renderedXs.start(), 0, renderedXs.width(), height()); 0223 } 0224 } 0225 0226 void AbstractColumnFrameRenderer::drawEmptyArea(QPainter* painter, int cx, int cy, int cw, int ch) 0227 { 0228 painter->fillRect(cx, cy, cw, ch, Qt::white); 0229 }