wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
DolphinZoom.cpp
Go to the documentation of this file.
1// Copyright 2015 - 2026, GIBIS-UNIFESP and the wiRedPanda contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
5
6#include <algorithm>
7#include <cmath>
8
9#include <QAbstractItemModel>
10#include <QApplication>
11#include <QHeaderView>
12#include <QScrollBar>
13#include <QTableView>
14
15static constexpr int kDefaultColumnWidth = 38;
16static constexpr int kDefaultRowHeight = 30;
17static constexpr double kZoomStep = 1.25;
18static constexpr int kMaxZoomLevel = 6;
19static constexpr double kMinFitScale = 0.05;
20static constexpr double kMaxFitScale = 20.0;
21static constexpr int kFitIterations = 6;
22static constexpr double kFitEpsilon = 1e-3;
23
24DolphinZoom::DolphinZoom(QTableView *view)
25 : m_view(view)
26{
27}
28
30{
31 return m_zoomLevel < kMaxZoomLevel;
32}
33
35{
36 return m_zoomLevel > 0;
37}
38
40{
41 // Column-width only; capped at kMaxZoomLevel discrete steps.
42 if (m_zoomLevel < kMaxZoomLevel) {
43 ++m_zoomLevel;
44 }
45 apply();
46}
47
49{
50 // Column-width only; floored at the baseline (no shrink below the default width).
51 if (m_zoomLevel > 0) {
52 --m_zoomLevel;
53 }
54 apply();
55}
56
58{
59 m_zoomLevel = 0;
60 m_fitScale = 1.0;
61 apply();
62}
63
65{
66 // Fit Screen scales everything (columns, rows, font) uniformly to fit, and resets
67 // the discrete column zoom.
68 const int cols = m_view->model() ? m_view->model()->columnCount() : 0;
69 const int rows = m_view->model() ? m_view->model()->rowCount() : 0;
70 // Degenerate geometry (empty/hidden table): nothing to fit, leave the zoom untouched.
71 if (cols <= 0 || rows <= 0) {
72 return;
73 }
74
75 auto *vHeader = m_view->verticalHeader();
76 auto *hHeader = m_view->horizontalHeader();
77 auto *vbar = m_view->verticalScrollBar();
78 auto *hbar = m_view->horizontalScrollBar();
79
80 // viewport() already excludes the header gutter/strip and shrinks under any visible
81 // scroll bar. Add both back to get the window interior available to the grid + its
82 // chrome — a quantity independent of the current zoom (so the fit cannot feed back on
83 // its own starting geometry, which is what forced a second Fit press to converge).
84 const double interiorW = m_view->viewport()->width()
85 + vHeader->width() + (vbar->isVisible() ? vbar->width() : 0);
86 const double interiorH = m_view->viewport()->height()
87 + hHeader->height() + (hbar->isVisible() ? hbar->height() : 0);
88
89 // The gutter/strip scale with the font, which scales with the fit factor, so the
90 // available cell area depends on the result. Solve the fixed point: recompute against
91 // the chrome size the new font yields (sizeHint() reflects the new font synchronously),
92 // re-applying until the scale stabilizes — converging in one Fit press.
93 for (int i = 0; i < kFitIterations; ++i) {
94 const double availW = interiorW - vHeader->sizeHint().width();
95 const double availH = interiorH - hHeader->sizeHint().height();
96 const double sW = availW / (kDefaultColumnWidth * cols);
97 const double sH = availH / (kDefaultRowHeight * rows);
98 // A hidden or too-small viewport yields a non-positive scale; leave the zoom untouched.
99 if (sW <= 0 || sH <= 0) {
100 return;
101 }
102 const double next = std::clamp((std::min)(sW, sH), kMinFitScale, kMaxFitScale);
103 m_zoomLevel = 0;
104 if (std::abs(next - m_fitScale) < kFitEpsilon) {
105 break;
106 }
107 m_fitScale = next;
108 apply();
109 }
110}
111
113{
114 // Sections use Fixed resize mode, so setting the header default section size resizes
115 // every row/column uniformly without a per-cell loop. The discrete column-zoom widens
116 // columns only; Fit Screen scales columns, rows, and font together.
117 const double colScale = m_fitScale * std::pow(kZoomStep, m_zoomLevel);
118 m_view->horizontalHeader()->setDefaultSectionSize(
119 static_cast<int>(std::lround(kDefaultColumnWidth * colScale)));
120 m_view->verticalHeader()->setDefaultSectionSize(
121 static_cast<int>(std::lround(kDefaultRowHeight * m_fitScale)));
122
123 // Scale the cell/header font with the Fit Screen factor only (column zoom leaves text
124 // untouched), from the application's base point size.
125 QFont font = QApplication::font();
126 const double basePointSize = font.pointSizeF() > 0 ? font.pointSizeF() : 10.0;
127 font.setPointSizeF(basePointSize * m_fitScale);
128 m_view->setFont(font);
129 m_view->horizontalHeader()->setFont(font);
130 m_view->verticalHeader()->setFont(font);
131}
static constexpr int kDefaultColumnWidth
Per-column pixel width at zoom 1.0 (matches the pre-refactor on-screen size).
static constexpr double kMaxFitScale
Largest allowed Fit Screen scale.
static constexpr int kMaxZoomLevel
Maximum discrete column-zoom step (baseline = 0).
static constexpr double kMinFitScale
Smallest allowed Fit Screen scale.
static constexpr double kZoomStep
Multiplicative factor per column-zoom step.
static constexpr double kFitEpsilon
Fit Screen scale convergence threshold.
static constexpr int kDefaultRowHeight
Per-row pixel height at zoom 1.0 (matches the pre-refactor on-screen size).
static constexpr int kFitIterations
Max passes to converge Fit Screen's fixed point.
DolphinZoom: zoom state and metrics for the beWavedDolphin waveform table.
bool canZoomIn() const
True if Zoom In is still possible (below the max level).
void zoomIn()
Increases the discrete column-zoom one step (capped) and re-applies.
void fitScreen()
void zoomOut()
Decreases the discrete column-zoom one step (floored at the baseline) and re-applies.
bool canZoomOut() const
True if Zoom Out is still possible (above the baseline).
void reset()
Resets both axes to the baseline (level 0, scale 1.0) and re-applies.
DolphinZoom(QTableView *view)
Constructs the zoom controller for view (not owned; must outlive this).
void apply() const
Applies the current zoom to the view's row/column section sizes and font.