Using the samples, I have the simple case of setting the cursor to pointer when the user hovers over a pin on the map; something akin to:
this.map.events.add('mouseover', layer, () => this.map.getCanvasContainer().style.cursor = 'pointer');
And a similar mouseout event for putting it back to grab. This works, no problems.
However, when I load the drawing module, this no longer works. If I inspect the elements, I can see the style is still switching between pointer and grab as I hover in the DOM, but it has no effect on the pointer at all any more...
Drawing module is loaded with something similar to:
this.drawingManager = new azDrawing.drawing.DrawingManager(this.map, {...});
The drawing stuff itself works fine. Though, actually, 'fine' is an operative word, because whist I'm drawing a polygon, the line rendering between the point and the cursor doesn't actually render properly. It doesn't update and render until I stop moving the mouse.
The samples have it nice and smooth and clearly rendering and following the mouse as it moves, but my implementation doesn't render that line until I stop moving the mouse.
I programmatically enter drawing mode with the simple:
this.drawingManager.setOptions({
mode: azDrawing.drawing.DrawingMode.drawPolygon
});
Omitted from the above module loading are just the options for enabling dragging and rotation that I have turned on.
It's a fairly simple setup, but there seem to be side effects and I don't really know where to look for what might be causing them to fix them.
Oh, additional version information...
This is using an Angular (12) SPA, and the following map imports in my index:
<link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/drawing/1/atlas-drawing.min.css" type="text/css" />
<script type="text/javascript" src="https://atlas.microsoft.com/sdk/javascript/drawing/1/atlas-drawing.min.js" async defer></script>
Any ideas on where I can look and/or more information required?
Thanks.
I went through and managed to reproduce your issue with the mouse cursor. Digging into this I eventually found the issue. The drawing manager sets the cursor on the map canvas element, while in most samples and your code, the cursor is set on the map container for the canvas (DIV element that the canvas is inside of). As such, the drawing manager sets the cursor to it's default state on the canvas when loaded, and that cursor takes priority over the parent map container. The solution if fairly simple, use map.getCanvas() instead of map.getCanvasContainer(). So the following should work:
this.map.events.add('mouseover', layer, () => this.map.getCanvas().style.cursor = 'pointer');
However, I did find that this ends up overriding the drawing managers behavior. With this in mind a solution is to check to see if the drawing manager is actively being used to draw. If it is, then don't set the cursor. Here is a sample that demonstrates this.
//Create an instance of the drawing manager and display the drawing toolbar.
drawingManager = new atlas.drawing.DrawingManager(map, {
toolbar: new atlas.control.DrawingToolbar({ position: 'top-right' })
});
//Load some source data.
datasource = new atlas.source.DataSource();
map.sources.add(datasource);
datasource.importDataFromUrl('https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson');
//Create a layer.
layer = new atlas.layer.SymbolLayer(datasource);
map.layers.add(layer);
//Add mouse events to power hover experience.
map.events.add('mouseover', layer, () => {
if(drawingManager.getOptions().mode === 'idle'){
map.getCanvas().style.cursor = 'pointer';
}
});
map.events.add('mouseout', layer, () => {
if(drawingManager.getOptions().mode === 'idle'){
map.getCanvas().style.cursor = 'grab';
}
});
Related
In short, I am trying to trigger a change detection loop between renderer.removeStyle and renderer.addStyle.
The style I am adding is an css animation. When it's removed and added in the same change detection loop Angular won't detect that something changed (for the same animation name).
More Details
Let's say I have a button event (click)="addAnimation('animation1')" that should add existing animation and add the new animation named animation1, animation2 ....
Of course the following code won't work:
addAnimation(animationName: string): void {
this.renderer.removeStyle(this.animate.nativeElement, 'animation');
// setTimeout(() => {
this.renderer.setStyle(this.animate.nativeElement, 'animation', animationName)
// }, 0);
}
since removing and adding a style under angular nose won't trigger any change.
One possible solution is adding a timeout (like the commented code above, but it has side effects that I am not interested in, and also the code is a little wired.
I was hoping to solve it by adding something like this.appRef.tick(); in between to force angular create another change detection loop.
That doesn't work, what am I missing? any suggestions how to do that correctly?
Thanks!
Try following way.
isClick=false;
onClick(){
isClick=true;
}
<div [ngClass]="{'yourCSSClass': isClick}">
I'm trying to implement a mask over my GoJS diagram when a details sidepanel is shown. This requires stopping propagation of click events to diagram parts beneath. How can I verify that the event target is the picture part and not the mask element which overlays it?
I've tried using the pointer-events CSS property, but that doesn't do the job.
Simplified markup:
<div id="diagramWrapper"></div>
<div id="sidebar-mask"></div>
Script snippet:
buildGo(go.Picture, {
source: 'images/icons/simplify-icon.png',
...,
click: function (e, node) {
if (e.diagram === node.part) { <-- this, right here, isn't right
let nodesToRemove = getLinkedNodes(e, node.part.data.key);
removeNodes(nodesToRemove);
}
}
})
I haven't gotten my head around the two-letter object properties that GoJS uses, which prevents me from easily finding the right property. Any insight into that would help greatly.
I suggest that you just set Diagram.isEnabled to false when you show a modal dialog. Be sure to set it back to true when the modal UI goes away for any reason. More possibilites are listed at https://gojs.net/latest/intro/permissions.html.
As documented at https://gojs.net/latest/intro/index.html#DevelopingYourDiagram, you should only be using documented API names in your code -- not the minified two character names which will change with each build.
I am trying to write a TestCafe functional test around drag and drop. I already have drag and drop working and I am trying to test the source/target functionality by trying to drag an element onto a part of the screen it is not allowed to drop. The mouse cursor shape changes to the "No entry" symbol when the drop is not permitted during the mouse hover. I cannot see anywhere in the TestCafe documentation where I can interrogate the mouse cursor icon shape.
I cannot see any documentation on this in TestCafe.
Does TestCafe support this?
Thanks
Mark
TestCafe selectors specify an element on the testing page and return an object with a set of properties and methods to the server.
Read more about Selector's properties in TestCafe docs.
You can use the getStyleProperty method to verify the cursor style of an element, e. g.
.expect(Selector('div').getStyleProperty('cursor')).eql('no-drop');
In any other cases, when you need to get specific information about a DOM element, you can use a combination of ClientFunction and Selector. This scenario may look as follows:
const el = Selector('#editable-div');
const getCursorStyle = ClientFunction(() => {
return window.getComputedStyle(el()).cursor;
}, {dependencies: {el}})
await t
.drag(Selector('#create'), -14, -255, {
offsetX: 38,
offsetY: 11
})
.expect(getCursorStyle()).eql("no-drop");
You can even use a more advanced approach - implementing addCustomDOMProperties, especially if you need to check the added property several times.
(I posted this initially on the Xamarin Forums, but then decided I might get a faster answer here?)
TL;DR: Some layouts will count a tap on a transparent background, others won't. Setting InputTransparent on a container sets it for all of its children, and I feel like children should be able to override the parent. I need to create elements that overlay another element and pass taps through a transparent region but still have tappable buttons. When I try this with a Grid, it doesn't work. I don't want to go back to AbsoluteLayouts. I'm mostly working in iOS right now, I'm not quite sure if it's a problem in Android yet. Windows Phone/UWP isn't on the table.
Longer version:
I'm rewriting some layouts that worked OK in an older Xamarin Forms (1.3 I think). We recently upgraded to 2.1, and it wreaked havoc on the layout code's bad decisions. I'm tasked with updating the layouts to behave themselves. While I recognize 2.2 has been released, I just tried an upgrade and everything I'm typing seems true in that version as well, so it's not a 2.1 vs. 2.2 issue, or at least if some improvements are made they aren't helping me.
It's a mapping application, so the centerpiece of all layouts is an expensive, temperamental OpenGL element. This element very much does not like to be reparented, so I've adopted a layout sort of like this imaginary XAML:
<ContentPage>
<CustomLayout>
<OurHeaderControl />
<TheMapControl />
<OurFooterControl />
<MapOverlay />
</CustomLayout>
</ContentPage
The purpose of "MapOverlay" is to implement our workflows by adding Xamarin elements on top of the header/footer areas and/or the map. For example, one layout adds a list of directions to the bottom above the footer, so it has less room for the map to appear. The custom layout understands this and lays out the map after the overlay so it can ask for the correct map bounds.
In this layout, I cannot tap on anything the MapOverlay is over. I can make it InputTransparent and tap those things, but then all of its children are also not tappable. This was not true in the old layouts.
Here's the only differences I see between the old layouts and the new:
The old layouts were a complicated mess of AbsoluteLayouts. It looked something like this, I didn't write it:
<ContentPage>
<AbsoluteLayout> // "main layout"
<AbsoluteLayout> // "map layout"
<Map /> // An AbsoluteLayout containing the OpenGL view.
</AbsoluteLayout>
<AbsoluteLayout> // "child layout"
<SubPage /> // An AbsoluteLayout
</AbsoluteLayout>
</AbsoluteLayout>
</ContentPage>
The main layout contains AbsoluteLayouts to constrain the child views. One child view is itself an AbsoluteLayout that contains the Map and a handful of other elements associated with it. The other child is the overlay, which is always an AbsoluteLayout that contains the elements relevant to that overlay. These layouts all reference each other in cycles and update each other as layout events change. It's a fascinating ping-ponging that eventually settles down. Usually. Sometimes things just disapper. Obviously there's a reason I'm rewriting it.
But I can click on what I need to click on at every layer, and I don't get that.
So, let's talk about what I need to work, and maybe figure out if it's a bug why it's not working, or if it's a coincidence that it worked with other layouts. Here's a non-XAML page layout that demonstrates, my project's got its roots in the days when you couldn't use XAML in shared libraries:
I need to be able to tap both buttons in this UI and have them respond.
public class MyPage : ContentPage {
public MyPage() {
var mainLayout = new AbsoluteLayout();
// Two buttons will be overlaid.
var overlaidButton = new Button() {
Text = "Overlaid",
Command = new Command((o) => DisplayAlert("Upper!", "Overlaid button.", "Ah."))
};
mainLayout.Children.Add(overlaidButton, new Rectangle(0.25, 0.25, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize), AbsoluteLayoutFlags.PositionProportional);
// The top overlay layout will be a grid.
var overlay = new Grid() {
RowDefinitions = { new RowDefinition() { Height = new GridLength(1.0, GridUnitType.Star) } },
ColumnDefinitions = {
new ColumnDefinition() { Width = new GridLength(1.0, GridUnitType.Star) },
new ColumnDefinition() { Width = new GridLength(1.0, GridUnitType.Star) },
},
BackgroundColor = Color.Transparent
};
var overlayingButton = new Button() {
Text = "Overlaying",
Command = new Command((o) => DisplayAlert("Upper!", "Overlaying button.", "Ah."))
};
overlay.Children.Add(overlayingButton, 0, 1);
mainLayout.Children.Add(overlay, new Rectangle(0, 0, 1.0, 1.0), AbsoluteLayoutFlags.All);
// This pair of property sets makes the overlaid button clickable, but not the overlaying!
// overlay.InputTransparent = true;
// upperOverlayButton.InputTransparent = false;
Content = mainLayout;
}
}
This only lets me tap the "overlaying" button even when I change the Grid to an AbsoluteLayout.
I'm stumped. It took me 2 weeks to debug the initial layouts and come up with a new solution. I really don't want to disassemble all of our layouts and put everything in one big AbsoluteLayout or a custom layout. In WPF, there were two kinds of transparent: "transparent background" meant the background could still hit test, and "null background" meant the background would not hit test. Is there some way to overlay layouts in Xamarin like this?
Or, more appropriate, why is the convoluted nest of numerous AbsoluteLayouts in our old layouts working like I need it to, but this much simpler layout isn't?
updates
Here's some additional information I remembered:
This behavior is iOS specific. On Android, both the example code and our code work.
I'm not the first person to have this problem: On StackOverflow. On Xamarin's Forums.
In general it seems as if the behavior with iOS in regards to how InputTransparent is being handled in a Grid compared to the other two platforms. I'm not particularly certain whether I'd quantify the current behavior as a bug at this time, but I understand that it's frustrating to run into a disparity in platform behavior.
There is a fix of sorts for your situation, though, if I'm understanding it correctly. It appears similar a similar report was filed before and behavior regarding iOS was mentioned via this SO link. The question is posed in the scope of a non-Forms iOS app, but the logic can be applied here.
By using a custom renderer (let's use a CustomGrid as an example), you can specifically implement the iOS implementation of the Grid to follow the aforementioned link's manner of finding underlying views:
CustomGrid.cs (PCL):
public class CustomGrid : Grid
{
public CustomGrid() { }
}
CustomGrid.cs (iOS):
[assembly: ExportRenderer(typeof(CustomGrid), typeof(CustomGridRenderer))]
public class CustomGridRenderer : ViewRenderer
{
public override UIKit.UIView HitTest(CoreGraphics.CGPoint point, UIKit.UIEvent uievent)
{
UIView hitView = base.HitTest(point, uievent);
if (hitView == this)
{
return null;
}
return hitView;
}
}
In this manner you should not explicitly set InputTransparent for iOS, and any taps on the Grid itself are sent through to anything below. Since Android works with InputTransparent, though, in this particular case you can wrap that inside a Device.OnPlatform statement and skip implementing the Android custom renderer if you don't want to:
Device.OnPlatform(Android: () =>
{
overlay.InputTransparent = true
});
Using your above code modified to use the CustomGrid and iOS renderer, I'm able to tap both buttons.
I have some problems with Jcrop's croppable area size. In my own, simple application I used Jcrop without any problem but now I'm using Jcrop inside another web application (Virtual Viewer Snowbound). Here is a screenshot of my problem-free application:
I added all Jcrop js and css files to the other website's folders into the right paths. I think there is no problem about file locations. But probably something inside that website's css and Jcrop's css conflicts and prevents it from working properly.
Firstly I added these libraries to the most below side of the head tags (I commented out the first one because website has Jquery 1.9.1 version so it conflicts:
<!-- <script src="js/jquery.min.js" type="text/javascript"></script>-->
<script src="js/jquery.Jcrop.min.js" type="text/javascript"></script>
<link rel="stylesheet" href="css/jquery.Jcrop.css" type="text/css" />
Then Jcrop functions are written:
jQuery(document).ready(function(){
jQuery(function ($) {
$('#vvImageCanvas').Jcrop({
onChange: updatePreview,
onSelect: updatePreview,
setSelect: [100,100,200,200],
bgColor: 'black',
allowSelect: true,
allowMove: true,
allowResize: true,
aspectRatio: 0
});
function updatePreview(c) {
if (parseInt(c.w) > 0) {
// Show image preview
var imageObject = $("#vvImageCanvas")[0];
var canvas = $("#previewMyCanvas")[0];
var context = canvas.getContext("2d");
if (imageObject != null && c.x != 0 && c.y != 0 && c.w != 0 && c.h != 0) {
context.drawImage(imageObject, c.x, c.y, c.w, c.h, 0, 0, canvas.width, canvas.height);
}
}
};
}
);
});
As you see the vvImageCanvas is the canvas which holds the image and I Show the preview inside previewMyCanvas canvas. The problem is croppable area size. If I add that below code:
style="width:auto!important; height:auto!important;"
into the
<canvas id="vvImageCanvas" class="vvCanvas">
tags then I have the below view:
As you see in the preview I can crop where I want but the croppable area does not have the same size with picture. I think that is the jcrop-holder div but I'm not proffesional in css issues.
If I don't add these style options then I have that:
The croppable area has same size with picture but the picture gets smaller and as you see in the preview, cropped area is different from where I crop. Maybe it crops from the actual size of image.
So, how can I use Jcrop functions without conflictions. I added
jQuery.noConflict(true);
but it didn't help.
Thanks.
EDIT:
Ok I realized the problem.
The Jcrop functions should work after the page load. But I cannot achieve that by writing these codes into document.ready or window.load blocks. That was the reason of asking this question.
Then I ran the code by a button click. It worked. But I don't know why it does not work inside document.ready and works in button click event. Also I have to make it work on page load automatically not by clicking a button.
Any advise?
It's a wild guess, since you didn't provide any demo, but I've come across similar issues in following cases:
Jcrop element was animated using CSS3 transitions
There was a CSS rule applied to the parent of jcrop element manipulating width, height, max-width or max-height of all img tags inside
Fix for the second case is obvious - you need to apply your custom rules only to the images you need adjusted - don't forget Jcrop creates its own elements in DOM in the same container as your initial image.
In case of using CSS3 transitions, you need to call Jcrop after all transitions for your element has finished:
$("#image").bind("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){
// your Jcrop code
});
You may encounter similar issues when using scripts to preload images.