Rewrite resize and rotate logic.

Fixes inconsistent resize behaviour, e.g. if mouse was moved more
than the applet could be shrunk, and then moved back a small amount,
the applet would immediately start expanding, instead of starting to
expand where it stopped shrinking.

svn path=/trunk/KDE/kdelibs/; revision=901173
This commit is contained in:
Ambroz Bizjak 2008-12-24 15:13:09 +00:00
parent bfed2f3c3b
commit 32e0e2ed35
2 changed files with 136 additions and 182 deletions

View File

@ -47,7 +47,9 @@
namespace Plasma namespace Plasma
{ {
qreal _k_angleForPoints(const QPointF &center, const QPointF &pt1, const QPointF &pt2); qreal _k_distanceForPoint(QPointF point);
qreal _k_pointAngle(QPointF in);
QPointF _k_rotatePoint(QPointF in, qreal rotateAngle);
AppletHandle::AppletHandle(Containment *parent, Applet *applet, const QPointF &hoverPos) AppletHandle::AppletHandle(Containment *parent, Applet *applet, const QPointF &hoverPos)
: QObject(), : QObject(),
@ -60,9 +62,6 @@ AppletHandle::AppletHandle(Containment *parent, Applet *applet, const QPointF &h
m_anim(FadeIn), m_anim(FadeIn),
m_animId(0), m_animId(0),
m_angle(0.0), m_angle(0.0),
m_tempAngle(0.0),
m_scaleWidth(1.0),
m_scaleHeight(1.0),
m_backgroundBuffer(0), m_backgroundBuffer(0),
m_currentView(applet->view()), m_currentView(applet->view()),
m_entryPos(hoverPos), m_entryPos(hoverPos),
@ -83,9 +82,7 @@ AppletHandle::AppletHandle(Containment *parent, Applet *applet, const QPointF &h
qreal cosine = originalMatrix.m11(); qreal cosine = originalMatrix.m11();
qreal sine = originalMatrix.m12(); qreal sine = originalMatrix.m12();
m_angle = _k_angleForPoints(QPointF(0, 0), m_angle = _k_pointAngle(QPointF(cosine, sine));
QPointF(1, 0),
QPointF(cosine, sine));
m_applet->setParentItem(this); m_applet->setParentItem(this);
@ -154,17 +151,17 @@ void AppletHandle::detachApplet ()
m_applet->removeSceneEventFilter(this); m_applet->removeSceneEventFilter(this);
QRectF rect = QRectF(m_applet->pos(), m_applet->size()); QRectF appletGeomLocal = m_applet->geometry();
QPointF center = m_applet->mapFromParent(rect.center()); QPointF center = mapToParent(appletGeomLocal.center());
QPointF appletPos = QPointF(center.x()-appletGeomLocal.width()/2, center.y()-appletGeomLocal.height()/2);
m_applet->setPos(appletPos);
QPointF newPos = transform().inverted().map(m_applet->pos()); // transform is relative to the applet
m_applet->setPos(mapToParent(newPos)); QTransform t;
t.translate(appletGeomLocal.width()/2, appletGeomLocal.height()/2);
QTransform matrix; t.rotateRadians(m_angle);
matrix.translate(center.x(), center.y()); t.translate(-appletGeomLocal.width()/2, -appletGeomLocal.height()/2);
matrix.rotateRadians(m_angle); m_applet->setTransform(t);
matrix.translate(-center.x(), -center.y());
m_applet->setTransform(matrix);
m_applet->setParentItem(m_containment); m_applet->setParentItem(m_containment);
@ -460,6 +457,24 @@ void AppletHandle::mousePressEvent(QGraphicsSceneMouseEvent *event)
m_pos = pos(); m_pos = pos();
} }
if (m_pressedButton == ResizeButton || m_pressedButton == RotateButton) {
m_origAppletCenter = mapToScene(m_applet->geometry().center());
m_origAppletSize = QPointF(m_applet->size().width(), m_applet->size().height());
// resize
if (m_buttonsOnRight) {
m_resizeStaticPoint = mapToScene(m_applet->geometry().bottomLeft());
} else {
m_resizeStaticPoint = mapToScene(m_applet->geometry().bottomRight());
}
m_resizeGrabPoint = mapToScene(event->pos());
QPointF cursorRelativeToStatic = m_resizeGrabPoint - m_resizeStaticPoint;
m_aspectResizeOrigRadius = sqrt(pow(cursorRelativeToStatic.x(), 2) + pow(cursorRelativeToStatic.y(), 2));
// rotate
m_rotateAngleOffset = m_angle - _k_pointAngle(mapToScene(event->pos()) - m_origAppletCenter);
}
event->accept(); event->accept();
update(); update();
@ -503,18 +518,6 @@ void AppletHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
if (m_applet && event->button() == Qt::LeftButton) { if (m_applet && event->button() == Qt::LeftButton) {
switch (m_pressedButton) { switch (m_pressedButton) {
case ResizeButton:
case RotateButton:
{
m_angle += m_tempAngle;
if (m_angle > M_PI) {
m_angle = m_angle - 2 * M_PI;
} else if (m_angle < -M_PI) {
m_angle = m_angle + 2 * M_PI;
}
m_tempAngle = 0;
break;
}
case ConfigureButton: case ConfigureButton:
//FIXME: Remove this call once the configuration management change was done //FIXME: Remove this call once the configuration management change was done
if (m_pressedButton == releasedAtButton) { if (m_pressedButton == releasedAtButton) {
@ -573,15 +576,34 @@ qreal _k_distanceForPoint(QPointF point)
return std::sqrt(point.x() * point.x() + point.y() * point.y()); return std::sqrt(point.x() * point.x() + point.y() * point.y());
} }
qreal _k_angleForPoints(const QPointF &center, const QPointF &pt1, const QPointF &pt2) qreal _k_pointAngle(QPointF in)
{ {
QPointF vec1 = pt1 - center; qreal r = sqrt(pow(in.x(), 2) + pow(in.y(), 2));
QPointF vec2 = pt2 - center; qreal cosine = in.x()/r;
qreal sine = in.y()/r;
qreal alpha = std::atan2(vec1.y(), vec1.x()); if (sine>=0) {
qreal beta = std::atan2(vec2.y(), vec2.x()); return acos(cosine);
} else {
return -acos(cosine);
}
}
return beta - alpha; QPointF _k_rotatePoint(QPointF in, qreal rotateAngle)
{
qreal r = sqrt(pow(in.x(), 2) + pow(in.y(), 2));
qreal cosine = in.x()/r;
qreal sine = in.y()/r;
qreal angle;
if (sine>=0) {
angle = acos(cosine);
} else {
angle = -acos(cosine);
}
qreal newAngle = angle + rotateAngle;
return QPointF(r*cos(newAngle), r*sin(newAngle));
} }
void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
@ -614,170 +636,94 @@ void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
} else { } else {
setPos(m_pos); setPos(m_pos);
} }
} else if (m_pressedButton == RotateButton || } else if (m_pressedButton == ResizeButton || m_pressedButton == RotateButton) {
m_pressedButton == ResizeButton) { QPointF cursorPoint = mapToScene(event->pos());
if (_k_distanceForPoint(deltaScene) <= 1.0) {
return; // the code below will adjust these based on the type of operation
QPointF newSize;
QPointF newCenter;
qreal newAngle;
// get size limits
QSizeF min = m_applet->minimumSize();
QSizeF max = m_applet->maximumSize();
// If the applet doesn't have a minimum size, calculate based on a
// minimum content area size of 16x16
if (min.isEmpty()) {
min = m_applet->boundingRect().size() - m_applet->boundingRect().size();
min += QSizeF(16, 16);
} }
QPointF pressPos = mapFromScene(event->buttonDownScenePos(Qt::LeftButton));
QRectF originalRect = m_totalRect;
QRectF rect = QRectF(m_applet->pos(), m_applet->size());
QPointF center = rect.center();
if (m_pressedButton == RotateButton) { if (m_pressedButton == RotateButton) {
m_tempAngle = _k_angleForPoints(center, pressPos, event->pos()); newSize = m_origAppletSize;
newCenter = m_origAppletCenter;
if (fabs(remainder(m_angle + m_tempAngle, snapAngle)) < 0.15) { QPointF centerRelativePoint = cursorPoint - m_origAppletCenter;
m_tempAngle = m_tempAngle - remainder(m_angle + m_tempAngle, snapAngle); if (_k_distanceForPoint(centerRelativePoint) < 10) {
newAngle = m_angle;
} else {
qreal cursorAngle = _k_pointAngle(centerRelativePoint);
newAngle = m_rotateAngleOffset + cursorAngle;
if (fabs(remainder(newAngle, snapAngle)) < 0.15) {
newAngle = newAngle - remainder(newAngle, snapAngle);
}
} }
m_scaleWidth = m_scaleHeight = 1.0;
} else { } else {
qreal w = m_applet->size().width(); // un-rotate screen points so we can read differences of coordinates
qreal h = m_applet->size().height(); QPointF rStaticPoint = _k_rotatePoint(m_resizeStaticPoint, -m_angle);
QSizeF min = m_applet->minimumSize(); QPointF rCursorPoint = _k_rotatePoint(cursorPoint, -m_angle);
QSizeF max = m_applet->maximumSize(); QPointF rGrabPoint = _k_rotatePoint(m_resizeGrabPoint, -m_angle);
QPointF resizePoint(0.0, 0.0);
if (m_buttonsOnRight) { if (m_buttonsOnRight) {
resizePoint = rect.topRight(); newSize = m_origAppletSize + QPointF(rCursorPoint.x() - rGrabPoint.x(), rGrabPoint.y() - rCursorPoint.y());
} else { } else {
resizePoint = rect.topLeft(); newSize = m_origAppletSize + QPointF(rGrabPoint.x() - rCursorPoint.x(), rGrabPoint.y() - rCursorPoint.y());
} }
// if preserving aspect ratio, project the calculated size point to the line
// theough the origin and the original size point
if (m_applet->aspectRatioMode() != Plasma::IgnoreAspectRatio) {
qreal ox = m_origAppletSize.x();
qreal oy = m_origAppletSize.y();
qreal sx = newSize.x();
qreal sy = newSize.y();
// If the applet doesn't have a minimum size, calculate based on a qreal x = ox*(sx*ox+sy*oy)/(pow(ox,2)+pow(oy,2));
// minimum content area size of 16x16 qreal y = oy*x/ox;
if (min.isEmpty()) { newSize = QPointF(x, y);
min = m_applet->boundingRect().size() - m_applet->boundingRect().size();
min += QSizeF(16, 16);
} }
bool ignoreAspectRatio = m_applet->aspectRatioMode() == Plasma::IgnoreAspectRatio; // limit size
newSize.rx() = qMin(max.width(), qMax(min.width(), newSize.x()));
newSize.ry() = qMin(max.height(), qMax(min.height(), newSize.y()));
if (QApplication::keyboardModifiers() & Qt::ControlModifier) { // move center such that the static corner remains in the same place
ignoreAspectRatio = !ignoreAspectRatio;
}
if (ignoreAspectRatio) {
// free resizing
qreal newScaleWidth = 1.0;
qreal newScaleHeight = 1.0;
qreal x = deltaScene.x() / m_applet->size().width();
qreal y = -deltaScene.y() / m_applet->size().height();
if (!m_buttonsOnRight) {
x *= -1.0;
}
newScaleWidth += x;
newScaleHeight += y;
if (qAbs(event->pos().x() - pressPos.x()) <= KGlobalSettings::dndEventDelay()) {
newScaleWidth = 1.0 + m_originalSize.width() / originalRect.width();
}
if (qAbs(event->pos().y() - pressPos.y()) <= KGlobalSettings::dndEventDelay()) {
newScaleHeight = 1.0 + m_originalSize.height() / originalRect.height();
}
if (newScaleHeight * h < min.height()) {
m_scaleHeight = min.height() / h;
} else if (newScaleHeight * h > max.height()) {
m_scaleHeight = max.height() / h;
} else {
m_scaleHeight = newScaleHeight;
}
if (newScaleWidth * w < min.width()) {
m_scaleWidth = min.width() / w;
} else if (newScaleWidth * w > max.width()) {
m_scaleWidth = max.width() / w;
} else {
m_scaleWidth = newScaleWidth;
}
} else {
// maintain aspect ratio
qreal newScale = 1.0;
qreal x = deltaScene.x() / m_applet->size().width();
qreal y = -deltaScene.y() / m_applet->size().height();
if (!m_buttonsOnRight) {
x *= -1.0;
}
newScale += (x + y) / 2; //divide by two to have slower resizing
if (qAbs(event->pos().y() - pressPos.y()) <= KGlobalSettings::dndEventDelay()) {
newScale = 1.0 + m_originalSize.height() / originalRect.height();
}
if (newScale * w < min.width() || newScale * h < min.height()) {
m_scaleWidth = m_scaleHeight = qMax(min.width() / w, min.height() / h);
} else if (newScale * w > max.width() && newScale * h > max.height()) {
m_scaleWidth = m_scaleHeight = qMin(max.width() / w, max.height() / h);
} else {
m_scaleHeight = m_scaleWidth = newScale;
}
}
// Resize Applet
QSizeF oldSize(m_applet->size());
const qreal newWidth = oldSize.width() * m_scaleWidth;
const qreal newHeight = oldSize.height() * m_scaleHeight;
m_applet->resize(newWidth, newHeight);
// Adjust position based on new applet size
qreal deltaX = 0;
qreal deltaY = 0;
const qreal deltaH = newHeight - oldSize.height();
const qreal deltaW = newWidth - oldSize.width();
if (m_buttonsOnRight) { if (m_buttonsOnRight) {
if (m_angle >= 0 && m_angle < M_PI_2) { newCenter = _k_rotatePoint(QPointF(rStaticPoint.x() + newSize.x()/2, rStaticPoint.y() - newSize.y()/2), m_angle);
deltaX = 0;
deltaY = -cos(m_angle) * deltaH;
} else if (m_angle >= M_PI_2 && m_angle <= M_PI) {
deltaX = cos(m_angle) * deltaW;
deltaY = 0;
} else if (m_angle >= -M_PI_2 && m_angle < 0) {
deltaX = sin(m_angle) * deltaH;
deltaY = -cos(m_angle) * deltaH + sin(m_angle) * deltaW;
} else if (m_angle >= -M_PI && m_angle < M_PI_2) {
deltaX = cos(m_angle) * deltaW + sin(m_angle) * deltaH;
deltaY = sin(m_angle) * deltaW;
}
} else { } else {
if (m_angle >= 0 && m_angle < M_PI_2) { newCenter = _k_rotatePoint(QPointF(rStaticPoint.x() - newSize.x()/2, rStaticPoint.y() - newSize.y()/2), m_angle);
deltaX = -cos(m_angle) * deltaW;
deltaY = -cos(m_angle) * deltaH - sin(m_angle) * deltaW;
} else if (m_angle >= M_PI_2 && m_angle <= M_PI) {
deltaX = 0;
deltaY = -sin(m_angle) * deltaW;
} else if (m_angle >= -M_PI_2 && m_angle < 0) {
deltaX = sin(m_angle) * deltaH - cos(m_angle) * deltaW;
deltaY = 0;
} else if (m_angle >= -M_PI && m_angle < M_PI_2) {
deltaX = sin(m_angle) * deltaH;
deltaY = 0;
}
} }
//kDebug() << "delta:" << deltaX << "," << deltaY; newAngle = m_angle;
//kDebug() << "Scale:" << m_scaleWidth << "," << m_scaleHeight;
moveBy(deltaX, deltaY);
} }
//kDebug() << "Angle:" << m_angle * 180/M_PI << "," << (m_angle + m_tempAngle)*180/M_PI; // set position of applet handle
QPointF newHandlePosInScene = newCenter - (m_applet->pos() + newSize/2);
QPointF newHandlePos = parentItem()->mapFromScene(newHandlePosInScene);
setPos(newHandlePos);
// set applet size
m_applet->resize(newSize.x(), newSize.y());
// set applet handle rotation - rotate around center of applet
QTransform t;
QPointF appletCenter = m_applet->geometry().center();
t.translate(appletCenter.x(), appletCenter.y());
t.rotateRadians(newAngle);
t.translate(-appletCenter.x(), -appletCenter.y());
setTransform(t);
m_angle = newAngle;
rect = QRectF(m_applet->pos(), m_applet->size());
center = rect.center();
QTransform matrix;
matrix.translate(center.x(), center.y());
matrix.rotateRadians(m_angle + m_tempAngle);
matrix.translate(-center.x(), -center.y());
setTransform(matrix);
m_applet->update(); m_applet->update();
} else { } else {
QGraphicsItem::mouseMoveEvent(event); QGraphicsItem::mouseMoveEvent(event);

View File

@ -114,9 +114,6 @@ class AppletHandle : public QObject, public QGraphicsItem
FadeType m_anim; FadeType m_anim;
int m_animId; int m_animId;
qreal m_angle; qreal m_angle;
qreal m_tempAngle;
qreal m_scaleWidth;
qreal m_scaleHeight;
QColor m_gradientColor; QColor m_gradientColor;
QTimer *m_hoverTimer; QTimer *m_hoverTimer;
QTimer *m_leaveTimer; QTimer *m_leaveTimer;
@ -130,7 +127,18 @@ class AppletHandle : public QObject, public QGraphicsItem
QPointF m_entryPos; //where the hover in event occurred QPointF m_entryPos; //where the hover in event occurred
QPointF m_pos; //current position of applet in sceneCoords QPointF m_pos; //current position of applet in sceneCoords
qreal m_zValue; //current zValue of the applet, so it can be restored after drag. qreal m_zValue; //current zValue of the applet, so it can be restored after drag.
QSizeF m_originalSize;
// used for both resize and rotate
QPointF m_origAppletCenter;
QPointF m_origAppletSize;
// used for resize
QPointF m_resizeStaticPoint;
QPointF m_resizeGrabPoint;
// used during aspect-ratio preserving resize
qreal m_aspectResizeOrigRadius;
// used for rotate
qreal m_rotateAngleOffset; // applet angle minus cursor angle
bool m_buttonsOnRight : 1; bool m_buttonsOnRight : 1;
bool m_pendingFade : 1; bool m_pendingFade : 1;