How to clone a node to appear exactly on top in a different pane - javafx-8

In my (full-screen) program I have several items displayed in a layout. What I would like to do is that when I tap or click on a item is to show a full screen editor. The idea is to have transition from the item shown to the editor.
When the editor is shown I first display a semi-transparent view on top of the stackpane, underneath the other items are still visible. The selected item should 'move' to the upper layer, and then start to transform to its editor shape. Closing the editor should of course be a reverse course of actions.
The first attempt was to move the from its container to the upper layer, but JavaFX complained about the bound properties. I rather not have to code a whole bunch of bind/unbinds, so I assumed this was not the route.
My next attempt was something like this:
private volatile FullScreenService fullscreenService;
private void showFullPhoto(MouseEvent e) {
FullScreenService service = fullscreenService;
if (service != null) {
try {
Node node = (Node) e.getSource();
ImageView photoView = (ImageView)node.lookup("#image");
Image image = photoView.getImage();
Pane pane = new Pane();
pane.setPrefSize(1920d, 1080d);
ImageView copy = new ImageView(image);
copy.setRotate(node.getRotate());
node.setVisible(false);
Bounds bounds = photoView.localToScene(photoView.getBoundsInLocal());
copy.setLayoutX(bounds.getMinX());
copy.setLayoutY(bounds.getMinY());
pane.getChildren().add(copy);
service.requestFullScreen(pane);
} catch (InUseException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
The idea is to hide the original view and 'clone' the part that should be shown (and then transitioned). In this case I extract the image part out of a photo (that also has a border and caption), and make a copy on the editor screen.
However the location of the image does not match the original location exactly, it is off by a couple of pixels, making the image 'jump'.
I've created the pane to be exact the size of the screen, so I thought I could assume scene coordinates are the same as the new node's coordinates.
Abstracting my question: given two overlapping panes, how to make sure a node is exactly on top of another node?

Related

Dragged Image Location

I am dragging an image to an NSView.
Works fine, except that if I insert the dragged image at the received NSDraggingInfo.draggingLocation, the image is suddenly offsetted from its current drag dimmed drawing.
Indeed, the user has taken the image with the cursor by clicking "somewhere" in the image, there is therefore an offset between the bottom left corner of the image, and the cursor itself. Then, the user drags, and drops the image. If I position the dropped image at the cursor position, the image suddenly shifts...
I tried to use NSDraggingInfo.draggedImageLocation, but the result is even worse. NSDraggingInfo.draggedImageLocation seems to contain an position information that is not at all what Apple describes in the doc.
How can I get the bottom left (or other) point of the dragged image so that I can have a smooth drop and insertion ?
Note: this is a cross-app drag & drop, so I don't have any information at the time the user starts the drag.
Edit: added some information as per comments
The image shifts only when dropped
Here is the code of the performDragOperation func
public func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
let pboard = sender.draggingPasteboard
if let images = pboard.readObjects(forClasses: [NSImage.self],
options: nil) as? [NSImage] {
for image in images {
self.imageDropDelegate?.receive(image: image,
at: sender.draggingLocation)
print("drop point : \(sender.draggingLocation)")
print("image point : \(sender.draggedImageLocation)")
}
}
return true
}
The delegate just creates an image view at the point it is given. The problem is that sender.draggedImageLocation is the poirnt where the cursor is when the mouse up event is detected, which is different from the bottom left corner of the dragged image.
Apple doc says sender.draggedImageLocation should be what I am after, but the prints says:
drop point : (413.18212890625, 458.5313720703125)
image point : (-1438.0, 969.0)
drop point : (439.2493896484375, 443.3453369140625)
image point : (-1438.0, 969.0)
The drop point is indeed the position of the cursor when I release the mouse (drop).
But image point is just completely un-understandable to me.
And I checked that sender.draggingFormation is 1 (so 'none')

Unity 5: how to zoom and translate an object from a grid layout to the center of the screen

I'm trying to create a scroll grid view in which every cell object is tapable.
When a cell object is tapped I want to scale and traslate it to the center of the screen and render it above other cells.
I was able to make it tapable and scale it in its position. Now I want to move the cell object to the center of the screen and render it above other cells.
I've tried many solutions but none of them works.
This is my hierarchy:
This is the grid in normal state:
This is the grid when a cell was tapped:
I'm populating the grid from a C# script dynamically.
void Populate()
{
GameObject cardContainerInstance, cardInstance;
foreach (var c in cardsCollection.GetAll())
{
if (c.IsOwned)
{
cardContainerInstance = Instantiate(cardContainer, transform);
cardInstance = cardContainerInstance.transform.Find("Card").gameObject;
var cardManager = cardInstance.GetComponent<CardManager>();
cardManager.card = c;
cardManager.AddListener(this);
}
else
{
Instantiate(cardSlot, transform);
}
}
}
public void OnCardClick(GameObject cardObject, Card card)
{
Debug.Log("OnCardClick " + card.name);
if (openedCard != null) {
if (openedCard.Number == card.Number)
{
CloseCard(openedCardObject);
}
else
{
CloseCard(openedCardObject);
OpenCard(cardObject, card);
}
}
else
{
OpenCard(cardObject, card);
}
}
void OpenCard(GameObject cardObject, Card card)
{
//cardObject.GetComponent<Canvas>().sortingOrder = 1;
var animator = cardObject.GetComponent<Animator>();
animator.SetTrigger("Open");
openedCard = card;
openedCardObject = cardObject;
}
void CloseCard(GameObject cardObject)
{
var animator = cardObject.GetComponent<Animator>();
animator.SetTrigger("Close");
openedCard = null;
openedCardObject = null;
}
I can't figure out how to move the cell to the center and render it above others.
Note that all is animated using an animator attached to the object itself.
Could anyone help me please? Thank you very much!
EDIT: more details
All cell object have the following hierarchy:
where:
CardContainer is an empty object added to use animator on Card child object
Card is the object itself that has a script, a canvas renderer and an animator
StatsImage is the object that slide out when the card is tapped
Image is a calssic UIImage with Image script, Shadow script and canvas renderer
Other component are simple texts.
EDIT: fix in progress
Trying to apply this suggestions I was able to manage the rendering order (as you see on the image below) but it seems that prevent touch events to be detected on the game object.
I've added a GraphicsRaycaster too and now the bottom horizontal scroll view scrolls again but only if I click and drag a card.
Moreover, with the GraphicsRaycaster, the main grid card still are not clickable and it's possible to open the card only if it is behind the bottom panel (if I click on the red spot in the image below the card behind the panel receives che click)
This is the CardContainer at runtime(note that I'm attaching new Canvas and GraphicsRaycaster on the CardContainer, which is the "root" element):
You didn't clarify whether you are using a sprite renderer or some other method but here is an answer for each.
Sprite renderer:
this the simple one. In each sprite renderer, there is a variable called "sortingOrder" in script and "Order in layer" in the inspector. sprite renderer with sorting Orders that are higher is rendered first. All you would need to do is call:
cardObject.GetComponent<SpriteRenderer>().sortingOrder = 1;
when you click the card, and
cardObject.GetComponent<SpriteRenderer>().sortingOrder = 0;
when you unclick it. I hope that makes sense!
Other Method:
this one is a bit harder and I would suggest that you switch to sprite renderers and it will be much easier and more stable down the road, but I can understand if you have already written a lot of scripts and don't want to go back and change them.
Anyway, all you will need to do Is create two layers: cardLower and cardUpper. then create a new camera and call it topCamera. now under the top camera object in the inspector, change the culling mask (it's near the top) and make sure cardUpper is selected. then change the Clear flags (first one) to "Don't Clear" finally change the depth to 0 (if that doesn't work change it to -2). Now objects in the cardUpper will always be rendered above everything else. You can change the layer through script with
cardObject.layer = "cardUpper"
or
cardObject.layer = "cardLower"
I hope that helps!
Ok, so its pretty simple. So you are going to want to add another canvas component to the game object, and check the override sorting to true. Then use
cardObject.GetComponent<Canvas>().sortingOrder = 1;
to place it in the front and
cardObject.GetComponent<Canvas>().sortingOrder = 0;
to put it in the back.
you are also going to need to put a GraphicsRaycaster on to each of the cardObjects
Ignore my other answer about sprite renderers, they are not needed here

Unity 3D - Matching Pairs (2D) Game

I am currently working on a game in Unity3D where you must click on colour pairs to match them and then they disappear. I am using 2D sprites to do this but I am struggling in terms of the logic to erase the pair when both is clicked via mouse.
Click the yellow then click yellow again to make both disappear. (Until board is cleared or colours.)
If clicked on yellow then anything other than yellow do nothing.
Thanks in advance.
Here is what the layout of the sprites looks like:
Would it be best to give every colour a tag?
Here is what I want to happen: When the game starts it picks 3 colours from an array of 6 then randomly places them (2 of each colour) on the screen. You then have to click the colour for example green (it will highlight) then click on the other green, and they will both disappear. If you were to, say, click on the green first then yellow, the game will just end.
This is the code that I have implemented at the moment:
// [...]
if (Input.GetMouseButtonDown(0))
{
CastRay();
}
}
function CastRay() {
var ray: Ray = Camera.main.ScreenPointToRay(Input.mousePosition);
var hit: RaycastHit2D = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if(hit.collider != null)
{
// Number is the amount of objects on the screen at one time.(6)
number --;
//Test to see if a mouse click interacts with the 2D Sprite.(Then destroys it)
Debug.Log ("Target Position: " + hit.collider.gameObject.transform.position + gameObject.tag);
Destroy(hit.collider.gameObject);
}
// This when the number hits 0 the level restarts (To check random elements)
if (number == 0)
{
Application.LoadLevel (0);
}
}
You need to have two variables storing the type of clicked cards and boolean variable to store info if you are clicking first or second card (false for . And then on click event you need to check few things:
1. Need to check if you are clicking on first or second card. If it is first card, check the boolean variable if it is false. If yes: change it to true and show the card. Store the card type in the firsClicked variable.
2. If it is second click your check for boolean should be true. In that case you should check if the type of second card is same as first card. if true - Profit. If false, turn the cards back. Thats all the logic here.
EDIT
Ok, here it is step by step.
You need to have a GameObject variable to store the circle that is chosen in the first click. Lets say private GameObject firstCircle = null; . Put it outside your click method so it is not initialized on every click.
Every of your circle objects have to have some fields that store their colour. I do not know how do you set them, I guess that there is a tag? I guess that they have tags like "green", "red" and so on?
In your click event you have to have if-else. Something like that (pseudocode only):
if(firstCircle == null)
{
firstCircle = hit.collider.gameobject; // this will store the first clicked circle for later comparison
}else{
firstCircle = null;
if(firstCircle.tag == hit.collider.gameObject.tag)
{
//here you can destroy both objects or add points or whatever like
Destroy(firstCircle);
Destroy(hit.collider.gameObject);
}else{
// here you do what you want when circles are not the same
}
}
Plese not this is just pseudocode and I could not test it, but I hope you get the idea behind this. Generally you need to store the first circle after the first clik to compare it with the circle after the second click. Please have in mind that you have to check if user do not click the same circle twice (I did not include this here)

How can I add more than custom control using code behind?

Here I create two objects form my Custom control and add them to the form but it show the first one only, why ?
private void frmWelcome_Load(object sender, EventArgs e)
{
// Count number of Contacts in XML
XmlDocument doc = new XmlDocument();
doc.Load(#"C:\Users\Taha\Documents\Visual Studio 2013\Projects\ContactsMangementSystem\ContactsMangementSystem\ContactsData.xml");
int numOfContacts = doc.GetElementsByTagName("Contact").Count;
XmlNodeList nodelist = doc.GetElementsByTagName("Contact");
foreach (XmlNode item in nodelist)
{
// Create Control for each Item
ContactControl.ContactControl userControl = new ContactControl.ContactControl();
// Fill Control With Data
userControl.labelOrganizationText = item.ChildNodes.Item(5).InnerText;
userControl.labelTitleText = item.ChildNodes.Item(0).InnerText;
userControl.labelWorkAddressText = item.ChildNodes.Item(12).InnerText;
userControl.labelWorkPhoneText = item.ChildNodes.Item(8).InnerText;
userControl.labelEmailText = item.ChildNodes.Item(7).InnerText;
Image img = Image.FromFile(item.ChildNodes.Item(9).InnerText);
userControl.ContactImage = img;
// Add item to the form
this.Controls.Add(userControl);
}
ContactControl.ContactControl userControl2 = new ContactControl.ContactControl();
this.Controls.Add(userControl2);
}
If you add controls all by yourself, aka programmaticaly, to the Control collection of a form, you become responsible for handling the position, bounds, size etc. I'll demonstrate the root cause of your issue with a trimmed down code example that is similar to yours.
First I create a UserControl, with one label on it. Set the label property Dock to Fill, Font to Point 28, TextAlign to MiddelCentre and most important Modifiers to Public:
Now we create a new Form in the Designer and add a Timer, a Button, two checkboxes and a FlowLayoutPanel to it. The FlowLayoutPanel has its Anchor property set to Top,Left,Right,Bottom so it will resize if the form resizes. Our design looks like this:
In the Timer_Tick event a new instance of the UserControl is created and either added to the Forms Control collection or the FlowLayoutPanel. There are two variables that keep track of the number of controls created and on which position the control needs to be.
Here is the code in the codebehind of the form:
int position = 0; // what position the control should be
int controlsCreated = 0; // how many controls did we create
private void timer1_Tick(object sender, EventArgs e)
{
// create the control
var ucnext = new UserControl1();
controlsCreated++;
ucnext.label1.Text = controlsCreated.ToString();
// where are we adding the control?
if (!chkFlow.Checked)
{
// do we want to use the Location?
// if not Top will be 0
if (chkLocation.Checked)
{
// Ok, position times height should gives us
// a free spot
ucnext.Top = position * ucnext.Height;
position++;
}
this.Controls.Add(ucnext);
ucnext.BringToFront(); // otherwise we always see control 1
} else
{
// now the FlowLayout takes over all Layout logic
this.flowLayoutPanel1.Controls.Add(ucnext);
}
}
private void button1_Click(object sender, EventArgs e)
{
this.timer1.Enabled = !this.timer1.Enabled;
this.btnStart.Text = this.timer1.Enabled ? "Pause" : "Continue";
}
If we run this we can check and uncheck the options for Location and Adding to the flowlayout to see the various effects of adding controls to either the form directly or the FlowLayout panel.
So keep in mind if you add a control yourself to the Control collection of a Form make sure to set its Location properties (Top, Left) to a suitable value. If you do this wrong your control is going to end up hidden behind another control or is placed outside the visible bounds of the form. If you don't know before hand how many controls you will have consider using one of the container controls, like a FlowLayoutPanel.

arcobjects can't draw dynamic draw screen objects

I enabled the map to allow dynamic objects
Then I grab the IDynamicDisplay from an event which I wired up.
void dynamicMapEvents_AfterDynamicDraw(esriDynamicMapDrawPhase DynamicMapDrawPhase, IDisplay Display, IDynamicDisplay dynamicDisplay)
{
if (DynamicMapDrawPhase != esriDynamicMapDrawPhase.esriDMDPDynamicLayers) return;
IDynamicDrawScreen m_dynamicDrawScreen = dynamicDisplay as IDynamicDrawScreen;
m_dynamicDrawScreen.DrawScreenText(myPoint, "Test");
}
"Test" should be drawn on the screen but its not. I have the feeling I'm missing one line of code somwhere.
you have to load the glyph into symbol properties, then you can draw it vis a vis dynamicCompoundMarker.