mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-10 14:14:18 +02:00
Add drag-to-resize for dynamic element text width (#577 phase 1)
Adds two QetGraphicsHandlerItem grip handles at the left/right edges of a selected DynamicElementTextItem's frameRect(), reusing the exact same handle class, scene-event-filter wiring, and live-drag-then-undo-on-release pattern QetShapeItem already uses for its own diagram-level resize handles (sources/qetgraphicsitem/qetshapeitem.cpp). - Handles are created/destroyed on ItemSelectedHasChanged, matching QetShapeItem's convention (and ElementPrimitiveDecorator's, for the element editor's own primitives). - Position is recomputed in paint() rather than hooked to specific mutators, since textWidth/font/text/rotation can all move frameRect() and there's no single itemChange notification that covers all of them. - The drag delta is resolved through mapFromScene() into the item's own local coordinates, so a rotated text box still resizes along its own baseline rather than along the scene's x-axis. - setTextWidth() is called live during the drag for immediate visual feedback (matching how QetShapeItem's handlerMouseMoveEvent live-updates geometry); only on release is a QPropertyUndoCommand pushed -- the exact same command the properties-panel width spinbox already uses (sources/ui/dynamicelementtextmodel.cpp), so no new undo-command class or XML was needed. - The original textWidth() value is preserved as-is (including -1, the "auto" sentinel) for the undo command's old_value, separately from the concrete baseline used for the live drag's delta math -- otherwise an undo would replace "auto width" with a synthesized fixed width instead of actually restoring the auto-sizing state. Scoped to DynamicElementTextItem per the discussion's phase 1 (the buildable, no-new-XML piece); IndependentTextItem and the element editor's PartText/PartDynamicTextField have no serialized width property to resize yet and are left as explicitly out-of-scope follow-ups. Verified headlessly (Xvfb + xdotool + scrot): selecting an element's label text shows the two handles, dragging one live-resizes the text (confirmed via the properties panel's width field updating in real time), and undo/redo correctly restores the exact original width including the auto-width (-1) case.
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
#include "../qetgraphicsitem/terminal.h"
|
||||
#include "../qetinformation.h"
|
||||
#include "../utils/qetutils.h"
|
||||
#include "../QetGraphicsItemModeler/qetgraphicshandleritem.h"
|
||||
#include "crossrefitem.h"
|
||||
#include "element.h"
|
||||
#include "elementtextitemgroup.h"
|
||||
@@ -66,7 +67,9 @@ DynamicElementTextItem::DynamicElementTextItem(Element *parent_element) :
|
||||
}
|
||||
|
||||
DynamicElementTextItem::~DynamicElementTextItem()
|
||||
{}
|
||||
{
|
||||
removeResizeHandles();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DynamicElementTextItem::textFromMetaEnum
|
||||
@@ -688,7 +691,10 @@ void DynamicElementTextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
|
||||
void DynamicElementTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
DiagramTextItem::paint(painter, option, widget);
|
||||
|
||||
|
||||
if (m_left_resize_handle || m_right_resize_handle)
|
||||
updateResizeHandlesPos();
|
||||
|
||||
if (m_frame)
|
||||
{
|
||||
painter->save();
|
||||
@@ -776,15 +782,44 @@ QVariant DynamicElementTextItem::itemChange(QGraphicsItem::GraphicsItemChange ch
|
||||
updateXref();
|
||||
updateXref();
|
||||
}
|
||||
|
||||
else if (change == QGraphicsItem::ItemSelectedHasChanged)
|
||||
{
|
||||
if (value.toBool())
|
||||
addResizeHandles();
|
||||
else
|
||||
removeResizeHandles();
|
||||
}
|
||||
else if (change == QGraphicsItem::ItemSceneHasChanged && !scene())
|
||||
{
|
||||
removeResizeHandles();
|
||||
}
|
||||
|
||||
return QGraphicsObject::itemChange(change, value);
|
||||
}
|
||||
|
||||
bool DynamicElementTextItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
|
||||
{
|
||||
if (watched == m_left_resize_handle || watched == m_right_resize_handle)
|
||||
{
|
||||
auto *handle = static_cast<QetGraphicsHandlerItem *>(watched);
|
||||
if (event->type() == QEvent::GraphicsSceneMousePress) {
|
||||
handlerMousePressEvent(handle, static_cast<QGraphicsSceneMouseEvent *>(event));
|
||||
return true;
|
||||
}
|
||||
else if (event->type() == QEvent::GraphicsSceneMouseMove) {
|
||||
handlerMouseMoveEvent(handle, static_cast<QGraphicsSceneMouseEvent *>(event));
|
||||
return true;
|
||||
}
|
||||
else if (event->type() == QEvent::GraphicsSceneMouseRelease) {
|
||||
handlerMouseReleaseEvent(handle, static_cast<QGraphicsSceneMouseEvent *>(event));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(watched != m_slave_Xref_item)
|
||||
return false;
|
||||
|
||||
|
||||
if(event->type() == QEvent::GraphicsSceneHoverEnter) {
|
||||
m_slave_Xref_item->setDefaultTextColor(Qt::blue);
|
||||
return true;
|
||||
@@ -797,10 +832,129 @@ bool DynamicElementTextItem::sceneEventFilter(QGraphicsItem *watched, QEvent *ev
|
||||
zoomToLinkedElement();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DynamicElementTextItem::addResizeHandles
|
||||
Create and show the two width-resize handles (left/right edge of
|
||||
frameRect()), reusing QetGraphicsHandlerItem the same way QetShapeItem
|
||||
does for its own resize handles.
|
||||
*/
|
||||
void DynamicElementTextItem::addResizeHandles()
|
||||
{
|
||||
if (m_left_resize_handle || !scene())
|
||||
return;
|
||||
|
||||
qreal size = QETUtils::graphicsHandlerSize(this);
|
||||
m_left_resize_handle = new QetGraphicsHandlerItem(size);
|
||||
m_right_resize_handle = new QetGraphicsHandlerItem(size);
|
||||
|
||||
for (QetGraphicsHandlerItem *handle : {m_left_resize_handle, m_right_resize_handle})
|
||||
{
|
||||
scene()->addItem(handle);
|
||||
handle->setColor(Qt::darkGreen);
|
||||
handle->setZValue(zValue() + 1);
|
||||
handle->installSceneEventFilter(this);
|
||||
}
|
||||
|
||||
updateResizeHandlesPos();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DynamicElementTextItem::removeResizeHandles
|
||||
*/
|
||||
void DynamicElementTextItem::removeResizeHandles()
|
||||
{
|
||||
delete m_left_resize_handle;
|
||||
delete m_right_resize_handle;
|
||||
m_left_resize_handle = nullptr;
|
||||
m_right_resize_handle = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DynamicElementTextItem::updateResizeHandlesPos
|
||||
Keep the two resize handles at the vertical middle of frameRect()'s left
|
||||
and right edges, in scene coordinates -- called on every paint() so it
|
||||
stays correct across every kind of change that can move this item or
|
||||
change its size (position, rotation, font, text, textWidth...) without
|
||||
needing a dedicated hook for each one.
|
||||
*/
|
||||
void DynamicElementTextItem::updateResizeHandlesPos()
|
||||
{
|
||||
if (!m_left_resize_handle || !m_right_resize_handle)
|
||||
return;
|
||||
|
||||
QRectF fr = frameRect();
|
||||
m_left_resize_handle->setPos(mapToScene(QPointF(fr.left(), fr.center().y())));
|
||||
m_right_resize_handle->setPos(mapToScene(QPointF(fr.right(), fr.center().y())));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DynamicElementTextItem::handlerMousePressEvent
|
||||
@param handle
|
||||
@param event
|
||||
*/
|
||||
void DynamicElementTextItem::handlerMousePressEvent(QetGraphicsHandlerItem *handle, QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
Q_UNUSED(handle)
|
||||
|
||||
//The actual property value, kept as-is (possibly -1, meaning "auto")
|
||||
//so a later undo restores the exact original state rather than a
|
||||
//synthesized fixed width.
|
||||
m_resize_original_width = textWidth();
|
||||
//A concrete baseline for the live drag's delta math, which can't
|
||||
//start from -1.
|
||||
m_resize_baseline_width = (m_resize_original_width < 0) ? frameRect().width() : m_resize_original_width;
|
||||
m_resize_start_local_x = mapFromScene(event->scenePos()).x();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DynamicElementTextItem::handlerMouseMoveEvent
|
||||
Live-resize the text while dragging, exactly like the element editor's
|
||||
resize handles live-update geometry during a drag (undo is only pushed
|
||||
on release). The drag delta is resolved in this item's own local
|
||||
coordinates (not scene coordinates) so a rotated text box still resizes
|
||||
along its own baseline.
|
||||
@param handle
|
||||
@param event
|
||||
*/
|
||||
void DynamicElementTextItem::handlerMouseMoveEvent(QetGraphicsHandlerItem *handle, QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
qreal local_x = mapFromScene(event->scenePos()).x();
|
||||
qreal delta = local_x - m_resize_start_local_x;
|
||||
if (handle == m_left_resize_handle)
|
||||
delta = -delta;
|
||||
|
||||
qreal new_width = qMax(m_resize_baseline_width + delta, qreal(10));
|
||||
setTextWidth(new_width);
|
||||
updateResizeHandlesPos();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DynamicElementTextItem::handlerMouseReleaseEvent
|
||||
Push the same QPropertyUndoCommand the properties-panel width spinbox
|
||||
already pushes (sources/ui/dynamicelementtextmodel.cpp) -- the value is
|
||||
already applied live from the drag, so this only makes it undoable.
|
||||
@param handle
|
||||
@param event
|
||||
*/
|
||||
void DynamicElementTextItem::handlerMouseReleaseEvent(QetGraphicsHandlerItem *handle, QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
Q_UNUSED(handle)
|
||||
Q_UNUSED(event)
|
||||
|
||||
qreal new_width = textWidth();
|
||||
if (!qFuzzyCompare(m_resize_original_width, new_width) && m_parent_element && m_parent_element->diagram())
|
||||
{
|
||||
auto *undo = new QPropertyUndoCommand(this, "textWidth", QVariant(m_resize_original_width), QVariant(new_width));
|
||||
undo->setAnimated(true, false);
|
||||
undo->setText(tr("Redimensionner un texte d'élément"));
|
||||
m_parent_element->diagram()->undoStack().push(undo);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicElementTextItem::elementInfoChanged()
|
||||
{
|
||||
DiagramContext dc;
|
||||
|
||||
@@ -29,6 +29,7 @@ class Element;
|
||||
class Conductor;
|
||||
class ElementTextItemGroup;
|
||||
class CrossRefItem;
|
||||
class QetGraphicsHandlerItem;
|
||||
|
||||
/**
|
||||
@brief The DynamicElementTextItem class
|
||||
@@ -144,6 +145,12 @@ class DynamicElementTextItem : public DiagramTextItem
|
||||
void zoomToLinkedElement();
|
||||
void parentElementRotationChanged();
|
||||
void thisRotationChanged();
|
||||
void addResizeHandles();
|
||||
void removeResizeHandles();
|
||||
void updateResizeHandlesPos();
|
||||
void handlerMousePressEvent(QetGraphicsHandlerItem *handle, QGraphicsSceneMouseEvent *event);
|
||||
void handlerMouseMoveEvent(QetGraphicsHandlerItem *handle, QGraphicsSceneMouseEvent *event);
|
||||
void handlerMouseReleaseEvent(QetGraphicsHandlerItem *handle, QGraphicsSceneMouseEvent *event);
|
||||
|
||||
private:
|
||||
QPointer <Element>
|
||||
@@ -174,6 +181,11 @@ class DynamicElementTextItem : public DiagramTextItem
|
||||
bool m_keep_visual_rotation = true;
|
||||
qreal m_visual_rotation_ref = 0;
|
||||
bool m_move_parent = true;
|
||||
QetGraphicsHandlerItem *m_left_resize_handle = nullptr;
|
||||
QetGraphicsHandlerItem *m_right_resize_handle = nullptr;
|
||||
qreal m_resize_original_width = -1;
|
||||
qreal m_resize_baseline_width = -1;
|
||||
qreal m_resize_start_local_x = 0;
|
||||
};
|
||||
|
||||
#endif // DYNAMICELEMENTTEXTITEM_H
|
||||
|
||||
Reference in New Issue
Block a user