7#include <QCoreApplication>
8#include <QFontMetricsF>
9#include <QRegularExpression>
13 static const char* captions[] = {
14 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Display"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"Setup"),
15 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Volumes"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"G4Dialog"),
16 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Generator"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"Analyzer"),
17 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Wire"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"Surface"),
18 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Cloud"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"Twinkle"),
19 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Hidden\nLines"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"Anti\nAliasing"),
20 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Auxiliary\nEdges"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"Field\nLines")
22 const QRegularExpression textTag(
"(<text\\b[^>]*>)([^<]*)(</text>)");
23 const QRegularExpression fontSize(
"font-size=\"([0-9.]+)\"");
24 const QRegularExpression fontFamily(
"font-family=\"([^\"]+)\"");
25 QList<QRegularExpressionMatch> matches;
27 auto iterator = textTag.globalMatch(svg);
28 while (iterator.hasNext()) {
29 const auto match = iterator.next();
30 const auto text = match.captured(2).trimmed();
31 if (text ==
">_" || text.isEmpty())
continue;
32 matches.append(match);
35 const auto source = lines.join(
'\n');
36 for (
const auto caption : captions) {
37 if (source != QString::fromUtf8(caption))
continue;
38 const auto translated = QCoreApplication::translate(
"GGuiIcons", caption).split(
'\n');
39 if (translated.size() != matches.size())
return svg;
40 for (
int index = matches.size() - 1; index >= 0; --index) {
41 const auto& match = matches[index];
42 auto opening = match.captured(1);
43 const auto size = fontSize.match(opening);
44 if (size.hasMatch()) {
45 const qreal pixels = size.captured(1).toDouble();
46 const auto family = fontFamily.match(opening);
47 QFont font(family.hasMatch() ? family.captured(1) : QString());
49 font.setPointSizeF(pixels);
50 const qreal width = QFontMetricsF(font).horizontalAdvance(translated[index]);
52 opening.replace(size.capturedStart(1), size.capturedLength(1),
53 QString::number(pixels * 32 / width,
'f', 2));
56 svg.replace(match.capturedStart(), match.capturedLength(),
57 opening + translated[index].toHtmlEscaped() + match.captured(3));
76 thisButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
81 const std::string filename = buttonName +
"_" + std::to_string(state) +
".svg";
83 QFile file(QString::fromStdString(filename));
84 if (!file.open(QIODevice::ReadOnly)) {
89 const QColor foreground = state == 2
90 ? palette.color(QPalette::HighlightedText)
91 : palette.color(QPalette::WindowText);
92 const QColor selectedBackground = palette.color(QPalette::Highlight);
94 svg.replace(
"width=\"48\" height=\"48\"",
95 QString(
"width=\"%1\" height=\"%2\"").arg(iconSize.width()).arg(iconSize.height()));
96 svg.replace(
"currentColor", foreground.name(QColor::HexRgb));
97 svg.replace(
"#aaddff", selectedBackground.name(QColor::HexRgb), Qt::CaseInsensitive);
100 if (!pixmap.loadFromData(svg.toUtf8(),
"SVG")) {
103 return QIcon(pixmap);
115 const std::vector<std::string>& bicons,
116 bool vertical, QWidget* parent)
118 constexpr int distanceToMargin = 12;
121 for (
const auto& b : bicons) {
128 buttonsWidget->setIconSize(QSize(
static_cast<int>(h),
static_cast<int>(v)));
132 "background-color: transparent; "
133 "QListWidget::item:selected { background: transparent; border: none; }");
136 for (
auto& b : buttons) {
143 this, &GQTButtonsWidget::buttonWasPressed);
146 QBoxLayout* layout = vertical
147 ?
static_cast<QBoxLayout*
>(
new QVBoxLayout(
this))
148 :
static_cast<QBoxLayout*
>(
new QHBoxLayout(
this));
149 layout->setContentsMargins(0, 0, 0, 0);
154 double hsize = (h + distanceToMargin) * (buttons.size());
155 double vsize = v + distanceToMargin;
157 hsize = h + distanceToMargin;
158 vsize = (v + distanceToMargin) * (buttons.size());
160 buttonsWidget->setFixedSize(
static_cast<int>(hsize),
static_cast<int>(vsize));
165 "QListWidget { background-color: transparent; }"
166 "QListWidget::item { background: transparent; border: none; }"
167 "QListWidget::item:selected { background: transparent; border: none; outline: none; }"
179void GQTButtonsWidget::buttonWasPressed(QListWidgetItem* item) {
190void GQTButtonsWidget::refresh_icons() {
198 const int state = row == currentRow ? 2 : 1;
199 buttonsWidget->item(row)->setIcon(buttons[row]->iconForState(state, iconSize, palette));
204 QWidget::changeEvent(
event);
206 if (
event->type() == QEvent::PaletteChange ||
207 event->type() == QEvent::ApplicationPaletteChange ||
208 event->type() == QEvent::StyleChange) {
214 QWidget::showEvent(
event);
227 const std::vector<std::string>& titles,
228 bool vertical, QWidget* parent)
230 buttonPressedIndex(-1) {
231 QBoxLayout* layout = vertical
232 ?
static_cast<QBoxLayout*
>(
new QVBoxLayout(
this))
233 :
static_cast<QBoxLayout*
>(
new QHBoxLayout(
this));
236 QStringList buttonStrings;
237 for (
const auto& title : titles) {
238 buttonStrings.append(QString::fromStdString(title));
242 for (
int i = 0; i < buttonStrings.size(); ++i) {
243 QPushButton* button =
new QPushButton(buttonStrings[i],
this);
244 button->setCheckable(
true);
245 button->setFixedSize(buttonWidth, buttonHeight);
248 button->setStyleSheet(
249 "QPushButton { border: none; background-color: transparent; padding: 0; margin: 0; }"
250 "QPushButton:checked { background-color: transparent; }"
253 layout->addWidget(button);
254 buttons.push_back(button);
257 connect(button, &QPushButton::clicked,
this, [
this, i]() {
258 this->setButtonPressed(i);
260 connect(button, &QPushButton::toggled,
this, [
this]() {
269 if (buttonPressedIndex >= 0 && buttonPressedIndex < buttons.size())
270 return buttons[buttonPressedIndex]->isChecked();
276 if (index >= 0 && index < buttons.size())
277 buttons[index]->toggle();
282 if (index >= 0 && index < buttons.size())
283 return buttons[index]->isChecked();
287void GQTToggleButtonWidget::setButtonPressed(
int index) {
289 buttonPressedIndex = index;
295 for (
auto& b : buttons) {
296 b->setChecked(
false);
301 if (index < 0 || index >=
static_cast<int>(buttons.size())) {
return; }
304 while (svgIcons.size() <= index) { svgIcons.append(SvgIcon{}); }
306 const QSize sz = iconSize.isValid() ? iconSize
307 : QSize(buttons[index]->width() - 6, buttons[index]->height() - 6);
308 svgIcons[index] = SvgIcon{ svgResourcePath, sz };
312void GQTToggleButtonWidget::refresh_svg_icons() {
313 const QColor windowText = palette().color(QPalette::WindowText);
314 const QColor hlText = palette().color(QPalette::HighlightedText);
315 const QColor hlBg = palette().color(QPalette::Highlight);
317 for (
int i = 0; i < svgIcons.size(); ++i) {
318 const SvgIcon& entry = svgIcons[i];
319 if (entry.path.isEmpty() || i >=
static_cast<int>(buttons.size())) {
continue; }
322 if (!f.open(QIODevice::ReadOnly)) {
continue; }
324 const bool checked = buttons[i]->isChecked();
325 const QColor fg = checked ? hlText : windowText;
326 const QString bg = checked ? hlBg.name(QColor::HexRgb) :
"none";
329 svg.replace(
"width=\"48\" height=\"48\"",
330 QString(
"width=\"%1\" height=\"%2\"").arg(entry.size.width()).arg(entry.size.height()));
331 svg.replace(
"currentColor", fg.name(QColor::HexRgb));
332 svg.replace(
"#aaddff", bg, Qt::CaseInsensitive);
335 if (pix.loadFromData(svg.toUtf8(),
"SVG")) {
336 buttons[i]->setIcon(QIcon(pix));
337 buttons[i]->setIconSize(entry.size);
343 QWidget::changeEvent(
event);
344 if (
event->type() == QEvent::PaletteChange ||
345 event->type() == QEvent::ApplicationPaletteChange ||
346 event->type() == QEvent::StyleChange) {
352 QWidget::showEvent(
event);
QString translateSvgLabels(QString svg)
Translate SVG toolbar captions and fit their font size without changing stored artwork.