Implemented a primitive decorator, allowing groups of primitives to be easily resized.

git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@2027 bfdf4180-ca20-0410-9c96-a3a8aa849046
This commit is contained in:
xavier
2013-02-08 22:05:15 +00:00
parent 73fa01c7b9
commit 2bed00fd25
21 changed files with 1566 additions and 124 deletions

View File

@@ -18,6 +18,7 @@
#include "parttextfield.h"
#include "textfieldeditor.h"
#include "editorcommands.h"
#include "elementprimitivedecorator.h"
#include "qetapp.h"
/**
@@ -29,14 +30,18 @@
PartTextField::PartTextField(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) :
QGraphicsTextItem(parent, scene),
CustomElementPart(editor),
follow_parent_rotations(true)
follow_parent_rotations(true),
previous_text(),
decorator_(0)
{
setDefaultTextColor(Qt::black);
setFont(QETApp::diagramTextsFont());
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
real_font_size_ = font().pointSize();
setFlags(QGraphicsItem::ItemIsSelectable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif
setAcceptHoverEvents(true);
setPlainText(QObject::tr("_", "default text when adding a textfield in the element editor"));
adjustItemPosition(1);
@@ -58,7 +63,7 @@ void PartTextField::fromXml(const QDomElement &xml_element) {
int font_size = xml_element.attribute("size").toInt(&ok);
if (!ok || font_size < 1) font_size = 20;
setFont(QETApp::diagramTextsFont(font_size));
setProperty("size", font_size);
setPlainText(xml_element.attribute("text"));
qreal default_rotation_angle = 0.0;
@@ -134,32 +139,83 @@ QPointF PartTextField::margin() const {
return(QPointF(0.0, boundingRect().bottom() / 2.0));
}
/**
Handle context menu events.
@param event Object describing the context menu event to handle.
*/
void PartTextField::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
Q_UNUSED(event);
}
/**
Handle events generated when the mouse hovers over the decorator.
@param event Object describing the hover event.
*/
void PartTextField::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
// force the cursor when the text is being edited
if (hasFocus() && decorator_) {
decorator_ -> setCursor(Qt::IBeamCursor);
}
QGraphicsTextItem::hoverMoveEvent(event);
}
/**
@reimp QGraphicsItem::sceneEventFilter(QGraphicsItem *, QEvent *).
Intercepts events before they reach the watched target, i.e. typically the
primitives decorator.
This method mainly works with key strokes (F2, escape) and double clicks to
begin or end text edition.
*/
bool PartTextField::sceneEventFilter(QGraphicsItem *watched, QEvent *event) {
if (watched != decorator_) return(false);
QPointF event_scene_pos = QET::graphicsSceneEventPos(event);
if (!event_scene_pos.isNull()) {
if (contains(mapFromScene(event_scene_pos))) {
if (hasFocus()) {
return sceneEvent(event); // manually deliver the event to this item
return(true); // prevent this event from being delivered to any item
} else {
if (event -> type() == QEvent::GraphicsSceneMouseDoubleClick) {
mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
}
}
}
}
else if (event -> type() == QEvent::KeyRelease || event -> type() == QEvent::KeyPress) {
// Intercept F2 and escape keystrokes to focus in and out
QKeyEvent *key_event = static_cast<QKeyEvent *>(event);
if (key_event -> key() == Qt::Key_F2) {
setEditable(true);
QTextCursor qtc = textCursor();
qtc.setPosition(qMax(0, document()->characterCount() - 1));
setTextCursor(qtc);
} else if (hasFocus() && key_event -> key() == Qt::Key_Escape) {
endEdition();
}
sceneEvent(event); // manually deliver the event to this item
return(true); // prevent this event from being delivered to any item
}
return(false);
}
/*
@reimp QGraphicsItem::focusInEvent(QFocusEvent *)
@param e The QFocusEvent object describing the focus gain.
Start text edition when the item gains focus.
*/
void PartTextField::focusInEvent(QFocusEvent *e) {
startEdition();
QGraphicsTextItem::focusInEvent(e);
}
/**
Permet a l'element texte de redevenir deplacable a la fin de l'edition de texte
@param e Le QFocusEvent decrivant la perte de focus
*/
void PartTextField::focusOutEvent(QFocusEvent *e) {
QGraphicsTextItem::focusOutEvent(e);
if (previous_text != toPlainText()) {
undoStack().push(
new ChangePartCommand(
TextFieldEditor::tr("contenu") + " " + name(),
this,
"text",
previous_text,
toPlainText()
)
);
previous_text = toPlainText();
}
// deselectionne le texte
QTextCursor qtc = textCursor();
qtc.clearSelection();
setTextCursor(qtc);
setTextInteractionFlags(Qt::NoTextInteraction);
setFlag(QGraphicsItem::ItemIsFocusable, false);
endEdition();
}
/**
@@ -167,11 +223,10 @@ void PartTextField::focusOutEvent(QFocusEvent *e) {
@param e Le QGraphicsSceneMouseEvent qui decrit le double-clic
*/
void PartTextField::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e) {
setFlag(QGraphicsItem::ItemIsFocusable, true);
setTextInteractionFlags(Qt::TextEditorInteraction);
previous_text = toPlainText();
QGraphicsTextItem::mouseDoubleClickEvent(e);
setFocus(Qt::MouseFocusReason);
if (e -> button() == Qt::LeftButton) {
setEditable(true);
}
}
/**
@@ -194,6 +249,11 @@ void PartTextField::setProperty(const QString &property, const QVariant &value)
} else if (property == "size") {
if (!value.canConvert(QVariant::Int)) return;
setFont(QETApp::diagramTextsFont(value.toInt()));
real_font_size_ = value.toInt();
} else if (property == "real_size") {
if (!value.canConvert(QVariant::Double)) return;
setFont(QETApp::diagramTextsFont(value.toInt()));
real_font_size_ = value.toDouble();
} else if (property == "text") {
setPlainText(value.toString());
} else if (property == "rotation angle") {
@@ -223,6 +283,8 @@ QVariant PartTextField::property(const QString &property) {
return(pos().y());
} else if (property == "size") {
return(font().pointSize());
} else if (property == "real_size") {
return(real_font_size_);
} else if (property == "text") {
return(toPlainText());
} else if (property == "rotation angle") {
@@ -275,7 +337,7 @@ bool PartTextField::isUseless() const {
void PartTextField::startUserTransformation(const QRectF &initial_selection_rect) {
Q_UNUSED(initial_selection_rect)
saved_point_ = pos(); // scene coordinates, no need to mapFromScene()
saved_font_size_ = font().pointSize();
saved_font_size_ = real_font_size_;
}
/**
@@ -286,12 +348,10 @@ void PartTextField::handleUserTransformation(const QRectF &initial_selection_rec
QPointF new_pos = mapPoints(initial_selection_rect, new_selection_rect, QList<QPointF>() << saved_point_).first();
setPos(new_pos);
// adjust the font size following the smallest scale factor
qreal sx = new_selection_rect.width() / initial_selection_rect.width();
// adjust the font size following the vertical scale factor
qreal sy = new_selection_rect.height() / initial_selection_rect.height();
qreal smallest_scale_factor = sx > sy ? sy : sx;
qreal new_font_size = saved_font_size_ * smallest_scale_factor;
setProperty("size", qMax(1, qRound(new_font_size)));
qreal new_font_size = saved_font_size_ * sy;
setProperty("real_size", qMax(1, qRound(new_font_size)));
}
/**
Dessine le texte statique.
@@ -300,8 +360,12 @@ void PartTextField::handleUserTransformation(const QRectF &initial_selection_rec
@param widget Widget sur lequel on dessine (facultatif)
*/
void PartTextField::paint(QPainter *painter, const QStyleOptionGraphicsItem *qsogi, QWidget *widget) {
QGraphicsTextItem::paint(painter, qsogi, widget);
// According to the source code of QGraphicsTextItem::paint(), this should
// avoid the drawing of the dashed rectangle around the text.
QStyleOptionGraphicsItem our_qsogi(*qsogi);
our_qsogi.state = QStyle::State_None;
QGraphicsTextItem::paint(painter, &our_qsogi, widget);
#ifdef QET_DEBUG_EDITOR_TEXTS
painter -> setPen(Qt::blue);
painter -> drawRect(boundingRect());
@@ -314,6 +378,64 @@ void PartTextField::paint(QPainter *painter, const QStyleOptionGraphicsItem *qso
#endif
}
/**
@reimp CustomElementPart::setDecorator(ElementPrimitiveDecorator *)
Install or remove a sceneEventFilter on the decorator and ensure it will
adjust itself while the text is being edited.
*/
void PartTextField::setDecorator(ElementPrimitiveDecorator *decorator) {
if (decorator) {
decorator -> installSceneEventFilter(this);
// ensure the decorator will adjust itself when the text area expands or shrinks
connect(document(), SIGNAL(contentsChanged()), decorator, SLOT(adjust()));
}
else {
decorator_ -> removeSceneEventFilter(this);
endEdition();
}
decorator_ = decorator;
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartTextField::singleItemPressEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
Q_UNUSED(event)
return(hasFocus());
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartTextField::singleItemMoveEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
Q_UNUSED(event)
return(hasFocus());
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartTextField::singleItemReleaseEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
Q_UNUSED(event)
return(hasFocus());
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartTextField::singleItemDoubleClickEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
// calling mouseDoubleClickEvent() will set this text item editable and grab keyboard focus
if (event -> button() == Qt::LeftButton) {
mouseDoubleClickEvent(event);
return(true);
}
return(false);
}
/**
Cette methode s'assure que la position du champ de texte est coherente
en repositionnant son origine (c-a-d le milieu du bord gauche du champ de
@@ -331,6 +453,59 @@ void PartTextField::adjustItemPosition(int new_block_count) {
setTransformOriginPoint(0.0, origin_offset);
}
/**
@param editable Whether this text item should be interactively editable.
*/
void PartTextField::setEditable(bool editable) {
if (editable) {
setFlag(QGraphicsItem::ItemIsFocusable, true);
setTextInteractionFlags(Qt::TextEditorInteraction);
setFocus(Qt::MouseFocusReason);
}
else {
setTextInteractionFlags(Qt::NoTextInteraction);
setFlag(QGraphicsItem::ItemIsFocusable, false);
}
}
/**
Start text edition by storing the former value of the text.
*/
void PartTextField::startEdition() {
// !previous_text.isNull() means the text is being edited
previous_text = toPlainText();
}
/**
End text edition, potentially generating a ChangePartCommand if the text
has changed.
*/
void PartTextField::endEdition() {
if (!previous_text.isNull()) {
// the text was being edited
QString new_text = toPlainText();
if (previous_text != new_text) {
// the text was changed
ChangePartCommand *text_change = new ChangePartCommand(
TextFieldEditor::tr("contenu") + " " + name(),
this,
"text",
previous_text,
new_text
);
previous_text = QString();
undoStack().push(text_change);
}
}
// deselectionne le texte
QTextCursor qtc = textCursor();
qtc.clearSelection();
setTextCursor(qtc);
setEditable(false);
}
#ifdef QET_DEBUG_EDITOR_TEXTS
/**
Dessine deux petites fleches pour mettre un point en valeur