1 #include <QDebug>
2 #include <QMouseEvent>
3 #include <QPainter>
4 #include <QScrollBar>
5
6 #include <cmath>
7
8 #include "glyphsview.h"
9
10 static const int COLUMNS_COUNT = 100;
11
GlyphsView(QWidget * parent)12 GlyphsView::GlyphsView(QWidget *parent) : QAbstractScrollArea(parent)
13 {
14 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
15 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
16 }
17
setFontInfo(const FontInfo & fi)18 void GlyphsView::setFontInfo(const FontInfo &fi)
19 {
20 m_fontInfo = fi;
21 m_glyphs.resize(fi.numberOfGlyphs);
22 #ifdef WITH_FREETYPE
23 m_ftGlyphs.resize(fi.numberOfGlyphs);
24 #endif
25 #ifdef WITH_HARFBUZZ
26 m_hbGlyphs.resize(fi.numberOfGlyphs);
27 #endif
28
29 m_indexes.clear();
30 for (int i = 0; i < fi.numberOfGlyphs; ++i) {
31 QStaticText text(QString::number(i));
32 text.prepare();
33 m_indexes << text;
34 }
35
36 updateScrollBars();
37 horizontalScrollBar()->setValue(0);
38 verticalScrollBar()->setValue(0);
39 }
40
setGlyph(int idx,const Glyph & glyph)41 void GlyphsView::setGlyph(int idx, const Glyph &glyph)
42 {
43 m_glyphs.replace(idx, glyph);
44 }
45
46 #ifdef WITH_FREETYPE
setFTGlyph(int idx,const Glyph & glyph)47 void GlyphsView::setFTGlyph(int idx, const Glyph &glyph)
48 {
49 m_ftGlyphs.replace(idx, glyph);
50 }
51 #endif
52
53 #ifdef WITH_HARFBUZZ
setHBGlyph(int idx,const Glyph & glyph)54 void GlyphsView::setHBGlyph(int idx, const Glyph &glyph)
55 {
56 m_hbGlyphs.replace(idx, glyph);
57 }
58 #endif
59
setDrawBboxes(const bool flag)60 void GlyphsView::setDrawBboxes(const bool flag)
61 {
62 m_drawBboxes = flag;
63 viewport()->update();
64 }
65
setDrawGlyphs(const bool flag)66 void GlyphsView::setDrawGlyphs(const bool flag)
67 {
68 m_drawGlyphs = flag;
69 viewport()->update();
70 }
71
setDrawFTGlyphs(const bool flag)72 void GlyphsView::setDrawFTGlyphs(const bool flag)
73 {
74 m_drawFTGlyphs = flag;
75 viewport()->update();
76 }
77
setDrawHBGlyphs(const bool flag)78 void GlyphsView::setDrawHBGlyphs(const bool flag)
79 {
80 m_drawHBGlyphs = flag;
81 viewport()->update();
82 }
83
paintEvent(QPaintEvent *)84 void GlyphsView::paintEvent(QPaintEvent *)
85 {
86 QPainter p(viewport());
87 p.translate(-horizontalScrollBar()->value(), -verticalScrollBar()->value());
88
89 const double cellHeight = m_fontInfo.height * m_scale;
90 drawGrid(p, cellHeight);
91
92 p.setRenderHint(QPainter::Antialiasing);
93
94 {
95 auto font = p.font();
96 font.setPointSize(10);
97 p.setFont(font);
98 }
99
100 int x = 0;
101 int y = m_fontInfo.ascender;
102 int num_y = m_fontInfo.height;
103 for (int i = 0; i < m_glyphs.size(); ++i) {
104 // Text rendering is the slowest part, so we are using preprocessed text.
105 p.setPen(palette().color(QPalette::Text));
106 p.drawStaticText(
107 qRound(x * m_scale + 1),
108 qRound(num_y * m_scale - p.fontMetrics().ascent() - 2),
109 m_indexes.at(i)
110 );
111
112 if (m_drawGlyphs) {
113 p.save();
114
115 const int dx = qRound((m_fontInfo.height - m_glyphs.at(i).bbox.width()) / 2.0)
116 - m_glyphs.at(i).bbox.x();
117
118 p.scale(m_scale, m_scale);
119 p.translate(x + dx, y);
120
121 if (m_drawBboxes) {
122 p.setPen(QPen(Qt::darkGreen, 0.5 / m_scale));
123 p.setBrush(Qt::NoBrush);
124 p.drawRect(m_glyphs.at(i).bbox);
125 }
126
127 p.setPen(Qt::NoPen);
128 p.setPen(Qt::NoPen);
129 if (m_drawFTGlyphs || m_drawHBGlyphs) {
130 p.setBrush(Qt::red);
131 } else {
132 p.setBrush(palette().color(QPalette::Text));
133 }
134
135 p.drawPath(m_glyphs.at(i).outline);
136
137 p.restore();
138 }
139
140 #ifdef WITH_HARFBUZZ
141 if (m_drawHBGlyphs) {
142 p.save();
143
144 const int dx = qRound((m_fontInfo.height - m_hbGlyphs.at(i).bbox.width()) / 2.0)
145 - m_hbGlyphs.at(i).bbox.x();
146
147 p.scale(m_scale, m_scale);
148 p.translate(x + dx, y);
149
150 if (m_drawBboxes) {
151 p.setPen(QPen(Qt::darkGreen, 0.5 / m_scale));
152 p.setBrush(Qt::NoBrush);
153 p.drawRect(m_hbGlyphs.at(i).bbox);
154 }
155
156 p.setPen(Qt::NoPen);
157 if (m_drawFTGlyphs) {
158 p.setBrush(Qt::blue);
159 } else {
160 p.setBrush(palette().color(QPalette::Text));
161 }
162
163 p.drawPath(m_hbGlyphs.at(i).outline);
164
165 p.restore();
166 }
167 #endif
168
169 #ifdef WITH_FREETYPE
170 if (m_drawFTGlyphs) {
171 p.save();
172
173 const int dx = qRound((m_fontInfo.height - m_ftGlyphs.at(i).bbox.width()) / 2.0)
174 - m_ftGlyphs.at(i).bbox.x();
175
176 p.scale(m_scale, m_scale);
177 p.translate(x + dx, y);
178
179 if (m_drawBboxes) {
180 p.setPen(QPen(Qt::darkGreen, 0.5 / m_scale));
181 p.setBrush(Qt::NoBrush);
182 p.drawRect(m_ftGlyphs.at(i).bbox);
183 }
184
185 p.setPen(Qt::NoPen);
186 p.setBrush(palette().color(QPalette::Text));
187
188 if (m_drawGlyphs || m_drawHBGlyphs) {
189 p.setBrush(palette().color(QPalette::Base));
190 }
191
192 p.drawPath(m_ftGlyphs.at(i).outline);
193
194 p.restore();
195 }
196 #endif
197
198 x += m_fontInfo.height;
199 if (i > 0 && (i + 1) % COLUMNS_COUNT == 0) {
200 x = 0;
201 y += m_fontInfo.height;
202 num_y += m_fontInfo.height;
203 }
204 }
205 }
206
drawGrid(QPainter & p,const double cellHeight)207 void GlyphsView::drawGrid(QPainter &p, const double cellHeight)
208 {
209 p.setRenderHint(QPainter::Antialiasing, false);
210 p.setPen(QPen(palette().color(QPalette::Text), 0.25));
211 p.setBrush(Qt::NoBrush);
212
213 const int rows = qRound(floor(m_glyphs.size() / COLUMNS_COUNT)) + 1;
214 const auto maxH = qMin(rows * cellHeight, (double)horizontalScrollBar()->maximum());
215
216 double x = cellHeight;
217 for (int c = 1; c < COLUMNS_COUNT; ++c) {
218 p.drawLine(QLineF(x, 0, x, maxH));
219 x += cellHeight;
220 }
221
222 double y = cellHeight;
223 for (int r = 1; r <= rows; ++r) {
224 p.drawLine(QLineF(0, y, horizontalScrollBar()->maximum() + viewport()->width(), y));
225 y += cellHeight;
226 }
227 }
228
mousePressEvent(QMouseEvent * e)229 void GlyphsView::mousePressEvent(QMouseEvent *e)
230 {
231 if (e->button() & Qt::LeftButton) {
232 m_mousePressPos = e->pos();
233 m_origOffset = QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value());
234 }
235 }
236
mouseMoveEvent(QMouseEvent * e)237 void GlyphsView::mouseMoveEvent(QMouseEvent *e)
238 {
239 if (m_mousePressPos.isNull()) {
240 return;
241 }
242
243 const auto diff = m_mousePressPos - e->pos();
244 horizontalScrollBar()->setValue(m_origOffset.x() + diff.x());
245 verticalScrollBar()->setValue(m_origOffset.y() + diff.y());
246 }
247
mouseReleaseEvent(QMouseEvent *)248 void GlyphsView::mouseReleaseEvent(QMouseEvent *)
249 {
250 m_mousePressPos = QPoint();
251 m_origOffset = QPoint();
252 }
253
wheelEvent(QWheelEvent * e)254 void GlyphsView::wheelEvent(QWheelEvent *e)
255 {
256 e->accept();
257
258 if (e->delta() > 0) {
259 m_scale += 0.01;
260 } else {
261 m_scale -= 0.01;
262 }
263
264 m_scale = qBound(0.03, m_scale, 1.0);
265
266 updateScrollBars();
267 viewport()->update();
268 }
269
resizeEvent(QResizeEvent * e)270 void GlyphsView::resizeEvent(QResizeEvent *e)
271 {
272 QAbstractScrollArea::resizeEvent(e);
273 updateScrollBars();
274 }
275
updateScrollBars()276 void GlyphsView::updateScrollBars()
277 {
278 const double cellHeight = m_fontInfo.height * m_scale;
279 const int rows = qRound(floor(m_glyphs.size() / COLUMNS_COUNT)) + 1;
280 const auto w = COLUMNS_COUNT * cellHeight - viewport()->width();
281 const auto h = rows * cellHeight - viewport()->height();
282 horizontalScrollBar()->setMinimum(0);
283 verticalScrollBar()->setMinimum(0);
284 horizontalScrollBar()->setMaximum(qMax(0, qRound(w)));
285 verticalScrollBar()->setMaximum(qMax(0, qRound(h)));
286 }
287