Files
create/src/Gui/ViewProviderAnnotation.cpp
hlorus f0f9e81f88 Simplify dragging of App::AnnotationLabel (#11602)
* ViewProviderAnnotationn: Always allow dragging

Removes the editmode

* ViewProviderAnnotation: Use the label as transformation handle

* ViewProviderAnnotationn: Always allow dragging

Removes the editmode

* ViewProviderAnnotation: Use the label as transformation handle

* ViewProviderAnnotation: Hide dragger feedback during translation

---------

Co-authored-by: WandererFan <WandererFan@gmail.com>
2023-12-11 14:18:27 -05:00

503 lines
18 KiB
C++

/***************************************************************************
* Copyright (c) 2008 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 <QMenu>
# include <QFont>
# include <QFontMetrics>
# include <QImage>
# include <QPainter>
# include <Inventor/actions/SoSearchAction.h>
# include <Inventor/nodes/SoAnnotation.h>
# include <Inventor/nodes/SoAsciiText.h>
# include <Inventor/nodes/SoBaseColor.h>
# include <Inventor/nodes/SoCoordinate3.h>
# include <Inventor/nodes/SoDrawStyle.h>
# include <Inventor/nodes/SoFont.h>
# include <Inventor/nodes/SoImage.h>
# include <Inventor/nodes/SoLineSet.h>
# include <Inventor/nodes/SoPointSet.h>
# include <Inventor/nodes/SoRotationXYZ.h>
# include <Inventor/nodes/SoText2.h>
# include <Inventor/nodes/SoTranslation.h>
#endif
# include <Inventor/draggers/SoTranslate2Dragger.h>
#include <App/Annotation.h>
#include <App/Document.h>
#include <App/PropertyStandard.h>
#include <Base/Parameter.h>
#include "ViewProviderAnnotation.h"
#include "Application.h"
#include "BitmapFactory.h"
#include "Document.h"
#include "SoFCSelection.h"
#include "Tools.h"
#include "Window.h"
using namespace Gui;
const char* ViewProviderAnnotation::JustificationEnums[]= {"Left","Right","Center",nullptr};
const char* ViewProviderAnnotation::RotationAxisEnums[]= {"X","Y","Z",nullptr};
PROPERTY_SOURCE(Gui::ViewProviderAnnotation, Gui::ViewProviderDocumentObject)
ViewProviderAnnotation::ViewProviderAnnotation()
{
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
unsigned long col = hGrp->GetUnsigned("AnnotationTextColor",4294967295UL); // light grey
float r,g,b;
r = ((col >> 24) & 0xff) / 255.0; g = ((col >> 16) & 0xff) / 255.0; b = ((col >> 8) & 0xff) / 255.0;
ADD_PROPERTY(TextColor,(r,g,b));
ADD_PROPERTY(Justification,((long)0));
Justification.setEnums(JustificationEnums);
ADD_PROPERTY(FontSize,(12));
ADD_PROPERTY(FontName,("Arial"));
ADD_PROPERTY(LineSpacing,(1.0));
ADD_PROPERTY(Rotation,(0));
ADD_PROPERTY(RotationAxis,((long)2));
RotationAxis.setEnums(RotationAxisEnums);
pFont = new SoFont();
pFont->ref();
pLabel = new SoText2();
pLabel->ref();
pLabel3d = new SoAsciiText();
pLabel3d->ref();
pColor = new SoBaseColor();
pColor->ref();
pTranslation = new SoTranslation();
pTranslation->ref();
pRotationXYZ = new SoRotationXYZ();
pRotationXYZ->ref();
RotationAxis.touch();
TextColor.touch();
FontSize.touch();
FontName.touch();
sPixmap = "Tree_Annotation";
}
ViewProviderAnnotation::~ViewProviderAnnotation()
{
pFont->unref();
pLabel->unref();
pLabel3d->unref();
pColor->unref();
pTranslation->unref();
pRotationXYZ->unref();
}
void ViewProviderAnnotation::onChanged(const App::Property* prop)
{
if (prop == &TextColor) {
const App::Color& c = TextColor.getValue();
pColor->rgb.setValue(c.r,c.g,c.b);
}
else if (prop == &Justification) {
if (Justification.getValue() == 0) {
pLabel->justification = SoText2::LEFT;
pLabel3d->justification = SoAsciiText::LEFT;
}
else if (Justification.getValue() == 1) {
pLabel->justification = SoText2::RIGHT;
pLabel3d->justification = SoAsciiText::RIGHT;
}
else if (Justification.getValue() == 2) {
pLabel->justification = SoText2::CENTER;
pLabel3d->justification = SoAsciiText::CENTER;
}
}
else if (prop == &FontSize) {
pFont->size = FontSize.getValue();
}
else if (prop == &FontName) {
pFont->name = FontName.getValue();
}
else if (prop == &LineSpacing) {
pLabel->spacing = LineSpacing.getValue();
pLabel3d->spacing = LineSpacing.getValue();
}
else if (prop == &RotationAxis) {
if (RotationAxis.getValue() == 0) {
pRotationXYZ->axis = SoRotationXYZ::X;
}
else if (RotationAxis.getValue() == 1) {
pRotationXYZ->axis = SoRotationXYZ::Y;
}
else if (RotationAxis.getValue() == 2) {
pRotationXYZ->axis = SoRotationXYZ::Z;
}
}
else if (prop == &Rotation) {
pRotationXYZ->angle = (Rotation.getValue()/360)*(2*M_PI);
}
else {
ViewProviderDocumentObject::onChanged(prop);
}
}
std::vector<std::string> ViewProviderAnnotation::getDisplayModes() const
{
// add modes
std::vector<std::string> StrList;
StrList.emplace_back("Screen");
StrList.emplace_back("World");
return StrList;
}
void ViewProviderAnnotation::setDisplayMode(const char* ModeName)
{
if (strcmp(ModeName, "Screen") == 0)
setDisplayMaskMode("Screen");
else if (strcmp(ModeName, "World")==0)
setDisplayMaskMode("World");
ViewProviderDocumentObject::setDisplayMode(ModeName);
}
void ViewProviderAnnotation::attach(App::DocumentObject* f)
{
ViewProviderDocumentObject::attach(f);
auto anno = new SoAnnotation();
auto anno3d = new SoAnnotation();
auto textsep = new SoFCSelection();
// set selection/highlight colors
float transparency;
ParameterGrp::handle hGrp = Gui::WindowParameter::getDefaultParameter()->GetGroup("View");
SbColor highlightColor = textsep->colorHighlight.getValue();
auto highlight = (unsigned long)(highlightColor.getPackedValue());
highlight = hGrp->GetUnsigned("HighlightColor", highlight);
highlightColor.setPackedValue((uint32_t)highlight, transparency);
textsep->colorHighlight.setValue(highlightColor);
// Do the same with the selection color
SbColor selectionColor = textsep->colorSelection.getValue();
auto selection = (unsigned long)(selectionColor.getPackedValue());
selection = hGrp->GetUnsigned("SelectionColor", selection);
selectionColor.setPackedValue((uint32_t)selection, transparency);
textsep->colorSelection.setValue(selectionColor);
textsep->objectName = pcObject->getNameInDocument();
textsep->documentName = pcObject->getDocument()->getName();
textsep->subElementName = "Main";
textsep->addChild(pTranslation);
textsep->addChild(pRotationXYZ);
textsep->addChild(pColor);
textsep->addChild(pFont); // causes problems
textsep->addChild(pLabel);
auto textsep3d = new SoFCSelection();
// set sel/highlight color here too
textsep3d->colorHighlight.setValue(highlightColor);
textsep3d->colorSelection.setValue(selectionColor);
textsep3d->objectName = pcObject->getNameInDocument();
textsep3d->documentName = pcObject->getDocument()->getName();
textsep3d->subElementName = "Main";
textsep3d->addChild(pTranslation);
textsep3d->addChild(pRotationXYZ);
textsep3d->addChild(pColor);
textsep3d->addChild(pFont);
textsep3d->addChild(pLabel3d);
anno->addChild(textsep);
anno3d->addChild(textsep3d);
addDisplayMaskMode(anno, "Screen");
addDisplayMaskMode(anno3d, "World");
}
void ViewProviderAnnotation::updateData(const App::Property* prop)
{
if (prop->is<App::PropertyStringList>() &&
strcmp(prop->getName(),"LabelText") == 0) {
const std::vector<std::string> lines = static_cast<const App::PropertyStringList*>(prop)->getValues();
int index=0;
pLabel->string.setNum((int)lines.size());
pLabel3d->string.setNum((int)lines.size());
for (const auto & line : lines) {
const char* cs = line.c_str();
if (line.empty())
cs = " "; // empty lines make coin crash, we use a space instead
#if (COIN_MAJOR_VERSION <= 3)
QByteArray latin1str;
latin1str = (QString::fromUtf8(cs)).toLatin1();
pLabel->string.set1Value(index, SbString(latin1str.constData()));
pLabel3d->string.set1Value(index, SbString(latin1str.constData()));
#else
pLabel->string.set1Value(index, SbString(cs));
pLabel3d->string.set1Value(index, SbString(cs));
#endif
index++;
}
}
else if (prop->is<App::PropertyVector>() &&
strcmp(prop->getName(),"Position") == 0) {
Base::Vector3d v = static_cast<const App::PropertyVector*>(prop)->getValue();
pTranslation->translation.setValue(v.x,v.y,v.z);
}
ViewProviderDocumentObject::updateData(prop);
}
// ----------------------------------------------------------------------------
const char* ViewProviderAnnotationLabel::JustificationEnums[]= {"Left","Right","Center",nullptr};
PROPERTY_SOURCE(Gui::ViewProviderAnnotationLabel, Gui::ViewProviderDocumentObject)
ViewProviderAnnotationLabel::ViewProviderAnnotationLabel()
{
ADD_PROPERTY(TextColor,(1.0f,1.0f,1.0f));
ADD_PROPERTY(BackgroundColor,(0.0f,0.333f,1.0f));
ADD_PROPERTY(Justification,((long)0));
Justification.setEnums(JustificationEnums);
QFont fn;
ADD_PROPERTY(FontSize,(fn.pointSize()));
ADD_PROPERTY(FontName,((const char*)fn.family().toLatin1()));
ADD_PROPERTY(Frame,(true));
pColor = new SoBaseColor();
pColor->ref();
pBaseTranslation = new SoTranslation();
pBaseTranslation->ref();
pTextTranslation = new TranslateManip();
pTextTranslation->ref();
pCoords = new SoCoordinate3();
pCoords->ref();
pImage = new SoImage();
pImage->ref();
BackgroundColor.touch();
sPixmap = "Tree_Annotation";
}
ViewProviderAnnotationLabel::~ViewProviderAnnotationLabel()
{
pColor->unref();
pBaseTranslation->unref();
pTextTranslation->unref();
pCoords->unref();
pImage->unref();
}
void ViewProviderAnnotationLabel::onChanged(const App::Property* prop)
{
if (prop == &BackgroundColor) {
const App::Color& c = BackgroundColor.getValue();
pColor->rgb.setValue(c.r,c.g,c.b);
}
if (prop == &TextColor || prop == &BackgroundColor ||
prop == &Justification || prop == &FontSize ||
prop == &FontName || prop == &Frame) {
if (getObject()) {
App::Property* label = getObject()->getPropertyByName("LabelText");
if (label && label->is<App::PropertyStringList>())
drawImage(static_cast<App::PropertyStringList*>(label)->getValues());
}
}
else {
ViewProviderDocumentObject::onChanged(prop);
}
}
std::vector<std::string> ViewProviderAnnotationLabel::getDisplayModes() const
{
// add modes
std::vector<std::string> StrList;
StrList.emplace_back("Line");
StrList.emplace_back("Object");
return StrList;
}
void ViewProviderAnnotationLabel::setDisplayMode(const char* ModeName)
{
if (strcmp(ModeName, "Line") == 0)
setDisplayMaskMode("Line");
else if (strcmp(ModeName, "Object")==0)
setDisplayMaskMode("Object");
ViewProviderDocumentObject::setDisplayMode(ModeName);
}
void ViewProviderAnnotationLabel::attach(App::DocumentObject* f)
{
ViewProviderDocumentObject::attach(f);
// plain image
SoSeparator* textsep = new SoAnnotation();
textsep->addChild(pBaseTranslation);
textsep->addChild(pImage);
// image with line
SoSeparator* linesep = new SoAnnotation();
linesep->addChild(pBaseTranslation);
linesep->addChild(pColor);
linesep->addChild(pCoords);
linesep->addChild(new SoLineSet());
auto ds = new SoDrawStyle();
ds->pointSize.setValue(3.0f);
linesep->addChild(ds);
linesep->addChild(new SoPointSet());
linesep->addChild(pTextTranslation);
linesep->addChild(pImage);
addDisplayMaskMode(linesep, "Line");
addDisplayMaskMode(textsep, "Object");
// Use the image node as the transform handle
SoSearchAction sa;
sa.setInterest(SoSearchAction::FIRST);
sa.setSearchingAll(true);
sa.setNode(this->pImage);
sa.apply(pcRoot);
SoPath * imagePath = sa.getPath();
if (imagePath) {
SoDragger* dragger = pTextTranslation->getDragger();
dragger->addStartCallback(dragStartCallback, this);
dragger->addFinishCallback(dragFinishCallback, this);
dragger->addMotionCallback(dragMotionCallback, this);
dragger->setPartAsPath("translator", imagePath);
// Hide the dragger feedback during translation
dragger->setPart("translatorActive", NULL);
dragger->setPart("xAxisFeedback", NULL);
dragger->setPart("yAxisFeedback", NULL);
}
}
void ViewProviderAnnotationLabel::updateData(const App::Property* prop)
{
if (prop->is<App::PropertyStringList>() &&
strcmp(prop->getName(),"LabelText") == 0) {
drawImage(static_cast<const App::PropertyStringList*>(prop)->getValues());
}
else if (prop->is<App::PropertyVector>() &&
strcmp(prop->getName(),"BasePosition") == 0) {
Base::Vector3d v = static_cast<const App::PropertyVector*>(prop)->getValue();
pBaseTranslation->translation.setValue(v.x,v.y,v.z);
}
else if (prop->is<App::PropertyVector>() &&
strcmp(prop->getName(),"TextPosition") == 0) {
Base::Vector3d v = static_cast<const App::PropertyVector*>(prop)->getValue();
pCoords->point.set1Value(1, SbVec3f(v.x,v.y,v.z));
pTextTranslation->translation.setValue(v.x,v.y,v.z);
}
ViewProviderDocumentObject::updateData(prop);
}
void ViewProviderAnnotationLabel::dragStartCallback(void *, SoDragger *)
{
// This is called when a manipulator is about to manipulating
Gui::Application::Instance->activeDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Transform"));
}
void ViewProviderAnnotationLabel::dragFinishCallback(void *, SoDragger *)
{
// This is called when a manipulator has done manipulating
Gui::Application::Instance->activeDocument()->commitCommand();
}
void ViewProviderAnnotationLabel::dragMotionCallback(void *data, SoDragger *drag)
{
auto that = static_cast<ViewProviderAnnotationLabel*>(data);
const SbMatrix& mat = drag->getMotionMatrix();
App::DocumentObject* obj = that->getObject();
if (obj && obj->is<App::AnnotationLabel>()) {
static_cast<App::AnnotationLabel*>(obj)->TextPosition.setValue(mat[3][0],mat[3][1],mat[3][2]);
}
}
void ViewProviderAnnotationLabel::drawImage(const std::vector<std::string>& s)
{
if (s.empty()) {
pImage->image = SoSFImage();
this->hide();
return;
}
QFont font(QString::fromLatin1(this->FontName.getValue()), (int)this->FontSize.getValue());
QFontMetrics fm(font);
int w = 0;
int h = fm.height() * s.size();
const App::Color& b = this->BackgroundColor.getValue();
QColor brush;
brush.setRgbF(b.r,b.g,b.b);
const App::Color& t = this->TextColor.getValue();
QColor front;
front.setRgbF(t.r,t.g,t.b);
QStringList lines;
for (const auto & it : s) {
QString line = QString::fromUtf8(it.c_str());
w = std::max<int>(w, QtTools::horizontalAdvance(fm, line));
lines << line;
}
QImage image(w+10,h+10,QImage::Format_ARGB32_Premultiplied);
image.fill(0x00000000);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
bool drawFrame = this->Frame.getValue();
if (drawFrame) {
painter.setPen(QPen(QColor(0,0,127), 2, Qt::SolidLine, Qt::RoundCap,
Qt::RoundJoin));
painter.setBrush(QBrush(brush, Qt::SolidPattern));
QRectF rectangle(0.0, 0.0, w+10, h+10);
painter.drawRoundedRect(rectangle, 5, 5);
}
painter.setPen(front);
Qt::Alignment align = Qt::AlignVCenter;
if (Justification.getValue() == 0)
align = Qt::AlignVCenter | Qt::AlignLeft;
else if (Justification.getValue() == 1)
align = Qt::AlignVCenter | Qt::AlignRight;
else
align = Qt::AlignVCenter | Qt::AlignHCenter;
QString text = lines.join(QLatin1String("\n"));
painter.setFont(font);
painter.drawText(5,5,w,h,align,text);
painter.end();
SoSFImage sfimage;
Gui::BitmapFactory().convert(image, sfimage);
pImage->image = sfimage;
}