/*************************************************************************** * Copyright (c) 2024 Pierre-Louis Boyer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #include #include #include #include #endif // #ifndef _PreComp_ #include #include #include "Application.h" #include "BitmapFactory.h" #include "CommandT.h" #include "MainWindow.h" #include "View3DInventor.h" #include "View3DInventorViewer.h" #include "ToolHandler.h" using namespace Gui; /**************************** ToolHandler *******************************************/ QString ToolHandler::getCrosshairCursorSVGName() const { return QString::fromLatin1("None"); } bool ToolHandler::activate() { // save the cursor at the time the DSH is activated QWidget* cw = getCursorWidget(); if (cw) { oldCursor = cw->cursor(); updateCursor(); this->preActivated(); this->activated(); return true; } return false; } void ToolHandler::deactivate() { this->deactivated(); this->postDeactivated(); unsetCursor(); } //************************************************************************** // Helpers unsigned long ToolHandler::getCrosshairColor() { unsigned long color = 0xFFFFFFFF; // white ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); color = hGrp->GetUnsigned("CursorCrosshairColor", color); // from rgba to rgb color = (color >> 8) & 0xFFFFFF; return color; } void ToolHandler::setCrosshairCursor(const QString& svgName) { const unsigned long defaultCrosshairColor = 0xFFFFFF; unsigned long color = getCrosshairColor(); auto colorMapping = std::map(); colorMapping[defaultCrosshairColor] = color; // hot spot of all SVG icons should be 8,8 for 32x32 size (16x16 for 64x64) int hotX = 8; int hotY = 8; setSvgCursor(svgName, hotX, hotY, colorMapping); } void ToolHandler::setCrosshairCursor(const char* svgName) { QString cursorName = QString::fromLatin1(svgName); setCrosshairCursor(cursorName); } void ToolHandler::setSvgCursor(const QString& cursorName, int x, int y, const std::map& colorMapping) { // The TechDraw_Pointer_*.svg icons have a default size of 64x64. When directly creating // them with a size of 32x32 they look very bad. // As a workaround the icons are created with 64x64 and afterwards the pixmap is scaled to // 32x32. This workaround is only needed if pRatio is equal to 1.0 // qreal pRatio = devicePixelRatio(); bool isRatioOne = (pRatio == 1.0); qreal defaultCursorSize = isRatioOne ? 64 : 32; qreal hotX = x; qreal hotY = y; #if !defined(Q_OS_WIN32) && !defined(Q_OS_MAC) if (qGuiApp->platformName() == QLatin1String("xcb")) { hotX *= pRatio; hotY *= pRatio; } #endif qreal cursorSize = defaultCursorSize * pRatio; QPixmap pointer = Gui::BitmapFactory().pixmapFromSvg(cursorName.toStdString().c_str(), QSizeF(cursorSize, cursorSize), colorMapping); if (isRatioOne) { pointer = pointer.scaled(32, 32); } pointer.setDevicePixelRatio(pRatio); setCursor(pointer, hotX, hotY, false); } void ToolHandler::setCursor(const QPixmap& p, int x, int y, bool autoScale) { QWidget* cw = getCursorWidget(); if (cw) { QCursor cursor; QPixmap p1(p); // TODO remove autoScale after all cursors are SVG-based if (autoScale) { qreal pRatio = devicePixelRatio(); int newWidth = p.width() * pRatio; int newHeight = p.height() * pRatio; p1 = p1.scaled(newWidth, newHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); p1.setDevicePixelRatio(pRatio); qreal hotX = x; qreal hotY = y; #if !defined(Q_OS_WIN32) && !defined(Q_OS_MAC) if (qGuiApp->platformName() == QLatin1String("xcb")) { hotX *= pRatio; hotY *= pRatio; } #endif cursor = QCursor(p1, hotX, hotY); } else { // already scaled cursor = QCursor(p1, x, y); } actCursor = cursor; actCursorPixmap = p1; setWidgetCursor(cursor); } } void ToolHandler::addCursorTail(std::vector& pixmaps) { // Create a pixmap that will contain icon and each autoconstraint icon QPixmap baseIcon = QPixmap(actCursorPixmap); baseIcon.setDevicePixelRatio(actCursorPixmap.devicePixelRatio()); qreal pixelRatio = baseIcon.devicePixelRatio(); // cursor size in device independent pixels qreal baseCursorWidth = baseIcon.width(); qreal baseCursorHeight = baseIcon.height(); int tailWidth = 0; for (auto const& p : pixmaps) { tailWidth += p.width(); } int newIconWidth = baseCursorWidth + tailWidth; int newIconHeight = baseCursorHeight; QPixmap newIcon(newIconWidth, newIconHeight); newIcon.fill(Qt::transparent); QPainter qp; qp.begin(&newIcon); qp.drawPixmap(QPointF(0, 0), baseIcon.scaled(baseCursorWidth * pixelRatio, baseCursorHeight * pixelRatio, Qt::KeepAspectRatio, Qt::SmoothTransformation)); // Iterate through pixmaps and them to the cursor pixmap qreal currentIconX = baseCursorWidth; qreal currentIconY; for (auto& icon : pixmaps) { currentIconY = baseCursorHeight - icon.height(); qp.drawPixmap(QPointF(currentIconX, currentIconY), icon); currentIconX += icon.width(); } qp.end(); // Finish painting // Create the new cursor with the icon. QPoint p = actCursor.hotSpot(); newIcon.setDevicePixelRatio(pixelRatio); QCursor newCursor(newIcon, p.x(), p.y()); applyCursor(newCursor); } void ToolHandler::updateCursor() { auto cursorstring = getCrosshairCursorSVGName(); if (cursorstring != QString::fromLatin1("None")) { setCrosshairCursor(cursorstring); } } void ToolHandler::applyCursor() { applyCursor(actCursor); } void ToolHandler::applyCursor(QCursor& newCursor) { setWidgetCursor(newCursor); } void ToolHandler::unsetCursor() { setWidgetCursor(oldCursor); } qreal ToolHandler::devicePixelRatio() { qreal pixelRatio = 1; QWidget* cw = getCursorWidget(); if (cw) { pixelRatio = cw->devicePixelRatio(); } return pixelRatio; } QWidget* ToolHandler::getCursorWidget() { Gui::View3DInventorViewer* viewer = getViewer(); if (viewer) { return viewer->getWidget(); } return nullptr; } void ToolHandler::setWidgetCursor(QCursor cursor) { QWidget* cw = getCursorWidget(); if (cw) { cw->setCursor(cursor); } } Gui::View3DInventorViewer* ToolHandler::getViewer() { Gui::MDIView* view = Gui::getMainWindow()->activeWindow(); if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) { return static_cast(view)->getViewer(); } return nullptr; }