Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(MultiSelection): add target from behind active selection #8744

Merged
merged 14 commits into from
Aug 22, 2023

Conversation

ShaMan123
Copy link
Contributor

@ShaMan123 ShaMan123 commented Feb 27, 2023

Motivation

Adding a target from behind active selection was not possible

Description

  • if active selection is the target of the event we search for children to remove from selection or objects behind it to add to selection
  • in case a group and all it's objects are selected we deselect group: fixes weird border box drawing

Changes

Gist

In Action

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2023

Build Stats

file / KB (diff) bundled minified
fabric 915.112 (+0.292) 305.555 (+0.056)

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2023

Coverage after merging fix-multi-select-add into master will be

82.97%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
index.node.ts7.69%100%0%14.29%17, 20, 23, 35, 38, 41
src
   ClassRegistry.ts100%100%100%100%
   Collection.ts94.71%94.64%86.67%97.09%101, 104, 207–208, 233–234
   CommonMethods.ts96.55%87.50%100%100%9
   Intersection.ts100%100%100%100%
   Observable.ts87.76%85.29%87.50%89.58%134–135, 160–161, 32–33, 41, 50, 78, 89
   Point.ts100%100%100%100%
   Shadow.ts98.36%95.65%100%100%178
   cache.ts97.06%90%100%100%57
   config.ts75%66.67%66.67%82.76%130, 138, 138, 138, 138, 138–140, 151–153
   constants.ts100%100%100%100%
src/Pattern
   Pattern.ts92.31%91.89%90%93.55%118, 129, 138, 31, 94
src/brushes
   BaseBrush.ts100%100%100%100%
   CircleBrush.ts0%0%0%0%108, 108, 108, 110, 112, 114–116, 118–121, 128–129, 13, 136, 138, 23–24, 32–36, 40–44, 51–54, 62–66, 68, 76, 76, 76, 76, 76–77, 79, 79, 79–82, 84, 92–93, 95, 97–99
   PatternBrush.ts97.06%87.50%100%100%21
   PencilBrush.ts91.06%82.35%100%93.81%122–123, 152, 152–154, 176, 176, 276, 280, 285–286, 68–69, 84–85
   SprayBrush.ts0%0%0%0%107, 107, 107, 107, 107–108, 110–111, 118–119, 121, 123–127, 136, 140–141, 141, 149, 149, 149–152, 154–157, 161–162, 164, 166–169, 17, 172, 179, 18, 180, 182, 184–185, 187, 194–195, 197–198, 20, 201, 201, 208, 208, 21, 212, 22, 22, 22–24, 28, 32, 39, 46, 53, 60, 67, 84–86, 94–96, 98–99
src/canvas
   Canvas.ts79.12%77.68%83.05%79.61%1001–1002, 1002, 1002, 1004, 1012, 1012, 1012–1014, 1014, 1014, 1020–1021, 1029–1030, 1030, 1030–1031, 1036, 1038, 1069–1071, 1074–1075, 1079–1080, 1193–1195, 1198–1199, 1272, 1391, 1513, 157, 182, 292–293, 296–300, 305, 328–329, 334–339, 359, 359, 359–360, 360, 360–361, 369, 37, 374–375, 375, 375–376, 378, 387, 393–394, 394, 394, 41, 437, 445, 449, 449, 449–450, 452, 534–535, 535, 535–536, 542, 542, 542–544, 564, 566, 566, 566–567, 567, 567, 570, 570, 570–571, 574, 583–584, 586–587, 589, 589–590, 592–593, 605–606, 606, 606–607, 609–614, 620, 627, 664, 664, 664, 666, 668–673, 679, 685, 685, 685–686, 688, 691, 696, 709, 737, 737, 795–796, 796, 796–797, 799, 802–803, 803, 803–804, 806–807, 810, 810–812, 815–816, 886, 898, 905, 926, 958, 979–980, 996–997, 997, 997–999
   CanvasOptions.ts100%100%100%100%
   SelectableCanvas.ts94.44%92.28%94.23%96.17%1098, 1100, 1102–1103, 301, 477–478, 480–481, 481, 481, 530–531, 592–593, 646–648, 680, 685–686, 713–714, 898, 898–899, 902, 922, 922, 971, 979
   StaticCanvas.ts96.78%93.09%100%98.53%1030, 1040, 1092–1093, 1096, 1131–1132, 1208, 1217, 1217, 1221, 1221, 1268–1269, 185–186, 202, 570, 582–583, 913–914, 914, 914–915
   StaticCanvasOptions.ts100%100%100%100%
   TextEditingManager.ts84.31%71.43%91.67%88%17–18, 18, 18–19, 19, 19
src/canvas/DOMManagers
   CanvasDOMManager.ts95.52%70%100%100%21–22, 29
   StaticCanvasDOMManager.ts100%100%100%100%
   util.ts86.67%80.56%83.33%93.94%14, 26, 63–64, 67, 67, 74, 93–94
src/color
   Color.ts94.96%91.67%96.30%96.05%233, 258–259, 267–268, 48
   color_map.ts100%100%100%100%
   constants.ts100%100%100%100%
   util.ts85.71%76.92%100%89.74%55–56, 56, 58, 58, 58–59, 61–62, 89
src/controls
   Control.ts93.33%87.88%91.67%97.78%175, 240, 327, 327, 362
   changeWidth.ts100%100%100%100%
   commonControls.ts100%100%100%100%
   controlRendering.ts81.63%78%100%84.78%106, 111, 121, 121, 45, 50, 61, 61, 65–72, 81–82
   drag.ts100%100%100%100%
   fireEvent.ts88.89%75%100%100%13
   polyControl.ts5.97%0%0%11.11%100, 105, 119, 119, 119, 119, 119, 121–124, 124, 127, 134, 17, 25–29, 29, 29, 29, 29, 29, 29, 29, 50–56, 56, 56, 56, 56, 58, 63–64, 66, 76, 82–83, 83, 83–84, 88–90, 90, 90, 90, 90, 92
   rotate.ts19.57%12.50%50%21.43%41, 45, 51, 51, 51–52, 55–57, 59, 59, 59, 59, 59–61, 61, 61–63, 65, 65, 65–67, 67, 67–68, 73, 73, 73–74, 76, 78, 80–81
   scale.ts93.57%92.94%100%93.67%129–130, 132–134, 148–149, 181–183, 42
   scaleSkew.ts78.79%64.29%100%85.71%27, 29, 29, 29, 31, 33, 35
   skew.ts91.03%79.31%100%97.67%131–132, 163–164, 171, 177, 179
   util.ts100%100%100%100%
   wrapWithFireEvent.ts100%100%100%100%
   wrapWithFixedAnchor.ts100%100%100%100%
src/env
   browser.ts84.21%77.78%50%100%

in case group and all it's objects are selected we deselect group
@ShaMan123 ShaMan123 changed the title fix(MultiSelection): add target from behind active selection fix(MultiSelection): add target from behind active selection + nested selection edge case Feb 27, 2023
Copy link
Contributor Author

@ShaMan123 ShaMan123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ready

@asturur
Copy link
Member

asturur commented Feb 27, 2023

@ShaMan123 can you explain the bug described as Adding a target from behind active selection was not possible when was it introduced? which PR broke it?
I think it was behind a modified key in 5.x

) {
// in case group and all it's objects are selected we deselect group
this.remove(parent);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this kind of interactivity with groups opens for a lot of edge cases.
I don't think automatically deselecting things outside user clicks is correct since we are assuming which is the correct UX here.
Why is deselecting the parent or the group or anything correct over the other option?
Does it create an interaction issue or just feel strange?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a bug
I assume, though I don't really understand, that since group doesn't layout when its children select/deselect something happens causing:

fabric.js.sandbox.-.Google.Chrome.2023-02-27.15-24-53_Trim.mp4

both textboxes and poly are grouped
active selection at the beginning is circle and group

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with interactive groups it will be possible to deselect them in a multi selection interaction if only one object remains unselected in group since the object becomes the deselection target and the group will hang around, SELECTED

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree maybe it should be moved to multiSelectAdd

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember you said groups are simply containers.
What meaning does an empty selected container have in a selection context?

Copy link
Member

@asturur asturur Mar 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Groups were simple containers, yes, meaning the would just contain object and render them.
Now they are interactive, and the surface edge cases.
Most of them should be left to the developer, and we should patch the ones that can be caused by the user alone.
Probably is enough to avoid rendering the control boxes, i m not sure at this point since it doesn't make sense to select a group and the objects inside.

What i would answer quickly would be that if the group is selected, you can't select objects inside.
And probably is the solution i would adapt if it simplifies our life.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What i would answer quickly would be that if the group is selected, you can't select objects inside.
And probably is the solution i would adapt if it simplifies our life.

Not bad

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your suggestion makes sense. If we agree on it I can try a patch

@ShaMan123
Copy link
Contributor Author

hich PR broke it?
I think it was behind a modified key in 5.x

I am not sure. Looking into it. But 5.x supports this

Kitchensink._.Fabric.js.Demos.-.Google.Chrome.2023-02-27.15-21-49_Trim.mp4

@ShaMan123
Copy link
Contributor Author

ShaMan123 commented Feb 27, 2023

Found it.
#8665 I refactored findTarget not to accept skipGroup and moved the logic to where it is now, and I missed this case of needing to search all objects, not only selected objects

    findTarget: function (e, skipGroup) {
      if (this.skipTargetFind) {
        return;
      }

      var ignoreZoom = true,
          pointer = this.getPointer(e, ignoreZoom),
          activeObject = this._activeObject,
          aObjects = this.getActiveObjects(),
          activeTarget, activeTargetSubs,
          isTouch = isTouchEvent(e),
+          shouldLookForActive = false;

      // first check current group (if one exists)
      // active group does not check sub targets like normal groups.
      // if active group just exits.
      this.targets = [];
+ // skipGroup skips all logic that I commented out
      // if we hit the corner of an activeObject, let's return that.
      // if (shouldLookForActive && activeObject._findTargetCorner(pointer, isTouch)) {
      //   return activeObject;
      // }
      // if (aObjects.length > 1 && !skipGroup && activeObject === this._searchPossibleTargets([activeObject], pointer)) {
      //   return activeObject;
      // }
      // if (aObjects.length === 1 &&
      //   activeObject === this._searchPossibleTargets([activeObject], pointer)) {
      //   if (!this.preserveObjectStacking) {
      //     return activeObject;
      //   }
      //   else {
      //     activeTarget = activeObject;
      //     activeTargetSubs = this.targets;
      //     this.targets = [];
      //   }
      // }
+      var target = this._searchPossibleTargets(this._objects, pointer); // I missed here
      if (e[this.altSelectionKey] && target && activeTarget && target !== activeTarget) {
        target = activeTarget;
        this.targets = activeTargetSubs;
      }
      return target;
    },

@ShaMan123 ShaMan123 requested a review from asturur February 27, 2023 13:55
@asturur
Copy link
Member

asturur commented Mar 5, 2023

hich PR broke it?
I think it was behind a modified key in 5.x

I am not sure. Looking into it. But 5.x supports this

Yes this was made possible in findTarget, i think at the end of the function where we would say that if there wasn't a sub target in the activeSelection, but there was eventually a target under it we would take that one

(object) => !prevActiveObjects.includes(object)
),
...prevActiveObjects,
],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I m trying to wrap my head around why we need to change the stack for searchPossibleTarget.
I understand you want the activeSelectionObject searched first, but i think that is job for altSelectionKey.
Isn'it?
Shouldn't we use the natural stack order here?

Copy link
Contributor Author

@ShaMan123 ShaMan123 Mar 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this was made possible in findTarget, i think at the end of the function where we would say that if there wasn't a sub target in the activeSelection, but there was eventually a target under it we would take that one

I followed this logic and:

// alt selection: select active object even though it is not the top most target

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we can achieve it in a different way.
If !target => search the rest of the object on canvas to see if there is an object behind the selection that we can add

@ShaMan123
Copy link
Contributor Author

@asturur ?

@ShaMan123
Copy link
Contributor Author

@asturur what about this?

@ShaMan123 ShaMan123 requested a review from asturur May 7, 2023 06:16
@ShaMan123
Copy link
Contributor Author

@asturur this is blocking me at work, can we merge it?

Copy link
Contributor Author

@ShaMan123 ShaMan123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

Comment on lines +1485 to +1490
target =
// first search active objects for a target to remove
this.searchPossibleTargets(prevActiveObjects, pointer) ||
// if not found, search under active selection for a target to add
// `prevActiveObjects` will be searched but we already know they will not be found
this.searchPossibleTargets(this._objects, pointer);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made this simpler perhaps in 347ff7f - revert it to see the prev revision
I belive this is more performant also

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asturur
asturur previously approved these changes Aug 22, 2023
@ShaMan123 ShaMan123 changed the title fix(MultiSelection): add target from behind active selection + nested selection edge case fix(MultiSelection): add target from behind active selection Aug 22, 2023
@asturur asturur merged commit 87b48aa into master Aug 22, 2023
@asturur asturur deleted the fix-multi-select-add branch August 22, 2023 07:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants