View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0017870 | Scribus | Tables | public | 2026-07-03 12:51 | 2026-09-08 13:40 |
| Reporter | buttar | Assigned To | |||
| Priority | normal | Severity | minor | Reproducibility | always |
| Status | new | Resolution | open | ||
| Platform | Linux | OS | Fedora Workstation | OS Version | 44 |
| Product Version | 1.7.4.svn | ||||
| Summary | 0017870: Cannot draw tables quickly (for initial size, when creating) | ||||
| Description | Slow is fine. See attached clip. | ||||
| Steps To Reproduce | Move the mouse a bit fast when drawing a table | ||||
| Additional Information | (Works fine in 1.7.2 flatpak.) | ||||
| Tags | #cant reproduce, #please_test | ||||
| Attached Files | |||||
| Patch | No | ||||
|
|
Works fine here on macOS |
|
|
I just found out that this issue is not table specific. Any frame I draw with a sudden/quick mouse movement has this issue: text- and image frames, shape, polygon, spiral. |
|
|
It's probably related to your mouse... Works for me too |
|
|
I don't think it is related to my mouse because I tested: 1. with my other (wired) mouse in 1.7.4.svn and things do not work (as described originally). 2. with both mice in the 1.7.2 flatpak and things work just fine. |
|
|
Cannot reproduce it on Linux Mint (LM) 22.2. Might be related to some other packages Fedora uses while those on LM can be a slightly old. |
|
|
I can't reproduce on Linux Mint 22.3 Works for me too |
|
|
IT turned out to be a real issue and the details below would make better sense to you. #Issue -- With Move/Resize Value Indicator enabled, rapid canvas drags can keep receiving mouse-move events while the active canvas mode rejects them before geometry update, freezing the live interaction while the pointer continues moving. -- Image Import used the same tooltip-based mouse-following mechanism. #Cause -- Live canvas feedback repeatedly called `QToolTip::showText()` from mouse-move paths. -- This could interfere with the button state seen by canvas modes. -- `QToolTip` is appropriate for transient hover help, but using it as a continuously updated drag-time HUD was effectively a misuse of the widget for this interaction pattern. #Fix -- Replaces drag-time canvas tooltips with a shared canvas-owned, mouse-transparent HUD overlay. -- Routes existing `Canvas::display*HUD()` readouts through it. -- Migrates Image Import preview to the same HUD and renders its thumbnail directly instead of repeatedly rebuilding tooltip HTML. -- Leaves genuine hover tooltips unchanged. #HUD behaviour -- Stays visible for the full active interaction. -- Uses a 150 ms post-release linger; movement beyond 5 px hides it immediately. -- Flips/clamps against the visible viewport with a 5 px safety margin. -- Uses tooltip-derived palette/font with lightweight badge styling. -- Image Import uses explicit mode lifecycle instead of the measurement linger timer. #Result -- Fixes the fast-drag freeze without special-casing `CreateMode`. -- Replaces the tooltip misuse with an interaction-appropriate shared canvas HUD. -- Centralizes live canvas feedback in one reusable path. -- Tested with frame creation, move/resize, rotation, guides, tables, lines, and Image Import. Patch included (canvas-hud-drag-fix-v1.05.patch). (Change the title of the ticket also? Maybe "Canvas drag updates freeze with Move/Resize Value Indicator enabled") canvas-hud-drag-fix-v1.05.patch (17,143 bytes)
Index: scribus/canvas.h
===================================================================
--- scribus/canvas.h (revision 27824)
+++ scribus/canvas.h (working copy)
@@ -39,6 +39,9 @@
#include "pageitempointer.h"
+class QLabel;
+class QTimer;
+
class ScPage;
class PageItem;
class ScLayer;
@@ -208,11 +211,16 @@
void displayRotHUD(QPointF m, double rot);
void displayRealRotHUD(QPointF m, double rot);
/**
- * Displays a tooltip of the format <code>{label}: {N} {unit}</code> where <code>N</code> is
+ * Displays a HUD of the format <code>{label}: {N} {unit}</code> where <code>N</code> is
* @a value converted to the current document unit and <code>unit</code> the current document
- * unit. The tooltip will be displayed nearby the global point @a point.
+ * unit. The HUD will be displayed nearby the global point @a point.
*/
void displayDoubleHUD(QPointF point, const QString& label, double value);
+ void displayHUD(QPointF globalPos, const QString& text, bool autoHide = true);
+ void displayHUD(QPointF globalPos, const QPixmap& pixmap, bool autoHide = true);
+ void beginHUDLinger(QPointF globalPos);
+ void handleHUDMouseMove(QPointF globalPos);
+ void hideHUD();
void setupEditHRuler(PageItem * item, bool forceAndReset = false);
@@ -263,6 +271,9 @@
QPixmap createPixmap(double w, double h);
// draw a potentially hidpi pixmap
void drawPixmap(QPainter &painter, double x, double y, const QPixmap &pixmap, double sx, double sy, double sw, double sh);
+ void positionHUD(QPointF globalPos, bool autoHide);
+ void updateHUDStyle();
+ void changeEvent(QEvent* event) override;
private:
ScribusDoc* m_doc;
@@ -275,6 +286,11 @@
QPixmap m_selectionBuffer;
QRect m_selectionRect;
QPoint m_oldMinCanvasCoordinate;
+ QLabel* m_hudLabel { nullptr };
+ QTimer* m_hudHideTimer { nullptr };
+ bool m_hudAutoHide { false };
+ bool m_hudLingering { false };
+ QPointF m_hudLingerOrigin;
};
Index: scribus/canvas.cpp
===================================================================
--- scribus/canvas.cpp (revision 27824)
+++ scribus/canvas.cpp (working copy)
@@ -19,6 +19,11 @@
#include <cmath>
// #include <QDebug>
+#include <QEvent>
+#include <QLabel>
+#include <QPainter>
+#include <QPalette>
+#include <QTimer>
#include <QToolTip>
#include <QWidget>
@@ -47,6 +52,28 @@
return p;
}
+constexpr int HUD_HIDE_DELAY_MS = 150;
+constexpr double HUD_MOVE_GRACE_PX = 5.0;
+
+class CanvasHUDLabel final : public QLabel
+{
+public:
+ using QLabel::QLabel;
+
+protected:
+ void paintEvent(QPaintEvent* event) override
+ {
+ {
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ painter.setPen(palette().color(QPalette::Mid));
+ painter.setBrush(palette().brush(QPalette::Window));
+ painter.drawRoundedRect(QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5), 3.0, 3.0);
+ }
+ QLabel::paintEvent(event);
+ }
+};
+
QDataStream &operator<< ( QDataStream & ds, const CanvasViewMode & vm )
{
ds << vm.scale
@@ -92,6 +119,17 @@
m_buffer = QPixmap();
m_bufferRect = QRect();
m_renderMode = RENDER_NORMAL;
+
+ m_hudLabel = new CanvasHUDLabel(this);
+ m_hudLabel->setAttribute(Qt::WA_TransparentForMouseEvents, true);
+ m_hudLabel->setFocusPolicy(Qt::NoFocus);
+ m_hudLabel->setContentsMargins(6, 4, 6, 4);
+ updateHUDStyle();
+ m_hudLabel->hide();
+
+ m_hudHideTimer = new QTimer(this);
+ m_hudHideTimer->setSingleShot(true);
+ connect(m_hudHideTimer, &QTimer::timeout, this, &Canvas::hideHUD);
}
void Canvas::setPreviewVisual(int mode)
@@ -2240,10 +2278,111 @@
painter.drawPixmap(x, y, pixmap, sx, sy, sw, sh);
}
+void Canvas::displayHUD(QPointF globalPos, const QString& text, bool autoHide)
+{
+ m_hudLabel->setPixmap(QPixmap());
+ m_hudLabel->setText(text);
+ positionHUD(globalPos, autoHide);
+}
+
+void Canvas::displayHUD(QPointF globalPos, const QPixmap& pixmap, bool autoHide)
+{
+ m_hudLabel->clear();
+ m_hudLabel->setPixmap(pixmap);
+ positionHUD(globalPos, autoHide);
+}
+
+void Canvas::beginHUDLinger(QPointF globalPos)
+{
+ if (!m_hudAutoHide || !m_hudLabel->isVisible())
+ return;
+
+ m_hudLingering = true;
+ m_hudLingerOrigin = globalPos;
+ m_hudHideTimer->start(HUD_HIDE_DELAY_MS);
+}
+
+void Canvas::handleHUDMouseMove(QPointF globalPos)
+{
+ if (!m_hudLingering || !m_hudLabel->isVisible())
+ return;
+
+ const QPointF delta = globalPos - m_hudLingerOrigin;
+ if (delta.x() * delta.x() + delta.y() * delta.y() > HUD_MOVE_GRACE_PX * HUD_MOVE_GRACE_PX)
+ hideHUD();
+}
+
+void Canvas::hideHUD()
+{
+ m_hudHideTimer->stop();
+ m_hudLingering = false;
+ m_hudAutoHide = false;
+ m_hudLabel->hide();
+}
+
+void Canvas::positionHUD(QPointF globalPos, bool autoHide)
+{
+ constexpr int hudOffsetX = 12;
+ constexpr int hudOffsetY = 16;
+ constexpr int hudViewportPadding = 5;
+
+ m_hudLabel->adjustSize();
+
+ QWidget* viewport = m_view->viewport();
+ const QRect visibleRect(viewport->mapToGlobal(QPoint(0, 0)), viewport->size());
+ const QRect hudBounds = visibleRect.adjusted(hudViewportPadding, hudViewportPadding, -hudViewportPadding, -hudViewportPadding);
+ const QPoint anchor = globalPos.toPoint();
+ QPoint hudGlobalPos = anchor + QPoint(hudOffsetX, hudOffsetY);
+ if (hudGlobalPos.x() + m_hudLabel->width() > hudBounds.right() + 1)
+ hudGlobalPos.setX(anchor.x() - m_hudLabel->width() - hudOffsetX);
+ if (hudGlobalPos.y() + m_hudLabel->height() > hudBounds.bottom() + 1)
+ hudGlobalPos.setY(anchor.y() - m_hudLabel->height() - hudOffsetY);
+
+ const int maxX = qMax(hudBounds.left(), hudBounds.right() - m_hudLabel->width() + 1);
+ const int maxY = qMax(hudBounds.top(), hudBounds.bottom() - m_hudLabel->height() + 1);
+ hudGlobalPos.setX(qBound(hudBounds.left(), hudGlobalPos.x(), maxX));
+ hudGlobalPos.setY(qBound(hudBounds.top(), hudGlobalPos.y(), maxY));
+ m_hudLabel->move(mapFromGlobal(hudGlobalPos));
+ if (!m_hudLabel->isVisible())
+ {
+ m_hudLabel->show();
+ m_hudLabel->raise();
+ }
+
+ m_hudAutoHide = autoHide;
+ m_hudLingering = false;
+ if (autoHide && !m_viewMode.m_MouseButtonPressed)
+ m_hudHideTimer->start(HUD_HIDE_DELAY_MS);
+ else
+ m_hudHideTimer->stop();
+}
+
+void Canvas::updateHUDStyle()
+{
+ if (!m_hudLabel)
+ return;
+
+ QPalette hudPalette = QToolTip::palette();
+ hudPalette.setBrush(QPalette::Window, hudPalette.brush(QPalette::ToolTipBase));
+ hudPalette.setBrush(QPalette::WindowText, hudPalette.brush(QPalette::ToolTipText));
+ m_hudLabel->setPalette(hudPalette);
+ m_hudLabel->setFont(QToolTip::font());
+}
+
+void Canvas::changeEvent(QEvent* event)
+{
+ QWidget::changeEvent(event);
+ if (event->type() == QEvent::PaletteChange || event->type() == QEvent::FontChange || event->type() == QEvent::StyleChange)
+ updateHUDStyle();
+}
+
void Canvas::displayXYHUD(QPointF m)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double gx, gy, gh, gw, r;
if (m_doc->m_Selection->isMultipleSelection())
{
@@ -2288,7 +2427,10 @@
void Canvas::displayCorrectedXYHUD(QPointF m, double x, double y)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double gx = x;
double gy = y;
if (m_doc->guidesPrefs().rulerMode)
@@ -2299,81 +2441,91 @@
gx -= m_doc->rulerXoffset;
gy -= m_doc->rulerYoffset;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("X: %1\nY: %2").arg(value2String(gx, m_doc->unitIndex(), true, true), value2String(gy, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("X: %1\nY: %2").arg(value2String(gx, m_doc->unitIndex(), true, true), value2String(gy, m_doc->unitIndex(), true, true)));
}
void Canvas::displayCorrectedSingleHUD(QPointF m, double val, bool isX)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double gx = val;
if (isX)
{
if (m_doc->guidesPrefs().rulerMode)
gx -= m_doc->currentPage()->xOffset();
gx -= m_doc->rulerXoffset;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("X: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("X: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)));
}
else
{
if (m_doc->guidesPrefs().rulerMode)
gx -= m_doc->currentPage()->yOffset();
gx -= m_doc->rulerYoffset;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("Y: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("Y: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)));
}
}
void Canvas::displayXYHUD(QPointF m, double x, double y)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("X: %1\nY: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("X: %1\nY: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)));
}
void Canvas::displaySizeHUD(QPointF m, double x, double y, bool isLine)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
if (isLine)
- QToolTip::showText(tooltipPos.toPoint(), tr("Length: %1\nAngle: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, SC_DEGREES, true, true)), this);
+ displayHUD(m, tr("Length: %1\nAngle: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, SC_DEGREES, true, true)));
else
- QToolTip::showText(tooltipPos.toPoint(), tr("Width: %1\nHeight: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("Width: %1\nHeight: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)));
}
void Canvas::displayRotHUD(QPointF m, double rot)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double r;
if (rot < 0.0)
r = rot * -1.0;
else
r = 360.0 - rot;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("Angle: %1").arg(value2String(r, SC_DEGREES, true, true)), this);
+ displayHUD(m, tr("Angle: %1").arg(value2String(r, SC_DEGREES, true, true)));
}
void Canvas::displayRealRotHUD(QPointF m, double rot)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("Angle: %1").arg(value2String(rot, SC_DEGREES, true, true)), this);
+ }
+ displayHUD(m, tr("Angle: %1").arg(value2String(rot, SC_DEGREES, true, true)));
}
void Canvas::displayDoubleHUD(QPointF point, const QString& label, double value)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
- QPointF tooltipPos = point + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), QString("%1: %2").arg(label, value2String(value, m_doc->unitIndex(), true, true)), this);
+ }
+ displayHUD(point, QString("%1: %2").arg(label, value2String(value, m_doc->unitIndex(), true, true)));
}
void Canvas::setupEditHRuler(PageItem * item, bool forceAndReset)
Index: scribus/canvasmode_imageimport.h
===================================================================
--- scribus/canvasmode_imageimport.h (revision 27824)
+++ scribus/canvasmode_imageimport.h (working copy)
@@ -55,12 +55,14 @@
private:
bool m_keyRepeat {false};
- QString m_tipText;
+ QPixmap m_previewPixmap;
+ QString m_previewText;
QStringList m_imageList;
ScribusMainWindow *m_ScMW {nullptr};
double m_Mx {0.0};
double m_My {0.0};
- void newToolTip(const QString& name);
+ void displayPreview(QPointF globalPos);
+ void updatePreview(const QString& name);
};
#endif // CANVASMODE_IMAGEIMPORT_H
Index: scribus/canvasmode_imageimport.cpp
===================================================================
--- scribus/canvasmode_imageimport.cpp (revision 27824)
+++ scribus/canvasmode_imageimport.cpp (working copy)
@@ -16,7 +16,6 @@
#include <QApplication>
-#include <QBuffer>
#include <QCursor>
#include <QEvent>
#include <QMouseEvent>
@@ -24,7 +23,6 @@
#include <QPixmap>
#include <QPoint>
#include <QRect>
-#include <QToolTip>
#include <QDebug>
#include "canvas.h"
@@ -38,6 +36,9 @@
CanvasMode_ImageImport::CanvasMode_ImageImport(ScribusView *view) : CanvasMode(view), m_ScMW(view->m_ScMW)
{
+ const QPoint cursorPos = QCursor::pos();
+ m_Mx = cursorPos.x();
+ m_My = cursorPos.y();
}
void CanvasMode_ImageImport::setImageList(QStringList l)
@@ -53,36 +54,48 @@
m_view->requestMode(submodePaintingDone);
}
else
- newToolTip(l.first());
+ updatePreview(l.first());
}
else
- newToolTip(l.first());
+ updatePreview(l.first());
}
-void CanvasMode_ImageImport::newToolTip(const QString& name)
+void CanvasMode_ImageImport::displayPreview(QPointF globalPos)
{
- QImage pm(80, 80, QImage::Format_ARGB32_Premultiplied);
- QBrush b(QColor(205,205,205), IconManager::instance().loadPixmap("testfill"));
-
- QPainter p;
- p.begin(&pm);
- p.fillRect(QRectF(0, 0, 80, 80), b);
+ if (!m_previewPixmap.isNull())
+ m_canvas->displayHUD(globalPos, m_previewPixmap, false);
+ else
+ m_canvas->displayHUD(globalPos, m_previewText, false);
+}
+
+void CanvasMode_ImageImport::updatePreview(const QString& name)
+{
+ QPixmap preview(80, 80);
+ QBrush background(QColor(205, 205, 205), IconManager::instance().loadPixmap("testfill"));
QImage thumb;
- if (thumb.load(name))
+ const bool hasThumbnail = thumb.load(name);
+ {
+ QPainter painter(&preview);
+ painter.fillRect(QRectF(0, 0, 80, 80), background);
+ if (hasThumbnail)
+ {
+ thumb = thumb.scaled(80, 80, Qt::KeepAspectRatio);
+ painter.drawImage((80 - thumb.width()) / 2, (80 - thumb.height()) / 2, thumb);
+ }
+ }
+
+ if (hasThumbnail)
{
- thumb = thumb.scaled(80,80,Qt::KeepAspectRatio);
- p.drawImage((80 - thumb.width()) / 2, (80 - thumb.height()) / 2, thumb);
- QBuffer buffer;
- buffer.open(QIODevice::WriteOnly);
- pm.save(&buffer, "PNG");
- QByteArray ba = buffer.buffer().toBase64();
- buffer.close();
- m_tipText = "<p align=\"center\"><img src=\"data:image/png;base64," + QString(ba) + "\"></p>";
+ m_previewPixmap = preview;
+ m_previewText.clear();
}
else
- m_tipText = "<p align=\"center\">" + name.right(name.lastIndexOf("/")) + "</p>";
- QToolTip::showText(QPoint(m_Mx,m_My), m_tipText, qApp->activeWindow());
- p.end();
+ {
+ m_previewPixmap = QPixmap();
+ m_previewText = name.right(name.lastIndexOf("/"));
+ }
+
+ displayPreview(QPointF(m_Mx, m_My));
}
void CanvasMode_ImageImport::drawControls(QPainter* p)
@@ -125,6 +138,7 @@
void CanvasMode_ImageImport::leaveEvent(QEvent *e)
{
+ m_canvas->hideHUD();
}
void CanvasMode_ImageImport::activate(bool fromGesture)
@@ -146,6 +160,7 @@
void CanvasMode_ImageImport::deactivate(bool forGesture)
{
+ m_canvas->hideHUD();
m_view->setRedrawMarkerShown(false);
CanvasMode::deactivate(forGesture);
}
@@ -161,8 +176,7 @@
{
m_Mx = m->globalPosition().x();
m_My = m->globalPosition().y();
- QToolTip::showText(m->globalPosition().toPoint(), m_tipText, qApp->activeWindow());
- QToolTip::showText(m->globalPosition().toPoint(), m_tipText + "<b></b>", qApp->activeWindow());
+ displayPreview(m->globalPosition());
m->accept();
PageItem *item;
if ((item = m_canvas->itemUnderCursor(m->globalPosition())) != nullptr)
@@ -249,8 +263,8 @@
if (m_imageList.isEmpty())
{
m_view->requestMode(submodePaintingDone);
- QToolTip::hideText();
+ m_canvas->hideHUD();
}
else
- newToolTip(m_imageList.constFirst());
+ updatePreview(m_imageList.constFirst());
}
Index: scribus/scribusview.cpp
===================================================================
--- scribus/scribusview.cpp (revision 27824)
+++ scribus/scribusview.cpp (working copy)
@@ -3369,6 +3369,7 @@
emit MousePos(p.x(),p.y());
horizRuler->draw(m->position().x() + qRound(m_doc->minCanvasCoordinate.x() * m_canvas->scale()));
vertRuler->draw(m->position().y() + qRound(m_doc->minCanvasCoordinate.y() * m_canvas->scale()));
+ m_canvas->handleHUDMouseMove(m->globalPosition());
m_canvasMode->mouseMoveEvent(m);
return true;
}
@@ -3377,6 +3378,7 @@
auto* m = dynamic_cast<QMouseEvent*> (event);
m_canvasMode->mouseReleaseEvent(m);
m_canvas->m_viewMode.m_MouseButtonPressed = false;
+ m_canvas->beginHUDLinger(m->globalPosition());
if (linkAfterDraw)
{
//user creates new frame using linking tool
|
|
|
Even just turning off the `View > Measurement > Move/Resize Value` Indicator solved it instantly. |
|
|
Here are two clips showing old and new HUD. |
|
|
A perceivable border, if you will. canvas-hud-drag-fix-v1.06.patch (17,222 bytes)
Index: scribus/canvas.h
===================================================================
--- scribus/canvas.h (revision 27824)
+++ scribus/canvas.h (working copy)
@@ -39,6 +39,9 @@
#include "pageitempointer.h"
+class QLabel;
+class QTimer;
+
class ScPage;
class PageItem;
class ScLayer;
@@ -208,11 +211,16 @@
void displayRotHUD(QPointF m, double rot);
void displayRealRotHUD(QPointF m, double rot);
/**
- * Displays a tooltip of the format <code>{label}: {N} {unit}</code> where <code>N</code> is
+ * Displays a HUD of the format <code>{label}: {N} {unit}</code> where <code>N</code> is
* @a value converted to the current document unit and <code>unit</code> the current document
- * unit. The tooltip will be displayed nearby the global point @a point.
+ * unit. The HUD will be displayed nearby the global point @a point.
*/
void displayDoubleHUD(QPointF point, const QString& label, double value);
+ void displayHUD(QPointF globalPos, const QString& text, bool autoHide = true);
+ void displayHUD(QPointF globalPos, const QPixmap& pixmap, bool autoHide = true);
+ void beginHUDLinger(QPointF globalPos);
+ void handleHUDMouseMove(QPointF globalPos);
+ void hideHUD();
void setupEditHRuler(PageItem * item, bool forceAndReset = false);
@@ -263,6 +271,9 @@
QPixmap createPixmap(double w, double h);
// draw a potentially hidpi pixmap
void drawPixmap(QPainter &painter, double x, double y, const QPixmap &pixmap, double sx, double sy, double sw, double sh);
+ void positionHUD(QPointF globalPos, bool autoHide);
+ void updateHUDStyle();
+ void changeEvent(QEvent* event) override;
private:
ScribusDoc* m_doc;
@@ -275,6 +286,11 @@
QPixmap m_selectionBuffer;
QRect m_selectionRect;
QPoint m_oldMinCanvasCoordinate;
+ QLabel* m_hudLabel { nullptr };
+ QTimer* m_hudHideTimer { nullptr };
+ bool m_hudAutoHide { false };
+ bool m_hudLingering { false };
+ QPointF m_hudLingerOrigin;
};
Index: scribus/canvas.cpp
===================================================================
--- scribus/canvas.cpp (revision 27824)
+++ scribus/canvas.cpp (working copy)
@@ -19,6 +19,11 @@
#include <cmath>
// #include <QDebug>
+#include <QEvent>
+#include <QLabel>
+#include <QPainter>
+#include <QPalette>
+#include <QTimer>
#include <QToolTip>
#include <QWidget>
@@ -47,6 +52,30 @@
return p;
}
+constexpr int HUD_HIDE_DELAY_MS = 150;
+constexpr double HUD_MOVE_GRACE_PX = 5.0;
+
+class CanvasHUDLabel final : public QLabel
+{
+public:
+ using QLabel::QLabel;
+
+protected:
+ void paintEvent(QPaintEvent* event) override
+ {
+ {
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ QColor borderColor = palette().color(QPalette::WindowText);
+ borderColor.setAlphaF(0.4);
+ painter.setPen(borderColor);
+ painter.setBrush(palette().brush(QPalette::Window));
+ painter.drawRoundedRect(QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5), 3.0, 3.0);
+ }
+ QLabel::paintEvent(event);
+ }
+};
+
QDataStream &operator<< ( QDataStream & ds, const CanvasViewMode & vm )
{
ds << vm.scale
@@ -92,6 +121,17 @@
m_buffer = QPixmap();
m_bufferRect = QRect();
m_renderMode = RENDER_NORMAL;
+
+ m_hudLabel = new CanvasHUDLabel(this);
+ m_hudLabel->setAttribute(Qt::WA_TransparentForMouseEvents, true);
+ m_hudLabel->setFocusPolicy(Qt::NoFocus);
+ m_hudLabel->setContentsMargins(6, 4, 6, 4);
+ updateHUDStyle();
+ m_hudLabel->hide();
+
+ m_hudHideTimer = new QTimer(this);
+ m_hudHideTimer->setSingleShot(true);
+ connect(m_hudHideTimer, &QTimer::timeout, this, &Canvas::hideHUD);
}
void Canvas::setPreviewVisual(int mode)
@@ -2240,10 +2280,111 @@
painter.drawPixmap(x, y, pixmap, sx, sy, sw, sh);
}
+void Canvas::displayHUD(QPointF globalPos, const QString& text, bool autoHide)
+{
+ m_hudLabel->setPixmap(QPixmap());
+ m_hudLabel->setText(text);
+ positionHUD(globalPos, autoHide);
+}
+
+void Canvas::displayHUD(QPointF globalPos, const QPixmap& pixmap, bool autoHide)
+{
+ m_hudLabel->clear();
+ m_hudLabel->setPixmap(pixmap);
+ positionHUD(globalPos, autoHide);
+}
+
+void Canvas::beginHUDLinger(QPointF globalPos)
+{
+ if (!m_hudAutoHide || !m_hudLabel->isVisible())
+ return;
+
+ m_hudLingering = true;
+ m_hudLingerOrigin = globalPos;
+ m_hudHideTimer->start(HUD_HIDE_DELAY_MS);
+}
+
+void Canvas::handleHUDMouseMove(QPointF globalPos)
+{
+ if (!m_hudLingering || !m_hudLabel->isVisible())
+ return;
+
+ const QPointF delta = globalPos - m_hudLingerOrigin;
+ if (delta.x() * delta.x() + delta.y() * delta.y() > HUD_MOVE_GRACE_PX * HUD_MOVE_GRACE_PX)
+ hideHUD();
+}
+
+void Canvas::hideHUD()
+{
+ m_hudHideTimer->stop();
+ m_hudLingering = false;
+ m_hudAutoHide = false;
+ m_hudLabel->hide();
+}
+
+void Canvas::positionHUD(QPointF globalPos, bool autoHide)
+{
+ constexpr int hudOffsetX = 12;
+ constexpr int hudOffsetY = 16;
+ constexpr int hudViewportPadding = 5;
+
+ m_hudLabel->adjustSize();
+
+ QWidget* viewport = m_view->viewport();
+ const QRect visibleRect(viewport->mapToGlobal(QPoint(0, 0)), viewport->size());
+ const QRect hudBounds = visibleRect.adjusted(hudViewportPadding, hudViewportPadding, -hudViewportPadding, -hudViewportPadding);
+ const QPoint anchor = globalPos.toPoint();
+ QPoint hudGlobalPos = anchor + QPoint(hudOffsetX, hudOffsetY);
+ if (hudGlobalPos.x() + m_hudLabel->width() > hudBounds.right() + 1)
+ hudGlobalPos.setX(anchor.x() - m_hudLabel->width() - hudOffsetX);
+ if (hudGlobalPos.y() + m_hudLabel->height() > hudBounds.bottom() + 1)
+ hudGlobalPos.setY(anchor.y() - m_hudLabel->height() - hudOffsetY);
+
+ const int maxX = qMax(hudBounds.left(), hudBounds.right() - m_hudLabel->width() + 1);
+ const int maxY = qMax(hudBounds.top(), hudBounds.bottom() - m_hudLabel->height() + 1);
+ hudGlobalPos.setX(qBound(hudBounds.left(), hudGlobalPos.x(), maxX));
+ hudGlobalPos.setY(qBound(hudBounds.top(), hudGlobalPos.y(), maxY));
+ m_hudLabel->move(mapFromGlobal(hudGlobalPos));
+ if (!m_hudLabel->isVisible())
+ {
+ m_hudLabel->show();
+ m_hudLabel->raise();
+ }
+
+ m_hudAutoHide = autoHide;
+ m_hudLingering = false;
+ if (autoHide && !m_viewMode.m_MouseButtonPressed)
+ m_hudHideTimer->start(HUD_HIDE_DELAY_MS);
+ else
+ m_hudHideTimer->stop();
+}
+
+void Canvas::updateHUDStyle()
+{
+ if (!m_hudLabel)
+ return;
+
+ QPalette hudPalette = QToolTip::palette();
+ hudPalette.setBrush(QPalette::Window, hudPalette.brush(QPalette::ToolTipBase));
+ hudPalette.setBrush(QPalette::WindowText, hudPalette.brush(QPalette::ToolTipText));
+ m_hudLabel->setPalette(hudPalette);
+ m_hudLabel->setFont(QToolTip::font());
+}
+
+void Canvas::changeEvent(QEvent* event)
+{
+ QWidget::changeEvent(event);
+ if (event->type() == QEvent::PaletteChange || event->type() == QEvent::FontChange || event->type() == QEvent::StyleChange)
+ updateHUDStyle();
+}
+
void Canvas::displayXYHUD(QPointF m)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double gx, gy, gh, gw, r;
if (m_doc->m_Selection->isMultipleSelection())
{
@@ -2288,7 +2429,10 @@
void Canvas::displayCorrectedXYHUD(QPointF m, double x, double y)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double gx = x;
double gy = y;
if (m_doc->guidesPrefs().rulerMode)
@@ -2299,81 +2443,91 @@
gx -= m_doc->rulerXoffset;
gy -= m_doc->rulerYoffset;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("X: %1\nY: %2").arg(value2String(gx, m_doc->unitIndex(), true, true), value2String(gy, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("X: %1\nY: %2").arg(value2String(gx, m_doc->unitIndex(), true, true), value2String(gy, m_doc->unitIndex(), true, true)));
}
void Canvas::displayCorrectedSingleHUD(QPointF m, double val, bool isX)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double gx = val;
if (isX)
{
if (m_doc->guidesPrefs().rulerMode)
gx -= m_doc->currentPage()->xOffset();
gx -= m_doc->rulerXoffset;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("X: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("X: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)));
}
else
{
if (m_doc->guidesPrefs().rulerMode)
gx -= m_doc->currentPage()->yOffset();
gx -= m_doc->rulerYoffset;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("Y: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("Y: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)));
}
}
void Canvas::displayXYHUD(QPointF m, double x, double y)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("X: %1\nY: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("X: %1\nY: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)));
}
void Canvas::displaySizeHUD(QPointF m, double x, double y, bool isLine)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
if (isLine)
- QToolTip::showText(tooltipPos.toPoint(), tr("Length: %1\nAngle: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, SC_DEGREES, true, true)), this);
+ displayHUD(m, tr("Length: %1\nAngle: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, SC_DEGREES, true, true)));
else
- QToolTip::showText(tooltipPos.toPoint(), tr("Width: %1\nHeight: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("Width: %1\nHeight: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)));
}
void Canvas::displayRotHUD(QPointF m, double rot)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double r;
if (rot < 0.0)
r = rot * -1.0;
else
r = 360.0 - rot;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("Angle: %1").arg(value2String(r, SC_DEGREES, true, true)), this);
+ displayHUD(m, tr("Angle: %1").arg(value2String(r, SC_DEGREES, true, true)));
}
void Canvas::displayRealRotHUD(QPointF m, double rot)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("Angle: %1").arg(value2String(rot, SC_DEGREES, true, true)), this);
+ }
+ displayHUD(m, tr("Angle: %1").arg(value2String(rot, SC_DEGREES, true, true)));
}
void Canvas::displayDoubleHUD(QPointF point, const QString& label, double value)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
- QPointF tooltipPos = point + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), QString("%1: %2").arg(label, value2String(value, m_doc->unitIndex(), true, true)), this);
+ }
+ displayHUD(point, QString("%1: %2").arg(label, value2String(value, m_doc->unitIndex(), true, true)));
}
void Canvas::setupEditHRuler(PageItem * item, bool forceAndReset)
Index: scribus/canvasmode_imageimport.h
===================================================================
--- scribus/canvasmode_imageimport.h (revision 27824)
+++ scribus/canvasmode_imageimport.h (working copy)
@@ -55,12 +55,14 @@
private:
bool m_keyRepeat {false};
- QString m_tipText;
+ QPixmap m_previewPixmap;
+ QString m_previewText;
QStringList m_imageList;
ScribusMainWindow *m_ScMW {nullptr};
double m_Mx {0.0};
double m_My {0.0};
- void newToolTip(const QString& name);
+ void displayPreview(QPointF globalPos);
+ void updatePreview(const QString& name);
};
#endif // CANVASMODE_IMAGEIMPORT_H
Index: scribus/canvasmode_imageimport.cpp
===================================================================
--- scribus/canvasmode_imageimport.cpp (revision 27824)
+++ scribus/canvasmode_imageimport.cpp (working copy)
@@ -16,7 +16,6 @@
#include <QApplication>
-#include <QBuffer>
#include <QCursor>
#include <QEvent>
#include <QMouseEvent>
@@ -24,7 +23,6 @@
#include <QPixmap>
#include <QPoint>
#include <QRect>
-#include <QToolTip>
#include <QDebug>
#include "canvas.h"
@@ -38,6 +36,9 @@
CanvasMode_ImageImport::CanvasMode_ImageImport(ScribusView *view) : CanvasMode(view), m_ScMW(view->m_ScMW)
{
+ const QPoint cursorPos = QCursor::pos();
+ m_Mx = cursorPos.x();
+ m_My = cursorPos.y();
}
void CanvasMode_ImageImport::setImageList(QStringList l)
@@ -53,36 +54,48 @@
m_view->requestMode(submodePaintingDone);
}
else
- newToolTip(l.first());
+ updatePreview(l.first());
}
else
- newToolTip(l.first());
+ updatePreview(l.first());
}
-void CanvasMode_ImageImport::newToolTip(const QString& name)
+void CanvasMode_ImageImport::displayPreview(QPointF globalPos)
{
- QImage pm(80, 80, QImage::Format_ARGB32_Premultiplied);
- QBrush b(QColor(205,205,205), IconManager::instance().loadPixmap("testfill"));
-
- QPainter p;
- p.begin(&pm);
- p.fillRect(QRectF(0, 0, 80, 80), b);
+ if (!m_previewPixmap.isNull())
+ m_canvas->displayHUD(globalPos, m_previewPixmap, false);
+ else
+ m_canvas->displayHUD(globalPos, m_previewText, false);
+}
+
+void CanvasMode_ImageImport::updatePreview(const QString& name)
+{
+ QPixmap preview(80, 80);
+ QBrush background(QColor(205, 205, 205), IconManager::instance().loadPixmap("testfill"));
QImage thumb;
- if (thumb.load(name))
+ const bool hasThumbnail = thumb.load(name);
+ {
+ QPainter painter(&preview);
+ painter.fillRect(QRectF(0, 0, 80, 80), background);
+ if (hasThumbnail)
+ {
+ thumb = thumb.scaled(80, 80, Qt::KeepAspectRatio);
+ painter.drawImage((80 - thumb.width()) / 2, (80 - thumb.height()) / 2, thumb);
+ }
+ }
+
+ if (hasThumbnail)
{
- thumb = thumb.scaled(80,80,Qt::KeepAspectRatio);
- p.drawImage((80 - thumb.width()) / 2, (80 - thumb.height()) / 2, thumb);
- QBuffer buffer;
- buffer.open(QIODevice::WriteOnly);
- pm.save(&buffer, "PNG");
- QByteArray ba = buffer.buffer().toBase64();
- buffer.close();
- m_tipText = "<p align=\"center\"><img src=\"data:image/png;base64," + QString(ba) + "\"></p>";
+ m_previewPixmap = preview;
+ m_previewText.clear();
}
else
- m_tipText = "<p align=\"center\">" + name.right(name.lastIndexOf("/")) + "</p>";
- QToolTip::showText(QPoint(m_Mx,m_My), m_tipText, qApp->activeWindow());
- p.end();
+ {
+ m_previewPixmap = QPixmap();
+ m_previewText = name.right(name.lastIndexOf("/"));
+ }
+
+ displayPreview(QPointF(m_Mx, m_My));
}
void CanvasMode_ImageImport::drawControls(QPainter* p)
@@ -125,6 +138,7 @@
void CanvasMode_ImageImport::leaveEvent(QEvent *e)
{
+ m_canvas->hideHUD();
}
void CanvasMode_ImageImport::activate(bool fromGesture)
@@ -146,6 +160,7 @@
void CanvasMode_ImageImport::deactivate(bool forGesture)
{
+ m_canvas->hideHUD();
m_view->setRedrawMarkerShown(false);
CanvasMode::deactivate(forGesture);
}
@@ -161,8 +176,7 @@
{
m_Mx = m->globalPosition().x();
m_My = m->globalPosition().y();
- QToolTip::showText(m->globalPosition().toPoint(), m_tipText, qApp->activeWindow());
- QToolTip::showText(m->globalPosition().toPoint(), m_tipText + "<b></b>", qApp->activeWindow());
+ displayPreview(m->globalPosition());
m->accept();
PageItem *item;
if ((item = m_canvas->itemUnderCursor(m->globalPosition())) != nullptr)
@@ -249,8 +263,8 @@
if (m_imageList.isEmpty())
{
m_view->requestMode(submodePaintingDone);
- QToolTip::hideText();
+ m_canvas->hideHUD();
}
else
- newToolTip(m_imageList.constFirst());
+ updatePreview(m_imageList.constFirst());
}
Index: scribus/scribusview.cpp
===================================================================
--- scribus/scribusview.cpp (revision 27824)
+++ scribus/scribusview.cpp (working copy)
@@ -3369,6 +3369,7 @@
emit MousePos(p.x(),p.y());
horizRuler->draw(m->position().x() + qRound(m_doc->minCanvasCoordinate.x() * m_canvas->scale()));
vertRuler->draw(m->position().y() + qRound(m_doc->minCanvasCoordinate.y() * m_canvas->scale()));
+ m_canvas->handleHUDMouseMove(m->globalPosition());
m_canvasMode->mouseMoveEvent(m);
return true;
}
@@ -3377,6 +3378,7 @@
auto* m = dynamic_cast<QMouseEvent*> (event);
m_canvasMode->mouseReleaseEvent(m);
m_canvas->m_viewMode.m_MouseButtonPressed = false;
+ m_canvas->beginHUDLinger(m->globalPosition());
if (linkAfterDraw)
{
//user creates new frame using linking tool
|
|
|
I kept experimenting and some more changes made sense moved here. | canvas-hud-drag-fix-v1.08.patch * Change from v1.06 -- Reparents the shared HUD widget from `Canvas` to the `ScribusView` viewport. -- Converts final HUD placement to viewport coordinates instead of canvas-content coordinates. * Reason -- The HUD is screen-space interaction feedback, while `Canvas` is scrollable/zoomable document content. -- Keeping the HUD under `Canvas` allowed zooming or scrolling to move a stationary HUD away from the pointer until the next mouse move. * Result -- HUDs remain pointer/screen anchored while the document zooms or scrolls underneath. -- The fix applies centrally to all shared HUD users, including create/move/resize/rotate and Image Import. -- No zoom-specific hooks or per-mode workaround required. -- Styling, timing, border, edge handling and other v1.06 behaviour are unchanged. canvas-hud-drag-fix-v1.08.patch (17,246 bytes)
Index: scribus/canvas.h
===================================================================
--- scribus/canvas.h (revision 27824)
+++ scribus/canvas.h (working copy)
@@ -39,6 +39,9 @@
#include "pageitempointer.h"
+class QLabel;
+class QTimer;
+
class ScPage;
class PageItem;
class ScLayer;
@@ -208,11 +211,16 @@
void displayRotHUD(QPointF m, double rot);
void displayRealRotHUD(QPointF m, double rot);
/**
- * Displays a tooltip of the format <code>{label}: {N} {unit}</code> where <code>N</code> is
+ * Displays a HUD of the format <code>{label}: {N} {unit}</code> where <code>N</code> is
* @a value converted to the current document unit and <code>unit</code> the current document
- * unit. The tooltip will be displayed nearby the global point @a point.
+ * unit. The HUD will be displayed nearby the global point @a point.
*/
void displayDoubleHUD(QPointF point, const QString& label, double value);
+ void displayHUD(QPointF globalPos, const QString& text, bool autoHide = true);
+ void displayHUD(QPointF globalPos, const QPixmap& pixmap, bool autoHide = true);
+ void beginHUDLinger(QPointF globalPos);
+ void handleHUDMouseMove(QPointF globalPos);
+ void hideHUD();
void setupEditHRuler(PageItem * item, bool forceAndReset = false);
@@ -263,6 +271,9 @@
QPixmap createPixmap(double w, double h);
// draw a potentially hidpi pixmap
void drawPixmap(QPainter &painter, double x, double y, const QPixmap &pixmap, double sx, double sy, double sw, double sh);
+ void positionHUD(QPointF globalPos, bool autoHide);
+ void updateHUDStyle();
+ void changeEvent(QEvent* event) override;
private:
ScribusDoc* m_doc;
@@ -275,6 +286,11 @@
QPixmap m_selectionBuffer;
QRect m_selectionRect;
QPoint m_oldMinCanvasCoordinate;
+ QLabel* m_hudLabel { nullptr };
+ QTimer* m_hudHideTimer { nullptr };
+ bool m_hudAutoHide { false };
+ bool m_hudLingering { false };
+ QPointF m_hudLingerOrigin;
};
Index: scribus/canvas.cpp
===================================================================
--- scribus/canvas.cpp (revision 27824)
+++ scribus/canvas.cpp (working copy)
@@ -19,6 +19,11 @@
#include <cmath>
// #include <QDebug>
+#include <QEvent>
+#include <QLabel>
+#include <QPainter>
+#include <QPalette>
+#include <QTimer>
#include <QToolTip>
#include <QWidget>
@@ -47,6 +52,30 @@
return p;
}
+constexpr int HUD_HIDE_DELAY_MS = 150;
+constexpr double HUD_MOVE_GRACE_PX = 5.0;
+
+class CanvasHUDLabel final : public QLabel
+{
+public:
+ using QLabel::QLabel;
+
+protected:
+ void paintEvent(QPaintEvent* event) override
+ {
+ {
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ QColor borderColor = palette().color(QPalette::WindowText);
+ borderColor.setAlphaF(0.4);
+ painter.setPen(borderColor);
+ painter.setBrush(palette().brush(QPalette::Window));
+ painter.drawRoundedRect(QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5), 3.0, 3.0);
+ }
+ QLabel::paintEvent(event);
+ }
+};
+
QDataStream &operator<< ( QDataStream & ds, const CanvasViewMode & vm )
{
ds << vm.scale
@@ -92,6 +121,17 @@
m_buffer = QPixmap();
m_bufferRect = QRect();
m_renderMode = RENDER_NORMAL;
+
+ m_hudLabel = new CanvasHUDLabel(m_view->viewport());
+ m_hudLabel->setAttribute(Qt::WA_TransparentForMouseEvents, true);
+ m_hudLabel->setFocusPolicy(Qt::NoFocus);
+ m_hudLabel->setContentsMargins(6, 4, 6, 4);
+ updateHUDStyle();
+ m_hudLabel->hide();
+
+ m_hudHideTimer = new QTimer(this);
+ m_hudHideTimer->setSingleShot(true);
+ connect(m_hudHideTimer, &QTimer::timeout, this, &Canvas::hideHUD);
}
void Canvas::setPreviewVisual(int mode)
@@ -2240,10 +2280,111 @@
painter.drawPixmap(x, y, pixmap, sx, sy, sw, sh);
}
+void Canvas::displayHUD(QPointF globalPos, const QString& text, bool autoHide)
+{
+ m_hudLabel->setPixmap(QPixmap());
+ m_hudLabel->setText(text);
+ positionHUD(globalPos, autoHide);
+}
+
+void Canvas::displayHUD(QPointF globalPos, const QPixmap& pixmap, bool autoHide)
+{
+ m_hudLabel->clear();
+ m_hudLabel->setPixmap(pixmap);
+ positionHUD(globalPos, autoHide);
+}
+
+void Canvas::beginHUDLinger(QPointF globalPos)
+{
+ if (!m_hudAutoHide || !m_hudLabel->isVisible())
+ return;
+
+ m_hudLingering = true;
+ m_hudLingerOrigin = globalPos;
+ m_hudHideTimer->start(HUD_HIDE_DELAY_MS);
+}
+
+void Canvas::handleHUDMouseMove(QPointF globalPos)
+{
+ if (!m_hudLingering || !m_hudLabel->isVisible())
+ return;
+
+ const QPointF delta = globalPos - m_hudLingerOrigin;
+ if (delta.x() * delta.x() + delta.y() * delta.y() > HUD_MOVE_GRACE_PX * HUD_MOVE_GRACE_PX)
+ hideHUD();
+}
+
+void Canvas::hideHUD()
+{
+ m_hudHideTimer->stop();
+ m_hudLingering = false;
+ m_hudAutoHide = false;
+ m_hudLabel->hide();
+}
+
+void Canvas::positionHUD(QPointF globalPos, bool autoHide)
+{
+ constexpr int hudOffsetX = 12;
+ constexpr int hudOffsetY = 16;
+ constexpr int hudViewportPadding = 5;
+
+ m_hudLabel->adjustSize();
+
+ QWidget* viewport = m_view->viewport();
+ const QRect visibleRect(viewport->mapToGlobal(QPoint(0, 0)), viewport->size());
+ const QRect hudBounds = visibleRect.adjusted(hudViewportPadding, hudViewportPadding, -hudViewportPadding, -hudViewportPadding);
+ const QPoint anchor = globalPos.toPoint();
+ QPoint hudGlobalPos = anchor + QPoint(hudOffsetX, hudOffsetY);
+ if (hudGlobalPos.x() + m_hudLabel->width() > hudBounds.right() + 1)
+ hudGlobalPos.setX(anchor.x() - m_hudLabel->width() - hudOffsetX);
+ if (hudGlobalPos.y() + m_hudLabel->height() > hudBounds.bottom() + 1)
+ hudGlobalPos.setY(anchor.y() - m_hudLabel->height() - hudOffsetY);
+
+ const int maxX = qMax(hudBounds.left(), hudBounds.right() - m_hudLabel->width() + 1);
+ const int maxY = qMax(hudBounds.top(), hudBounds.bottom() - m_hudLabel->height() + 1);
+ hudGlobalPos.setX(qBound(hudBounds.left(), hudGlobalPos.x(), maxX));
+ hudGlobalPos.setY(qBound(hudBounds.top(), hudGlobalPos.y(), maxY));
+ m_hudLabel->move(viewport->mapFromGlobal(hudGlobalPos));
+ if (!m_hudLabel->isVisible())
+ {
+ m_hudLabel->show();
+ m_hudLabel->raise();
+ }
+
+ m_hudAutoHide = autoHide;
+ m_hudLingering = false;
+ if (autoHide && !m_viewMode.m_MouseButtonPressed)
+ m_hudHideTimer->start(HUD_HIDE_DELAY_MS);
+ else
+ m_hudHideTimer->stop();
+}
+
+void Canvas::updateHUDStyle()
+{
+ if (!m_hudLabel)
+ return;
+
+ QPalette hudPalette = QToolTip::palette();
+ hudPalette.setBrush(QPalette::Window, hudPalette.brush(QPalette::ToolTipBase));
+ hudPalette.setBrush(QPalette::WindowText, hudPalette.brush(QPalette::ToolTipText));
+ m_hudLabel->setPalette(hudPalette);
+ m_hudLabel->setFont(QToolTip::font());
+}
+
+void Canvas::changeEvent(QEvent* event)
+{
+ QWidget::changeEvent(event);
+ if (event->type() == QEvent::PaletteChange || event->type() == QEvent::FontChange || event->type() == QEvent::StyleChange)
+ updateHUDStyle();
+}
+
void Canvas::displayXYHUD(QPointF m)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double gx, gy, gh, gw, r;
if (m_doc->m_Selection->isMultipleSelection())
{
@@ -2288,7 +2429,10 @@
void Canvas::displayCorrectedXYHUD(QPointF m, double x, double y)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double gx = x;
double gy = y;
if (m_doc->guidesPrefs().rulerMode)
@@ -2299,81 +2443,91 @@
gx -= m_doc->rulerXoffset;
gy -= m_doc->rulerYoffset;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("X: %1\nY: %2").arg(value2String(gx, m_doc->unitIndex(), true, true), value2String(gy, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("X: %1\nY: %2").arg(value2String(gx, m_doc->unitIndex(), true, true), value2String(gy, m_doc->unitIndex(), true, true)));
}
void Canvas::displayCorrectedSingleHUD(QPointF m, double val, bool isX)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double gx = val;
if (isX)
{
if (m_doc->guidesPrefs().rulerMode)
gx -= m_doc->currentPage()->xOffset();
gx -= m_doc->rulerXoffset;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("X: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("X: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)));
}
else
{
if (m_doc->guidesPrefs().rulerMode)
gx -= m_doc->currentPage()->yOffset();
gx -= m_doc->rulerYoffset;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("Y: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("Y: %1").arg(value2String(gx, m_doc->unitIndex(), true, true)));
}
}
void Canvas::displayXYHUD(QPointF m, double x, double y)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("X: %1\nY: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("X: %1\nY: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)));
}
void Canvas::displaySizeHUD(QPointF m, double x, double y, bool isLine)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
if (isLine)
- QToolTip::showText(tooltipPos.toPoint(), tr("Length: %1\nAngle: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, SC_DEGREES, true, true)), this);
+ displayHUD(m, tr("Length: %1\nAngle: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, SC_DEGREES, true, true)));
else
- QToolTip::showText(tooltipPos.toPoint(), tr("Width: %1\nHeight: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)), this);
+ displayHUD(m, tr("Width: %1\nHeight: %2").arg(value2String(x, m_doc->unitIndex(), true, true), value2String(y, m_doc->unitIndex(), true, true)));
}
void Canvas::displayRotHUD(QPointF m, double rot)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
+ }
double r;
if (rot < 0.0)
r = rot * -1.0;
else
r = 360.0 - rot;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("Angle: %1").arg(value2String(r, SC_DEGREES, true, true)), this);
+ displayHUD(m, tr("Angle: %1").arg(value2String(r, SC_DEGREES, true, true)));
}
void Canvas::displayRealRotHUD(QPointF m, double rot)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
- QPointF tooltipPos = m + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), tr("Angle: %1").arg(value2String(rot, SC_DEGREES, true, true)), this);
+ }
+ displayHUD(m, tr("Angle: %1").arg(value2String(rot, SC_DEGREES, true, true)));
}
void Canvas::displayDoubleHUD(QPointF point, const QString& label, double value)
{
if (!PrefsManager::instance().appPrefs.displayPrefs.showMouseCoordinates)
+ {
+ hideHUD();
return;
- QPointF tooltipPos = point + QPointF(5.0, 5.0);
- QToolTip::showText(tooltipPos.toPoint(), QString("%1: %2").arg(label, value2String(value, m_doc->unitIndex(), true, true)), this);
+ }
+ displayHUD(point, QString("%1: %2").arg(label, value2String(value, m_doc->unitIndex(), true, true)));
}
void Canvas::setupEditHRuler(PageItem * item, bool forceAndReset)
Index: scribus/canvasmode_imageimport.h
===================================================================
--- scribus/canvasmode_imageimport.h (revision 27824)
+++ scribus/canvasmode_imageimport.h (working copy)
@@ -55,12 +55,14 @@
private:
bool m_keyRepeat {false};
- QString m_tipText;
+ QPixmap m_previewPixmap;
+ QString m_previewText;
QStringList m_imageList;
ScribusMainWindow *m_ScMW {nullptr};
double m_Mx {0.0};
double m_My {0.0};
- void newToolTip(const QString& name);
+ void displayPreview(QPointF globalPos);
+ void updatePreview(const QString& name);
};
#endif // CANVASMODE_IMAGEIMPORT_H
Index: scribus/canvasmode_imageimport.cpp
===================================================================
--- scribus/canvasmode_imageimport.cpp (revision 27824)
+++ scribus/canvasmode_imageimport.cpp (working copy)
@@ -16,7 +16,6 @@
#include <QApplication>
-#include <QBuffer>
#include <QCursor>
#include <QEvent>
#include <QMouseEvent>
@@ -24,7 +23,6 @@
#include <QPixmap>
#include <QPoint>
#include <QRect>
-#include <QToolTip>
#include <QDebug>
#include "canvas.h"
@@ -38,6 +36,9 @@
CanvasMode_ImageImport::CanvasMode_ImageImport(ScribusView *view) : CanvasMode(view), m_ScMW(view->m_ScMW)
{
+ const QPoint cursorPos = QCursor::pos();
+ m_Mx = cursorPos.x();
+ m_My = cursorPos.y();
}
void CanvasMode_ImageImport::setImageList(QStringList l)
@@ -53,36 +54,48 @@
m_view->requestMode(submodePaintingDone);
}
else
- newToolTip(l.first());
+ updatePreview(l.first());
}
else
- newToolTip(l.first());
+ updatePreview(l.first());
}
-void CanvasMode_ImageImport::newToolTip(const QString& name)
+void CanvasMode_ImageImport::displayPreview(QPointF globalPos)
{
- QImage pm(80, 80, QImage::Format_ARGB32_Premultiplied);
- QBrush b(QColor(205,205,205), IconManager::instance().loadPixmap("testfill"));
-
- QPainter p;
- p.begin(&pm);
- p.fillRect(QRectF(0, 0, 80, 80), b);
+ if (!m_previewPixmap.isNull())
+ m_canvas->displayHUD(globalPos, m_previewPixmap, false);
+ else
+ m_canvas->displayHUD(globalPos, m_previewText, false);
+}
+
+void CanvasMode_ImageImport::updatePreview(const QString& name)
+{
+ QPixmap preview(80, 80);
+ QBrush background(QColor(205, 205, 205), IconManager::instance().loadPixmap("testfill"));
QImage thumb;
- if (thumb.load(name))
+ const bool hasThumbnail = thumb.load(name);
+ {
+ QPainter painter(&preview);
+ painter.fillRect(QRectF(0, 0, 80, 80), background);
+ if (hasThumbnail)
+ {
+ thumb = thumb.scaled(80, 80, Qt::KeepAspectRatio);
+ painter.drawImage((80 - thumb.width()) / 2, (80 - thumb.height()) / 2, thumb);
+ }
+ }
+
+ if (hasThumbnail)
{
- thumb = thumb.scaled(80,80,Qt::KeepAspectRatio);
- p.drawImage((80 - thumb.width()) / 2, (80 - thumb.height()) / 2, thumb);
- QBuffer buffer;
- buffer.open(QIODevice::WriteOnly);
- pm.save(&buffer, "PNG");
- QByteArray ba = buffer.buffer().toBase64();
- buffer.close();
- m_tipText = "<p align=\"center\"><img src=\"data:image/png;base64," + QString(ba) + "\"></p>";
+ m_previewPixmap = preview;
+ m_previewText.clear();
}
else
- m_tipText = "<p align=\"center\">" + name.right(name.lastIndexOf("/")) + "</p>";
- QToolTip::showText(QPoint(m_Mx,m_My), m_tipText, qApp->activeWindow());
- p.end();
+ {
+ m_previewPixmap = QPixmap();
+ m_previewText = name.right(name.lastIndexOf("/"));
+ }
+
+ displayPreview(QPointF(m_Mx, m_My));
}
void CanvasMode_ImageImport::drawControls(QPainter* p)
@@ -125,6 +138,7 @@
void CanvasMode_ImageImport::leaveEvent(QEvent *e)
{
+ m_canvas->hideHUD();
}
void CanvasMode_ImageImport::activate(bool fromGesture)
@@ -146,6 +160,7 @@
void CanvasMode_ImageImport::deactivate(bool forGesture)
{
+ m_canvas->hideHUD();
m_view->setRedrawMarkerShown(false);
CanvasMode::deactivate(forGesture);
}
@@ -161,8 +176,7 @@
{
m_Mx = m->globalPosition().x();
m_My = m->globalPosition().y();
- QToolTip::showText(m->globalPosition().toPoint(), m_tipText, qApp->activeWindow());
- QToolTip::showText(m->globalPosition().toPoint(), m_tipText + "<b></b>", qApp->activeWindow());
+ displayPreview(m->globalPosition());
m->accept();
PageItem *item;
if ((item = m_canvas->itemUnderCursor(m->globalPosition())) != nullptr)
@@ -249,8 +263,8 @@
if (m_imageList.isEmpty())
{
m_view->requestMode(submodePaintingDone);
- QToolTip::hideText();
+ m_canvas->hideHUD();
}
else
- newToolTip(m_imageList.constFirst());
+ updatePreview(m_imageList.constFirst());
}
Index: scribus/scribusview.cpp
===================================================================
--- scribus/scribusview.cpp (revision 27824)
+++ scribus/scribusview.cpp (working copy)
@@ -3369,6 +3369,7 @@
emit MousePos(p.x(),p.y());
horizRuler->draw(m->position().x() + qRound(m_doc->minCanvasCoordinate.x() * m_canvas->scale()));
vertRuler->draw(m->position().y() + qRound(m_doc->minCanvasCoordinate.y() * m_canvas->scale()));
+ m_canvas->handleHUDMouseMove(m->globalPosition());
m_canvasMode->mouseMoveEvent(m);
return true;
}
@@ -3377,6 +3378,7 @@
auto* m = dynamic_cast<QMouseEvent*> (event);
m_canvasMode->mouseReleaseEvent(m);
m_canvas->m_viewMode.m_MouseButtonPressed = false;
+ m_canvas->beginHUDLinger(m->globalPosition());
if (linkAfterDraw)
{
//user creates new frame using linking tool
|
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-07-03 12:51 | buttar | New Issue | |
| 2026-07-03 12:51 | buttar | File Added: cannot_draw_tables_fast.mp4 | |
| 2026-07-03 21:07 | cbradney | Note Added: 0053939 | |
| 2026-07-04 05:32 | buttar | Note Added: 0053943 | |
| 2026-07-04 07:25 | ale | Note Added: 0053949 | |
| 2026-07-04 07:26 | ale | Tag Attached: #cant reproduce | |
| 2026-07-04 08:00 | buttar | Note Added: 0053954 | |
| 2026-07-04 08:00 | buttar | File Added: quick_mouse_movement_1.7.2flatpak.mp4 | |
| 2026-07-06 04:45 | buttar | Note Added: 0053983 | |
| 2026-07-06 05:51 | PeterBenedek | Note Added: 0053986 | |
| 2026-09-08 07:52 | buttar | Note Added: 0054435 | |
| 2026-09-08 07:52 | buttar | File Added: canvas-hud-drag-fix-v1.05.patch | |
| 2026-09-08 07:52 | buttar | Tag Attached: #please_test | |
| 2026-09-08 07:54 | buttar | Note Added: 0054436 | |
| 2026-09-08 08:10 | buttar | Note Added: 0054437 | |
| 2026-09-08 08:10 | buttar | File Added: new hud (no issue).mp4 | |
| 2026-09-08 08:10 | buttar | File Added: old hud (with issue).mp4 | |
| 2026-09-08 09:25 | buttar | Note Added: 0054438 | |
| 2026-09-08 09:25 | buttar | File Added: canvas-hud-drag-fix-v1.06.patch | |
| 2026-09-08 09:25 | buttar | File Added: HUD, border.png | |
| 2026-09-08 13:40 | buttar | Note Added: 0054439 | |
| 2026-09-08 13:40 | buttar | File Added: canvas-hud-drag-fix-v1.08.patch |