Bug 131739

Summary: Snap to behaviour does not operate during a resize
Product: [Applications] kst Reporter: Andrew Walker <arwalker>
Component: generalAssignee: kst
Status: RESOLVED FIXED    
Severity: normal    
Priority: NOR    
Version: 1.x   
Target Milestone: ---   
Platform: unspecified   
OS: Linux   
Latest Commit: Version Fixed In:
Sentry Crash Report:

Description Andrew Walker 2006-08-02 19:37:32 UTC
Version:           HEAD (using KDE KDE 3.5.0)
OS:                Linux

PROBLEM:
When an object is being resized it is no longer snapping to the border of other view objects

STEPS TO REPRODUCE:
Start Kst
Create two rectangles in the default window (with a sibling relationship)
Switch to layout mode
Move one of the rectangles around

RESULTS:
The rectangle being moved does not snap to the other rectangle when their edges are close

RESULTS:
The rectangle being moved does snap to the other rectangle when their edges are close
Comment 1 Duncan Hanson 2006-08-02 20:42:39 UTC
Do we still want this? I find snapping behaviour on resize difficult to
use. 

On Wed, 2006-08-02 at 17:37 +0000, Andrew Walker wrote:
[bugs.kde.org quoted mail]
Comment 2 Andrew Walker 2007-05-03 20:43:33 UTC
SVN commit 660784 by arwalker:

CCBUG:131739 Reinstate snap to border on resize. Alt key can be used to override snap behaviour for both move and resize. Still need to address *moving end point of line *centred resize of ellipse while maintaining aspect ratio *snap behaviour of rectangles when maintaining aspect

 M  +74 -51    ksttoplevelview.cpp  
 M  +7 -7      ksttoplevelview.h  
 M  +1 -1      kstviewwidget.cpp  


--- branches/work/kst/1.5/kst/src/libkstapp/ksttoplevelview.cpp #660783:660784
@@ -517,9 +517,9 @@
   for (KstViewObjectList::ConstIterator i = obj->children().begin(); i != obj->children().end(); ++i) {
     if (_selectionList.find(*i) == _selectionList.end() && _pressTarget != *i) {
       const QRect rect((*i)->geometry());
-      
+
       moveSnapToBorders(xMin, yMin, *i, r);
-            
+
       int overlapLo = r.top() > rect.top() ? r.top() : rect.top();
       int overlapHi = r.bottom() < rect.bottom() ? r.bottom() : rect.bottom();
       if (overlapHi - overlapLo > 0) {
@@ -533,7 +533,7 @@
           *xMin = r.right() - rect.right();
         }
       }
-  
+
       overlapLo = r.left() > rect.left() ? r.left() : rect.left();
       overlapHi = r.right() < rect.right() ? r.right() : rect.right();
       if (overlapHi - overlapLo > 0) {
@@ -556,9 +556,9 @@
   for (KstViewObjectList::ConstIterator i = obj->children().begin(); i != obj->children().end(); ++i) {
     if (_pressTarget != *i) {
       const QRect rect((*i)->geometry());
-      
+
       resizeSnapToBorders(xMin, yMin, *i, r, direction);
-      
+
       int overlapLo = r.top() > rect.top() ? r.top() : rect.top();
       int overlapHi = r.bottom() < rect.bottom() ? r.bottom() : rect.bottom();
       if (overlapHi - overlapLo > 0) {
@@ -566,7 +566,7 @@
           if (labs(r.left() - rect.left()) < labs(*xMin)) {
             *xMin = r.left() - rect.left();
           } else if (labs(r.left() - rect.right()) < labs(*xMin)) {
-            *xMin = r.left() - rect.right();              
+            *xMin = r.left() - rect.right();
           }
         } else if (direction & RIGHT) {
           if (labs(r.right() - rect.left()) < labs(*xMin)) {
@@ -574,7 +574,7 @@
           } else if (labs(r.right() - rect.right()) < labs(*xMin)) {
             *xMin = r.right() - rect.right();
           }
-        }                 
+        }
       }
 
       overlapLo = r.left() > rect.left() ? r.left() : rect.left();
@@ -584,7 +584,7 @@
           if (labs(r.top() - rect.top()) < labs(*yMin)) {
             *yMin = r.top() - rect.top();
           } else if (labs(r.top() - rect.bottom()) < labs(*yMin)) {
-            *yMin = r.top() - rect.bottom();              
+            *yMin = r.top() - rect.bottom();
           }
         } else if (direction & DOWN) {
           if (labs(r.bottom() - rect.top()) < labs(*yMin)) {
@@ -603,25 +603,37 @@
   QRect rectNew = r;
   int xMin = STICKY_THRESHOLD;
   int yMin = STICKY_THRESHOLD;
-  
+
   resizeSnapToBorders(&xMin, &yMin, this, r, direction);
 
   if (labs(yMin) < STICKY_THRESHOLD) {
     if (direction & UP) {
       rectNew.setTop(r.top() - yMin);
+      if (direction & CENTEREDRESIZE) {
+        rectNew.setBottom(r.bottom() + yMin);
+      }
     } else if (direction & DOWN) {
       rectNew.setBottom(r.bottom() - yMin);
+      if (direction & CENTEREDRESIZE) {
+        rectNew.setTop(r.top() + yMin);
+      }
     }
   }
- 
+
   if (labs(xMin) < STICKY_THRESHOLD) {
     if (direction & LEFT) {
       rectNew.setLeft(r.left() - xMin);
-     } else if (direction & RIGHT) {
+      if (direction & CENTEREDRESIZE) {
+        rectNew.setRight(r.right() + xMin);
+      }
+    } else if (direction & RIGHT) {
       rectNew.setRight(r.right() - xMin);
+      if (direction & CENTEREDRESIZE) {
+        rectNew.setLeft(r.left() + xMin);
+      }
     }
   }
- 
+
   return rectNew.normalize();
 }
 
@@ -645,18 +657,18 @@
 }
 
 
-void KstTopLevelView::pressMove(const QPoint& pos, bool shift) {
+void KstTopLevelView::pressMove(const QPoint& pos, bool shift, bool alt) {
   if (_activeHandler) {
     _activeHandler->pressMove(this, pos, shift, _geom);  
     return;
   }
-  
+
   // in these cases there is nothing to do         
   if (_mode == DisplayMode || _mode == Unknown) {
     _pressTarget = 0L;
     return;
   }
-  
+
   if (_pressDirection == -1 && _pressTarget) { // menu released
     return;
   }
@@ -664,44 +676,47 @@
   if (shift && _moveOffset == QPoint(-1, -1) && _pressDirection < 1) {
     return;
   }
-  
+
   _mouseMoved = true;
-  
+
   // handle as in layout mode
-  pressMoveLayoutMode(pos, shift);
+  pressMoveLayoutMode(pos, shift, alt);
 }
 
 
-void KstTopLevelView::pressMoveLayoutMode(const QPoint& pos, bool shift) {
+void KstTopLevelView::pressMoveLayoutMode(const QPoint& pos, bool shift, bool alt) {
   if (_pressTarget) {
+    bool snapToBorder = !alt;
+
     if (_pressDirection == 0) {
       // moving an object
-      pressMoveLayoutModeMove(pos, shift);
+      pressMoveLayoutModeMove(pos, shift, snapToBorder);
       KstApp::inst()->slotUpdateDataMsg(i18n("(x0,y0)-(x1,y1)", "(%1,%2)-(%3,%4)").arg(_prevBand.topLeft().x()).arg(_prevBand.topLeft().y()).arg(_prevBand.bottomRight().x()).arg(_prevBand.bottomRight().y()));
     } else if (_pressTarget->isResizable()) {
       bool maintainAspect = shift ^ _pressTarget->maintainAspect(); // if default behaviour is to maintainAspect on resize, then shift will now have opposite behaviour.
+
       if (_pressDirection & ENDPOINT) {
         // moving an endpoint of an object
-        pressMoveLayoutModeEndPoint(pos, maintainAspect);
+        pressMoveLayoutModeEndPoint(pos, maintainAspect, snapToBorder);
       } else if (_pressDirection & CENTEREDRESIZE) {
         // resizing an object with fixed center
-        pressMoveLayoutModeCenteredResize(pos, maintainAspect);
+        pressMoveLayoutModeCenteredResize(pos, maintainAspect, snapToBorder);
       } else {
         // resizing a rectangular object
-        pressMoveLayoutModeResize(pos, maintainAspect);
+        pressMoveLayoutModeResize(pos, maintainAspect, snapToBorder);
       }
       KstApp::inst()->slotUpdateDataMsg(i18n("(x0,y0)-(x1,y1)", "(%1,%2)-(%3,%4)").arg(_prevBand.topLeft().x()).arg(_prevBand.topLeft().y()).arg(_prevBand.bottomRight().x()).arg(_prevBand.bottomRight().y()));
     }
   } else {
     // selecting objects
-    pressMoveLayoutModeSelect(pos, shift);
+    pressMoveLayoutModeSelect(pos);
   }  
 }
 
 
-void KstTopLevelView::pressMoveLayoutModeMove(const QPoint& pos, bool shift) {
+void KstTopLevelView::pressMoveLayoutModeMove(const QPoint& pos, bool shift, bool snapToBorder) {
   Q_UNUSED(shift)
-  
+
   const QRect old(_prevBand);
 
   QRect r(_pressTarget->geometry());
@@ -712,31 +727,32 @@
   QPoint topLeft(pos - _moveOffset - _pressTarget->geometry().topLeft() + r.topLeft());
   r.moveTopLeft(topLeft);
   _moveOffsetSticky = QPoint(0, 0);
-      
-  int xMin = STICKY_THRESHOLD;
-  int yMin = STICKY_THRESHOLD;
-  
-  moveSnapToBorders(&xMin, &yMin, this, r); 
-  
-  if (labs(xMin) < STICKY_THRESHOLD) {
-    _moveOffsetSticky.setX(xMin);
-    topLeft.setX(topLeft.x() - xMin);
+
+  if (snapToBorder) {
+    int xMin = STICKY_THRESHOLD;
+    int yMin = STICKY_THRESHOLD;
+
+    moveSnapToBorders(&xMin, &yMin, this, r); 
+
+    if (labs(xMin) < STICKY_THRESHOLD) {
+      _moveOffsetSticky.setX(xMin);
+      topLeft.setX(topLeft.x() - xMin);
+    }
+
+    if (labs(yMin) < STICKY_THRESHOLD) {
+      _moveOffsetSticky.setY(yMin);
+      topLeft.setY(topLeft.y() - yMin);
+    }
+    r.moveTopLeft(topLeft);
   }
-  
-  if (labs(yMin) < STICKY_THRESHOLD) {
-    _moveOffsetSticky.setY(yMin);
-    topLeft.setY(topLeft.y() - yMin);
-  } 
 
-  r.moveTopLeft(topLeft);      
-
   if (!_geom.contains(r, true)) {
     slideInto(_geom, r);
   }
   _prevBand = r;
   if (_prevBand != old) {
     KstPainter p;
-        
+
     p.begin(_w);
     p.setRasterOp(Qt::NotROP);
     p.setPen(QPen(Qt::black, 0, Qt::DotLine));
@@ -758,13 +774,18 @@
 }
 
 
-void KstTopLevelView::pressMoveLayoutModeResize(const QPoint& pos, bool maintainAspect) {
+void KstTopLevelView::pressMoveLayoutModeResize(const QPoint& pos, bool maintainAspect, bool snapToBorders) {
   const QRect old(_prevBand);
 
   _prevBand = newSize(_pressTarget->geometry(), _pressTarget->_parent->geometry(), _pressDirection, pos, maintainAspect);
+
+  if (snapToBorders) {
+    _prevBand = resizeSnapToObjects(_prevBand, _pressDirection); 
+  }
+
   if (_prevBand != old) {
     KstPainter p;
-        
+
     p.begin(_w);
     p.setRasterOp(Qt::NotROP);
     p.setPen(QPen(Qt::black, 0, Qt::DotLine));
@@ -777,9 +798,7 @@
 }
 
 
-void KstTopLevelView::pressMoveLayoutModeSelect(const QPoint& pos, bool shift) {
-  Q_UNUSED(shift)
-  
+void KstTopLevelView::pressMoveLayoutModeSelect(const QPoint& pos) {
   const QRect old(_prevBand);
   QRect r;
   r.setTopLeft(_moveOffset);
@@ -797,7 +816,7 @@
 }
 
 
-void KstTopLevelView::pressMoveLayoutModeEndPoint(const QPoint& pos, bool maintainAspect) {
+void KstTopLevelView::pressMoveLayoutModeEndPoint(const QPoint& pos, bool maintainAspect, bool snapToBorder) {
   // FIXME: remove this!!  Should not know about any specific type
   // for now we only know how to deal with lines 
 
@@ -821,7 +840,7 @@
     } else {
       return;
     }
-    
+
     if (maintainAspect) {
       movePoint = KstGfxMouseHandlerUtils::findNearestPtOnLine(anchorPoint, movePoint, pos, bounds);
     } else {
@@ -851,12 +870,16 @@
 }
 
 
-void KstTopLevelView::pressMoveLayoutModeCenteredResize(const QPoint& pos, bool maintainAspect) {
+void KstTopLevelView::pressMoveLayoutModeCenteredResize(const QPoint& pos, bool maintainAspect, bool snapToBorder) {
   //centered resize means that the center of the object stays constant
   const QRect old(_prevBand);
   
   _prevBand = newSizeCentered(_pressTarget->geometry(), _pressTarget->_parent->geometry(), _pressDirection, pos, maintainAspect);
 
+  if (snapToBorder) {
+    _prevBand = resizeSnapToObjects(_prevBand, _pressDirection); 
+  }
+
   if (_prevBand != old) {
     KstPainter p;
 
--- branches/work/kst/1.5/kst/src/libkstapp/ksttoplevelview.h #660783:660784
@@ -103,14 +103,14 @@
     bool handleDoubleClick(const QPoint& pos, bool shift = false);
     
     // press move handlers
-    void pressMove(const QPoint& pos, bool shift = false);
-    void pressMoveLayoutMode(const QPoint& pos, bool shift = false);
+    void pressMove(const QPoint& pos, bool shift = false, bool alt = false);
+    void pressMoveLayoutMode(const QPoint& pos, bool shift = false, bool alt = false);
     // helpers for pressMoveLayoutMode
-    void pressMoveLayoutModeMove(const QPoint& pos, bool shift = false);
-    void pressMoveLayoutModeResize(const QPoint& pos, bool maintainAspect = false);
-    void pressMoveLayoutModeSelect(const QPoint& pos, bool shift = false);
-    void pressMoveLayoutModeEndPoint(const QPoint& pos, bool maintainAspect = false);
-    void pressMoveLayoutModeCenteredResize(const QPoint& pos, bool maintainAspect = false);
+    void pressMoveLayoutModeMove(const QPoint& pos, bool shift = false, bool snapToBorder = true);
+    void pressMoveLayoutModeResize(const QPoint& pos, bool maintainAspect = false, bool snapToBorder = true);
+    void pressMoveLayoutModeSelect(const QPoint& pos);
+    void pressMoveLayoutModeEndPoint(const QPoint& pos, bool maintainAspect = false, bool snapToBorder = true);
+    void pressMoveLayoutModeCenteredResize(const QPoint& pos, bool maintainAspect = false, bool snapToBorder = true);
     
     // release press handlers 
     void releasePress(const QPoint& pos, bool shift = false);
--- branches/work/kst/1.5/kst/src/libkstapp/kstviewwidget.cpp #660783:660784
@@ -187,7 +187,7 @@
       e->accept();
     } else if (e->state() & Qt::LeftButton) {
       //setCursor(QCursor(Qt::ArrowCursor));
-      _view->pressMove(e->pos(), e->state() & Qt::ShiftButton);
+      _view->pressMove(e->pos(), e->state() & Qt::ShiftButton, e->state() & Qt::AltButton);
       e->accept();
     }
   }
Comment 3 Andrew Walker 2007-05-04 02:58:42 UTC
SVN commit 660878 by arwalker:

CCBUG:131739 still needs further testing

 M  +11 -2     kstgfxmousehandlerutils.cpp  
 M  +92 -15    ksttoplevelview.cpp  
 M  +3 -0      ksttoplevelview.h  


--- branches/work/kst/1.5/kst/src/libkstapp/kstgfxmousehandlerutils.cpp #660877:660878
@@ -175,15 +175,24 @@
     if (vertical) {
       newHalfHeight = kMin(newHalfHeight, anchorPoint.y() - bounds.top());
       newHalfHeight = kMin(newHalfHeight, bounds.bottom() - anchorPoint.y());
-      newSize.scale(originalRect.width(), 2*newHalfHeight, QSize::ScaleMin);
+      if (newHalfHeight > originalRect.height()/2) {
+        newSize.scale(originalRect.width(), 2*newHalfHeight, QSize::ScaleMax);
+      } else {
+        newSize.scale(originalRect.width(), 2*newHalfHeight, QSize::ScaleMin);
+      }
     } else {
       newHalfWidth = kMin(newHalfWidth, anchorPoint.x() - bounds.left());
       newHalfWidth = kMin(newHalfWidth, bounds.right() - anchorPoint.x());
-      newSize.scale(2*newHalfWidth, originalRect.height(), QSize::ScaleMin);
+      if (newHalfWidth > originalRect.width()/2) {
+        newSize.scale(2*newHalfWidth, originalRect.height(), QSize::ScaleMax);
+      } else {
+        newSize.scale(2*newHalfWidth, originalRect.height(), QSize::ScaleMin);
+      }
     }
 
     newRect.setSize(newSize);
     newRect.moveCenter(anchorPoint);
+    newRect = newRect.intersect(bounds);
   } else {
     if (vertical) {
       newRect = QRect(0, 0, originalRect.width(), 2*newHalfHeight);
--- branches/work/kst/1.5/kst/src/libkstapp/ksttoplevelview.cpp #660877:660878
@@ -599,6 +599,91 @@
 }
 
 
+void KstTopLevelView::pointSnapToBorders(int *xMin, int *yMin, const KstViewObjectPtr &obj, const QPoint &p) const {
+  for (KstViewObjectList::ConstIterator i = obj->children().begin(); i != obj->children().end(); ++i) {
+    if (_pressTarget != *i) {
+      const QRect rect((*i)->geometry());
+
+      pointSnapToBorders(xMin, yMin, *i, p);
+
+      if (rect.top() <= p.y() && rect.bottom() >= p.y()) {
+        if (labs(p.x() - rect.left()) < labs(*xMin)) {
+          *xMin = p.x() - rect.left();
+        } else if (labs(p.x() - rect.right()) < labs(*xMin)) {
+          *xMin = p.x() - rect.right();
+        }
+      }
+
+      if (rect.left() <= p.x() && rect.right() >= p.x()) {
+        if (labs(p.y() - rect.top()) < labs(*yMin)) {
+          *yMin = p.y() - rect.top();
+        } else if (labs(p.y() - rect.bottom()) < labs(*yMin)) {
+          *yMin = p.y() - rect.bottom();
+        }
+      }
+    }
+  }
+}
+
+
+QPoint KstTopLevelView::pointSnapToObjects(const QPoint& p) {
+  QRect rectNew;
+  QRect r;
+  int xMin = STICKY_THRESHOLD;
+  int yMin = STICKY_THRESHOLD;
+
+  r.setTopLeft(QPoint(0, 0));
+  r.setBottomRight(p);
+
+  pointSnapToBorders(&xMin, &yMin, this, p);
+
+  if (labs(yMin) < STICKY_THRESHOLD) {
+    r.setBottom(r.bottom() - yMin);
+  }
+
+  if (labs(xMin) < STICKY_THRESHOLD) {
+    r.setRight(r.right() - xMin);
+  }
+
+  return r.bottomRight();
+}
+
+
+QRect KstTopLevelView::resizeCenteredSnapToObjects(const QRect& r, const QRect& bounds, int direction) {
+  QRect rectNew = r;
+  int xMin = STICKY_THRESHOLD;
+  int yMin = STICKY_THRESHOLD;
+
+  resizeSnapToBorders(&xMin, &yMin, this, r, direction);
+
+  if (labs(yMin) < STICKY_THRESHOLD) {
+    if (direction & UP) {
+      rectNew.setTop(r.top() - yMin);
+      rectNew.setBottom(r.bottom() + yMin);
+    } else if (direction & DOWN) {
+      rectNew.setBottom(r.bottom() - yMin);
+      rectNew.setTop(r.top() + yMin);
+    }
+  }
+
+  if (labs(xMin) < STICKY_THRESHOLD) {
+    if (direction & LEFT) {
+      rectNew.setLeft(r.left() - xMin);
+      rectNew.setRight(r.right() + xMin);
+    } else if (direction & RIGHT) {
+      rectNew.setRight(r.right() - xMin);
+      rectNew.setLeft(r.left() + xMin);
+    }
+  }
+
+  if (!bounds.contains(rectNew)) {
+    rectNew = r;
+  }
+
+  return rectNew.normalize();
+}
+
+
 QRect KstTopLevelView::resizeSnapToObjects(const QRect& r, int direction) {
   QRect rectNew = r;
   int xMin = STICKY_THRESHOLD;
@@ -609,28 +694,16 @@
   if (labs(yMin) < STICKY_THRESHOLD) {
     if (direction & UP) {
       rectNew.setTop(r.top() - yMin);
-      if (direction & CENTEREDRESIZE) {
-        rectNew.setBottom(r.bottom() + yMin);
-      }
     } else if (direction & DOWN) {
       rectNew.setBottom(r.bottom() - yMin);
-      if (direction & CENTEREDRESIZE) {
-        rectNew.setTop(r.top() + yMin);
-      }
     }
   }
 
   if (labs(xMin) < STICKY_THRESHOLD) {
     if (direction & LEFT) {
       rectNew.setLeft(r.left() - xMin);
-      if (direction & CENTEREDRESIZE) {
-        rectNew.setRight(r.right() + xMin);
-      }
     } else if (direction & RIGHT) {
       rectNew.setRight(r.right() - xMin);
-      if (direction & CENTEREDRESIZE) {
-        rectNew.setLeft(r.left() + xMin);
-      }
     }
   }
 
@@ -851,6 +924,10 @@
       }
     }
 
+    if (snapToBorder) {
+      movePoint = pointSnapToObjects(movePoint); 
+    }
+
     const QRect old(_prevBand);
     _prevBand.setTopLeft(*fromPoint);
     _prevBand.setBottomRight(*toPoint);
@@ -873,11 +950,11 @@
 void KstTopLevelView::pressMoveLayoutModeCenteredResize(const QPoint& pos, bool maintainAspect, bool snapToBorder) {
   //centered resize means that the center of the object stays constant
   const QRect old(_prevBand);
-  
+
   _prevBand = newSizeCentered(_pressTarget->geometry(), _pressTarget->_parent->geometry(), _pressDirection, pos, maintainAspect);
 
   if (snapToBorder) {
-    _prevBand = resizeSnapToObjects(_prevBand, _pressDirection); 
+    _prevBand = resizeCenteredSnapToObjects(_prevBand, _pressTarget->_parent->geometry(), _pressDirection); 
   }
 
   if (_prevBand != old) {
@@ -933,7 +1010,7 @@
   if (!_selectionList.isEmpty()) {
     for (KstViewObjectList::ConstIterator i = _selectionList.begin(); i != _selectionList.end(); ++i) {
       obj = obj.unite((*i)->geometry());
-    } 
+    }
   }
   const QPoint objOffset(old.topLeft() - obj.topLeft());
 
--- branches/work/kst/1.5/kst/src/libkstapp/ksttoplevelview.h #660877:660878
@@ -131,7 +131,10 @@
     QRect correctHeightForRatio(const QRect& oldRect, double ratio, int direction, int origRight, int origLeft);
     void moveSnapToBorders(int *xMin, int *yMin, const KstViewObjectPtr &obj, const QRect &r) const;
     void resizeSnapToBorders(int *xMin, int *yMin, const KstViewObjectPtr& obj, const QRect &r, int direction) const;
+    QRect resizeCenteredSnapToObjects(const QRect& r, const QRect& bounds, int direction);
     QRect resizeSnapToObjects(const QRect& r, int direction);
+    void pointSnapToBorders(int *xMin, int *yMin, const KstViewObjectPtr &obj, const QPoint &p) const;
+    QPoint pointSnapToObjects(const QPoint& p);
     // Called as a response to drag re-entering widget()
     void restartMove();
     
Comment 4 Andrew Walker 2007-05-07 21:14:24 UTC
No problems encountered, so close the bug.