[krita/kazakov/svg-loading] libs/flake: Implement two modes of selecting shapes
Dmitry Kazakov
null at kde.org
Tue Dec 27 13:28:09 UTC 2016
Git commit 5a9c73990f5cd91d7d82a2704cce900d5ce40bd0 by Dmitry Kazakov.
Committed on 27/12/2016 at 10:26.
Pushed by dkazakov into branch 'kazakov/svg-loading'.
Implement two modes of selecting shapes
1) Left-to-right mode selects only shapes that that completely inside
the selection frame.
2) Right-to-left mode selects the shapes that are crossed by the frame.
3) Blue frame means "covering" (or "containing") selection
4) Green frame means "crossing" selection
5) TODO: make two cursors/icons for the two modes, so people could
understand it without reading the manual.
See point 4A in the requirements for the details:
https://phabricator.kde.org/w/krita/vector_tool_requirements/
CC:kimageshop at kde.org
M +42 -0 libs/flake/KoRTree.h
M +21 -10 libs/flake/KoShapeManager.cpp
M +1 -1 libs/flake/KoShapeManager.h
M +33 -10 libs/flake/tools/KoShapeRubberSelectStrategy.cpp
M +10 -0 libs/flake/tools/KoShapeRubberSelectStrategy.h
M +5 -0 libs/flake/tools/KoZoomStrategy.cpp
M +3 -0 libs/flake/tools/KoZoomStrategy.h
https://commits.kde.org/krita/5a9c73990f5cd91d7d82a2704cce900d5ce40bd0
diff --git a/libs/flake/KoRTree.h b/libs/flake/KoRTree.h
index bcc8fa4ba5b..0675f80c5be 100644
--- a/libs/flake/KoRTree.h
+++ b/libs/flake/KoRTree.h
@@ -105,6 +105,16 @@ public:
QList<T> contains(const QPointF &point) const;
/**
+ * @brief Find all data item which contain the point
+ * The items are sorted by insertion time in ascending order.
+ *
+ * @param point which should be contained in the objects
+ *
+ * @return objects which contain the point
+ */
+ QList<T> contained(const QRectF &point) const;
+
+ /**
* @brief Find all data rectangles
* The order is NOT guaranteed to be the same as that used by values().
*
@@ -162,6 +172,7 @@ protected:
virtual void intersects(const QRectF& rect, QMap<int, T> & result) const = 0;
virtual void contains(const QPointF & point, QMap<int, T> & result) const = 0;
+ virtual void contained(const QRectF & point, QMap<int, T> & result) const = 0;
virtual void keys(QList<QRectF> & result) const = 0;
virtual void values(QMap<int, T> & result) const = 0;
@@ -252,6 +263,7 @@ class NonLeafNode : virtual public Node
virtual void intersects(const QRectF& rect, QMap<int, T> & result) const;
virtual void contains(const QPointF & point, QMap<int, T> & result) const;
+ virtual void contained(const QRectF & point, QMap<int, T> & result) const;
virtual void keys(QList<QRectF> & result) const;
virtual void values(QMap<int, T> & result) const;
@@ -286,6 +298,7 @@ class LeafNode : virtual public Node
virtual void intersects(const QRectF& rect, QMap<int, T> & result) const;
virtual void contains(const QPointF & point, QMap<int, T> & result) const;
+ virtual void contained(const QRectF & point, QMap<int, T> & result) const;
virtual void keys(QList<QRectF> & result) const;
virtual void values(QMap<int, T> & result) const;
@@ -471,6 +484,15 @@ QList<T> KoRTree<T>::contains(const QPointF &point) const
}
template <typename T>
+QList<T> KoRTree<T>::contained(const QRectF& rect) const
+{
+ QMap<int, T> found;
+ m_root->contained(rect, found);
+ return found.values();
+}
+
+
+template <typename T>
QList<QRectF> KoRTree<T>::keys() const
{
QList<QRectF> found;
@@ -871,6 +893,16 @@ void KoRTree<T>::NonLeafNode::contains(const QPointF & point, QMap<int, T> & res
}
template <typename T>
+void KoRTree<T>::NonLeafNode::contained(const QRectF& rect, QMap<int, T> & result) const
+{
+ for (int i = 0; i < this->m_counter; ++i) {
+ if (this->m_childBoundingBox[i].intersects(rect)) {
+ m_childs[i]->contained(rect, result);
+ }
+ }
+}
+
+template <typename T>
void KoRTree<T>::NonLeafNode::keys(QList<QRectF> & result) const
{
for (int i = 0; i < this->m_counter; ++i) {
@@ -1041,6 +1073,16 @@ void KoRTree<T>::LeafNode::contains(const QPointF & point, QMap<int, T> & result
}
template <typename T>
+void KoRTree<T>::LeafNode::contained(const QRectF& rect, QMap<int, T> & result) const
+{
+ for (int i = 0; i < this->m_counter; ++i) {
+ if (rect.contains(this->m_childBoundingBox[i])) {
+ result.insert(m_dataIds[i], m_data[i]);
+ }
+ }
+}
+
+template <typename T>
void KoRTree<T>::LeafNode::keys(QList<QRectF> & result) const
{
for (int i = 0; i < this->m_counter; ++i) {
diff --git a/libs/flake/KoShapeManager.cpp b/libs/flake/KoShapeManager.cpp
index 5f8f8cdc06a..691d6c4b69b 100644
--- a/libs/flake/KoShapeManager.cpp
+++ b/libs/flake/KoShapeManager.cpp
@@ -497,25 +497,36 @@ KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelectio
return 0; // missed everything
}
-QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes)
+QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes, bool containedMode)
{
d->updateTree();
- QList<KoShape*> intersectedShapes(d->tree.intersects(rect));
-
- for (int count = intersectedShapes.count() - 1; count >= 0; count--) {
+ QList<KoShape*> shapes(containedMode ? d->tree.contained(rect) : d->tree.intersects(rect));
+
+ for (int count = shapes.count() - 1; count >= 0; count--) {
- KoShape *shape = intersectedShapes.at(count);
+ KoShape *shape = shapes.at(count);
- if (omitHiddenShapes && ! shape->isVisible(true)) {
- intersectedShapes.removeAt(count);
+ if (omitHiddenShapes && !shape->isVisible(true)) {
+ shapes.removeAt(count);
} else {
const QPainterPath outline = shape->absoluteTransformation(0).map(shape->outline());
- if (! outline.intersects(rect) && ! outline.contains(rect)) {
- intersectedShapes.removeAt(count);
+
+ if (!containedMode && !outline.intersects(rect) && !outline.contains(rect)) {
+ shapes.removeAt(count);
+
+ } else if (containedMode) {
+
+ QPainterPath containingPath;
+ containingPath.addRect(rect);
+
+ if (!containingPath.contains(outline)) {
+ shapes.removeAt(count);
+ }
}
}
}
- return intersectedShapes;
+
+ return shapes;
}
void KoShapeManager::update(QRectF &rect, const KoShape *shape, bool selectionHandles)
diff --git a/libs/flake/KoShapeManager.h b/libs/flake/KoShapeManager.h
index c5f3128f42f..ca164c82bbd 100644
--- a/libs/flake/KoShapeManager.h
+++ b/libs/flake/KoShapeManager.h
@@ -147,7 +147,7 @@ public:
* @param rect the rectangle in the document coordinate system.
* @param omitHiddenShapes if true, only visible shapes are considered
*/
- QList<KoShape *> shapesAt(const QRectF &rect, bool omitHiddenShapes = true);
+ QList<KoShape *> shapesAt(const QRectF &rect, bool omitHiddenShapes = true, bool containedMode = false);
/**
* Request a repaint to be queued.
diff --git a/libs/flake/tools/KoShapeRubberSelectStrategy.cpp b/libs/flake/tools/KoShapeRubberSelectStrategy.cpp
index 0b0a0e8a3d4..727c5d5d6c0 100644
--- a/libs/flake/tools/KoShapeRubberSelectStrategy.cpp
+++ b/libs/flake/tools/KoShapeRubberSelectStrategy.cpp
@@ -29,6 +29,7 @@
#include "KoSelection.h"
#include "KoCanvasBase.h"
+
KoShapeRubberSelectStrategy::KoShapeRubberSelectStrategy(KoToolBase *tool, const QPointF &clicked, bool useSnapToGrid)
: KoInteractionStrategy(*(new KoShapeRubberSelectStrategyPrivate(tool)))
{
@@ -44,14 +45,23 @@ void KoShapeRubberSelectStrategy::paint(QPainter &painter, const KoViewConverter
Q_D(KoShapeRubberSelectStrategy);
painter.setRenderHint(QPainter::Antialiasing, false);
- QColor selectColor(Qt::blue); // TODO make configurable
- selectColor.setAlphaF(0.5);
- QBrush sb(selectColor, Qt::SolidPattern);
- painter.setPen(QPen(sb, 0));
- painter.setBrush(sb);
+ const QColor crossingColor(80,130,8);
+ const QColor coveringColor(8,60,167);
+
+ QColor selectColor(
+ currentMode() == CrossingSelection ?
+ crossingColor : coveringColor);
+
+ selectColor.setAlphaF(0.8);
+ painter.setPen(QPen(selectColor, 0));
+
+ selectColor.setAlphaF(0.4);
+ const QBrush fillBrush(selectColor);
+ painter.setBrush(fillBrush);
+
QRectF paintRect = converter.documentToView(d->selectedRect());
paintRect = paintRect.normalized();
- paintRect.adjust(0., -0.5, 0.5, 0.);
+
painter.drawRect(paintRect);
}
@@ -59,7 +69,7 @@ void KoShapeRubberSelectStrategy::handleMouseMove(const QPointF &p, Qt::Keyboard
{
Q_D(KoShapeRubberSelectStrategy);
QPointF point = d->snapGuide->snap(p, modifiers);
- if ((modifiers & Qt::AltModifier) != 0) {
+ if (modifiers & Qt::AltModifier || modifiers & Qt::ControlModifier) {
d->tool->canvas()->updateCanvas(d->selectedRect());
d->selectRect.moveTopLeft(d->selectRect.topLeft() - (d->lastPos - point));
d->lastPos = point;
@@ -101,16 +111,29 @@ void KoShapeRubberSelectStrategy::finishInteraction(Qt::KeyboardModifiers modifi
Q_D(KoShapeRubberSelectStrategy);
Q_UNUSED(modifiers);
KoSelection * selection = d->tool->canvas()->shapeManager()->selection();
- QList<KoShape *> shapes(d->tool->canvas()->shapeManager()->shapesAt(d->selectRect));
+
+ const bool useContainedMode = currentMode() == CoveringSelection;
+
+ QList<KoShape *> shapes =
+ d->tool->canvas()->shapeManager()->
+ shapesAt(d->selectedRect(), true, useContainedMode);
+
Q_FOREACH (KoShape * shape, shapes) {
- if (!(shape->isSelectable() && shape->isVisible()))
- continue;
+ if (!shape->isSelectable()) continue;
+
selection->select(shape);
}
+
d->tool->repaintDecorations();
d->tool->canvas()->updateCanvas(d->selectedRect());
}
+KoShapeRubberSelectStrategy::SelectionMode KoShapeRubberSelectStrategy::currentMode() const
+{
+ Q_D(const KoShapeRubberSelectStrategy);
+ return d->selectRect.left() < d->selectRect.right() ? CoveringSelection : CrossingSelection;
+}
+
KUndo2Command *KoShapeRubberSelectStrategy::createCommand()
{
return 0;
diff --git a/libs/flake/tools/KoShapeRubberSelectStrategy.h b/libs/flake/tools/KoShapeRubberSelectStrategy.h
index f0ec46fc3b7..5981814e133 100644
--- a/libs/flake/tools/KoShapeRubberSelectStrategy.h
+++ b/libs/flake/tools/KoShapeRubberSelectStrategy.h
@@ -33,6 +33,9 @@ class KoShapeRubberSelectStrategyPrivate;
/**
* Implement the rubber band selection of flake objects.
+ *
+ * When the user selects stuff in left-to-right way, selection is in "covering"
+ * (or "containing") mode, when in "left-to-right" in "crossing" mode
*/
class KRITAFLAKE_EXPORT KoShapeRubberSelectStrategy : public KoInteractionStrategy
{
@@ -57,6 +60,13 @@ protected:
/// constructor
KoShapeRubberSelectStrategy(KoShapeRubberSelectStrategyPrivate &);
+ enum SelectionMode {
+ CrossingSelection,
+ CoveringSelection
+ };
+
+ virtual SelectionMode currentMode() const;
+
private:
Q_DECLARE_PRIVATE(KoShapeRubberSelectStrategy)
};
diff --git a/libs/flake/tools/KoZoomStrategy.cpp b/libs/flake/tools/KoZoomStrategy.cpp
index f4c22ed984a..36dd71ac039 100644
--- a/libs/flake/tools/KoZoomStrategy.cpp
+++ b/libs/flake/tools/KoZoomStrategy.cpp
@@ -60,6 +60,11 @@ void KoZoomStrategy::cancelInteraction()
d->tool->canvas()->updateCanvas(d->selectedRect().toRect().normalized());
}
+KoShapeRubberSelectStrategy::SelectionMode KoZoomStrategy::currentMode() const
+{
+ return CoveringSelection;
+}
+
void KoZoomStrategy::forceZoomOut()
{
m_forceZoomOut = true;
diff --git a/libs/flake/tools/KoZoomStrategy.h b/libs/flake/tools/KoZoomStrategy.h
index 1db4d09d914..bff94e6e2bb 100644
--- a/libs/flake/tools/KoZoomStrategy.h
+++ b/libs/flake/tools/KoZoomStrategy.h
@@ -46,6 +46,9 @@ public:
/// Execute the zoom
virtual void finishInteraction(Qt::KeyboardModifiers modifiers);
virtual void cancelInteraction();
+
+protected:
+ SelectionMode currentMode() const override;
private:
KoCanvasController *m_controller;
More information about the kimageshop
mailing list