From 9738345b45c5eb3f635aa5d08ebc2b3361f6ea4c Mon Sep 17 00:00:00 2001 From: ispyisail Date: Sat, 26 Sep 2026 08:22:12 +1200 Subject: [PATCH] Add an option to keep the folio row and column headers visible (#1034) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Affichage > "Garder les en-têtes visibles" adds a bar along the top and the left of the diagram view that repeats the folio's column numbers and row letters, aligned with the cells at any zoom, so they stay in sight when the folio's own headers are scrolled away. Off by default; the choice is stored as diagrameditor/cell_rulers. The bars are CellRuler widgets in the view's margins (setViewportMargins), not scene items, so printing and PDF/PNG/DXF export never see them, and they paint with the application palette outside of the dark-palette inversion. They keep a constant thickness; when cells get narrower than their labels, only every 2nd, 5th, 10th... label is written. A bar is hidden when the folio hides that header. Showing or hiding them keeps the centre of the view where it was. The labels come from BorderCellLabels, now also used by BorderTitleBlock::draw(), so the bars and the border cannot disagree. PNG export of all 133 folios of the examples is pixel-identical to master, with border-columns_0 true and false. Known limits: changing border-columns_0 repaints the bars at the next scroll or zoom; the menu toggle updates the views of its own editor window only, like the grid toggle. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG --- cmake/qet_compilation_vars.cmake | 3 + sources/bordercelllabels.h | 51 ++++++++ sources/bordertitleblock.cpp | 16 +-- sources/cellruler.cpp | 170 ++++++++++++++++++++++++++ sources/cellruler.h | 54 ++++++++ sources/diagramview.cpp | 91 ++++++++++++++ sources/diagramview.h | 10 ++ sources/qetdiagrameditor.cpp | 14 +++ sources/qetdiagrameditor.h | 1 + tests/qttest/CMakeLists.txt | 6 + tests/qttest/tst_bordercelllabels.cpp | 55 +++++++++ 11 files changed, 460 insertions(+), 11 deletions(-) create mode 100644 sources/bordercelllabels.h create mode 100644 sources/cellruler.cpp create mode 100644 sources/cellruler.h create mode 100644 tests/qttest/tst_bordercelllabels.cpp diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake index 5721e4111..1bf68b48d 100644 --- a/cmake/qet_compilation_vars.cmake +++ b/cmake/qet_compilation_vars.cmake @@ -168,6 +168,9 @@ set(QET_SRC_FILES ${QET_DIR}/sources/borderproperties.h ${QET_DIR}/sources/bordertitleblock.cpp ${QET_DIR}/sources/bordertitleblock.h + ${QET_DIR}/sources/bordercelllabels.h + ${QET_DIR}/sources/cellruler.cpp + ${QET_DIR}/sources/cellruler.h ${QET_DIR}/sources/conductorautonumerotation.cpp ${QET_DIR}/sources/conductorautonumerotation.h ${QET_DIR}/sources/conductornumexport.cpp diff --git a/sources/bordercelllabels.h b/sources/bordercelllabels.h new file mode 100644 index 000000000..d5dbb6e29 --- /dev/null +++ b/sources/bordercelllabels.h @@ -0,0 +1,51 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#ifndef BORDERCELLLABELS_H +#define BORDERCELLLABELS_H + +#include + +/// The labels the border of a folio writes on its rows and columns, shared +/// by BorderTitleBlock::draw() and the cell rulers of DiagramView so the +/// two cannot disagree. Header-only so it can be unit-tested directly -- +/// see tests/qttest/tst_bordercelllabels.cpp. +namespace BorderCellLabels { + + /// @return the label of row \a row, counted from 1: A..Z, then AA, AB... + /// (the sequence BorderTitleBlock::incrementLetters() walks through). + inline QString rowLabel(int row) + { + QString label; + while (row > 0) { + --row; + label.prepend(QChar('A' + row % 26)); + row /= 26; + } + return label; + } + + /// @return the label of column \a column, counted from 1. When + /// \a starts_at_zero (the "border-columns_0" setting) the first column + /// is labelled 0. + inline QString columnLabel(int column, bool starts_at_zero) + { + return QString::number(starts_at_zero ? column - 1 : column); + } +} + +#endif // BORDERCELLLABELS_H diff --git a/sources/bordertitleblock.cpp b/sources/bordertitleblock.cpp index 4a2a2dfdc..46e3f7ba8 100644 --- a/sources/bordertitleblock.cpp +++ b/sources/bordertitleblock.cpp @@ -17,6 +17,7 @@ */ #include "bordertitleblock.h" +#include "bordercelllabels.h" #include "createdxf.h" #include "diagram.h" #include "diagramposition.h" @@ -533,6 +534,8 @@ void BorderTitleBlock::draw(QPainter *painter) //Draw the nums of columns if (display_border_ && display_columns_) { + const bool columns_start_at_zero = + settings.value("border-columns_0", true).toBool(); for (int i = 1 ; i <= columns_count_ ; ++ i) { QRectF numbered_rectangle = QRectF( diagram_rect_.topLeft().x() @@ -543,23 +546,15 @@ void BorderTitleBlock::draw(QPainter *painter) columns_header_height_ ); painter -> drawRect(numbered_rectangle); - if (settings.value("border-columns_0", true).toBool()){ painter -> drawText(numbered_rectangle, Qt::AlignVCenter | Qt::AlignCenter, - QString("%1").arg(i - 1)); - }else{ - painter -> drawText(numbered_rectangle, - Qt::AlignVCenter - | Qt::AlignCenter, - QString("%1").arg(i)); - } + BorderCellLabels::columnLabel(i, columns_start_at_zero)); } } //Draw the nums of rows if (display_border_ && display_rows_) { - QString row_string("A"); for (int i = 1 ; i <= rows_count_ ; ++ i) { QRectF lettered_rectangle = QRectF( diagram_rect_.topLeft().x(), @@ -575,8 +570,7 @@ void BorderTitleBlock::draw(QPainter *painter) painter -> drawText(lettered_rectangle, Qt::AlignVCenter | Qt::AlignCenter, - row_string); - row_string = incrementLetters(row_string); + BorderCellLabels::rowLabel(i)); } } diff --git a/sources/cellruler.cpp b/sources/cellruler.cpp new file mode 100644 index 000000000..299d70eb8 --- /dev/null +++ b/sources/cellruler.cpp @@ -0,0 +1,170 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include "cellruler.h" + +#include "bordercelllabels.h" +#include "diagram.h" +#include "diagramview.h" + +#include +#include + +/** + @brief CellRuler::CellRuler + @param orientation : Qt::Horizontal for the column numbers along the + top, Qt::Vertical for the row letters along the left + @param view : the view this ruler is placed on +*/ +CellRuler::CellRuler(Qt::Orientation orientation, DiagramView *view) : + QWidget(view), + m_orientation(orientation), + m_view(view) +{ + setAttribute(Qt::WA_OpaquePaintEvent); + hide(); +} + +/** + @brief CellRuler::thickness + @return the height of the top ruler, which is also the width of the + side ruler, so the corner they share is square. Constant whatever the + zoom. +*/ +int CellRuler::thickness() const +{ + const QFontMetrics metrics = fontMetrics(); + return qMax(metrics.height(), metrics.horizontalAdvance(QStringLiteral("WW"))) + 6; +} + +/** + @brief CellRuler::setLeadingSpace + @param space : pixels left empty before the first pixel of the + viewport, along this ruler +*/ +void CellRuler::setLeadingSpace(int space) +{ + m_leading_space = space; + update(); +} + +/** + @brief CellRuler::paintEvent + Draw a cell for each column (or row) of the folio border, at the + position the view shows it. When the cells get too small for their + labels, only one label every 2, 5, 10... cells is written. +*/ +void CellRuler::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + painter.fillRect(rect(), palette().color(QPalette::Button)); + + const bool horizontal = m_orientation == Qt::Horizontal; + const int length = horizontal ? width() : height(); + const int depth = horizontal ? height() : width(); + + //Separate the ruler from the drawing + painter.setPen(palette().color(QPalette::Dark)); + if (horizontal) { + painter.drawLine(0, depth - 1, length, depth - 1); + } else { + painter.drawLine(depth - 1, 0, depth - 1, length); + } + + Diagram *diagram = m_view->diagram(); + if (!diagram) { + return; + } + const BorderTitleBlock &border = diagram->border_and_titleblock; + const QRectF inside = border.insideBorderRect(); + const QTransform transform = m_view->viewportTransform(); + + const int count = horizontal ? border.columnsCount() : border.rowsCount(); + const qreal cell_size = horizontal ? border.columnsWidth() : border.rowsHeight(); + const qreal first = horizontal ? inside.left() : inside.top(); + const qreal scale = horizontal ? transform.m11() : transform.m22(); + const qreal offset = (horizontal ? transform.dx() : transform.dy()) + m_leading_space; + const qreal cell_pixels = cell_size * scale; + if (count < 1 || cell_pixels <= 0) { + return; + } + + const bool columns_start_at_zero = + QSettings().value("border-columns_0", true).toBool(); + auto label = [&](int index) { + return horizontal ? BorderCellLabels::columnLabel(index, columns_start_at_zero) + : BorderCellLabels::rowLabel(index); + }; + + //Room one label needs along the ruler, and the smallest step + //between written labels that gives it that room + const QFontMetrics metrics = fontMetrics(); + const int label_room = horizontal + ? metrics.horizontalAdvance(label(count)) + 6 + : metrics.height() + 2; + int step = 1; + for (int candidate : {1, 2, 5, 10, 20, 50, 100, 200, 500}) { + step = candidate; + if (candidate * cell_pixels >= label_room) { + break; + } + } + + for (int i = 1 ; i <= count ; ++i) { + const qreal start = offset + (first + (i - 1) * cell_size) * scale; + const qreal end = start + cell_pixels; + if (end < m_leading_space || start > length) { + continue; + } + + //Cell edges, drawn only when the cells are wide enough + //for them to read as cells rather than as a hatching + painter.setPen(palette().color(QPalette::Dark)); + if (cell_pixels >= 4) { + if (horizontal) { + painter.drawLine(QPointF(start, 0), QPointF(start, depth - 1)); + if (i == count) painter.drawLine(QPointF(end, 0), QPointF(end, depth - 1)); + } else { + painter.drawLine(QPointF(0, start), QPointF(depth - 1, start)); + if (i == count) painter.drawLine(QPointF(0, end), QPointF(depth - 1, end)); + } + } + + //Written labels are the ones a multiple of step: 0, 5, 10... + //for the columns, A, F, K... for the rows + const int position = (horizontal && !columns_start_at_zero) ? i : i - 1; + if (position % step != 0) { + continue; + } + + painter.setPen(palette().color(QPalette::ButtonText)); + const qreal centre = (start + end) / 2; + const QRectF text_rect = horizontal + ? QRectF(centre - label_room / 2.0, 0, label_room, depth - 1) + : QRectF(0, centre - label_room / 2.0, depth - 1, label_room); + painter.drawText(text_rect, Qt::AlignCenter | Qt::TextDontClip, label(i)); + } + + //Keep the corner empty: the other ruler's labels do not belong there + if (m_leading_space > 0) { + painter.fillRect(horizontal ? QRect(0, 0, m_leading_space, depth - 1) + : QRect(0, 0, depth - 1, m_leading_space), + palette().color(QPalette::Button)); + } +} diff --git a/sources/cellruler.h b/sources/cellruler.h new file mode 100644 index 000000000..619b2d52e --- /dev/null +++ b/sources/cellruler.h @@ -0,0 +1,54 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#ifndef CELLRULER_H +#define CELLRULER_H + +#include + +class DiagramView; + +/** + @brief The CellRuler class + A bar along the top or the left edge of a DiagramView that repeats the + column numbers or the row letters of the folio border, aligned with the + cells at any zoom, so they stay in sight however far the view is + scrolled. It sits in the view's margins, outside of the scene: printing + and exporting are unaffected. +*/ +class CellRuler : public QWidget +{ + Q_OBJECT + + public: + CellRuler(Qt::Orientation orientation, DiagramView *view); + + int thickness() const; + void setLeadingSpace(int space); + + protected: + void paintEvent(QPaintEvent *event) override; + + private: + Qt::Orientation m_orientation; + DiagramView *m_view; + /// Pixels before the viewport starts, left empty: the corner the + /// side ruler fills when both rulers are shown. + int m_leading_space = 0; +}; + +#endif // CELLRULER_H diff --git a/sources/diagramview.cpp b/sources/diagramview.cpp index 843b694be..276ccc15d 100644 --- a/sources/diagramview.cpp +++ b/sources/diagramview.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "diagramview.h" +#include "cellruler.h" #include "lastusedstyle.h" #include "qetproject.h" #include "QPropertyUndoCommand/qpropertyundocommand.h" @@ -107,6 +108,13 @@ DiagramView::DiagramView(Diagram *diagram, QWidget *parent) : connect(&(m_diagram -> border_and_titleblock), &BorderTitleBlock::informationChanged, this, &DiagramView::updateWindowTitle); connect(diagram, &Diagram::findElementRequired, this, &DiagramView::findElementRequired); + m_top_ruler = new CellRuler(Qt::Horizontal, this); + m_side_ruler = new CellRuler(Qt::Vertical, this); + m_cell_rulers_shown = QSettings().value("diagrameditor/cell_rulers", false).toBool(); + connect(&m_diagram->border_and_titleblock, &BorderTitleBlock::borderChanged, this, &DiagramView::updateCellRulers); + connect(&m_diagram->border_and_titleblock, &BorderTitleBlock::displayChanged, this, &DiagramView::updateCellRulers); + updateCellRulers(); + QShortcut *edit_conductor_color_shortcut = new QShortcut(QKeySequence(Qt::Key_F2), this); connect(edit_conductor_color_shortcut, &QShortcut::activated, [this]() { @@ -1170,6 +1178,82 @@ void DiagramView::paintingInverted(bool inverted) m_diagram->setInvertedLightness(inverted); } +/** + @brief DiagramView::setCellRulersShown + Show or hide the rulers that keep the column numbers and the row + letters of the folio border in sight along the edges of this view. + @param shown +*/ +void DiagramView::setCellRulersShown(bool shown) +{ + m_cell_rulers_shown = shown; + updateCellRulers(); +} + +/** + @brief DiagramView::updateCellRulers + Show each ruler when the rulers are wanted and the folio shows the + matching header, and give it room in the margins of the view. The + part of the folio in sight stays in sight when the viewport resizes. +*/ +void DiagramView::updateCellRulers() +{ + const BorderTitleBlock &border = m_diagram->border_and_titleblock; + const bool top = m_cell_rulers_shown + && border.borderIsDisplayed() && border.columnsAreDisplayed(); + const bool side = m_cell_rulers_shown + && border.borderIsDisplayed() && border.rowsAreDisplayed(); + const int thickness = m_top_ruler->thickness(); + + m_top_ruler->setVisible(top); + m_side_ruler->setVisible(side); + m_side_ruler->setLeadingSpace(top ? thickness : 0); + + const QMargins margins(side ? thickness : 0, top ? thickness : 0, 0, 0); + if (margins != viewportMargins()) { + const QPointF centre = mapToScene(viewport()->rect().center()); + setViewportMargins(margins); + centerOn(centre); + } + placeCellRulers(); + m_top_ruler->update(); + m_side_ruler->update(); +} + +/** + @brief DiagramView::placeCellRulers + Lay the rulers along the top and the left edges of the viewport, the + side ruler covering the corner too when both are shown. +*/ +void DiagramView::placeCellRulers() +{ + if (!m_top_ruler) { + return; + } + const QRect viewport_rect = viewport()->geometry(); + const int thickness = m_top_ruler->thickness(); + const int corner = m_top_ruler->isHidden() ? 0 : thickness; + m_top_ruler->setGeometry(viewport_rect.left(), viewport_rect.top() - thickness, + viewport_rect.width(), thickness); + m_side_ruler->setGeometry(viewport_rect.left() - thickness, viewport_rect.top() - corner, + thickness, viewport_rect.height() + corner); +} + +/** + @brief DiagramView::viewportEvent + Keep the rulers along the viewport when it resizes, which it also does + without the view resizing, when the scroll bars come and go. + @param event + @return what QGraphicsView::viewportEvent() returns +*/ +bool DiagramView::viewportEvent(QEvent *event) +{ + if (event->type() == QEvent::Resize) { + placeCellRulers(); + } + return PaletteGraphicsView::viewportEvent(event); +} + /** @brief DiagramView::paintEvent Reimplemented from QGraphicsView @@ -1179,6 +1263,13 @@ void DiagramView::paintEvent(QPaintEvent *event) { PaletteGraphicsView::paintEvent(event); + //Scrolling and zooming both repaint the viewport: follow them + if (viewportTransform() != m_rulers_transform) { + m_rulers_transform = viewportTransform(); + m_top_ruler->update(); + m_side_ruler->update(); + } + if (m_free_rubberbanding && m_free_rubberband.count() >= 3) { QPainter painter(viewport()); diff --git a/sources/diagramview.h b/sources/diagramview.h index eb73432cb..f7544b66a 100644 --- a/sources/diagramview.h +++ b/sources/diagramview.h @@ -24,6 +24,7 @@ #include #include "palettegraphicsview.h" +class CellRuler; class Conductor; class Diagram; class QETDiagramEditor; @@ -62,6 +63,11 @@ class DiagramView : public PaletteGraphicsView QList m_separators; QPolygonF m_free_rubberband; bool m_free_rubberbanding = false; + CellRuler *m_top_ruler = nullptr; + CellRuler *m_side_ruler = nullptr; + bool m_cell_rulers_shown = false; + /// Last viewport transform the rulers were painted for + QTransform m_rulers_transform; public: @@ -78,6 +84,7 @@ class DiagramView : public PaletteGraphicsView /// cursor query (QCursor::pos()/setPos() are silently ignored by /// several window managers and compositors, Wayland included). QPoint lastMousePos() const { return m_last_mouse_pos; } + void setCellRulersShown(bool shown); protected: void mouseDoubleClickEvent(QMouseEvent *) override; @@ -91,6 +98,7 @@ class DiagramView : public PaletteGraphicsView ///Set for one call only, by the Escape handler, to let focus leave the view. bool m_releasing_focus = false; void paintEvent(QPaintEvent *event) override; + bool viewportEvent(QEvent *event) override; void paintingInverted(bool inverted) override; void mousePressEvent(QMouseEvent *) override; void mouseMoveEvent(QMouseEvent *) override; @@ -113,6 +121,8 @@ class DiagramView : public PaletteGraphicsView QRectF viewedSceneRect() const; bool mustIntegrateTitleBlockTemplate(const TitleBlockTemplateLocation &) const; bool gestures() const; + void updateCellRulers(); + void placeCellRulers(); /// Lowest and highest allowed value of the view transform scale (m11). /// Prevents wheel-zoom from driving the transform to overflow, which diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index da26262b2..2565c41a7 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -493,6 +493,18 @@ void QETDiagramEditor::setUpActions() } }); + //Keep the column numbers and row letters of the folio in sight + m_cell_rulers = new QAction(tr("Garder les en-têtes visibles"), this); + m_cell_rulers->setStatusTip(tr("Garde les numéros de colonne et les lettres de ligne du folio visibles au bord de la vue")); + m_cell_rulers->setCheckable(true); + m_cell_rulers->setChecked(settings.value("diagrameditor/cell_rulers", false).toBool()); + connect(m_cell_rulers, &QAction::triggered, [this](bool checked) { + QSettings().setValue("diagrameditor/cell_rulers", checked); + foreach (ProjectView *prjv, this->openedProjects()) + foreach (DiagramView *dv, prjv->diagram_views()) + dv->setCellRulersShown(checked); + }); + //Edit current diagram properties m_edit_diagram_properties = new QAction(QET::Icons::DialogInformation, tr("Propriétés du folio"), this); ShortcutManager::instance().registerAction(m_edit_diagram_properties, "diagrameditor.edit_diagram_properties", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_L); @@ -1110,6 +1122,7 @@ void QETDiagramEditor::setUpMenu() menu_affichage -> addSeparator(); menu_affichage -> addAction(m_draw_grid); menu_affichage -> addAction(m_draw_guides); + menu_affichage -> addAction(m_cell_rulers); menu_affichage -> addMenu(m_background_color_button->menu()); menu_affichage -> addSeparator(); menu_affichage -> addActions(m_zoom_actions_group.actions()); @@ -1941,6 +1954,7 @@ void QETDiagramEditor::slot_updateActions() m_background_color_button-> setEnabled(opened_diagram); m_draw_grid-> setEnabled(opened_diagram); m_draw_guides-> setEnabled(opened_diagram); + m_cell_rulers-> setEnabled(opened_diagram); //Project menu m_project_edit_properties -> setEnabled(opened_project); diff --git a/sources/qetdiagrameditor.h b/sources/qetdiagrameditor.h index 71ad56244..41a95fa48 100644 --- a/sources/qetdiagrameditor.h +++ b/sources/qetdiagrameditor.h @@ -205,6 +205,7 @@ class QETDiagramEditor : public QETMainWindow *m_auto_break_conductor, ///< Enable/Disable the use of auto break conductor *m_draw_grid, ///< Switch the background grid display or not *m_draw_guides = nullptr, ///< Switch the custom guides display or not + *m_cell_rulers = nullptr, ///< Keep the folio column/row headers in sight or not *m_project_edit_properties, ///< Edit the properties of the current project. *m_project_add_diagram, ///< Add a diagram to the current project. *m_remove_diagram_from_project, ///< Delete a diagram from the current project diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 2fec5922f..4274e13d2 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -84,6 +84,12 @@ add_test(NAME tst_diagramsortkeys COMMAND tst_diagramsortkeys) target_include_directories(tst_diagramsortkeys PRIVATE ${QET_DIR}/sources) target_link_libraries(tst_diagramsortkeys PRIVATE Qt::Test) +# bordercelllabels.h is header-only too. +add_executable(tst_bordercelllabels tst_bordercelllabels.cpp) +add_test(NAME tst_bordercelllabels COMMAND tst_bordercelllabels) +target_include_directories(tst_bordercelllabels PRIVATE ${QET_DIR}/sources) +target_link_libraries(tst_bordercelllabels PRIVATE Qt::Test) + # contactusage.h is a header-only helper holding the contact counting # rules, so this test builds independently of the rest of the QET sources. add_executable(tst_contactusage tst_contactusage.cpp) diff --git a/tests/qttest/tst_bordercelllabels.cpp b/tests/qttest/tst_bordercelllabels.cpp new file mode 100644 index 000000000..7c4ca074b --- /dev/null +++ b/tests/qttest/tst_bordercelllabels.cpp @@ -0,0 +1,55 @@ +#include + +#include "bordercelllabels.h" + +class tst_bordercelllabels : public QObject +{ + Q_OBJECT + + // A copy of BorderTitleBlock::incrementLetters(), the walk the folio + // border used to draw its row labels with, as the reference rowLabel() + // must reproduce. + static QString incrementLetters(const QString &string) + { + if (string.isEmpty()) + return QStringLiteral("A"); + const QString first_digits(string.left(string.length() - 1)); + const QChar last_digit(string.at(string.length() - 1)); + if (last_digit != QLatin1Char('Z')) + return first_digits + QChar(last_digit.unicode() + 1); + return incrementLetters(first_digits) + QLatin1Char('A'); + } + +private slots: + void rowLabelsFollowTheBorderSequence() + { + QString expected(QStringLiteral("A")); + for (int row = 1; row <= 1000; ++row) { + QCOMPARE(BorderCellLabels::rowLabel(row), expected); + expected = incrementLetters(expected); + } + } + + void rowLabelSamples() + { + QCOMPARE(BorderCellLabels::rowLabel(1), QStringLiteral("A")); + QCOMPARE(BorderCellLabels::rowLabel(26), QStringLiteral("Z")); + QCOMPARE(BorderCellLabels::rowLabel(27), QStringLiteral("AA")); + QCOMPARE(BorderCellLabels::rowLabel(52), QStringLiteral("AZ")); + QCOMPARE(BorderCellLabels::rowLabel(53), QStringLiteral("BA")); + QCOMPARE(BorderCellLabels::rowLabel(702), QStringLiteral("ZZ")); + QCOMPARE(BorderCellLabels::rowLabel(703), QStringLiteral("AAA")); + } + + void columnLabels() + { + QCOMPARE(BorderCellLabels::columnLabel(1, true), QStringLiteral("0")); + QCOMPARE(BorderCellLabels::columnLabel(1, false), QStringLiteral("1")); + QCOMPARE(BorderCellLabels::columnLabel(17, true), QStringLiteral("16")); + QCOMPARE(BorderCellLabels::columnLabel(17, false), QStringLiteral("17")); + } +}; + +QTEST_APPLESS_MAIN(tst_bordercelllabels) + +#include "tst_bordercelllabels.moc"