mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-06 19:54:13 +02:00
010e44ab8a
TextResizeHandles puts four handles at the corners of a selected text (any QGraphicsTextItem with a qreal "textWidth" property, -1 = automatic width). Dragging one changes the width: the opposite corner stays in place in scene coordinates, for any rotation and rotation point, the text wraps and the height follows it. Vertical mouse movements are ignored, the width never goes below the longest word and is rounded to whole pixels, and a click without a move changes nothing. Double-clicking a handle goes back to the automatic width. The handles are a child of the text, so they follow it when it moves, rotates or is zoomed (as for the symbol text handles, #1002). The width is changed live; the owner makes it undoable with TextResizeCommand, which sets the width then the position on both undo and redo -- a text may move itself when its width changes, so two property commands would restore them in the wrong order. The geometry is header-only (textresize.h). Nothing uses the handles yet: the follow-up changes add them to free texts and to text fields in the element editor. Test: tst_textresizehandles, on a plain QGraphicsTextItem. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
225 lines
6.4 KiB
C++
225 lines
6.4 KiB
C++
/*
|
|
Copyright 2006-2026 The QElectroTech Team
|
|
This file is part of QElectroTech.
|
|
|
|
QElectroTech is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QElectroTech 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "textresizehandles.h"
|
|
|
|
#include "qetgraphicshandleritem.h"
|
|
|
|
#include <QAbstractTextDocumentLayout>
|
|
#include <QCursor>
|
|
#include <QGraphicsSceneMouseEvent>
|
|
#include <QGraphicsTextItem>
|
|
#include <QTextDocument>
|
|
|
|
#include <cmath>
|
|
|
|
/**
|
|
@brief TextResizeHandles::TextResizeHandles
|
|
@param text : the text to resize, becomes the parent of the handles.
|
|
It must be in a scene, the handles filter their own events.
|
|
@param handle_size : see QETUtils::graphicsHandlerSize()
|
|
*/
|
|
TextResizeHandles::TextResizeHandles(QGraphicsTextItem *text, qreal handle_size) :
|
|
m_text(text)
|
|
{
|
|
setFlag(QGraphicsItem::ItemHasNoContents);
|
|
setParentItem(text);
|
|
|
|
for (int i = TextResize::TopLeft ; i <= TextResize::BottomLeft ; ++i)
|
|
{
|
|
auto *handle = new QetGraphicsHandlerItem(handle_size);
|
|
handle->setParentItem(this);
|
|
handle->setColor(Qt::darkGreen);
|
|
handle->setToolTip(tr("Glisser pour changer la largeur du texte, "
|
|
"double-cliquer pour une largeur automatique"));
|
|
handle->setCursor(i == TextResize::TopLeft || i == TextResize::BottomRight
|
|
? Qt::SizeFDiagCursor : Qt::SizeBDiagCursor);
|
|
if (scene())
|
|
handle->installSceneEventFilter(this);
|
|
m_handles << handle;
|
|
}
|
|
|
|
m_size_connection = connect(text->document()->documentLayout(),
|
|
&QAbstractTextDocumentLayout::documentSizeChanged,
|
|
this, &TextResizeHandles::updateHandlesPos);
|
|
updateHandlesPos();
|
|
}
|
|
|
|
TextResizeHandles::~TextResizeHandles()
|
|
{
|
|
disconnect(m_size_connection);
|
|
}
|
|
|
|
QRectF TextResizeHandles::boundingRect() const {
|
|
return QRectF();
|
|
}
|
|
|
|
/**
|
|
@return the handle at corner
|
|
*/
|
|
QetGraphicsHandlerItem *TextResizeHandles::handle(TextResize::Corner corner) const {
|
|
return m_handles.at(corner);
|
|
}
|
|
|
|
/**
|
|
@brief TextResizeHandles::updateHandlesPos
|
|
Put the handles at the corners of the box of the text. Called when the
|
|
size of the text changes; the owner calls it when the box changes in an
|
|
other way.
|
|
The box is boundingRect(), the box a user sees selected and resizes,
|
|
not the tighter frame of a framed text (qelectrotech#591).
|
|
*/
|
|
void TextResizeHandles::updateHandlesPos()
|
|
{
|
|
const QRectF rect = m_text->boundingRect();
|
|
for (int i = TextResize::TopLeft ; i <= TextResize::BottomLeft ; ++i)
|
|
m_handles.at(i)->setPos(TextResize::cornerOf(rect, TextResize::Corner(i)));
|
|
}
|
|
|
|
bool TextResizeHandles::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
|
|
{
|
|
const int index = m_handles.indexOf(static_cast<QetGraphicsHandlerItem *>(watched));
|
|
if (index < 0)
|
|
return false;
|
|
|
|
switch (event->type())
|
|
{
|
|
case QEvent::GraphicsSceneMousePress:
|
|
{
|
|
auto *mouse_event = static_cast<QGraphicsSceneMouseEvent *>(event);
|
|
if (mouse_event->button() != Qt::LeftButton)
|
|
return false;
|
|
pressed(TextResize::Corner(index), mouse_event->scenePos());
|
|
return true;
|
|
}
|
|
case QEvent::GraphicsSceneMouseMove:
|
|
if (m_dragging)
|
|
moved(static_cast<QGraphicsSceneMouseEvent *>(event)->scenePos());
|
|
return true;
|
|
case QEvent::GraphicsSceneMouseRelease:
|
|
released();
|
|
return true;
|
|
case QEvent::GraphicsSceneMouseDoubleClick:
|
|
resetToAutomaticWidth();
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void TextResizeHandles::pressed(TextResize::Corner corner, const QPointF &scene_pos)
|
|
{
|
|
m_dragging = true;
|
|
m_press_scene = scene_pos;
|
|
m_dragged = corner;
|
|
m_old_width = currentWidth();
|
|
m_old_pos = m_text->pos();
|
|
m_press_inverse = m_text->sceneTransform().inverted();
|
|
m_fixed_local = TextResize::cornerOf(m_text->boundingRect(), TextResize::opposite(corner));
|
|
m_fixed_scene = m_text->mapToScene(m_fixed_local);
|
|
m_min_width = TextResize::minimumWidth(m_text->document());
|
|
}
|
|
|
|
void TextResizeHandles::moved(const QPointF &scene_pos)
|
|
{
|
|
//A click without a move keeps an automatic width automatic
|
|
if (scene_pos == m_press_scene)
|
|
return;
|
|
|
|
//Whole pixels, as in the width spin boxes
|
|
const qreal width = qMax(qreal(qRound(TextResize::widthForDrag(m_press_inverse, m_fixed_local,
|
|
scene_pos, m_dragged, 0))),
|
|
std::ceil(m_min_width));
|
|
m_text->setProperty("textWidth", width);
|
|
TextResize::pinCorner(m_text, TextResize::opposite(m_dragged), m_fixed_scene);
|
|
updateHandlesPos();
|
|
}
|
|
|
|
void TextResizeHandles::released()
|
|
{
|
|
if (!m_dragging)
|
|
return;
|
|
m_dragging = false;
|
|
|
|
const qreal new_width = currentWidth();
|
|
if (!qFuzzyCompare(m_old_width, new_width) || m_old_pos != m_text->pos())
|
|
emit resizeFinished(m_old_width, new_width, m_old_pos, m_text->pos());
|
|
}
|
|
|
|
/**
|
|
@brief TextResizeHandles::resetToAutomaticWidth
|
|
Back to the automatic width (-1): the text keeps the point chosen by its
|
|
alignment in place, as it does for any other change of its width.
|
|
*/
|
|
void TextResizeHandles::resetToAutomaticWidth()
|
|
{
|
|
m_dragging = false;
|
|
|
|
const qreal old_width = currentWidth();
|
|
if (old_width < 0)
|
|
return;
|
|
|
|
const QPointF old_pos = m_text->pos();
|
|
m_text->setProperty("textWidth", qreal(-1));
|
|
updateHandlesPos();
|
|
emit resizeFinished(old_width, currentWidth(), old_pos, m_text->pos());
|
|
}
|
|
|
|
qreal TextResizeHandles::currentWidth() const {
|
|
return m_text->property("textWidth").toReal();
|
|
}
|
|
|
|
|
|
/**
|
|
@brief TextResizeCommand::TextResizeCommand
|
|
@param text : a text with a "textWidth" property
|
|
@param old_width
|
|
@param new_width
|
|
@param old_pos
|
|
@param new_pos
|
|
@param parent
|
|
*/
|
|
TextResizeCommand::TextResizeCommand(QGraphicsObject *text,
|
|
qreal old_width, qreal new_width,
|
|
const QPointF &old_pos, const QPointF &new_pos,
|
|
QUndoCommand *parent) :
|
|
QUndoCommand(parent),
|
|
m_text(text),
|
|
m_old_width(old_width),
|
|
m_new_width(new_width),
|
|
m_old_pos(old_pos),
|
|
m_new_pos(new_pos)
|
|
{
|
|
setText(tr("Redimensionner un texte"));
|
|
}
|
|
|
|
void TextResizeCommand::undo() {
|
|
apply(m_old_width, m_old_pos);
|
|
}
|
|
|
|
void TextResizeCommand::redo() {
|
|
apply(m_new_width, m_new_pos);
|
|
}
|
|
|
|
void TextResizeCommand::apply(qreal width, const QPointF &pos)
|
|
{
|
|
if (!m_text)
|
|
return;
|
|
m_text->setProperty("textWidth", width);
|
|
m_text->setPos(pos);
|
|
}
|