wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
ICPreviewPopup.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
8#include <QFrame>
9#include <QGuiApplication>
10#include <QScreen>
11#include <QVBoxLayout>
12
13#include "App/Core/Settings.h"
14#include "App/Element/IC.h"
15
17 : QWidget(parent, Qt::ToolTip | Qt::FramelessWindowHint)
18{
19 setAttribute(Qt::WA_TranslucentBackground);
20 setAttribute(Qt::WA_NoSystemBackground); // Prevent solid black fill on Windows
21 setAttribute(Qt::WA_ShowWithoutActivating);
22
23 auto *layout = new QVBoxLayout(this);
24 layout->setContentsMargins(4, 4, 4, 4);
25
26 // Inset frame holds both the filename caption and the preview image, so the
27 // name reads against the same framed background as the rendered circuit
28 // rather than floating on the popup chrome outside the frame.
29 auto *frame = new QFrame(this);
30 frame->setObjectName(QStringLiteral("previewFrame"));
31 auto *frameLayout = new QVBoxLayout(frame);
32 frameLayout->setContentsMargins(6, 6, 6, 6);
33 frameLayout->setSpacing(4);
34
35 m_titleLabel = new QLabel(frame);
36 m_titleLabel->setObjectName(QStringLiteral("title"));
37 m_titleLabel->setAlignment(Qt::AlignCenter);
38 frameLayout->addWidget(m_titleLabel);
39
40 m_imageLabel = new QLabel(frame);
41 m_imageLabel->setObjectName(QStringLiteral("preview"));
42 m_imageLabel->setAlignment(Qt::AlignCenter);
43 frameLayout->addWidget(m_imageLabel);
44
45 layout->addWidget(frame);
46
47 // Outer popup chrome + an inset frame around the caption and pixmap so the
48 // content reads as a single framed card rather than floating content.
49 // Selectors are scoped by objectName so the inner labels don't inherit the
50 // frame's border/background.
51 setStyleSheet(
52 "ICPreviewPopup {"
53 " background-color: rgba(30, 30, 30, 230);"
54 " border: 1px solid rgba(120, 120, 120, 180);"
55 " border-radius: 6px;"
56 "}"
57 "QFrame#previewFrame {"
58 " border: 1px solid rgba(170, 170, 170, 200);"
59 " border-radius: 3px;"
60 " background-color: rgba(15, 15, 15, 200);"
61 "}"
62 "QLabel#title {"
63 " color: rgba(230, 230, 230, 235);"
64 " font-weight: bold;"
65 " padding: 2px 4px;"
66 "}"
67 );
68
69 m_hideTimer.setSingleShot(true);
70 m_hideTimer.setInterval(300);
71 connect(&m_hideTimer, &QTimer::timeout, this, &QWidget::hide);
72
73 m_showTimer.setSingleShot(true);
74 m_showTimer.setInterval(1000);
75 connect(&m_showTimer, &QTimer::timeout, this, &ICPreviewPopup::executeShow);
76}
77
78void ICPreviewPopup::showForIC(IC *ic, const QPoint &screenPos)
79{
81 return;
82 }
83
84 cancelHide();
85
86 if (!ic) {
87 return;
88 }
89
90 m_pendingIC = ic;
91 m_pendingPos = screenPos;
92
93 if (isVisible()) {
94 // If the popup is already visible (e.g., moved quickly from another IC),
95 // update it immediately without a delay.
96 executeShow();
97 } else {
98 m_showTimer.start();
99 }
100}
101
102void ICPreviewPopup::executeShow()
103{
104 // Resolve the guarded pointer once: QPointer::operator-> re-checks the target
105 // on every access, so repeated m_pendingIC-> uses read as a possible null
106 // dereference under optimization (-Werror=null-dereference). A single raw
107 // pointer after the guard is provably non-null for the rest of the function.
108 IC *ic = m_pendingIC;
109 if (!ic) {
110 return;
111 }
112
113 // The popup carries the IC's name (formerly a separate Qt tooltip) plus the
114 // rendered preview. The pixmap is null when the IC was empty or oversized at
115 // load time, or when generation failed — in that case we still show the name.
116 const QString title = ic->displayName();
117 const QPixmap &preview = ic->previewPixmap();
118
119 if (title.isEmpty() && preview.isNull()) {
120 hide();
121 return;
122 }
123
124 m_titleLabel->setText(title);
125 m_titleLabel->setVisible(!title.isEmpty());
126
127 if (preview.isNull()) {
128 m_imageLabel->clear();
129 m_imageLabel->hide();
130 } else {
131 m_imageLabel->setPixmap(preview);
132 m_imageLabel->show();
133 }
134
135 adjustSize();
136
137 // Position slightly offset from the cursor, then clamp to the available screen
138 // geometry so the popup never extends off-screen. Resolve the screen from the cursor
139 // itself — the offset position can fall past the edge near a corner, making screenAt()
140 // return null and (previously) skipping the clamp entirely.
141 QPoint pos = m_pendingPos + QPoint(16, 16);
142 if (const auto *screen = QGuiApplication::screenAt(m_pendingPos)) {
143 pos = clampedPopupPos(m_pendingPos, size(), screen->availableGeometry());
144 }
145 move(pos);
146 show();
147}
148
149QPoint ICPreviewPopup::clampedPopupPos(const QPoint &cursorPos, const QSize &popupSize, const QRect &availableGeometry)
150{
151 QPoint pos = cursorPos + QPoint(16, 16);
152 // Clamp the far edges first, then the near edges, so a popup larger than the available
153 // area still anchors to top-left rather than landing off-screen.
154 pos.setX((std::min)(pos.x(), availableGeometry.right() - popupSize.width()));
155 pos.setY((std::min)(pos.y(), availableGeometry.bottom() - popupSize.height()));
156 pos.setX((std::max)(pos.x(), availableGeometry.left()));
157 pos.setY((std::max)(pos.y(), availableGeometry.top()));
158 return pos;
159}
160
162{
163 m_showTimer.stop();
164 m_hideTimer.start();
165}
166
168{
169 m_hideTimer.stop();
170 m_showTimer.stop();
171}
172
173void ICPreviewPopup::updatePendingPos(const QPoint &screenPos)
174{
175 if (!isVisible()) {
176 m_pendingPos = screenPos;
177 }
178}
179
181{
182 return m_pendingIC;
183}
184
186{
187 return m_pendingIC == ic && (isVisible() || m_showTimer.isActive());
188}
189
190void ICPreviewPopup::enterEvent(QEnterEvent *event)
191{
192 Q_UNUSED(event)
193 cancelHide();
194}
195
196void ICPreviewPopup::leaveEvent(QEvent *event)
197{
198 Q_UNUSED(event)
199 scheduleHide();
200}
Frameless popup widget that shows a preview of an IC's internal circuit.
Integrated Circuit (IC) graphic element that encapsulates a sub-circuit file.
Typed wrappers around QSettings for all application preferences.
void leaveEvent(QEvent *event) override
IC * pendingIC() const
Returns the IC currently pending display (may be null).
ICPreviewPopup(QWidget *parent=nullptr)
Constructs the popup as a frameless, non-activating child of parent.
bool isShowActiveFor(const IC *ic) const
static QPoint clampedPopupPos(const QPoint &cursorPos, const QSize &popupSize, const QRect &availableGeometry)
void cancelHide()
Cancels a pending show/hide.
void showForIC(IC *ic, const QPoint &screenPos)
void updatePendingPos(const QPoint &screenPos)
void enterEvent(QEnterEvent *event) override
Graphic element representing an Integrated Circuit (sub-circuit) box.
Definition IC.h:31
QString displayName() const
Definition IC.cpp:274
const QPixmap & previewPixmap() const
Definition IC.h:91
static bool icPreviewDisabled()
Definition Settings.cpp:120