In my cocoa code, I’ve implemented a drag & drop for a file path. It is triggered not by a mouseDown, but as a result of a callback I receive from javascript code (after click-dragging an HTML object).
The problem is that it works only 80% of the time: I do the same click-drag operation over and over again and get two behaviors. In both, an icon representing the dragged file path begins following the cursor around (as expected).
Now, in most of the times the dragged file path is “accepted” by the receiver, an appropriate visual feedback is given during the drag, and eventually when the mouse is released – the file path is accepted and dropped.
However there are times where the file path is not “accepted” when hovering over the target. When this happens and the mouse is released, the icon slides back to its origin and the file path is not dropped onto the target.
Here’s my code (simplified), based on Apple’s docs:
void myInitiateDrag(const char * in_filepath, NSView * view)
{
NSString *nsfilepath = [NSString stringWithUTF8String:in_filepath];
NSEvent *theEvent = [[view window] currentEvent];
NSPoint dragPosition = [view convertPoint:[theEvent locationInWindow] fromView:nil];
NSRect aRect = NSMakeRect(dragPosition.x, dragPosition.y, 10, 10);
[view dragFile:nsfilepath fromRect:aRect slideBack:YES event:theEvent];
}
Using this exact code within mouseDown works perfectly fine, 100% of the times. However as I said, I don’t receive a mouseDown in my case.
Another observation: when the mouse button is released after a drag operation, mouseUp is not being called. This is regardless of where I initiate the drag operation, and is also mentioned in another SO question. So, I don’t know if this has to do with my problem.
Among the things I’ve done, I tried to find differences between the currentEvent when successful and when not successful, with no luck.
In the developer docs it explicitly says “A dragging session is initiated by the user clicking the mouse inside a window or view and moving the mouse [...] You invoke this method in the mouseDown: or mouseDragged: method of your subclass of NSView or NSWindow.”.
Is this mandatory? What is wrong with what I’m doing and what other options do I have?
Have the same issue, finally was able to make it work with event copy:
[ NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDragged handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) {
[self.controller mouseEventGlobal:event];
return event;
}];
After that in the controller, I'm able to use beginDraggingSessionWithItems:event:source after I have callback from JS.
Related
I'm working on an Image Uploader that allows the user to drag and drop images. It works some of the time, but once in a few it just decides not to work. I checked the e.dataTransfer of the drag and drop events, and when it doesn't work, the types array is either empty or has text/plain instead of Files, and the files is empty.
I assumed this might be because of where I grab the file when I start dragging it, but there doesn't seem to be a difference - sometimes it works and sometimes it just doesn't. Also, seems to me like there has to be some way to ensure that it works...
Tried screwing around with the dataTransfer.effectAllowed and dataTransfer.dropEffect to no avail...
Here is my Drag Event handler
(onDragEnter, onDragOver, onDragLeave)
const handleDrag = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
// some stuff to set styling of the component
}
And my drop handler:
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
// more styling stuff
// temporary warning I have displayed so I can see when it doesn't work more easily
if (!e.dataTransfer.types.includes("Files") setError();
// handles the displaying of the image
if (e.dataTransfer.files && e.dataTransfer.files[0]) onFileDrop(e.dataTransfer.files[0]);
}
I checked, and when it doesn't work, the path to the image - like file:///home/USERNAME/Pictures/fantastic_mr_fox_2.jpg... I tried to simply display the image at that path but got an error message that reads Not allowed to load local resource.
Is there any way to either prevent the path from being the dragged item or to use that path and display the image?
Would highly appreciate help!
As I learn by "doing" I find myself venturing into uncharted territory with the document.getElementById and addEventListener functions. I am using these functions for a small image (button) that will have — based on various steps the user takes — states of a) disabled, b) enabled, c) mouseover, d) mouseout, e) swap image, and f) various cursor styles.
Yesterday I spent a couple of hours reading through Stack Overflow and MDN Web Docs to understand how all the pieces work together. I then built the code and it worked perfectly. That's the good news. The bad news is this morning I just happened to click a tab on a tabbar (that exists on the same page) and when I went back to the original tab all of the event listeners had dropped and I do not understand why. By choosing "document" as my main target for the getElementById, I thought I was safe in that the event listeners and global variable would be active as long I was within the "document." Could someone help me understand what I'm missing? Here are the various steps I've taken:
I first declare the global variable
window.elem_populate = 0;
Then in the window.onload I apply a value to the variable. BTW, I use window.onload because the ID for the target was not being created in time and was throwing an "undefined" error.
elem_populate = document.getElementById("toolbar_populate");
Then, depending on input from the user I use this approach:
elem_populate.addEventListener('mouseover', hover_populate_on);
elem_populate.addEventListener('mouseout', hover_populate_off);
Here are a couple of examples of the functions being called:
hover_populate_off = function hover_populate_off() {
elem_populate.style.opacity = "1.0";
elem_populate.style.cursor = "not-allowed";
}
hover_populate_on = function hover_populate_on() {
elem_populate.style.opacity = "0.8";
elem_populate.style.cursor = "pointer";
}
The actual target ("toolbar_populate") is positioned on Tab 1 of a two-tab tabbar. If I don't move off the tab, everything works magically. But when I move to Tab 2 and then go back to Tab 1 all the references are lost and the functionality stops. Because everything is on the same document I'm not understanding why I'm losing the reference. Any help or direction would be appreciated. Thanks in advance ...
I have a DIV with id='obsah_popis' (which basically hold all the page content) and this div is filled dynamically upon request (by clicking on menu buttons, photogalery buttons etc.) so its height is not constant but changes overtime as different objects go in or out (loaded in/out).
I need to monitor its actual height for my custom build (coded) scroller which recalculate its height and everything necessary around it according to that height value.
I made a MutationObserver for this reason that works great...but ONLY IN Firefox (46+) - when I run it under Chrome (63) or Opera (50) it does not work at all.
My code for the observer part looks like this (for the purpose of testing I only added alert() to fire up letting me know it was triggered):
var test = new MutationObserver(function(mutations) {
alert();
//resizeSkrolerHandle();
});
test.observe(document.querySelector('#obsah_popis'), {
attributes:true,
childList:true,
characterData:true,
subtree:true
});
BTW strangelly enough (at least for me) this other MutationObserver I run at the same place (just underneath my not functioning one) work perfectly in all browsers:
var bbb = new MutationObserver(function(mutations) {
document.getElementById("parallax_pozadie").style.top = document.getElementById("parallax_static").style.top = (document.getElementById("skroler").getBoundingClientRect().top * -1) * scrollSpeedMultiply + "px";
});
bbb.observe(document.querySelector('#skroler'), {
attributes:true,
childList:false
});
The only difference I see there is the fact that this working one is actually fired up by MANUAL INPUT (dragging of my custom scroller) whereas that non-working one is supposed to be fired up programmatically.
Does anyone know the reason and possible solution to this?
I have an application that use JsPlumb Framework. It works fine on a desktop, however when using an touch device it fails to work correctly.
I have an object that can be dragged around the screen, this works fine on both a touch device and desktop. However I have also got an action that when a user click on the device it can set a connector to drag a line to another object to join them together. However on the touch devices it fails to set the connector.
The touch device will draw an icon but it will display appear straight away. Is this before the touch device can't tell the difference between the drag/touch option.
I have added some code to see if it has something to do with my code.
$("#container").append(state)
jsPlumb.draggable("state" + i);
jsPlumb.makeSource($('.item'), {
connector: 'StateMachine',
});
jsPlumb.makeTarget($('.item'), {
anchor: 'Continuous',
reattach:true,
isTarget:true,
beforeDrop:function(params) {
'Some code'
}
});
EDIT
JSFiddle
http://jsfiddle.net/8jMqG/6/
EDIT AGAIN
From the docs the following is showed
Tip: use the three-argument addEndpoint method for common data
One thing that happens quite often is that you have an Endpoint whose appearance and behaviour is largely the same between usages on different elements, with just a few differences.
var exampleGreyEndpointOptions = {
endpoint:"Rectangle",
paintStyle:{ width:25, height:21, fillStyle:'#666' },
isSource:true,
connectorStyle : { strokeStyle:"#666" },
isTarget:true
};
Which is what I'm doing on the jsfiddle.
Thanks,
James
I have been using jsPlumb for some time now. My app works both on desktop & mobile. Of course, there's a difference between click & touch.
I use jquery-ui-touchpunch for mobile device support; clicking, dragging objects/connections & creating connections works smooth on mobile too.
http://touchpunch.furf.com/
Update
After creating the endpoints or making certain elements source & target, the next step is to join them for the connection to appear. Of course, if the sole-aim is to just connect, then you can skip creating endpoints/sources/targets & directly join them using
jsPlumb.connect({
source:"element1",
target:"element2",
anchors:["Right", "Left" ],
endpoint:"Rectangle",
endpointStyle:{ fillStyle: "yellow" }
});
DOCS
Currently, you're trying to make the same element(s) .item both source & target.
Also, you're trying to make the same elements source & target with every click (event). Why repeating it ?
APIDOCS
I guess there's some confusion in what you need and what the code says.
I'm using the code below for dragging an image (#StndSoln1). It works perfectly in Chrome and all, but not in Firefox. Here startDrag() is the function which i attached to the mousedown event listener. Could anybody please help me.
function initialFunction(){
document.getElementById("StndSoln1").addEventListener('mousedown',startDrag,false);
document.getElementById("StndSoln1").addEventListener('mousemove',drag,false);
document.getElementById("StndSoln1").addEventListener('mouseup',stopDrag,false);
}
function startDrag()
{
if (!moveFlag){
currentTraget=document.getElementById("StndSoln1");
offsetX=currentTraget.offsetLeft;
offsetY=currentTraget.offsetTop;
ImgPlaced=false;
moveFlag=true;
x=window.event.clientX;
y=window.event.clientY;
event.preventDefault();
}
}
// Fn for drag the current target object...
function drag(){
if (moveFlag && !ImgPlaced){
currentTraget.style.left=(offsetX+window.event.clientX-x)+"px";
currentTraget.style.top=(offsetY+window.event.clientY-y)+"px";
}
}
I actually had a similar problem, so I can try to help even without the code you're using.
See, the Firefox developers had this bright idea of making it so that, when you drag an image, you can "move" it around and possibly drop it in an Explorer window to quickly and easily download it, or to the tab bar to open the image in a new tab. The obvious downside of this is that it results in a default behaviour that other browsers don't have.
The simple solution is to make sure that all your events are properly cancelling the default action (event.preventDefault, return false, that kind of thing). Should that fail too, then you should use a <div> element with a background-image instead of an <img> element.