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",
"Physics"),
18 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Wire"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"Surface"),
19 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Cloud"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"Twinkle"),
20 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Hidden\nLines"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"Anti\nAliasing"),
21 QT_TRANSLATE_NOOP(
"GGuiIcons",
"Auxiliary\nEdges"), QT_TRANSLATE_NOOP(
"GGuiIcons",
"Field\nLines")
23 const QRegularExpression textTag(
"(<text\\b[^>]*>)([^<]*)(</text>)");
24 const QRegularExpression fontSize(
"font-size=\"([0-9.]+)\"");
25 const QRegularExpression fontFamily(
"font-family=\"([^\"]+)\"");
26 QList<QRegularExpressionMatch> matches;
28 auto iterator = textTag.globalMatch(svg);
29 while (iterator.hasNext()) {
30 const auto match = iterator.next();
31 const auto text = match.captured(2).trimmed();
32 if (text ==
">_" || text.isEmpty())
continue;
33 matches.append(match);
36 const auto source = lines.join(
'\n');
37 for (
const auto caption : captions) {
38 if (source != QString::fromUtf8(caption))
continue;
39 const auto translated = QCoreApplication::translate(
"GGuiIcons", caption).split(
'\n');
40 if (translated.size() != matches.size())
return svg;
41 for (
int index = matches.size() - 1; index >= 0; --index) {
42 const auto& match = matches[index];
43 auto opening = match.captured(1);
44 const auto size = fontSize.match(opening);
45 if (size.hasMatch()) {
46 const qreal pixels = size.captured(1).toDouble();
47 const auto family = fontFamily.match(opening);
48 QFont font(family.hasMatch() ? family.captured(1) : QString());
50 font.setPointSizeF(pixels);
51 const qreal width = QFontMetricsF(font).horizontalAdvance(translated[index]);
53 opening.replace(size.capturedStart(1), size.capturedLength(1),
54 QString::number(pixels * 32 / width,
'f', 2));
57 svg.replace(match.capturedStart(), match.capturedLength(),
58 opening + translated[index].toHtmlEscaped() + match.captured(3));
77 thisButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
82 const std::string filename = buttonName +
"_" + std::to_string(state) +
".svg";
84 QFile file(QString::fromStdString(filename));
85 if (!file.open(QIODevice::ReadOnly)) {
90 const QColor foreground = state == 2
91 ? palette.color(QPalette::HighlightedText)
92 : palette.color(QPalette::WindowText);
93 const QColor selectedBackground = palette.color(QPalette::Highlight);
95 svg.replace(
"width=\"48\" height=\"48\"",
96 QString(
"width=\"%1\" height=\"%2\"").arg(iconSize.width()).arg(iconSize.height()));
97 svg.replace(
"currentColor", foreground.name(QColor::HexRgb));
98 svg.replace(
"#aaddff", selectedBackground.name(QColor::HexRgb), Qt::CaseInsensitive);
101 if (!pixmap.loadFromData(svg.toUtf8(),
"SVG")) {
104 return QIcon(pixmap);
116 const std::vector<std::string>& bicons,
117 bool vertical, QWidget* parent)
119 constexpr int distanceToMargin = 12;
122 for (
const auto& b : bicons) {
129 buttonsWidget->setIconSize(QSize(
static_cast<int>(h),
static_cast<int>(v)));
133 "background-color: transparent; "
134 "QListWidget::item:selected { background: transparent; border: none; }");
137 for (
auto& b : buttons) {
144 this, &GQTButtonsWidget::buttonWasPressed);
147 QBoxLayout* layout = vertical
148 ?
static_cast<QBoxLayout*
>(
new QVBoxLayout(
this))
149 :
static_cast<QBoxLayout*
>(
new QHBoxLayout(
this));
150 layout->setContentsMargins(0, 0, 0, 0);
155 double hsize = (h + distanceToMargin) * (buttons.size());
156 double vsize = v + distanceToMargin;
158 hsize = h + distanceToMargin;
159 vsize = (v + distanceToMargin) * (buttons.size());
161 buttonsWidget->setFixedSize(
static_cast<int>(hsize),
static_cast<int>(vsize));
166 "QListWidget { background-color: transparent; }"
167 "QListWidget::item { background: transparent; border: none; }"
168 "QListWidget::item:selected { background: transparent; border: none; outline: none; }"
180void GQTButtonsWidget::buttonWasPressed(QListWidgetItem* item) {
191void GQTButtonsWidget::refresh_icons() {
199 const int state = row == currentRow ? 2 : 1;
200 buttonsWidget->item(row)->setIcon(buttons[row]->iconForState(state, iconSize, palette));
205 QWidget::changeEvent(
event);
207 if (
event->type() == QEvent::PaletteChange ||
208 event->type() == QEvent::ApplicationPaletteChange ||
209 event->type() == QEvent::StyleChange) {
215 QWidget::showEvent(
event);
228 const std::vector<std::string>& titles,
229 bool vertical, QWidget* parent)
231 buttonPressedIndex(-1) {
232 QBoxLayout* layout = vertical
233 ?
static_cast<QBoxLayout*
>(
new QVBoxLayout(
this))
234 :
static_cast<QBoxLayout*
>(
new QHBoxLayout(
this));
237 QStringList buttonStrings;
238 for (
const auto& title : titles) {
239 buttonStrings.append(QString::fromStdString(title));
243 for (
int i = 0; i < buttonStrings.size(); ++i) {
244 QPushButton* button =
new QPushButton(buttonStrings[i],
this);
245 button->setCheckable(
true);
246 button->setFixedSize(buttonWidth, buttonHeight);
249 button->setStyleSheet(
250 "QPushButton { border: none; background-color: transparent; padding: 0; margin: 0; }"
251 "QPushButton:checked { background-color: transparent; }"
254 layout->addWidget(button);
255 buttons.push_back(button);
258 connect(button, &QPushButton::clicked,
this, [
this, i]() {
259 this->setButtonPressed(i);
261 connect(button, &QPushButton::toggled,
this, [
this]() {
270 if (buttonPressedIndex >= 0 && buttonPressedIndex < buttons.size())
271 return buttons[buttonPressedIndex]->isChecked();
277 if (index >= 0 && index < buttons.size())
278 buttons[index]->toggle();
283 if (index >= 0 && index < buttons.size())
284 return buttons[index]->isChecked();
288void GQTToggleButtonWidget::setButtonPressed(
int index) {
290 buttonPressedIndex = index;
296 for (
auto& b : buttons) {
297 b->setChecked(
false);
302 if (index < 0 || index >=
static_cast<int>(buttons.size())) {
return; }
305 while (svgIcons.size() <= index) { svgIcons.append(SvgIcon{}); }
307 const QSize sz = iconSize.isValid() ? iconSize
308 : QSize(buttons[index]->width() - 6, buttons[index]->height() - 6);
309 svgIcons[index] = SvgIcon{ svgResourcePath, sz };
313void GQTToggleButtonWidget::refresh_svg_icons() {
314 const QColor windowText = palette().color(QPalette::WindowText);
315 const QColor hlText = palette().color(QPalette::HighlightedText);
316 const QColor hlBg = palette().color(QPalette::Highlight);
318 for (
int i = 0; i < svgIcons.size(); ++i) {
319 const SvgIcon& entry = svgIcons[i];
320 if (entry.path.isEmpty() || i >=
static_cast<int>(buttons.size())) {
continue; }
323 if (!f.open(QIODevice::ReadOnly)) {
continue; }
325 const bool checked = buttons[i]->isChecked();
326 const QColor fg = checked ? hlText : windowText;
327 const QString bg = checked ? hlBg.name(QColor::HexRgb) :
"none";
330 svg.replace(
"width=\"48\" height=\"48\"",
331 QString(
"width=\"%1\" height=\"%2\"").arg(entry.size.width()).arg(entry.size.height()));
332 svg.replace(
"currentColor", fg.name(QColor::HexRgb));
333 svg.replace(
"#aaddff", bg, Qt::CaseInsensitive);
336 if (pix.loadFromData(svg.toUtf8(),
"SVG")) {
337 buttons[i]->setIcon(QIcon(pix));
338 buttons[i]->setIconSize(entry.size);
344 QWidget::changeEvent(
event);
345 if (
event->type() == QEvent::PaletteChange ||
346 event->type() == QEvent::ApplicationPaletteChange ||
347 event->type() == QEvent::StyleChange) {
353 QWidget::showEvent(
event);
QString translateSvgLabels(QString svg)
Translate SVG toolbar captions and fit their font size without changing stored artwork.