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

feat(IText): Draggable text #7802

Merged
merged 103 commits into from
Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
4a00d88
feat(IText): native like text dragging
ShaMan123 Mar 11, 2022
d6d1eae
fire event on object
ShaMan123 Mar 11, 2022
672f945
Update itext_click_behavior.mixin.js
ShaMan123 Mar 11, 2022
0298b3b
lint
ShaMan123 Mar 11, 2022
c7a39a0
dragend
ShaMan123 Mar 12, 2022
f5957c8
Update itext_click_behavior.mixin.js
ShaMan123 Mar 12, 2022
d4bdf7f
Update itext_click_behavior.mixin.js
ShaMan123 Mar 12, 2022
a77f365
dragover + drop
ShaMan123 Mar 12, 2022
51c06ae
Update itext_behavior.mixin.js
ShaMan123 Mar 12, 2022
a04e57d
Update itext_behavior.mixin.js
ShaMan123 Mar 12, 2022
04ee04f
Update itext_click_behavior.mixin.js
ShaMan123 Mar 12, 2022
afedfc2
fix(): drag end
ShaMan123 Mar 12, 2022
fc00090
Update itext_behavior.mixin.js
ShaMan123 Mar 12, 2022
7dfa629
support `editable`/`canDrop`
ShaMan123 Mar 12, 2022
934dd3c
Update itext_behavior.mixin.js
ShaMan123 Mar 12, 2022
f70651c
lint
ShaMan123 Mar 12, 2022
3d32720
Update itext_behavior.mixin.js
ShaMan123 Mar 12, 2022
34bc7b5
fix bug
ShaMan123 Mar 12, 2022
7ce1e7d
point
ShaMan123 Mar 13, 2022
8d500b3
drag image
ShaMan123 Mar 13, 2022
de9ca36
fix: drag over controls
ShaMan123 Mar 13, 2022
aa8c371
Update itext_behavior.mixin.js
ShaMan123 Mar 13, 2022
0efa15a
JSDOC
ShaMan123 Mar 13, 2022
4f457b9
Update canvas_events.js
ShaMan123 Mar 13, 2022
4d516ea
Update itext_behavior.mixin.js
ShaMan123 Mar 13, 2022
76d2a4d
Update itext_behavior.mixin.js
ShaMan123 Mar 14, 2022
cbe456c
cleanup
ShaMan123 Mar 14, 2022
6047c29
Update itext.class.js
ShaMan123 Mar 14, 2022
3df67f2
Update itext.class.js
ShaMan123 Mar 14, 2022
784a27a
setDragImage
ShaMan123 Mar 14, 2022
17b3f9a
revert `getRelativeCursorPosition`
ShaMan123 Mar 14, 2022
1ae3043
Update itext_behavior.mixin.js
ShaMan123 Mar 14, 2022
8fdf77b
retina scaling
ShaMan123 Mar 14, 2022
d112f3b
fix(): drag image offset
ShaMan123 Mar 14, 2022
57e81a1
Update itext_behavior.mixin.js
ShaMan123 Mar 14, 2022
63bbdfe
better drag image
ShaMan123 Mar 14, 2022
1b640f5
Update itext_behavior.mixin.js
ShaMan123 Mar 14, 2022
a2d2225
support styles
ShaMan123 Mar 14, 2022
48e2dde
dragstart + trailing spcae
ShaMan123 Mar 14, 2022
eef6895
drop + trailing space
ShaMan123 Mar 14, 2022
34b5686
fix: edit after drop + selection
ShaMan123 Mar 14, 2022
f6f701a
Update itext_behavior.mixin.js
ShaMan123 Mar 14, 2022
10a43d9
Update itext_behavior.mixin.js
ShaMan123 Mar 14, 2022
4642784
Update itext_behavior.mixin.js
ShaMan123 Mar 14, 2022
8e69763
refactor clipboard events
ShaMan123 Mar 14, 2022
4d441f3
finalize clipboard data
ShaMan123 Mar 14, 2022
e78310a
JSDOC
ShaMan123 Mar 14, 2022
f6d1311
lint
ShaMan123 Mar 14, 2022
04f379a
rtl support prep
ShaMan123 Mar 14, 2022
f9ed862
Update itext_key_behaviour.js
ShaMan123 Mar 14, 2022
c257c91
Update itext_key_behaviour.js
ShaMan123 Mar 14, 2022
72b9a77
Update itext_key_behavior.mixin.js
ShaMan123 Mar 14, 2022
df3f7f2
Update itext_behavior.mixin.js
ShaMan123 Mar 21, 2022
efdd61e
typo
ShaMan123 Mar 22, 2022
97cebb1
remove `disableStyleCopyPaste`
ShaMan123 Mar 22, 2022
ab637ac
Merge branch 'master' into draggable-text
ShaMan123 Mar 29, 2022
fe7cc4d
Merge branch 'master' into draggable-text
ShaMan123 Apr 2, 2022
8b06dfe
fix(): can't drop over selection
ShaMan123 Apr 2, 2022
1b12168
render drag selection
ShaMan123 Apr 2, 2022
6b8a1d9
renderDragStartSelection
ShaMan123 Apr 2, 2022
49aa851
better drag selection start rendering
ShaMan123 Apr 2, 2022
cfa7445
Update itext_behavior.mixin.js
ShaMan123 Apr 2, 2022
6033478
Update itext.class.js
ShaMan123 Apr 2, 2022
33d1cd6
Update itext.class.js
ShaMan123 Apr 2, 2022
f7ed62c
fix drag start selection
ShaMan123 Apr 2, 2022
46206b1
remove `trailingSpace` logic
ShaMan123 Apr 2, 2022
cdf9abd
fix(): clear context
ShaMan123 Apr 2, 2022
a9ca01e
Update itext_behavior.mixin.js
ShaMan123 Apr 2, 2022
de44140
lint
ShaMan123 Apr 2, 2022
c8b413b
fix: dragleave on canvas
ShaMan123 Apr 2, 2022
2069128
Update canvas_events.js
ShaMan123 Apr 2, 2022
c8c10f0
fix dragenter
ShaMan123 Apr 2, 2022
513199f
Update canvas_events.js
ShaMan123 Apr 2, 2022
d610383
Update canvas_events.js
ShaMan123 Apr 2, 2022
9ebdc62
fire always
ShaMan123 Apr 2, 2022
0d89e2b
Update canvas_events.mixin.js
ShaMan123 Apr 2, 2022
9734207
dragend
ShaMan123 Apr 2, 2022
c181512
Update itext_key_behavior.mixin.js
ShaMan123 Apr 2, 2022
29456b1
Update itext_behavior.mixin.js
ShaMan123 Apr 2, 2022
4058520
Update itext_behavior.mixin.js
ShaMan123 Apr 2, 2022
61c96fa
Update itext_behavior.mixin.js
ShaMan123 Apr 2, 2022
ea9c099
fix(): dragsource dragend edit mode state
ShaMan123 Apr 2, 2022
829bc67
Update itext_behavior.mixin.js
ShaMan123 Apr 2, 2022
43afb2e
Update canvas_events.mixin.js
ShaMan123 Apr 2, 2022
fd8515c
lint
ShaMan123 Apr 2, 2022
ecbf266
Update itext_behavior.mixin.js
ShaMan123 Apr 2, 2022
bd2b6c3
Update canvas_events.mixin.js
ShaMan123 Apr 2, 2022
a875dfe
Revert "Update itext_behavior.mixin.js"
ShaMan123 Apr 2, 2022
bc4e4c1
Update itext_behavior.mixin.js
ShaMan123 Apr 2, 2022
186d26d
fix(): always fire events
ShaMan123 Apr 3, 2022
d4fa70c
dragSource attr on drop event
ShaMan123 Apr 3, 2022
23f450f
fix(): firing `drag` event on drag source
ShaMan123 Apr 3, 2022
c60716d
lint
ShaMan123 Apr 3, 2022
0ebb4bb
fire dragstart on canvas
ShaMan123 Apr 3, 2022
61a48f7
Merge branch 'master' into draggable-text
ShaMan123 Apr 3, 2022
a6b5aa1
revert clipboard event changes
ShaMan123 Apr 25, 2022
51e15fe
revert: remove `disableStyleCopyPaste`
ShaMan123 Apr 25, 2022
d07d6d3
Merge branch 'master' into draggable-text
ShaMan123 May 1, 2022
be4cf22
Merge branch 'master' into draggable-text
ShaMan123 Jun 10, 2022
d6f9c8f
Merge branch 'master' into draggable-text
ShaMan123 Jul 5, 2022
b38b34c
add additional tweaks
ShaMan123 Jul 9, 2022
2db76ec
lint
ShaMan123 Jul 10, 2022
f00e547
Changes for text dragging (#8065)
asturur Jul 28, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"test:visual:coverage": "nyc --silent --no-clean qunit test/node_test_setup.js test/lib test/visual",
"coverage:report": "nyc report --reporter=lcov --reporter=text",
"lint": "eslint --config .eslintrc.json src",
"lint:fix": "eslint --fix --config .eslintrc.json src",
"lint_tests": "eslint test/unit --config .eslintrc_tests && eslint test/visual --config .eslintrc_tests",
"all": "npm run build && npm run test -- --all && npm run lint && npm run lint_tests && npm run export",
"testem": "testem .",
Expand Down
2 changes: 1 addition & 1 deletion src/canvas.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
* // no winners
* }
* else if(!opt.didDrop) {
* // my objects didn't win, some other lucky bastard
* // my objects didn't win, some other lucky object
* }
* else {
* // we have a winner it's opt.target!!
Expand Down
33 changes: 24 additions & 9 deletions src/mixins/canvas_events.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,21 @@
/**
* @private
*/
_renderDragStartSelection: function () {
this._dragSource && typeof this._dragSource.renderDragStartSelection === 'function'
&& this._dragSource.renderDragStartSelection();
_renderDragEffects: function (e, source, target) {
var ctx = this.contextTop;
if (source) {
source.clearContextTop(true);
source.renderDragSourceEffect(e);
}
if (target) {
if (target !== source) {
ctx.restore();
ctx.save();
target.clearContextTop(true);
}
target.renderDropTargetEffect(e);
}
ctx.restore();
},

/**
Expand Down Expand Up @@ -295,26 +307,29 @@
dragSource: this._dragSource,
canDrop: false,
dropTarget: undefined
};
},
dropTarget;
// fire on canvas
this.fire(eventType, options);
// make sure we fire dragenter events before dragover
// if dragleave is needed, object will not fire dragover so we don't need to trouble ourselves with it
this._fireEnterLeaveEvents(target, options);
if (target) {
// render drag selection before rendering target cursor for correct visuals
target.canDrop(e) && this._renderDragStartSelection();
// render drag selection before rendering target cursor for correct visuals
if (target.canDrop(e)) { dropTarget = target; };
target.fire(eventType, options);
}
// propagate the event to subtargets
for (var i = 0; i < targets.length; i++) {
target = targets[i];
// accept event only if previous targets didn't
!e.defaultPrevented && target.canDrop(e) && this._renderDragStartSelection();
if (!e.defaultPrevented && target.canDrop(e)) {
dropTarget = target;
}
target.fire(eventType, options);
}
// render drag selection in case no target accepted the event
!e.defaultPrevented && this._renderDragStartSelection();
// render drag effects now that relations between source and target is clear
this._renderDragEffects(e, this._dragSource, dropTarget);
},

/**
Expand Down
6 changes: 3 additions & 3 deletions src/mixins/canvas_serialization.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Populates canvas with data from the specified JSON.
* JSON format must conform to the one of {@link fabric.Canvas#toJSON}
*
*
* **IMPORTANT**: It is recommended to abort loading tasks before calling this method to prevent race conditions and unnecessary networking
*
*
* @param {String|Object} json JSON string or object
* @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.
* @param {Object} [options] options
Expand All @@ -23,7 +23,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* }).then((canvas) => {
* ... canvas is restored, add your code.
* });
*
*
*/
loadFromJSON: function (json, reviver, options) {
if (!json) {
Expand Down
40 changes: 11 additions & 29 deletions src/mixins/itext_behavior.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,7 @@
* @private
*/
_animateCursor: function(obj, targetOpacity, duration, completeMethod) {

var tickState;

tickState = {
var tickState = {
isAborted: false,
abort: function() {
this.isAborted = true;
Expand Down Expand Up @@ -150,7 +147,6 @@
delay = restart ? 0 : this.cursorDelay;

this.abortCursorAnimation();
this._currentCursorOpacity = 1;
if (delay) {
this._cursorTimeout2 = setTimeout(function () {
_this._tick();
Expand All @@ -172,12 +168,11 @@
clearTimeout(this._cursorTimeout1);
clearTimeout(this._cursorTimeout2);

this._currentCursorOpacity = 0;
this._currentCursorOpacity = 1;

// make sure we clear context even if instance is not editing
if (shouldClear) {
var ctx = this._clearContextTop();
ctx && ctx.restore();
this.clearContextTop();
}
},

Expand Down Expand Up @@ -346,7 +341,6 @@
if (this.isEditing || !this.editable) {
return;
}

if (this.canvas) {
this.canvas.calcOffset();
this.exitEditingOnOthers(this.canvas);
Expand Down Expand Up @@ -512,6 +506,7 @@
e.dataTransfer.effectAllowed = 'copyMove';
this.setDragImage(e, data);
}
this.abortCursorAnimation();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

for future follow up
this line might be causing selection to be cleared on drag start

return this.__isDragging;
},

Expand Down Expand Up @@ -546,8 +541,6 @@
var canDrop = !e.defaultPrevented && this.canDrop(e);
if (!this.__isDraggingOver && canDrop) {
this.__isDraggingOver = true;
this.enterEditing(e);
this.__isDragging && this.abortCursorAnimation();
}
},

Expand All @@ -562,26 +555,18 @@
var canDrop = !e.defaultPrevented && this.canDrop(e);
if (!this.__isDraggingOver && canDrop) {
this.__isDraggingOver = true;
this.enterEditing(e);
this.__isDragging && this.abortCursorAnimation();
}
else if (this.__isDraggingOver && !canDrop) {
// drop state has changed
this.__isDraggingOver = false;
!this.__isDragging && this.clearContextTop();
this.exitEditing();
}
if (this.__isDraggingOver) {
// can be dropped, inform browser
e.preventDefault();
// inform event subscribers
options.canDrop = true;
options.dropTarget = this;
// render
this.setCursorByClick(e);
this._updateTextarea();
this.restartCursorIfNeeded();
this.renderCursorOrSelection();
// find cursor under the drag part.
}
},

Expand All @@ -592,8 +577,6 @@
dragLeaveHandler: function () {
if (this.__isDraggingOver || this.__isDragging) {
this.__isDraggingOver = false;
!this.__isDragging && this.clearContextTop();
this.exitEditing();
}
},

Expand Down Expand Up @@ -621,8 +604,7 @@
this._updateTextarea();
}
else {
var ctx = this._clearContextTop();
ctx && ctx.restore();
this.clearContextTop();
if (dropEffect === 'move') {
this.insertChars('', null, selectionStart, selectionEnd);
this.selectionStart = this.selectionEnd = selectionStart;
Expand Down Expand Up @@ -662,15 +644,12 @@
e.preventDefault();
var insert = e.dataTransfer.getData('text/plain');
if (insert && !didDrop) {
var insertAt = this.selectionStart;
var insertAt = this.getSelectionStartFromPointer(e);
var data = e.dataTransfer.types.includes('application/fabric') ?
JSON.parse(e.dataTransfer.getData('application/fabric')) :
{};
var styles = data.styles;
ShaMan123 marked this conversation as resolved.
Show resolved Hide resolved
var trailing = insert[Math.max(0, insert.length - 1)];
this.canvas.discardActiveObject();
this.canvas.setActiveObject(this);
this.enterEditing(e);
var selectionStartOffset = 0;
// drag and drop in same instance
if (this.__dragStartSelection) {
Expand All @@ -696,12 +675,16 @@
options.dropTarget = this;
// finalize
this.insertChars(insert, styles, insertAt);
// can this part be moved in an outside event? andrea to check.
this.canvas.setActiveObject(this);
this.enterEditing();
this.selectionStart = Math.min(insertAt + selectionStartOffset, this._text.length);
this.selectionEnd = Math.min(this.selectionStart + insert.length, this._text.length);
this.hiddenTextarea && (this.hiddenTextarea.value = this.text);
this._updateTextarea();
this.fire('changed', { index: insertAt + selectionStartOffset, action: 'drop' });
this.canvas.fire('text:changed', { target: this });
this.canvas.contextTopDirty = true;
this.canvas.requestRenderAll();
}
},
Expand Down Expand Up @@ -910,7 +893,6 @@
this.hiddenTextarea = null;
this.abortCursorAnimation();
this._restoreEditingProps();
this._currentCursorOpacity = 0;
if (this._shouldClearDimensionCache()) {
this.initDimensions();
this.setCoords();
Expand Down
11 changes: 1 addition & 10 deletions src/mixins/itext_click_behavior.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
if (!this.canvas || !this.editable || this.__isDragging || (options.e.button && options.e.button !== 1)) {
return;
}

this.__isMousedown = true;

if (this.selected) {
Expand Down Expand Up @@ -154,15 +153,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* standard handler for mouse up, overridable
* @private
*/
mouseUpHandler: function (options) {
// restore selection state after dragging
if (this.__isDragging && !this.__dragStartFired) {
// drag didn't occur, so we revert to click behavior
this.__isDragging = false;
this._mouseDownHandler(options);
}
this.__isDragging = this.__dragStartFired = false;

mouseUpHandler: function(options) {
this.__isMousedown = false;
if (!this.editable ||
(this.group && !this.group.interactive) ||
Expand Down
2 changes: 1 addition & 1 deletion src/mixins/itext_key_behavior.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* Override this method to customize cursor behavior on textbox blur
*/
blur: function () {
!this.__isDragging && this.abortCursorAnimation();
this.abortCursorAnimation();
},

/**
Expand Down
6 changes: 3 additions & 3 deletions src/mixins/object_ancestry.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot

/**
* Returns an object that represent the ancestry situation.
*
*
* @typedef {object} AncestryComparison
* @property {Ancestors} common ancestors of `this` and `other` (may include `this` | `other`)
* @property {Ancestors} fork ancestors that are of `this` only
* @property {Ancestors} otherFork ancestors that are of `other` only
*
*
* @param {fabric.Object} other
* @param {boolean} [strict] finds only ancestors that are objects (without canvas)
* @returns {AncestryComparison | undefined}
*
*
*/
findCommonAncestors: function (other, strict) {
if (this === other) {
Expand Down
36 changes: 36 additions & 0 deletions src/mixins/object_interactivity.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,34 @@
return this;
},

/**
* Clears the canvas.contextTop in a specific area that corresponds to the object's bounding box
* that is in the canvas.contextContainer.
* This function is used to clear pieces of contextTop where we render ephemeral effects on top of the object.
* Example: blinking cursror text selection, drag effects.
* // TODO: discuss swapping restoreManually with a renderCallback, but think of async issues
* @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 restored to neutral transform
*/
clearContextTop: function(restoreManually) {
if (!this.canvas || !this.canvas.contextTop) {
return;
}
var ctx = this.canvas.contextTop, v = this.canvas.viewportTransform;
if (!ctx) {
return;
}
ctx.save();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
this.transform(ctx);
// we add 4 pixel, to be sure to do not leave any pixel out
var width = this.width + 4, height = this.height + 4;
ctx.clearRect(-width / 2, -height / 2, width, height);

restoreManually || ctx.restore();
return ctx;
},

/**
* This callback function is called every time _discardActiveObject or _setActiveObject
Expand Down Expand Up @@ -311,5 +339,13 @@
return false;
},

renderDragSourceEffect: function() {
// for subclasses
},

renderDropTargetEffect: function(/* e */) {
// for subclasses
},

});
})();
Loading