-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Changes for text dragging #8065
Conversation
This PR solves a bug by chance. There is also another small bug in the original PR, and is not a regression ( maybe is a generic bug in fabricJS right now ), sometimes the style of the dropped text is applied uncorrectly, shifting right by one char or by as many char as the newlines contained in the selection. |
I am not sure I understand what you mean. HTML drop selects the text that was dropped to the drop target. This PR changed it and I disagree with it. See this gif showing regressions:
|
src/mixins/itext_behavior.mixin.js
Outdated
} | ||
if (this.__isDraggingOver) { | ||
if (this.__isDraggingOver && canDrop) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are you sure about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this is a consequence of the previous ifs, and i made explicit.
I didn't think of it too much, could be wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh i see is not the same because this just an if and not an else if.
needs to be removed
src/mixins/itext_behavior.mixin.js
Outdated
this.renderCursorOrSelection(); | ||
// find cursor under the drag part. | ||
var dragSelection = this.getSelectionStartFromPointer(e); | ||
this.renderCursorAt(dragSelection); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly we should render the selection here in case dragSource===dragOverTarget
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fabric.js/src/shapes/itext.class.js
Line 335 in 2db76ec
this.__isDragging && this._renderDragStartSelection(ctx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the pr description i wrote that:
One regression for sure is there, and is the fact that if i drag/drop in the same text box the dragStart selection isn't visible.
I know why this is broken, and is because of the double clear textarea, i was supposed to fix it yesterday but then i couldn't.
So HTML has only textareas that are permanently in edit state. I'm happy adding a feature that allow for drag/drop text, but not adding a full flow that put the developer in a rigid user experience. We implement the minimal and we let them use the events to build the experience they prefer using the drop event or drag end event (enterEdit after drop, select after drop, or just place the cursor at the end after drop) |
src/shapes/itext.class.js
Outdated
/** | ||
* Renders text selection | ||
* @private | ||
* @param {{ selectionStart: number, selectionEnd: number }} selection | ||
* @param {Object} boundaries Object with left/top/leftOffset/topOffset | ||
* @param {CanvasRenderingContext2D} ctx transformed context to draw on | ||
*/ | ||
_renderSelection: function (selection, boundaries, ctx) { | ||
_renderSelection: function (ctx, selection, boundaries, ctx) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_renderSelection: function (ctx, selection, boundaries, ctx) { | |
_renderSelection: function (ctx, selection, boundaries) { |
src/shapes/itext.class.js
Outdated
@@ -446,6 +470,8 @@ | |||
- charHeight * (1 - this._fontSizeFraction); | |||
|
|||
if (this.inCompositionMode) { | |||
// TODO: investigate why there isn't a return inside the if, | |||
// and why can't happe top of the function | |||
this.renderSelection(boundaries, ctx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to swap these arguments
src/shapes/itext.class.js
Outdated
ctx.restore(); | ||
}, | ||
|
||
_renderCursorAt: function(selecetionStart) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_renderCursorAt: function(selecetionStart) { | |
_renderCursorAt: function(selectionStart) { |
src/shapes/itext.class.js
Outdated
@@ -434,7 +454,11 @@ | |||
* @param {CanvasRenderingContext2D} ctx transformed context to draw on | |||
*/ | |||
renderCursor: function(boundaries, ctx) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to swap arguments here
src/shapes/itext.class.js
Outdated
ctx.restore(); | ||
}, | ||
|
||
_renderCursorAt: function(selecetionStart) { | ||
var boundaries = this._getCursorBoundaries(selectionStart, true); | ||
this._renderCursor(ctx, boundaries, selectionStart); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ctx is missing here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider that yesterday i had to go to bed, i was checking where it was failing and i couldn't see this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will hopefully go away with rollup and sourcemaps
@@ -462,23 +488,26 @@ | |||
* @param {Object} boundaries Object with left/top/leftOffset/topOffset | |||
* @param {CanvasRenderingContext2D} ctx transformed context to draw on | |||
*/ | |||
renderSelection: function (boundaries, ctx) { | |||
renderSelection: function (ctx, boundaries) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i moved those argument orders, in general, because we put ctx as first argument when we render.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent
I left as is but didn't like it
* @private | ||
*/ | ||
__widthOfSpace: [], | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
completely unused
oh the UTs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the approach of the last commits renderDropEffect
etc.
I will test behavior on browser and comment if I find regressions.
src/shapes/itext.class.js
Outdated
ctx.restore(); | ||
}, | ||
|
||
_renderCursorAt: function(selecetionStart) { | ||
var boundaries = this._getCursorBoundaries(selectionStart, true); | ||
this._renderCursor(ctx, boundaries, selectionStart); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will hopefully go away with rollup and sourcemaps
@@ -462,23 +488,26 @@ | |||
* @param {Object} boundaries Object with left/top/leftOffset/topOffset | |||
* @param {CanvasRenderingContext2D} ctx transformed context to draw on | |||
*/ | |||
renderSelection: function (boundaries, ctx) { | |||
renderSelection: function (ctx, boundaries) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent
I left as is but didn't like it
src/shapes/itext.class.js
Outdated
); | ||
} | ||
}, | ||
|
||
renderDropTargetEffect: function(e, dontClear) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we rename dontClear
?
preserveContextState
?
or negate it to clearContext
?
Actually it is ugly IMO and should be removed. MonkeyPatch written all over it.
The caller _renderDragEffects
should clear context if needed and prepare it so renderDropTargetEffect
receives a ctx in a state that is transformed to the object's plane (like all rendering functions across fabric). And modify this function accordingly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i tried it but didn't work, there was a case in which i was killing the source effect anyway.
What we can do is trying to transform ctx here, and pass it along and make that the effects receive a ctx.
move clearContextTop from text to object
clearContextTop for source, for target,
then draw them in sequence.
This will mean that we change the signature of the functions source and target to accept e and ctx.
is that ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds better IMO
There's a small regression on dragstart. It clears the selection but should not. Minor and can be revisited |
* @param {Boolean} [restoreManually] true to don't restore the context after clear, in order to draw something else. | ||
* @returns {CanvasRenderingContext2D|undefined} canvas.contextTop transformed on the object center | ||
*/ | ||
clearContextTop: function(restoreManually) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this for me solves what i didn't like of the ctx = this.prepareContextTop() and also streamline a bit the code that was divied in 3 methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i know that restoreManually seems a bit like dontClear but isn't exactly as that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
It is better
The callback approach sounds good
Let's merge
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe make jsdoc a bit better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some function hasn't be jsdoced, i know. but i really need to stop. Let me open an issue to come back to those things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor stuff
src/mixins/itext_behavior.mixin.js
Outdated
@@ -172,7 +172,7 @@ | |||
|
|||
// make sure we clear context even if instance is not editing | |||
if (shouldClear) { | |||
var ctx = this._clearContextTop(); | |||
var ctx = this.clearContextTop(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ctx is restored on the next line so can we let the method restore it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because i did a blind search/replace 😄
correcting this
src/mixins/itext_behavior.mixin.js
Outdated
@@ -605,7 +605,7 @@ | |||
this._updateTextarea(); | |||
} | |||
else { | |||
var ctx = this._clearContextTop(); | |||
var ctx = this.clearContextTop(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
graphics-et-al
* This function is used to clear pieces of contextTop where we render ephemeral effects | ||
* Example: blinking cursror text selection. | ||
* // TODO: discuss swapping restoreManually with a renderCallback, but think of async issues | ||
* @param {Boolean} [restoreManually] true to don't restore the context after clear, in order to draw something else. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove 'to'
@@ -279,6 +279,32 @@ | |||
return this; | |||
}, | |||
|
|||
/** | |||
* Clears the canvas.contextTop on top of the object. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unclear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true
* Example: blinking cursror text selection. | ||
* // TODO: discuss swapping restoreManually with a renderCallback, but think of async issues | ||
* @param {Boolean} [restoreManually] true to don't restore the context after clear, in order to draw something else. | ||
* @returns {CanvasRenderingContext2D|undefined} canvas.contextTop transformed on the object center |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On object center is weird english
* @returns {CanvasRenderingContext2D|undefined} canvas.contextTop transformed on the object center | ||
* @param {Boolean} [restoreManually] When true won't restore the context after clear, in order to draw something else. | ||
* @return {CanvasRenderingContext2D|undefined} canvas.contextTop that is either still transformed | ||
* with the object transformMatrix, or restord to neutral transform | ||
*/ | ||
clearContextTop: function(restoreManually) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can live with this though I think it is better to have the arg be restore
instead of dontRestore
I will create a branch of that for a quick merge
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can have restore = true as soon as we use es6 syntax
Co-authored-by: Shachar <34343793+ShaMan123@users.noreply.github.com>
Co-authored-by: Shachar <34343793+ShaMan123@users.noreply.github.com>
follow up #8095 |
This PR makes the first change for the text drag pr.
It removes the need to enter and exit editing the textboxes in order to make the drag effect work.
I m actually comparing right now if i introduced some regressions.
One regression for sure is there, and is the fact that if i drag/drop in the same text box the dragStart selection isn't visible.
Why this change?
EnterEdit and exitEdit do change the status of the textbox, changing their selection start and selection end, plus is an expensive operation since we create a textarea and we add it to the dom, we style it, and we manipulate it, on top of that we are making state changes to the textboxes in order to render a cursor, instead of just rendering a cursor.
The rendering needs to be slow down anyway, i see too many render loop in one second, it must be capped at 60fps OR it must be called once for each time the drop position actually change.
I plan on finishing this tomorrow.
Will follow other small tenatives to reduce the code of this feature, while maintaining all the functionalities.
For merging the main branch, this is the only PR that is important to me, the other are small details that can follow if they require more than on day each.
Performances:
![image](https://user-images.githubusercontent.com/1194048/179427179-608255e8-fd4d-4b97-84a6-4bae2afce505.png)
Original PR, 5 frames of dragover
this PR, 5 frames of dragover
![image](https://user-images.githubusercontent.com/1194048/179427250-0b8e0ee0-e6d0-4a24-b1e6-a3d9d11a9438.png)
after additional changes doesn't seem we lost or gained much
![image](https://user-images.githubusercontent.com/1194048/181008012-fbde861c-01c4-4623-87a4-f86323b64fbc.png)