TechDraw: Annotation tool rework (#24624)
This commit is contained in:
@@ -922,6 +922,12 @@ QToolBar::separator:vertical {
|
||||
background-color: @GeneralBorderColor;
|
||||
}
|
||||
|
||||
MRichTextEdit[floating="true"] {
|
||||
border: 1px solid @GeneralBorderColor;
|
||||
border-radius: 2px;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
/*The "show more" button (it can also be stylable with "QToolBarExtension" icon is not working
|
||||
Qproperty works but breaks when you move the toolbar*/
|
||||
|
||||
|
||||
+1
-1
Submodule src/Mod/AddonManager updated: 937b687723...d9c593594a
@@ -20,6 +20,7 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <Base/Reader.h>
|
||||
|
||||
#include "DrawRichAnno.h"
|
||||
#include "DrawRichAnnoPy.h" // generated from DrawRichAnnoPy.xml
|
||||
@@ -41,6 +42,8 @@ DrawRichAnno::DrawRichAnno()
|
||||
"Object to which this annontation is attached");
|
||||
ADD_PROPERTY_TYPE(AnnoText, (""), group, App::Prop_None, "Annotation text");
|
||||
ADD_PROPERTY_TYPE(ShowFrame, (true), group, App::Prop_None, "Outline rectangle on/off");
|
||||
// Necessary to support legacy files made before #24624.
|
||||
ADD_PROPERTY_TYPE(OriginCentered, (false), group, App::Prop_None, "Center the annotation on it's origin.");
|
||||
ADD_PROPERTY_TYPE(MaxWidth, (-1.0), group, App::Prop_None, "Width limit before auto wrap");
|
||||
Caption.setStatus(App::Property::Hidden, true);
|
||||
Scale.setStatus(App::Property::Hidden, true);
|
||||
@@ -48,6 +51,40 @@ DrawRichAnno::DrawRichAnno()
|
||||
|
||||
}
|
||||
|
||||
void DrawRichAnno::Restore(Base::XMLReader& reader)
|
||||
{
|
||||
bool originCenteredFound = false;
|
||||
|
||||
// Start parsing the properties block.
|
||||
reader.readElement("Properties");
|
||||
int propCount = reader.getAttribute<long>("Count");
|
||||
|
||||
for (int i = 0; i < propCount; i++) {
|
||||
reader.readElement("Property");
|
||||
const char* propName = reader.getAttribute<const char*>("name");
|
||||
|
||||
// The "checking" part:
|
||||
if (strcmp(propName, "OriginCentered") == 0) {
|
||||
originCenteredFound = true;
|
||||
}
|
||||
|
||||
// The "restoring" part:
|
||||
App::Property* prop = getPropertyByName(propName);
|
||||
if (prop) {
|
||||
prop->Restore(reader); // Restore the value
|
||||
}
|
||||
|
||||
reader.readEndElement("Property");
|
||||
}
|
||||
|
||||
reader.readEndElement("Properties");
|
||||
|
||||
// Ensure backward compatibility: Old files have their anno centered on origin.
|
||||
if (!originCenteredFound) {
|
||||
OriginCentered.setValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRichAnno::onChanged(const App::Property* prop)
|
||||
{
|
||||
if (!isRestoring()) {
|
||||
|
||||
@@ -44,7 +44,9 @@ public:
|
||||
App::PropertyString AnnoText;
|
||||
App::PropertyBool ShowFrame;
|
||||
App::PropertyFloat MaxWidth;
|
||||
App::PropertyBool OriginCentered;
|
||||
|
||||
void Restore(Base::XMLReader& reader) override;
|
||||
short mustExecute() const override;
|
||||
App::DocumentObjectExecReturn *execute() override;
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ CmdTechDrawRichTextAnnotation::CmdTechDrawRichTextAnnotation()
|
||||
sToolTipText = QT_TR_NOOP("Inserts a rich text annotation in the current page");
|
||||
sWhatsThis = "TechDraw_RichTextAnnotation";
|
||||
sStatusTip = sToolTipText;
|
||||
sPixmap = "actions/TechDraw_RichTextAnnotation";
|
||||
sPixmap = "actions/TechDraw_Annotation";
|
||||
}
|
||||
|
||||
void CmdTechDrawRichTextAnnotation::activated(int iMsg)
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
# include <QPainter>
|
||||
# include <QRectF>
|
||||
# include <QStyleOptionGraphicsItem>
|
||||
#include <QKeyEvent>
|
||||
#include <QTextBlock>
|
||||
#include <QTextCursor>
|
||||
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Parameter.h>
|
||||
@@ -167,6 +170,8 @@ void QGCustomText::setTightBounding(bool tight)
|
||||
|
||||
void QGCustomText::paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) {
|
||||
QStyleOptionGraphicsItem myOption(*option);
|
||||
// Remove HasFocus state to prevent the dashed rectangle from being drawn
|
||||
myOption.state &= ~QStyle::State_HasFocus;
|
||||
myOption.state &= ~QStyle::State_Selected;
|
||||
|
||||
// painter->setPen(Qt::green);
|
||||
@@ -269,3 +274,78 @@ void QGCustomText::makeMark(Base::Vector3d v)
|
||||
makeMark(v.x, v.y);
|
||||
}
|
||||
|
||||
void QGCustomText::focusInEvent(QFocusEvent* event)
|
||||
{
|
||||
// Store the initial cursor state when the item gains focus
|
||||
m_lastCursor = textCursor();
|
||||
QGraphicsTextItem::focusInEvent(event);
|
||||
}
|
||||
|
||||
void QGCustomText::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
// If the user returns more than once, then the format (the font
|
||||
// size we set as default) is lost. So we need to handle manually
|
||||
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
|
||||
QTextCursor cursor = textCursor();
|
||||
|
||||
// 1. Preserve the all-important character format from the current cursor position.
|
||||
QTextCharFormat formatToPreserve = cursor.charFormat();
|
||||
|
||||
// 2. Preserve the block format (for things like alignment, indentation).
|
||||
QTextBlockFormat blockFormatToPreserve = cursor.blockFormat();
|
||||
|
||||
// 3. Manually insert a new block (a newline).
|
||||
// This automatically deletes any selected text, which is the correct behavior.
|
||||
cursor.insertBlock(blockFormatToPreserve, formatToPreserve);
|
||||
|
||||
// 4. After inserting the block, the cursor is at the start of the new line.
|
||||
// Its charFormat should already be correct because we passed it to insertBlock.
|
||||
// We don't need to do anything further with the cursor.
|
||||
|
||||
// 5. Explicitly apply the cursor back to the item to ensure the view updates.
|
||||
setTextCursor(cursor);
|
||||
|
||||
// 6. Notify that the selection/cursor has changed.
|
||||
checkCursorChange();
|
||||
|
||||
// 7. Accept the event to stop it from being processed further.
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
// Let the base class handle the key press first (which moves the cursor)
|
||||
QGraphicsTextItem::keyPressEvent(event);
|
||||
// Now check if the cursor or selection changed as a result
|
||||
checkCursorChange();
|
||||
}
|
||||
|
||||
void QGCustomText::mousePressEvent(QGraphicsSceneMouseEvent* event)
|
||||
{
|
||||
// Let the base class handle the mouse press first
|
||||
QGraphicsTextItem::mousePressEvent(event);
|
||||
checkCursorChange();
|
||||
}
|
||||
|
||||
void QGCustomText::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
|
||||
{
|
||||
// Let the base class handle the mouse release first
|
||||
QGraphicsTextItem::mouseReleaseEvent(event);
|
||||
checkCursorChange();
|
||||
}
|
||||
|
||||
void QGCustomText::checkCursorChange()
|
||||
{
|
||||
QTextCursor currentCursor = textCursor();
|
||||
|
||||
// Compare the properties of the cursors that define selection and position.
|
||||
if (currentCursor.position() != m_lastCursor.position()
|
||||
|| currentCursor.anchor() != m_lastCursor.anchor()) {
|
||||
// The anchor and position define the selection. If either has changed,
|
||||
// the selection/cursor has changed.
|
||||
Q_EMIT selectionChanged();
|
||||
}
|
||||
|
||||
m_lastCursor = currentCursor;
|
||||
}
|
||||
|
||||
#include <Mod/TechDraw/Gui/moc_QGCustomText.cpp>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <QGraphicsItem>
|
||||
#include <QGraphicsTextItem>
|
||||
#include <QPointF>
|
||||
#include <QTextCursor>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QPainter;
|
||||
@@ -43,6 +44,7 @@ namespace TechDrawGui
|
||||
|
||||
class TechDrawGuiExport QGCustomText : public QGraphicsTextItem
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QGCustomText(QGraphicsItem* parent = nullptr);
|
||||
~QGCustomText() override {}
|
||||
@@ -81,7 +83,14 @@ public:
|
||||
void makeMark(double x, double y);
|
||||
void makeMark(Base::Vector3d v);
|
||||
|
||||
Q_SIGNALS:
|
||||
void selectionChanged();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override;
|
||||
void focusInEvent(QFocusEvent* event) override;
|
||||
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
|
||||
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
|
||||
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
|
||||
@@ -92,6 +101,8 @@ protected:
|
||||
QColor m_colNormal;
|
||||
|
||||
private:
|
||||
void checkCursorChange();
|
||||
QTextCursor m_lastCursor;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -22,17 +22,27 @@
|
||||
|
||||
# include <cmath>
|
||||
|
||||
# include <QAbstractTextDocumentLayout>
|
||||
# include <QDialog>
|
||||
# include <QGraphicsItem>
|
||||
# include <QGraphicsSceneMouseEvent>
|
||||
# include <QPainter>
|
||||
# include <QRegularExpression>
|
||||
# include <QApplication>
|
||||
# include <QCursor>
|
||||
# include <QRegularExpressionMatch>
|
||||
# include <QTextBlock>
|
||||
# include <QTextCursor>
|
||||
# include <QTextDocument>
|
||||
# include <QTextDocumentFragment>
|
||||
# include <QTimer>
|
||||
# include <QGraphicsScene>
|
||||
# include <QGraphicsView>
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <Gui/Command.h>
|
||||
#include <Gui/Control.h>
|
||||
|
||||
#include <Mod/TechDraw/App/DrawRichAnno.h>
|
||||
#include <Mod/TechDraw/App/DrawUtil.h>
|
||||
|
||||
@@ -51,13 +61,26 @@ using namespace TechDraw;
|
||||
using namespace TechDrawGui;
|
||||
using DU = DrawUtil;
|
||||
|
||||
const double QGIRichAnno::HandleInteractionMargin = 30.0; // Scene units for hover/click
|
||||
const double QGIRichAnno::MinTextWidthDocument = 5.0; // Min width in document units
|
||||
|
||||
//**************************************************************
|
||||
QGIRichAnno::QGIRichAnno() :
|
||||
m_isExportingPdf(false), m_isExportingSvg(false), m_hasHover(false)
|
||||
m_isExportingPdf(false),
|
||||
m_isExportingSvg(false),
|
||||
m_currentResizeHandle(ResizeHandle::NoHandle),
|
||||
m_isResizing(false),
|
||||
m_isDraggingMidResize(false),
|
||||
m_transactionOpen(false),
|
||||
m_dragStartMouseScenePos(),
|
||||
m_initialItemScenePos(),
|
||||
m_initialTextWidthScene(0.0),
|
||||
m_isEditing(false),
|
||||
m_textScaleFactor(1.0),
|
||||
m_lastGoodWidthScene(0.0)
|
||||
{
|
||||
setHandlesChildEvents(false);
|
||||
setAcceptHoverEvents(false);
|
||||
setAcceptHoverEvents(true); // Enable hover events for cursor changes
|
||||
setFlag(QGraphicsItem::ItemIsSelectable, true);
|
||||
setFlag(QGraphicsItem::ItemIsMovable, true);
|
||||
setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true);
|
||||
@@ -68,15 +91,18 @@ QGIRichAnno::QGIRichAnno() :
|
||||
m_text->setDefaultTextColor(PreferencesGui::normalQColor());
|
||||
addToGroup(m_text);
|
||||
m_text->setZValue(ZVALUE::DIMENSION);
|
||||
m_text->centerAt(0.0, 0.0);
|
||||
|
||||
m_rect = new QGCustomRect();
|
||||
addToGroup(m_rect);
|
||||
m_rect->setZValue(ZVALUE::DIMENSION - 1);
|
||||
m_rect->centerAt(0.0, 0.0);
|
||||
|
||||
setZValue(ZVALUE::DIMENSION);
|
||||
|
||||
connect(m_text->document(),
|
||||
&QTextDocument::contentsChanged,
|
||||
this,
|
||||
&QGIRichAnno::onContentsChanged);
|
||||
connect(m_text, &QGCustomText::selectionChanged, this, &QGIRichAnno::selectionChanged);
|
||||
}
|
||||
|
||||
void QGIRichAnno::updateView(bool update)
|
||||
@@ -100,24 +126,15 @@ void QGIRichAnno::updateView(bool update)
|
||||
setFlag(QGraphicsItem::ItemIsMovable, true);
|
||||
}
|
||||
|
||||
if (annoFeat->X.isTouched() ||
|
||||
annoFeat->Y.isTouched()) {
|
||||
float x = Rez::guiX(annoFeat->X.getValue());
|
||||
float y = Rez::guiX(annoFeat->Y.getValue());
|
||||
m_text->centerAt(x, -y);
|
||||
m_rect->centerAt(x, -y);
|
||||
}
|
||||
// Convert the word processing font size spec (in typographic points) to scene units for
|
||||
// the screen or pdf rendering
|
||||
constexpr double mmPerPoint {25.4 / 72}; // mm/in / points/inch
|
||||
m_textScaleFactor = Rez::getRezFactor() * mmPerPoint; // scene units per point: 3.53
|
||||
m_text->setScale(m_textScaleFactor);
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
void QGIRichAnno::drawBorder()
|
||||
{
|
||||
////Leaders have no border!
|
||||
// QGIView::drawBorder(); //good for debugging
|
||||
}
|
||||
|
||||
|
||||
void QGIRichAnno::draw()
|
||||
{
|
||||
// Base::Console().log("QGIRA::draw() - %s - parent: %X\n", getFeature()->getNameInDocument(), parentItem());
|
||||
@@ -146,45 +163,53 @@ void QGIRichAnno::setTextItem()
|
||||
// Base::Console().message("QGIRA::setTextItem() - %s - exportingSvg: %d\n", getViewName(), getExportingSvg());
|
||||
TechDraw::DrawRichAnno* annoFeat = getFeature();
|
||||
|
||||
// convert the text size
|
||||
QString inHtml = QString::fromUtf8(annoFeat->AnnoText.getValue());
|
||||
QString outHtml = convertTextSizes(inHtml);
|
||||
updateLayout();
|
||||
|
||||
//position the text
|
||||
prepareGeometryChange();
|
||||
// control auto line break
|
||||
if (annoFeat->MaxWidth.getValue() > 0.0) {
|
||||
// we have set a maximum width, so convert it to scene units
|
||||
m_text->setTextWidth(Rez::guiX(annoFeat->MaxWidth.getValue()));
|
||||
} else {
|
||||
// we don't want to break lines
|
||||
m_text->setTextWidth(annoFeat->MaxWidth.getValue());
|
||||
// convert the text size
|
||||
if (!m_isEditing) {
|
||||
m_text->setHtml(QString::fromUtf8(annoFeat->AnnoText.getValue()));
|
||||
}
|
||||
m_text->setHtml(outHtml);
|
||||
|
||||
// 1. Get the bounding rectangle of the text in its own local coordinates.
|
||||
QRectF textParentRect = m_text->mapRectToParent(m_text->boundingRect());
|
||||
|
||||
QPointF offset(0.0, 0.0);
|
||||
if (annoFeat->OriginCentered.getValue()) {
|
||||
offset = QPointF(-textParentRect.width() / 2.0, -textParentRect.height() / 2.0);
|
||||
}
|
||||
m_text->setPos(offset);
|
||||
textParentRect = m_text->mapRectToParent(m_text->boundingRect());
|
||||
|
||||
if (annoFeat->OriginCentered.getValue() || !getExportingSvg()) {
|
||||
m_rect->setRect(textParentRect);
|
||||
}
|
||||
|
||||
m_rect->setPen(rectPen());
|
||||
m_rect->setBrush(Qt::NoBrush);
|
||||
m_rect->setVisible(annoFeat->ShowFrame.getValue());
|
||||
|
||||
if (getExportingSvg()) {
|
||||
// Convert the word processing font size spec (in typographic points) to CSS pixels
|
||||
// for Svg rendering
|
||||
constexpr double mmPerPoint {25.4 / 72};
|
||||
constexpr double cssPxPerPoint {16 / 12}; // CSS says 12 pt text is 16 px high
|
||||
m_text->setScale(cssPxPerPoint);
|
||||
|
||||
// QSvgRenderer places the text's top edge flush with the item's origin.
|
||||
// We must manually shift the QGraphicsTextItem down to create padding.
|
||||
QTextBlock firstBlock = m_text->document()->begin();
|
||||
if (firstBlock.isValid()) {
|
||||
QTextCursor cursor(firstBlock);
|
||||
double fontSizePx = cursor.charFormat().fontPointSize();
|
||||
m_text->setY(m_text->pos().y() + (fontSizePx * mmPerPoint));
|
||||
}
|
||||
|
||||
// lines are correctly spaced on screen or in pdf, but svg needs this
|
||||
setLineSpacing(100);
|
||||
}
|
||||
|
||||
if (!getExportingSvg()) {
|
||||
// screen or pdf rendering
|
||||
m_text->centerAt(0.0, 0.0);
|
||||
}
|
||||
|
||||
// align the frame rectangle to the text
|
||||
constexpr double frameMargin{10.0};
|
||||
QRectF outRect = m_text->boundingRect().adjusted(-frameMargin, -frameMargin, frameMargin, frameMargin);
|
||||
m_rect->setPen(rectPen());
|
||||
m_rect->setBrush(Qt::NoBrush);
|
||||
if (!getExportingSvg()) {
|
||||
m_rect->setRect(outRect);
|
||||
m_rect->setPos(m_text->pos().x() - frameMargin, m_text->pos().y() - frameMargin);
|
||||
}
|
||||
|
||||
if (annoFeat->ShowFrame.getValue()) {
|
||||
m_rect->show();
|
||||
} else {
|
||||
m_rect->hide();
|
||||
if (m_isEditing) {
|
||||
Q_EMIT positionChanged(scenePos());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,53 +253,6 @@ void QGIRichAnno::setLineSpacing(int lineSpacing)
|
||||
}
|
||||
}
|
||||
|
||||
//! convert the word processing font size spec (in typographic points) to scene units for the screen or
|
||||
//! pdf rendering or to CSS pixels for Svg rendering
|
||||
QString QGIRichAnno::convertTextSizes(const QString& inHtml) const
|
||||
{
|
||||
constexpr double mmPerPoint{0.353}; // 25.4 mm/in / 72 points/inch
|
||||
constexpr double cssPxPerPoint{1.333333}; // CSS says 12 pt text is 16 px high
|
||||
double sceneUnitsPerPoint = Rez::getRezFactor() * mmPerPoint; // scene units per point: 3.53
|
||||
|
||||
QRegularExpression rxFontSize(QStringLiteral("font-size:([0-9]*)pt;"));
|
||||
QRegularExpressionMatch match;
|
||||
QStringList findList;
|
||||
QStringList replList;
|
||||
|
||||
// find each occurrence of "font-size:..." and calculate the equivalent size in scene units
|
||||
// or CSS pixels
|
||||
int pos = 0;
|
||||
while ((pos = inHtml.indexOf(rxFontSize, pos, &match)) != -1) {
|
||||
QString found = match.captured(0);
|
||||
findList << found;
|
||||
QString qsOldSize = match.captured(1);
|
||||
|
||||
QString repl = found;
|
||||
double newSize = qsOldSize.toDouble(); // in points
|
||||
// The font size in the QGraphicsTextItem html is interpreted differently
|
||||
// in QSvgRenderer rendering compared to painting the screen or pdf
|
||||
if (getExportingSvg()) {
|
||||
// scale point size to CSS pixels
|
||||
newSize = newSize * cssPxPerPoint;
|
||||
} else {
|
||||
// scale point size to scene units
|
||||
newSize = newSize * sceneUnitsPerPoint;
|
||||
}
|
||||
QString qsNewSize = QString::number(newSize, 'f', 2);
|
||||
repl.replace(qsOldSize, qsNewSize);
|
||||
replList << repl;
|
||||
pos += match.capturedLength();
|
||||
}
|
||||
QString outHtml = inHtml;
|
||||
int iRepl = 0;
|
||||
//TODO: check list for duplicates?
|
||||
for ( ; iRepl < findList.size(); iRepl++) {
|
||||
outHtml = outHtml.replace(findList[iRepl], replList[iRepl]);
|
||||
}
|
||||
|
||||
return outHtml;
|
||||
}
|
||||
|
||||
TechDraw::DrawRichAnno* QGIRichAnno::getFeature()
|
||||
{
|
||||
return static_cast<TechDraw::DrawRichAnno*>(getViewObject());
|
||||
@@ -284,10 +262,7 @@ TechDraw::DrawRichAnno* QGIRichAnno::getFeature()
|
||||
// TODO: this rect is the right size, but not in the right place
|
||||
QRectF QGIRichAnno::boundingRect() const
|
||||
{
|
||||
QRectF roughRect = m_text->boundingRect() | m_rect->boundingRect();
|
||||
double halfWidth = roughRect.width() / 2.0;
|
||||
double halfHeight = roughRect.height() / 2.0;
|
||||
return { -halfWidth, - halfHeight, halfWidth * 2.0, halfHeight * 2.0 };
|
||||
return childrenBoundingRect();
|
||||
}
|
||||
|
||||
void QGIRichAnno::paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) {
|
||||
@@ -327,35 +302,368 @@ QFont QGIRichAnno::prefFont()
|
||||
return PreferencesGui::labelFontQFont();
|
||||
}
|
||||
|
||||
void QGIRichAnno::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) {
|
||||
Q_UNUSED(event);
|
||||
|
||||
TechDraw::DrawRichAnno *annotation = dynamic_cast<TechDraw::DrawRichAnno *>(getViewObject());
|
||||
if (!annotation)
|
||||
void QGIRichAnno::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
|
||||
{
|
||||
TechDraw::DrawRichAnno* annoFeat = getFeature();
|
||||
if (!annoFeat || annoFeat->isLocked()) {
|
||||
setCursor(Qt::ArrowCursor);
|
||||
QGIView::hoverMoveEvent(event);
|
||||
return;
|
||||
}
|
||||
// Mouse position in QGIRichAnno's local coordinates
|
||||
QPointF localPos = mapFromScene(event->scenePos());
|
||||
|
||||
QString text = QString::fromUtf8(annotation->AnnoText.getValue());
|
||||
// Calculate visual edges of m_rect in QGIRichAnno's local coordinates
|
||||
double visualRectLeftEdgeX = m_rect->x() + m_rect->rect().left();
|
||||
double visualRectRightEdgeX = m_rect->x() + m_rect->rect().right();
|
||||
|
||||
QDialog dialog(nullptr);
|
||||
dialog.setWindowTitle(QObject::tr("Rich text editor"));
|
||||
dialog.setMinimumWidth(400);
|
||||
dialog.setMinimumHeight(400);
|
||||
bool onLeftEdge = qAbs(localPos.x() - visualRectLeftEdgeX) < HandleInteractionMargin;
|
||||
bool onRightEdge = qAbs(localPos.x() - visualRectRightEdgeX) < HandleInteractionMargin;
|
||||
|
||||
MRichTextEdit richEdit(&dialog, text);
|
||||
QGridLayout gridLayout(&dialog);
|
||||
gridLayout.addWidget(&richEdit, 0, 0, 1, 1);
|
||||
if (onLeftEdge || onRightEdge) {
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
}
|
||||
else {
|
||||
setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
QGIView::hoverMoveEvent(event);
|
||||
}
|
||||
|
||||
connect(&richEdit, &MRichTextEdit::saveText, &dialog, &QDialog::accept);
|
||||
connect(&richEdit, &MRichTextEdit::editorFinished, &dialog, &QDialog::reject);
|
||||
void QGIRichAnno::mousePressEvent(QGraphicsSceneMouseEvent* event)
|
||||
{
|
||||
TechDraw::DrawRichAnno* annoFeat = getFeature();
|
||||
// Allow resizing even if MaxWidth is initially -1 or 0, as long as frame is shown
|
||||
if (event->button() != Qt::LeftButton || !annoFeat || annoFeat->isLocked()) {
|
||||
QGIView::mousePressEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dialog.exec()) {
|
||||
QString newText = richEdit.toHtml();
|
||||
if (newText != text) {
|
||||
App::GetApplication().setActiveTransaction("Set Rich Annotation Text");
|
||||
annotation->AnnoText.setValue(newText.toStdString());
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
// Mouse position in QGIRichAnno's local coordinates
|
||||
QPointF localPos = mapFromScene(event->scenePos());
|
||||
m_currentResizeHandle = ResizeHandle::NoHandle;
|
||||
|
||||
// Calculate visual edges of m_rect in QGIRichAnno's local coordinates
|
||||
// m_rect's geometry should be up-to-date from the last draw/updateView
|
||||
double visualRectLeftEdgeX = m_rect->x() + m_rect->rect().left();
|
||||
double visualRectRightEdgeX = m_rect->x() + m_rect->rect().right();
|
||||
|
||||
if (qAbs(localPos.x() - visualRectLeftEdgeX) < HandleInteractionMargin) {
|
||||
m_currentResizeHandle = ResizeHandle::LeftHandle;
|
||||
}
|
||||
else if (qAbs(localPos.x() - visualRectRightEdgeX) < HandleInteractionMargin) {
|
||||
m_currentResizeHandle = ResizeHandle::RightHandle;
|
||||
}
|
||||
|
||||
if (m_currentResizeHandle != ResizeHandle::NoHandle) {
|
||||
m_isResizing = true;
|
||||
m_isDraggingMidResize = false;
|
||||
m_transactionOpen = false;
|
||||
m_dragStartMouseScenePos = event->scenePos();
|
||||
m_initialItemScenePos = this->scenePos();
|
||||
|
||||
// Determine initial text width for resizing
|
||||
if (annoFeat->MaxWidth.getValue() > 0.0) {
|
||||
m_initialTextWidthScene = Rez::guiX(annoFeat->MaxWidth.getValue());
|
||||
}
|
||||
else {
|
||||
if (m_rect) {
|
||||
m_initialTextWidthScene = m_rect->rect().width();
|
||||
}
|
||||
else {
|
||||
// Fallback, should not happen
|
||||
m_initialTextWidthScene = Rez::guiX(MinTextWidthDocument * 2);
|
||||
}
|
||||
}
|
||||
|
||||
m_lastGoodWidthScene = m_initialTextWidthScene;
|
||||
|
||||
event->accept();
|
||||
}
|
||||
else {
|
||||
m_isResizing = false;
|
||||
QGIView::mousePressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void QGIRichAnno::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
|
||||
{
|
||||
if (m_isResizing && m_currentResizeHandle != ResizeHandle::NoHandle) {
|
||||
TechDraw::DrawRichAnno* annoFeat = getFeature();
|
||||
if (!annoFeat) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_isDraggingMidResize) { // First actual move during this resize op
|
||||
if (!Gui::Control().activeDialog()) {
|
||||
Gui::Command::openCommand(
|
||||
QObject::tr("Resize Rich Annotation").toStdString().c_str());
|
||||
}
|
||||
m_transactionOpen = true;
|
||||
m_isDraggingMidResize = true;
|
||||
}
|
||||
|
||||
QPointF currentMouseScenePos = event->scenePos();
|
||||
double mouseDeltaSceneX = currentMouseScenePos.x() - m_dragStartMouseScenePos.x();
|
||||
|
||||
// 1. Calculate the raw target width based on mouse movement
|
||||
double targetVisualWidthScene = (m_currentResizeHandle == ResizeHandle::RightHandle)
|
||||
? m_initialTextWidthScene + mouseDeltaSceneX
|
||||
: m_initialTextWidthScene - mouseDeltaSceneX;
|
||||
|
||||
// Clamp against the geometric minimum
|
||||
double geometricMinWidthScene = Rez::guiX(MinTextWidthDocument);
|
||||
if (targetVisualWidthScene < geometricMinWidthScene) {
|
||||
targetVisualWidthScene = geometricMinWidthScene;
|
||||
}
|
||||
|
||||
// --- 2. Perform Synchronous "What-If" Analysis ---
|
||||
double finalVisualWidthScene = 0;
|
||||
const double originalDocWidth = m_text->document()->textWidth();
|
||||
const double targetWidthLocal = targetVisualWidthScene / m_textScaleFactor;
|
||||
m_text->document()->setTextWidth(targetWidthLocal);
|
||||
QSizeF actualSizeLocal = m_text->document()->documentLayout()->documentSize();
|
||||
m_text->document()->setTextWidth(originalDocWidth);
|
||||
|
||||
constexpr double tolerance = 1e-5;
|
||||
if (actualSizeLocal.width() > targetWidthLocal + tolerance) {
|
||||
finalVisualWidthScene = m_lastGoodWidthScene;
|
||||
}
|
||||
else {
|
||||
finalVisualWidthScene = targetVisualWidthScene;
|
||||
m_lastGoodWidthScene = finalVisualWidthScene;
|
||||
}
|
||||
|
||||
if (annoFeat->OriginCentered.getValue()) {
|
||||
// --- 3. Calculate Final CENTER Position based on Validated Width ---
|
||||
// Calculate the *actual* change in width that resulted from the what-if analysis.
|
||||
const double actualWidthChange = finalVisualWidthScene - m_initialTextWidthScene;
|
||||
|
||||
// The center of the item moves by exactly half of this actual change.
|
||||
double positionDeltaX = 0;
|
||||
if (m_currentResizeHandle == ResizeHandle::RightHandle) {
|
||||
// Width was added to the right side, so center moves right.
|
||||
positionDeltaX = actualWidthChange / 2.0;
|
||||
}
|
||||
else { // LeftHandle
|
||||
// Width was added to the left side, so center moves left.
|
||||
positionDeltaX = -actualWidthChange / 2.0;
|
||||
}
|
||||
|
||||
const double newItemScenePosX = m_initialItemScenePos.x() + positionDeltaX;
|
||||
|
||||
annoFeat->MaxWidth.setValue(Rez::appX(finalVisualWidthScene));
|
||||
annoFeat->X.setValue(Rez::appX(newItemScenePosX));
|
||||
|
||||
updateView(true);
|
||||
}
|
||||
else {
|
||||
// --- 3. Calculate Final Position based on Validated Width ---
|
||||
double newItemScenePosX = 0;
|
||||
if (m_currentResizeHandle == ResizeHandle::RightHandle) {
|
||||
newItemScenePosX = m_initialItemScenePos.x();
|
||||
}
|
||||
else { // LeftHandle
|
||||
newItemScenePosX =
|
||||
m_initialItemScenePos.x() + (m_initialTextWidthScene - finalVisualWidthScene);
|
||||
}
|
||||
|
||||
// --- 4. Commit Final, Validated State to the Feature ---
|
||||
annoFeat->MaxWidth.setValue(Rez::appX(finalVisualWidthScene));
|
||||
annoFeat->X.setValue(Rez::appX(newItemScenePosX));
|
||||
}
|
||||
|
||||
// The property changes will trigger QGIRichAnno::updateView, which handles the visual
|
||||
// update.
|
||||
|
||||
QTimer::singleShot(0, this, [this]() {
|
||||
if (this && scene()) {
|
||||
Q_EMIT positionChanged(scenePos());
|
||||
}
|
||||
});
|
||||
|
||||
event->accept();
|
||||
}
|
||||
else {
|
||||
QGIView::mouseMoveEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void QGIRichAnno::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
|
||||
{
|
||||
if (m_isResizing) {
|
||||
if (m_transactionOpen) {
|
||||
// Only commit if actual dragging (and thus property changes) occurred.
|
||||
// m_isDraggingMidResize flag indicates if mouseMoveEvent was processed.
|
||||
if (!Gui::Control().activeDialog()) {
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
m_transactionOpen = false;
|
||||
widthChanged();
|
||||
}
|
||||
m_isResizing = false;
|
||||
m_isDraggingMidResize = false;
|
||||
m_currentResizeHandle = ResizeHandle::NoHandle;
|
||||
setCursor(Qt::ArrowCursor); // Reset cursor
|
||||
|
||||
// Ensure focus returns to the text item after the resize handle is released
|
||||
refocusAnnotation();
|
||||
|
||||
Q_EMIT positionChanged(scenePos());
|
||||
|
||||
if (!isUnderMouse()) {
|
||||
QGraphicsSceneHoverEvent leaveEvent(QEvent::GraphicsSceneHoverLeave);
|
||||
hoverLeaveEvent(&leaveEvent); // Manually trigger leave event
|
||||
}
|
||||
|
||||
event->accept();
|
||||
}
|
||||
else {
|
||||
QGIView::mouseReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void QGIRichAnno::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) {
|
||||
Q_UNUSED(event);
|
||||
|
||||
// If resizing was in progress, cancel it to avoid conflict with dialog
|
||||
if (m_isResizing) {
|
||||
if (m_transactionOpen) {
|
||||
// To avoid partial changes, might need to revert or just abort.
|
||||
// For simplicity, we commit if open. A better way would be to store original values and revert.
|
||||
if (!Gui::Control().activeDialog()) {
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
m_transactionOpen = false;
|
||||
}
|
||||
m_isResizing = false;
|
||||
m_isDraggingMidResize = false;
|
||||
m_currentResizeHandle = ResizeHandle::NoHandle;
|
||||
setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
|
||||
auto vp = static_cast<ViewProviderRichAnno*>(getViewProvider(getViewObject()));
|
||||
if (!vp) {
|
||||
return;
|
||||
}
|
||||
vp->doubleClicked();
|
||||
}
|
||||
|
||||
void QGIRichAnno::setEditMode(bool enable)
|
||||
{
|
||||
m_isEditing = enable;
|
||||
if (enable) {
|
||||
m_text->setTextInteractionFlags(Qt::TextEditorInteraction);
|
||||
|
||||
QTextCursor cursor = m_text->textCursor();
|
||||
|
||||
// Check if the document is empty. If so, create a default format.
|
||||
if (m_text->document()->isEmpty()) {
|
||||
// Document is empty, so we need to create a default style from scratch.
|
||||
// Let's use the default label font from preferences.
|
||||
QFont font = PreferencesGui::labelFontQFont();
|
||||
|
||||
QTextCharFormat defaultFormat;
|
||||
defaultFormat.setFontPointSize(font.pointSizeF());
|
||||
cursor.setCharFormat(defaultFormat);
|
||||
}
|
||||
else {
|
||||
// Document has content. Let's use the format of the first character
|
||||
// as the default for any new text.
|
||||
cursor.setPosition(0);
|
||||
QTextCharFormat formatAtStart = cursor.charFormat();
|
||||
|
||||
// Move the cursor back to its original position (or end of document)
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
|
||||
// Apply the format from the start of the document to the current cursor position.
|
||||
// This sets the "default" format for subsequent typing.
|
||||
cursor.setCharFormat(formatAtStart);
|
||||
}
|
||||
|
||||
// IMPORTANT: Apply the modified cursor back to the text item.
|
||||
m_text->setTextCursor(cursor);
|
||||
|
||||
refocusAnnotation();
|
||||
|
||||
Q_EMIT positionChanged(scenePos());
|
||||
}
|
||||
else {
|
||||
m_text->setTextInteractionFlags(Qt::NoTextInteraction);
|
||||
m_text->clearFocus();
|
||||
clearFocus();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void QGIRichAnno::refocusAnnotation()
|
||||
{
|
||||
if (scene()) {
|
||||
if (!scene()->views().isEmpty()) {
|
||||
scene()->views().first()->setFocus();
|
||||
}
|
||||
|
||||
scene()->setFocusItem(m_text, Qt::OtherFocusReason);
|
||||
}
|
||||
}
|
||||
|
||||
QTextDocument* QGIRichAnno::document() const
|
||||
{
|
||||
return m_text->document();
|
||||
}
|
||||
|
||||
QTextCursor QGIRichAnno::textCursor() const
|
||||
{
|
||||
return m_text->textCursor();
|
||||
}
|
||||
|
||||
void QGIRichAnno::setTextCursor(const QTextCursor& cursor)
|
||||
{
|
||||
m_text->setTextCursor(cursor);
|
||||
}
|
||||
|
||||
void QGIRichAnno::onContentsChanged()
|
||||
{
|
||||
// Only process changes when in edit mode to avoid loops during setup
|
||||
if (m_isEditing) {
|
||||
// Update the feature property in real-time
|
||||
getFeature()->AnnoText.setValue(m_text->toHtml().toUtf8());
|
||||
// Emit signal for the task panel
|
||||
Q_EMIT textChanged();
|
||||
drawBorder(); // Make sure view frame is updated.
|
||||
}
|
||||
}
|
||||
|
||||
QVariant QGIRichAnno::itemChange(GraphicsItemChange change, const QVariant& value)
|
||||
{
|
||||
if (change == QGraphicsItem::ItemScenePositionHasChanged
|
||||
&& scene()) {
|
||||
Q_EMIT positionChanged(scenePos());
|
||||
}
|
||||
return QGIView::itemChange(change, value);
|
||||
}
|
||||
|
||||
void QGIRichAnno::updateLayout()
|
||||
{
|
||||
TechDraw::DrawRichAnno* annoFeat = getFeature();
|
||||
if (!annoFeat || !m_text || m_textScaleFactor <= 0.0) {
|
||||
return;
|
||||
}
|
||||
prepareGeometryChange();
|
||||
|
||||
double maxWidthDoc = annoFeat->MaxWidth.getValue();
|
||||
if (maxWidthDoc > 0.0) {
|
||||
m_text->setTextWidth(Rez::guiX(maxWidthDoc) / m_textScaleFactor);
|
||||
}
|
||||
else {
|
||||
m_text->setTextWidth(-1.0);
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
if (scene()) {
|
||||
Q_EMIT positionChanged(scenePos());
|
||||
}
|
||||
drawBorder();
|
||||
}
|
||||
|
||||
#include <Mod/TechDraw/Gui/moc_QGIRichAnno.cpp>
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Mod/TechDraw/TechDrawGlobal.h>
|
||||
@@ -33,10 +32,11 @@
|
||||
#include "QGIUserTypes.h"
|
||||
|
||||
|
||||
namespace TechDraw {
|
||||
namespace TechDraw
|
||||
{
|
||||
class DrawRichAnno;
|
||||
class DrawLeaderLine;
|
||||
}
|
||||
} // namespace TechDraw
|
||||
|
||||
namespace TechDrawGui
|
||||
{
|
||||
@@ -50,23 +50,33 @@ class QGCustomRect;
|
||||
|
||||
//*******************************************************************
|
||||
|
||||
class TechDrawGuiExport QGIRichAnno : public QGIView
|
||||
class TechDrawGuiExport QGIRichAnno: public QGIView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum {Type = UserType::QGIRichAnno};
|
||||
|
||||
enum class ResizeHandle
|
||||
{
|
||||
NoHandle,
|
||||
LeftHandle,
|
||||
RightHandle
|
||||
// Future: TopHandle, BottomHandle, CornerHandles
|
||||
};
|
||||
|
||||
explicit QGIRichAnno();
|
||||
~QGIRichAnno() override = default;
|
||||
|
||||
int type() const override { return Type;}
|
||||
void paint( QPainter * painter,
|
||||
const QStyleOptionGraphicsItem * option,
|
||||
QWidget * widget = nullptr ) override;
|
||||
int type() const override
|
||||
{
|
||||
return Type;
|
||||
}
|
||||
void paint(QPainter* painter,
|
||||
const QStyleOptionGraphicsItem* option,
|
||||
QWidget* widget = nullptr) override;
|
||||
QRectF boundingRect() const override;
|
||||
|
||||
void drawBorder() override;
|
||||
void updateView(bool update = false) override;
|
||||
|
||||
void setTextItem();
|
||||
@@ -74,26 +84,74 @@ public:
|
||||
virtual TechDraw::DrawRichAnno* getFeature();
|
||||
QPen rectPen() const;
|
||||
|
||||
void setExportingPdf(bool b) { m_isExportingPdf = b; }
|
||||
bool getExportingPdf() const { return m_isExportingPdf; }
|
||||
void setExportingSvg(bool b) { m_isExportingSvg = b; }
|
||||
bool getExportingSvg() const { return m_isExportingSvg; }
|
||||
void setExportingPdf(bool b)
|
||||
{
|
||||
m_isExportingPdf = b;
|
||||
}
|
||||
bool getExportingPdf() const
|
||||
{
|
||||
return m_isExportingPdf;
|
||||
}
|
||||
void setExportingSvg(bool b)
|
||||
{
|
||||
m_isExportingSvg = b;
|
||||
}
|
||||
bool getExportingSvg() const
|
||||
{
|
||||
return m_isExportingSvg;
|
||||
}
|
||||
|
||||
void setEditMode(bool enable);
|
||||
QTextDocument* document() const;
|
||||
QTextCursor textCursor() const;
|
||||
void setTextCursor(const QTextCursor& cursor);
|
||||
void updateLayout();
|
||||
|
||||
void refocusAnnotation();
|
||||
|
||||
Q_SIGNALS:
|
||||
void widthChanged();
|
||||
void textChanged();
|
||||
void selectionChanged();
|
||||
void positionChanged(const QPointF& scenePos);
|
||||
|
||||
protected:
|
||||
void draw() override;
|
||||
void hoverMoveEvent(QGraphicsSceneHoverEvent* event) override;
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent* event) override;
|
||||
void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override;
|
||||
|
||||
void setLineSpacing(int lineSpacing);
|
||||
QFont prefFont(void);
|
||||
|
||||
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) override;
|
||||
|
||||
QString convertTextSizes(const QString& inHtml) const;
|
||||
QVariant itemChange(GraphicsItemChange change, const QVariant& value) override;
|
||||
|
||||
bool m_isExportingPdf;
|
||||
bool m_isExportingSvg;
|
||||
QGCustomText* m_text;
|
||||
bool m_hasHover;
|
||||
QGCustomRect* m_rect;
|
||||
|
||||
// For resizing
|
||||
ResizeHandle m_currentResizeHandle;
|
||||
bool m_isResizing;
|
||||
bool m_isDraggingMidResize; // True if mouse has moved significantly after press during resize
|
||||
bool m_transactionOpen; // True if a Gui::Command transaction is open
|
||||
QPointF m_dragStartMouseScenePos;
|
||||
QPointF m_initialItemScenePos; // Scene pos of QGIRichAnno item (center)
|
||||
double m_initialTextWidthScene; // Scene units, from MaxWidth property
|
||||
|
||||
static const double HandleInteractionMargin; // Margin for grabbing handles (scene units)
|
||||
static const double MinTextWidthDocument; // Minimum resizable width (document units)
|
||||
|
||||
bool m_isEditing;
|
||||
double m_textScaleFactor;
|
||||
double m_lastGoodWidthScene;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onContentsChanged();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -696,7 +696,11 @@ void QGIView::layoutDecorations(const QRectF& contentArea,
|
||||
constexpr double padding{10};
|
||||
QRectF paddedContentArea = contentArea.adjusted(-padding, -padding, padding, padding);
|
||||
|
||||
double frameWidth = qMax(paddedContentArea.width(), labelRect.width());
|
||||
double frameWidth = paddedContentArea.width();
|
||||
// For standard views, expand frame to fit label. For RichAnno, keep frame tight to text.
|
||||
if (type() != UserType::QGIRichAnno) {
|
||||
frameWidth = qMax(frameWidth, labelRect.width());
|
||||
}
|
||||
double frameHeight = paddedContentArea.height();
|
||||
|
||||
outFrameRect = QRectF(paddedContentArea.center().x() - (frameWidth / 2),
|
||||
|
||||
@@ -393,6 +393,15 @@ void QGVPage::keyPressEvent(QKeyEvent* event)
|
||||
toolHandler->keyPressEvent(event);
|
||||
}
|
||||
else {
|
||||
if (scene() && scene()->focusItem() != nullptr) {
|
||||
// The event belongs to the focused item. The base QGraphicsView implementation
|
||||
// will handle forwarding it correctly.
|
||||
QGraphicsView::keyPressEvent(event);
|
||||
|
||||
// We MUST return here to prevent the navigation style from also
|
||||
// processing (and likely consuming) the event.
|
||||
return;
|
||||
}
|
||||
m_navStyle->handleKeyPressEvent(event);
|
||||
}
|
||||
if (!event->isAccepted()) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,9 +26,8 @@
|
||||
#include <Gui/TaskView/TaskDialog.h>
|
||||
#include <Gui/TaskView/TaskView.h>
|
||||
#include <Mod/TechDraw/TechDrawGlobal.h>
|
||||
|
||||
|
||||
class MRichTextEdit;
|
||||
#include "mrichtextedit.h"
|
||||
#include "MDIViewPage.h"
|
||||
|
||||
class Ui_TaskRichAnno;
|
||||
|
||||
@@ -43,9 +42,9 @@ namespace TechDrawGui
|
||||
{
|
||||
class QGIView;
|
||||
class QGIPrimPath;
|
||||
class QGIRichAnno;
|
||||
class MDIViewPage;
|
||||
class QGMText;
|
||||
class QGIRichAnno;
|
||||
class ViewProviderRichAnno;
|
||||
class ViewProviderPage;
|
||||
|
||||
@@ -54,45 +53,60 @@ class TaskRichAnno : public QWidget
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TaskRichAnno(TechDraw::DrawView* baseFeat,
|
||||
TechDraw::DrawPage* page);
|
||||
TaskRichAnno(TechDraw::DrawView* baseFeat, TechDraw::DrawPage* page);
|
||||
explicit TaskRichAnno(TechDrawGui::ViewProviderRichAnno* annoVP);
|
||||
~TaskRichAnno() override = default;
|
||||
~TaskRichAnno() override;
|
||||
|
||||
void finishSetup();
|
||||
|
||||
virtual bool accept();
|
||||
virtual bool reject();
|
||||
virtual void setCreateMode(bool mode) { m_createMode = mode; }
|
||||
virtual bool getCreateMode() { return m_createMode; }
|
||||
void updateTask();
|
||||
void saveButtons(QPushButton* btnOK,
|
||||
QPushButton* btnCancel);
|
||||
void saveButtons(QPushButton* btnOK, QPushButton* btnCancel);
|
||||
void enableTaskButtons(bool enable);
|
||||
|
||||
public Q_SLOTS:
|
||||
void onEditorClicked(bool clicked);
|
||||
ViewProviderRichAnno* getAnnoVP() const
|
||||
{
|
||||
return m_annoVP;
|
||||
}
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *event) override;
|
||||
void changeEvent(QEvent* event) override;
|
||||
void focusOutEvent(QFocusEvent* event) override;
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
|
||||
void createAnnoFeature();
|
||||
void updateAnnoFeature();
|
||||
void createAnnoFeature(const QPointF* scenePos = nullptr);
|
||||
void commonFeatureUpdate();
|
||||
void removeFeature();
|
||||
|
||||
QPointF calcTextStartPos(double scale);
|
||||
|
||||
void setUiPrimary();
|
||||
void setUiEdit();
|
||||
void enableTextUi(bool enable);
|
||||
void enableVPUi(bool enable);
|
||||
double prefWeight() const;
|
||||
Base::Color prefLineColor();
|
||||
|
||||
protected Q_SLOTS:
|
||||
void onSaveAndExit(QString);
|
||||
void onEditorExit();
|
||||
void onMaxWidthChanged(double value);
|
||||
void onViewWidthChanged();
|
||||
void onShowFrameToggled(bool checked);
|
||||
void onFrameColorChanged();
|
||||
void onFrameWidthChanged(double value);
|
||||
void onFrameStyleChanged(int index);
|
||||
void onViewTransformed();
|
||||
void refocusAnnotation();
|
||||
|
||||
void onViewSelectionChanged();
|
||||
void onViewPositionChanged(const QPointF& scenePos);
|
||||
|
||||
private:
|
||||
void removeViewFilter();
|
||||
void enterPlacementMode();
|
||||
void createAndSetupAnnotation(const QPointF* scenePos = nullptr);
|
||||
void createAnnoIfNotAlready();
|
||||
|
||||
std::unique_ptr<Ui_TaskRichAnno> ui;
|
||||
|
||||
ViewProviderPage* m_vpp;
|
||||
@@ -103,18 +117,22 @@ private:
|
||||
QGIView* m_qgParent;
|
||||
std::string m_qgParentName;
|
||||
|
||||
Base::Vector3d m_attachPoint;
|
||||
|
||||
bool m_createMode;
|
||||
bool m_placementMode;
|
||||
|
||||
bool m_inProgressLock;
|
||||
|
||||
QPushButton* m_btnOK;
|
||||
QPushButton* m_btnCancel;
|
||||
|
||||
QDialog* m_textDialog;
|
||||
MRichTextEdit* m_rte;
|
||||
QString m_title;
|
||||
|
||||
QGIRichAnno* m_qgiAnno;
|
||||
bool m_syncLock;
|
||||
MDIViewPage* m_view;
|
||||
|
||||
QPointer<MRichTextEdit> m_toolbar {nullptr};
|
||||
QPointer<QWidget> m_viewport {nullptr};
|
||||
};
|
||||
|
||||
class TaskDlgRichAnno : public Gui::TaskView::TaskDialog
|
||||
@@ -122,28 +140,28 @@ class TaskDlgRichAnno : public Gui::TaskView::TaskDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TaskDlgRichAnno(TechDraw::DrawView* baseFeat,
|
||||
TechDraw::DrawPage* page);
|
||||
TaskDlgRichAnno(TechDraw::DrawView* baseFeat, TechDraw::DrawPage* page);
|
||||
explicit TaskDlgRichAnno(TechDrawGui::ViewProviderRichAnno* annoVP);
|
||||
~TaskDlgRichAnno() override;
|
||||
|
||||
public:
|
||||
/// is called the TaskView when the dialog is opened
|
||||
void open() override;
|
||||
/// is called by the framework if an button is clicked which has no accept or reject role
|
||||
void clicked(int) override;
|
||||
/// is called by the framework if the dialog is accepted (Ok)
|
||||
bool accept() override;
|
||||
/// is called by the framework if the dialog is rejected (Cancel)
|
||||
bool reject() override;
|
||||
void autoClosedOnTransactionChange() override;
|
||||
/// is called by the framework if the user presses the help button
|
||||
bool isAllowedAlterDocument() const override
|
||||
{ return false; }
|
||||
void update();
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void modifyStandardButtons(QDialogButtonBox* box) override;
|
||||
|
||||
protected:
|
||||
bool isFor(ViewProviderRichAnno* vp) const
|
||||
{
|
||||
return (widget->getAnnoVP() == vp);
|
||||
}
|
||||
|
||||
private:
|
||||
TaskRichAnno * widget;
|
||||
|
||||
@@ -6,22 +6,10 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>409</width>
|
||||
<height>419</height>
|
||||
<width>150</width>
|
||||
<height>80</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Rich Text Annotation Block</string>
|
||||
</property>
|
||||
@@ -31,209 +19,157 @@
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<widget class="QLabel" name="label_BaseFeature">
|
||||
<property name="text">
|
||||
<string>Base feature</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Box</enum>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="leBaseView">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_MaxWidth">
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="dsbMaxWidth">
|
||||
<property name="toolTip">
|
||||
<string>Maximal width of the annotation block, if -1 then automatic width</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>-1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="gbFrame">
|
||||
<property name="title">
|
||||
<string>Frame</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_FrameProperties">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Base feature</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="leBaseView">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Max width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="dsbMaxWidth">
|
||||
<property name="toolTip">
|
||||
<string>Maximal width, if -1 then automatic width</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>-1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QLabel" name="label_frameThickness">
|
||||
<property name="text">
|
||||
<string>Thickness</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="dsbWidth">
|
||||
<property name="toolTip">
|
||||
<string>Line width</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.500000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<widget class="QLabel" name="label_patternThickness">
|
||||
<property name="text">
|
||||
<string>Style</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="cFrameStyle">
|
||||
<property name="toolTip">
|
||||
<string>Line style</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>NoLine</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Continuous</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Dash</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Dot</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DashDot</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DashDotDot</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QPushButton" name="pbEditor">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QLabel" name="label_colorThickness">
|
||||
<property name="text">
|
||||
<string>Start Rich Text Editor</string>
|
||||
<string>Color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QTextEdit" name="teAnnoText">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<item row="2" column="1">
|
||||
<widget class="Gui::ColorButton" name="cpFrameColor">
|
||||
<property name="toolTip">
|
||||
<string>Line color</string>
|
||||
</property>
|
||||
<property name="color">
|
||||
<color>
|
||||
<red>0</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Show frame</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="cbShowFrame">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::ColorButton" name="cpFrameColor">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Line color</string>
|
||||
</property>
|
||||
<property name="color">
|
||||
<color>
|
||||
<red>0</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="dsbWidth">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Line width</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.500000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Style</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="cFrameStyle">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Line style</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>NoLine</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Continuous</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Dash</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Dot</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DashDot</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DashDotDot</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -256,52 +192,52 @@
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>cbShowFrame</sender>
|
||||
<sender>gbFrame</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>cpFrameColor</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>183</x>
|
||||
<y>270</y>
|
||||
<x>250</x> <!-- Adjust X hint if needed -->
|
||||
<y>380</y> <!-- Adjust Y hint if needed -->
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>183</x>
|
||||
<y>294</y>
|
||||
<x>380</x> <!-- Adjust X hint if needed -->
|
||||
<y>380</y> <!-- Adjust Y hint if needed -->
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>cbShowFrame</sender>
|
||||
<sender>gbFrame</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>cFrameStyle</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>236</x>
|
||||
<y>277</y>
|
||||
<x>250</x>
|
||||
<y>380</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>236</x>
|
||||
<y>354</y>
|
||||
<x>330</x> <!-- Adjust X hint if needed -->
|
||||
<y>380</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>cbShowFrame</sender>
|
||||
<sender>gbFrame</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>dsbWidth</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>236</x>
|
||||
<y>277</y>
|
||||
<x>250</x>
|
||||
<y>380</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>236</x>
|
||||
<y>328</y>
|
||||
<x>280</x> <!-- Adjust X hint if needed -->
|
||||
<y>380</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
</ui>
|
||||
@@ -57,7 +57,7 @@ const char* ViewProviderRichAnno::LineStyleEnums[] = { "NoLine",
|
||||
|
||||
ViewProviderRichAnno::ViewProviderRichAnno()
|
||||
{
|
||||
sPixmap = "actions/TechDraw_RichTextAnnotation";
|
||||
sPixmap = "actions/TechDraw_Annotation";
|
||||
|
||||
static const char *group = "Frame Format";
|
||||
|
||||
@@ -70,6 +70,21 @@ ViewProviderRichAnno::ViewProviderRichAnno()
|
||||
}
|
||||
|
||||
|
||||
bool ViewProviderRichAnno::setEdit(int ModNum)
|
||||
{
|
||||
if (ModNum != Gui::ViewProvider::Default) {
|
||||
return Gui::ViewProviderDocumentObject::setEdit(ModNum);
|
||||
}
|
||||
if (Gui::Control().activeDialog()) {
|
||||
return false; // TaskPanel already open!
|
||||
}
|
||||
|
||||
// clear the selection (convenience)
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Control().showDialog(new TaskDlgRichAnno(this));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ViewProviderRichAnno::doubleClicked()
|
||||
{
|
||||
// Base::Console().message("VPRA::doubleClicked()\n");
|
||||
@@ -201,3 +216,20 @@ std::vector<App::DocumentObject*> ViewProviderRichAnno::claimChildren() const
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
bool ViewProviderRichAnno::onDelete(const std::vector<std::string>& subs)
|
||||
{
|
||||
Q_UNUSED(subs);
|
||||
|
||||
// Check if there is an active dialog
|
||||
if (Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog()) {
|
||||
// Check if the active dialog is our RichAnno dialog
|
||||
if (auto* richAnnoDlg = dynamic_cast<TaskDlgRichAnno*>(dlg)) {
|
||||
// Check if the dialog is for THIS specific view provider
|
||||
if (richAnnoDlg->isFor(this)) {
|
||||
Gui::Control().closeDialog(); // Close the dialog gracefully
|
||||
}
|
||||
}
|
||||
}
|
||||
return true; // Allow deletion to proceed
|
||||
}
|
||||
@@ -54,8 +54,10 @@ public:
|
||||
bool useNewSelectionModel() const override {return false;}
|
||||
void updateData(const App::Property* prop) override;
|
||||
void onChanged(const App::Property* prop) override;
|
||||
bool onDelete(const std::vector<std::string>&) override;
|
||||
bool doubleClicked() override;
|
||||
bool canDelete(App::DocumentObject* obj) const override;
|
||||
bool setEdit(int ModNum) override;
|
||||
|
||||
static const char* LineStyleEnums[];
|
||||
|
||||
|
||||
@@ -387,9 +387,8 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
|
||||
|
||||
Gui::ToolBarItem* anno = new Gui::ToolBarItem(root);
|
||||
anno->setCommand("TechDraw Annotation");
|
||||
*anno << "TechDraw_Annotation";
|
||||
*anno << "TechDraw_LeaderLine";
|
||||
*anno << "TechDraw_RichTextAnnotation";
|
||||
*anno << "TechDraw_LeaderLine";
|
||||
*anno << "TechDraw_CosmeticVertexGroup";
|
||||
*anno << "TechDraw_CenterLineGroup";
|
||||
*anno << "TechDraw_2PointCosmeticLine";
|
||||
|
||||
@@ -58,8 +58,12 @@
|
||||
using namespace TechDrawGui;
|
||||
using namespace TechDraw;
|
||||
|
||||
MRichTextEdit::MRichTextEdit(QWidget *parent, QString textIn) : QWidget(parent) {
|
||||
MRichTextEdit::MRichTextEdit(QWidget *parent, QString textIn) : QFrame(parent) {
|
||||
setupUi(this);
|
||||
|
||||
f_fontsize->setEditable(true);
|
||||
f_fontsize->setMinimumContentsLength(3);
|
||||
|
||||
m_lastBlockList = nullptr;
|
||||
f_textedit->setTabStopDistance(40);
|
||||
setDefFontSize(TechDrawGui::PreferencesGui::labelFontSizePX());
|
||||
@@ -228,8 +232,6 @@ MRichTextEdit::MRichTextEdit(QWidget *parent, QString textIn) : QWidget(parent)
|
||||
int intSize = round(currSize);
|
||||
QString qsSize = QString::number(intSize);
|
||||
addFontSize(qsSize);
|
||||
int fSize = f_fontsize->findText(qsSize);
|
||||
f_fontsize ->setCurrentIndex(fSize);
|
||||
} else {
|
||||
QTextCursor cursor = f_textedit->textCursor();
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
@@ -239,10 +241,9 @@ MRichTextEdit::MRichTextEdit(QWidget *parent, QString textIn) : QWidget(parent)
|
||||
fmt.setFontPointSize(getDefFontSizeNum());
|
||||
|
||||
addFontSize(getDefFontSize());
|
||||
|
||||
f_fontsize->setCurrentIndex(f_fontsize->findText(getDefFontSize()));
|
||||
}
|
||||
|
||||
updateFontSizeDisplay();
|
||||
}
|
||||
|
||||
|
||||
@@ -486,14 +487,15 @@ void MRichTextEdit::list(bool checked, QTextListFormat::Style style) {
|
||||
QTextBlockFormat bfmt;
|
||||
bfmt.setIndent(obfmt.indent());
|
||||
cursor.setBlockFormat(bfmt);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
QTextListFormat listFmt;
|
||||
if (cursor.currentList()) {
|
||||
listFmt = cursor.currentList()->format();
|
||||
}
|
||||
listFmt.setStyle(style);
|
||||
cursor.createList(listFmt);
|
||||
}
|
||||
}
|
||||
cursor.endEditBlock();
|
||||
}
|
||||
|
||||
@@ -507,17 +509,15 @@ void MRichTextEdit::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
|
||||
f_textedit->setFocus(Qt::TabFocusReason);
|
||||
}
|
||||
|
||||
void MRichTextEdit::slotCursorPositionChanged() {
|
||||
void MRichTextEdit::slotCursorPositionChanged()
|
||||
{
|
||||
updateFontSizeDisplay();
|
||||
|
||||
//why do we change text style when selecting text?
|
||||
QTextCursor cursor = f_textedit->textCursor();
|
||||
if (cursor.hasSelection()) { //let selection logic handle this
|
||||
return;
|
||||
}
|
||||
//set font size widget to match curr char size
|
||||
QTextCharFormat fmt = cursor.charFormat();
|
||||
double currSize = fmt.fontPointSize();
|
||||
int fSize = f_fontsize->findText(QString::number(currSize));
|
||||
f_fontsize ->setCurrentIndex(fSize);
|
||||
|
||||
QTextList *l = f_textedit->textCursor().currentList();
|
||||
|
||||
@@ -704,20 +704,25 @@ void MRichTextEdit::onExit()
|
||||
Q_EMIT editorFinished();
|
||||
}
|
||||
|
||||
void MRichTextEdit::onSelectionChanged()
|
||||
void MRichTextEdit::updateFontSizeDisplay()
|
||||
{
|
||||
if (hasMultipleSizes()) {
|
||||
f_fontsize->setEditText(QString());
|
||||
f_fontsize->setCurrentIndex(-1);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
QTextCursor cursor = f_textedit->textCursor();
|
||||
QTextCharFormat fmt = cursor.charFormat();
|
||||
double currSize = fmt.fontPointSize();
|
||||
int fSize = f_fontsize->findText(QString::number(currSize));
|
||||
f_fontsize ->setCurrentIndex(fSize);
|
||||
f_fontsize->setEditText(QString::number(currSize, 'g', 4));
|
||||
}
|
||||
}
|
||||
|
||||
void MRichTextEdit::onSelectionChanged()
|
||||
{
|
||||
updateFontSizeDisplay();
|
||||
}
|
||||
|
||||
//does selection have multiple sizes?
|
||||
bool MRichTextEdit::hasMultipleSizes()
|
||||
{
|
||||
@@ -801,33 +806,55 @@ QFont MRichTextEdit::getDefFont()
|
||||
// if the conversion factor is float, will generate non-standard sizes
|
||||
void MRichTextEdit::addFontSize(QString fontSize)
|
||||
{
|
||||
bool ok;
|
||||
const double newSize = fontSize.toDouble(&ok);
|
||||
if (!ok) {
|
||||
return; // Ignore invalid numbers
|
||||
}
|
||||
|
||||
// 1. Collect all existing sizes as doubles
|
||||
QList<double> sizes;
|
||||
for (int i = 0; i < f_fontsize->count(); ++i) {
|
||||
sizes.append(f_fontsize->itemText(i).toDouble());
|
||||
}
|
||||
|
||||
// 2. Check if the new size is already in the list (using fuzzy comparison for doubles)
|
||||
for (double existingSize : qAsConst(sizes)) {
|
||||
if (qFuzzyCompare(existingSize, newSize)) {
|
||||
// Already exists, just make sure it's the current text
|
||||
f_fontsize->setCurrentText(QString::number(newSize, 'g', 4));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Add the new size and re-sort
|
||||
sizes.append(newSize);
|
||||
std::sort(sizes.begin(), sizes.end());
|
||||
|
||||
// 4. Repopulate the combobox with the sorted, correctly formatted list
|
||||
QStringList newList;
|
||||
int fsVal = fontSize.toInt();
|
||||
int size = f_fontsize->count();
|
||||
bool added = false;
|
||||
for (int i = 0; i < size; i++) {
|
||||
QString item = f_fontsize->itemText(i);
|
||||
int itemVal = item.toInt();
|
||||
if (added) {
|
||||
newList << item;
|
||||
continue;
|
||||
}
|
||||
if (itemVal < fsVal) {
|
||||
newList << item;
|
||||
} else if (itemVal == fsVal) { // already in list
|
||||
newList << item;
|
||||
added = true;
|
||||
} else { //item > fs, add both
|
||||
newList << fontSize;
|
||||
newList << item;
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
if (!added) { //bigger than all choices, add to end of list
|
||||
newList << fontSize;
|
||||
for (double size : qAsConst(sizes)) {
|
||||
newList << QString::number(size, 'g', 4);
|
||||
}
|
||||
|
||||
const QString currentText = f_fontsize->currentText(); // Save current text
|
||||
f_fontsize->blockSignals(true);
|
||||
f_fontsize->clear();
|
||||
f_fontsize->addItems(newList);
|
||||
f_fontsize->setCurrentText(currentText); // Restore current text
|
||||
f_fontsize->blockSignals(false);
|
||||
|
||||
// 5. Set the new size as the current item
|
||||
f_fontsize->setCurrentText(fontSize);
|
||||
}
|
||||
|
||||
void MRichTextEdit::setMinimalMode(bool on)
|
||||
{
|
||||
f_save->setVisible(!on);
|
||||
f_exit->setVisible(!on);
|
||||
f_cut->setVisible(!on);
|
||||
f_copy->setVisible(!on);
|
||||
f_paste->setVisible(!on);
|
||||
}
|
||||
|
||||
#include <Mod/TechDraw/Gui/moc_mrichtextedit.cpp>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
/**
|
||||
* @brief A simple rich-text editor
|
||||
*/
|
||||
class MRichTextEdit : public QWidget, protected Ui::MRichTextEdit {
|
||||
class MRichTextEdit : public QFrame, protected Ui::MRichTextEdit {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -55,6 +55,8 @@ public:
|
||||
int getDefFontSizeNum();
|
||||
QFont getDefFont();
|
||||
|
||||
void setMinimalMode(bool on);
|
||||
|
||||
public Q_SLOTS:
|
||||
void setText(const QString &text);
|
||||
|
||||
@@ -72,6 +74,7 @@ protected:
|
||||
void focusInEvent(QFocusEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
bool hasMultipleSizes();
|
||||
void updateFontSizeDisplay();
|
||||
|
||||
void addFontSize(QString fontSize);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user