Diffusion Krita dbbaede31f5b

Remove shape tracking from KoShapeControllerBase

Authored by dkazakov on May 26 2020, 10:15 AM.

Description

Remove shape tracking from KoShapeControllerBase

Long ago, when Flake was flourishing as a cross-application framework,
it supported shapes being toplevel shapes (that is, having no parent).
It created a lot of troubles, because all the roots still had to be
stored somewhere and some entity had to track shapes addition and
removal from the hierarchy.

That was exactly the reason why KoShapeControllerBase was born. Those
days it was called KoShapeDocumentBase, which meant "a document
that stores all shapes, including the root ones". Legends say
that it was also supposed to add/remove shapes from KoShapeManager,
but no written proofs of it survived.

When Krita introduced vector layers, every vector layer became a
flake's "canvas". Every canvas also had a KoShapeManager, that was
responsible for painting the shapes. But Krita diverged from the
expected way to track the shapes: instead of adding a
KoShapeDocumentBase to every vector layer, it added only one "document"
for all the layers. This document (now called KisShapeController)
dispatched add/remove notifications to the active layer. That was fun,
but never worked as expected. Due to complexity of signal delivery and
multithreading, it could dispatch add/remove notifications to a wrong
layer. It also made managing shapes' lifetime rather cumbersome: when
a group shape was destroyed, it had to destroy all the children, but it
had *no* link to the controller, so it couldn't notify it!

Due to these problems, at some point Krita switched to "no multiple
roots" paradigm. Every vector layer started to have exactly one root
at the top of the shapes hierarchy (it was the vector layer itself).
It solved all the notifications problems: the root could track all
its children and manage KoShapeManager properly. It also solved the
lifetime problem, since there was a single point where shapes could
be added/removed, KoShape::setParent().

After this change KoShapeControllerBase stopped serving its original
purpose and was only needed to add lazily create vecotor layers or
shape selections, when new shapes were dropped to the canvas. But we
still had to pass all shape additions through it. It resulted in a very
weird chain of calls, when one wanted to add a new shape to the image:

  1. KoShapeController::addShapeDirect()
  2. KoShapeCreateCommand::redo()
  3. KoShapeControllerBase::addShape()
  4. KoCanvasBase::addCommand()
  5. NOTE ^^^: adding a command to the undo stack while another
  6. command is running!

This patch breaks this chain. KoShapeControllerBase now doesn't track
anything and is not even allowed to track anything. Having multiple
toplevel (root) shapes is now prohibited.

Instead, all the shapes hierarchy is now tracked by the single root
shape. To achieve that, special methods were added to
SimpleShapeContainerModel to manage KoShapeManager contents
(they were moved from ShapeLayerContainerModel).

From now on, the official ways to add a shape to a hierarchy is the
following:

  1. If you know what parent you want to add the shape to, just create KoShapeCreateCommand() and it will do everything for you (adding and resolving z-index collisions)
  1. If you just want to drop the shape to the canvas and want it to be added "somewhere", then use KoShapeController::addShapesDirect(). If will find a suitable layer for the shape (or create a new one). All that will be done in undoable manner.

I hope in the next few iterations we will be able to remove
KoShapeControllerBase completely from the Krita's codebase. It doesn't
do much useful work anymore. It's main reason for exintence now is to
provide KisImage size and DPI value.

CCBUG:411394

Details

Committed
dkazakovJun 2 2020, 9:49 AM
Parents
R37:78d71cd29855: Remove update routines from KisShapeController::removeShape()
Branches
Unknown
Tags
Unknown