Unity 4.6 (new UI): Button click with complex image - image

I have a Button (Canvas>Panel>Button) with a complex image (is not square), the issue here is that I want it to execute the on click event when i click on the IMAGE (excluding parts of the image with no opacity) but since the new UI doesn't work with colliders I can't achieve the same click behaviour that could be achieved with a polygon collider, and its feels completely wrong to click outside the button (image) where the alpha is cero (0) and still execute the click events..
I hope my problem is clear enough and someone can help me, thanks in advance.

I found a solution, Unity should have an option to achieve this but at least this works perfectly: http://forum.unity3d.com/threads/none-rectangle-shaped-button.263684/#post-1744521

I found another (probably better) solution for this problem. I used a Collider2D (PolygonCollider2D) to define the clickable shape and then used this RaycastFilter:
using UnityEngine;
using UnityEngine.UI;
[RequireComponent (typeof (RectTransform), typeof (Collider2D))]
public class Collider2DRaycastFilter : MonoBehaviour, ICanvasRaycastFilter
{
Collider2D myCollider;
RectTransform rectTransform;
void Awake ()
{
myCollider = GetComponent<Collider2D>();
rectTransform = GetComponent<RectTransform>();
}
#region ICanvasRaycastFilter implementation
public bool IsRaycastLocationValid (Vector2 screenPos, Camera eventCamera)
{
var worldPoint = Vector3.zero;
var isInside = RectTransformUtility.ScreenPointToWorldPointInRectangle(
rectTransform,
screenPos,
eventCamera,
out worldPoint
);
if (isInside)
isInside = myCollider.OverlapPoint(worldPoint);
return isInside;
}
#endregion
}
Using a collider for this is a bit hacky but the result is much simpler, probably faster and makes it much easier to adjust the clickable shape.
This could be improved by creating a custom alternative to replace the Collider2D but that's a bit too much work for what I need.

Related

Instagram/FB-like back navigation w/ animation

I need to add back navigation on swipe. I can do that fairly easily by just adding a swipe listener to the page view and calling goBack. But I really would like the animation that goes with it (in Instagram or FB) where as soon as you start dragging your thumb, the page translates to the right and the previous page starts to translate into view. And then once you get to a certain point it actually performs the navigation.
I tried animating the page, as well as the frame to the right figuring since the view isn't being destroyed it might work. But it doesn't display the page Im navigating back to.
Looking for help on how to accomplish this!
I guess you might have come across the other SO thread answering this question natively.
All you have to do is modify the default gesture recogniser on iOS frame.
export function onNavigatedFrom(args: EventData) {
console.log("Adding gesture...");
const frame = (<Page>args.object).frame;
if (frame.ios && !(<any>frame)._gestureRecognizer) {
const controller = frame.ios.controller;
const popGestureRecognizer = controller.interactivePopGestureRecognizer;
const targets = popGestureRecognizer.valueForKey("targets");
if (targets) {
let gestureRecognizer = UIPanGestureRecognizer.alloc().init();
gestureRecognizer.setValueForKey(targets, "targets");
frame.nativeView.addGestureRecognizer(gestureRecognizer);
(<any>frame)._gestureRecognizer = gestureRecognizer;
}
}
}
export function onNavigatedTo(args: EventData) {
console.log("Back to root page, removing gesture...");
const frame = (<Page>args.object).frame;
if (frame.ios && (<any>frame)._gestureRecognizer) {
frame.nativeView.removeGestureRecognizer((<any>frame)._gestureRecognizer);
(<any>frame)._gestureRecognizer = null;
}
}
Playground Sample

Unity onClick.addlistener not working

I load a Canvas prefab at runtime when an event occurs. The canvas simply has a Panel inside it, which in turn has 2 buttons. I'm trying to add OnClick events to both these buttons in the script, but it only works for the first button somehow!
I have the following two lines of code one after the other:
GameObject.Find("RestartButton").GetComponent<Button>().onClick.AddListener(() => RestartClicked());
GameObject.Find("ViewButton").GetComponent<Button>().onClick.AddListener(() => ViewClicked());
The callback only works for the RestartButton, and not for the ViewButton.
It may well be a very small thing, but I searched Google and Bing extensively and remain clueless, so any help would be appreciated.
Thanks!
Edit:
The script instantiates the prefab and tries to reference the two buttons in the prefab through GameObject.Find(), given that once Instantiate is called the Canvas should be active in the heirarchy.
I also opened up the part of the code that attaches buttons to the listener for debugging. I dont think it attaches the listener at all.
void Start () {
SetupScene();
var prefab = Resources.Load("RestartOrViewPrefab");
if (prefab == null)
{
Debug.LogAssertion("Prefab missing!");
return;
}
restartCanvas = (GameObject)Instantiate(prefab);
Button btn = GameObject.Find("ViewButton").GetComponent<Button>();
btn.onClick.RemoveAllListeners();
btn.onClick.AddListener(() => ViewToggle());
Button btn2 = GameObject.Find("RestartButton").GetComponent<Button>();
btn2.onClick.AddListener(() => RestartClicked());
}
private void RestartClicked()
{
Debug.Log("RESTARTING");
SetupScene();
}
private void ViewToggle()
{
Debug.Log("TOGGLE");
if (freshStart)
{
//Something here
freshStart = false;
}
else
{
//Something else here
freshStart = true;
}
}
So I took two days to solve my problem. Apparently, doing a GameObject.Find() for a GameObject within a prefab that you just instantiated doesn't work. We always need to use the GameObject that the Instantiate() method returns to find any component within the prefab.
The code I used to make my scene work may not be the best solution if your prefab is very big/complex (say you have 15 buttons and need to do something with one), but it sure does work. :)
I replaced the following lines of code:
Button btn = GameObject.Find("ViewButton").GetComponent<Button>();
btn.onClick.RemoveAllListeners();
btn.onClick.AddListener(() => ViewToggle());
Button btn2 = GameObject.Find("RestartButton").GetComponent<Button>();
btn2.onClick.AddListener(() => RestartClicked());
With the following lines of code:
Button[] buttons = restartCanvas.GetComponentsInChildren<Button>();
foreach(Button but in buttons)
{
if(but.gameObject.name == "RestartButton")
but.onClick.AddListener(() => RestartClicked());
else if(but.gameObject.name == "ViewButton")
but.onClick.AddListener(() => ViewToggle());
}
So I just reused the restartCanvas that I got a reference to.
restartCanvas = (GameObject)Instantiate(prefab);
Hope this helps someone. :)
I answered this on another post, but I'll answer it here for people that still need this info.
The GameObject your instantiated button is a child of must have a CanvasRenderer component on it.
I'm not sure why this works, but it does.
Once the parent of the button GameObject has the CanvasRenderer on it, you can call myButton.onClick.AddListener(() => MyMethod(MyArgs)); as you normally would.
Ok make sure you are not getting any null reference error, and check tht spelling of you buttons , output some debug logs in your second function to see if its even reaching there

GWT Image Drag and Drop on IE9 not working

I'm using GWT 2.5 and I want to make some images in one Panel Draggable and Dropable in another Panel. This is working fine on Firefox, but on IE9 it just don't want to work.
I searched really almost all the day, and didn't find any solution for that. The best threads I found were these here:
Drag and Drop in GWT 2.4
GWT native drag & drop - DragStartEvent not firing on IE9
The difference is that I'm dragging Images, an not Labels. So this could be the problem.
So the code looks something like this:
// Draggable Image
final Image img = new Image(imageName);
img.addDragStartHandler(new DragStartHandler() {
#Override
public void onDragStart(final DragStartEvent event) {
event.setData("text", img.getUrl());
}
});
img.getElement().setDraggable(Element.DRAGGABLE_TRUE);
Then I put it in my "source" panel:
// Image in the source container
wunschContainer = new AbsolutePanel();
img.setPixelSize(size, size);
wunschContainer.add(img, left, top);
The Images are displayed correctly (Firefox and IE9)
The Target Panel looks like this (it's a subclass of AbsolutePanel and implements HasDragOverHandlers and HasDropHandlers)
// Event-handlers for the target container.
this.addDragOverHandler(new DragOverHandler() {
#Override
public void onDragOver(final DragOverEvent event) {
}
});
this.addDropHandler(new DropHandler() {
#Override
public void onDrop(final DropEvent event) {
event.preventDefault();
final String data = event.getData("text");
/* ... some more handling ..., creates an image to be displayed here */
add(image, left, top);
}
});
In Firefox, when I drag the image, the dragging image is displayed under the mouse pointer. On IE9 I can't see an image at all, just a symbol (circle with a slash on it, this "not allowed" symbol). And when I release the mouse button, then nothing happen. I checked also with "F12" that I was using the IE9-mode, and no compatibility mode.
I really don't know what the problem is. The code seems ok (works on FF). Is there a problem making DnD with GWT-Images? The interesting part is, that the "onDragStart()" methode is being fired. But the "drag" is not being done.
Oh, I'm using GWT devmode to test this. Can this be a problem?
I'd like to solve this using only GWT. Or should I go and use another library, like gwt-dnd? Does anybody got plain GWT DnD working on IE9? I can't believe I'm the first one trying that :-(.
So, I tried really a lot of things, and I came across with a solution.
The problem seems to be in the "onDragOver()" method. As I posted in my original question, I have an empty implementation of the method:
this.addDragOverHandler(new DragOverHandler() {
#Override
public void onDragOver(final DragOverEvent event) {
}
});
As this is how I found in many pages. The method must be present, or else the Drag n Drop wont work. But it seems that for IE I need even to prevent the default behaviour (or do something else. Just logging the method was not "something"). So I added a "event.preventDefault();" to the method, and now it works.
this.addDragOverHandler(new DragOverHandler() {
#Override
public void onDragOver(final DragOverEvent event) {
event.preventDefault();
}
});
Just strange, that I couldn't find this information anywhere. This is hopefully useful for somebody else 8-).
I have been having the same issue with IE9 using GWT 2.6 and I have found that setting the data in the onDragStart puts IE9 off. All other browsers work fine. So I ended up doing the following:
this.addDragStartHandler(new DragStartHandler(){
#Override
public void onDragStart(DragStartEvent event) {
if(!isIE9()){
event.setData(RangeSliderControl.KEY, key); // This causes IE9 to not work
}
}}
);
and off course I have added the isIE9() method to my code as follows:
/**
* Gets the name of the used browser.
*/
public static boolean isIE9() {
return (getBrowserName().indexOf("msie 9.0") != -1);
};
/**
* Gets the name of the used browser.
*/
public static native String getBrowserName() /*-{
return navigator.userAgent.toLowerCase();
}-*/;

JavaFX 2 - what component use to represent mechanical switch

i would like to design a component that look like a switch that has 3 position. One neutral position, and upper switch position, when the user click on the upper half of the component and a lower switch position, when the user click the lower half of the component. When the mouse is released the switch go back to neutral position. I have got 3 switch images for these position. I was thinking using a Button then check if the mouse click coordinate is in the upper half or the lower half then set images accordingly. I am looking for any better suggestion, if possible with the use of css for the images (i don't know if it is possible though), or any other suggestion with the use of another component if it is more suitable. Thank you.
Here is what i did, it works...
final Button rstButton = new Button();
final Image rstNeutral = new Image(MyClass.class.getResourceAsStream("images/switch_neutral.png"));
final Image rstUp = new Image(MyClass.class.getResourceAsStream("images/switch_on.png"));
final Image rstDown = new Image(MyClass.class.getResourceAsStream("images/switch_off.png"));
final ImageView rstImage = new ImageView();
rstImage.setImage(rstNeutral);
rstButton.setGraphic(rstImage);
rstButton.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
double mouseY = me.getY();
double buttonY = rstButton.getLayoutY();
double buttonHeight = rstButton.getHeight();
if(buttonY + mouseY > buttonY + (buttonHeight / 2)) {
rstImage.setImage(rstDown);
} else {
rstImage.setImage(rstUp);
}
}
});
rstButton.setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
rstImage.setImage(rstNeutral);
}
});
I think you should use three component instead of one.
For exemple, you could use three instance of ToogleButton. This component is like a button in appearance, but have a toogle state (clicked/unclicked). I never use it in javaFX but I think you will find enough help on the oracle site (for example : http://docs.oracle.com/javafx/2/ui_controls/toggle-button.htm)
EDIT :
After comment, I think an image of a mechanical switch (http://docs.oracle.com/javafx/2/api/javafx/scene/image/Image.html) could be the solution. If you're showing the switch from the side you can change the rotation of the image according to the state of the switch.
For knowing where the user clicked, locating the position of the button could work.
Another solution could be to had three transparent region on top of the image and intercept the click event on these region, instead of the image. Having never used such a solution, I can really help you more.
Last solution I see could be the use of two another image component on the side of the switch image, showing two arrow, for pulling up or down the switch.

Trying to add a ToolStrip to a ToolStripPanel side-by-side with an existing ToolStrip

I'm using .net 2.0 with Visual Studio 2005 and I am trying to add two different toolstrips to the top of the form such that they show up side-by-side. I want it to be like Word 2003, where you can add multiple toolstrips to the same row and have them show up in line with each other, rather than dedicating a row to each toolstrip.
So I added a ToolStripPanel and docked it to the top of the form (I didn't use a ToolStripContainer because I don't need all the extra panels; I just need the one at the top). I added both toolstrips and set their Stretch properties to False. I can get them to show up in the designer window side-by-side, but at runtime the ToolStripPanel separates the toolstrips and gives each toolstrip its own dedicated row. As if to add insult to injury, when i stop debugging and return back to the designer, I am finding that the designer is moving the toolstrips to their own row as well! Am I doing something wrong here?
I have been Googling all day and found some information about a ToolStripPanelRow object, but I don't see an easy way to add toolstrips to it (i.e. it doesn't have a ToolStripPanelRow.Controls.Add method or anything like that), all it has is a Controls() property that returns an Array of control objects, and I haven't had much luck trying to add items to that array. I also found some documentation on the ToolStripPanel.Join method, which sounds like it should do the job, so I tried all 3 overloads but they don't work as advertised. No matter what I do or which options I try, it always adds the new toolstrip to the top of the panel on its own row and pushes everything else down.
In the interests of full disclosure I should warn you that I have the ToolStripPanel and one of the toolstrips added to a baseclass form, and I am trying to add the other toolstrip to a subclass form that inherits from the baseclass form. The ToolStripPanel and ToolStrip in the baseclass form are both declared "Protected Friend", so this should be working. As I mentioned, the subclass form's designer window will allow me to do it (at least, for a time).
If anyone can help me get this working or at least shed some light on why it isn't, I would be extremely grateful.
I created a custom ToolStripPanel so that I could overload the LayoutEngine;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Layout;
namespace CustomGUI
{
class CustomToolStripPanel : ToolStripPanel
{
private LayoutEngine _layoutEngine;
public override LayoutEngine LayoutEngine
{
get
{
if (_layoutEngine == null) _layoutEngine = new CustomLayoutEngine();
return _layoutEngine;
}
}
public override Size GetPreferredSize(Size proposedSize)
{
Size size = base.GetPreferredSize(proposedSize);
foreach(Control control in Controls)
{
int newHeight = control.Height + control.Margin.Vertical + Padding.Vertical;
if (newHeight > size.Height) size.Height = newHeight;
}
return size;
}
}
}
Then the custom LayoutEngine lays out the ToolStrips;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Layout;
namespace CustomGUI
{
class CustomLayoutEngine : LayoutEngine
{
public override bool Layout(object container, LayoutEventArgs layoutEventArgs)
{
Control parent = container as Control;
Rectangle parentDisplayRectangle = parent.DisplayRectangle;
Control [] source = new Control[parent.Controls.Count];
parent.Controls.CopyTo(source, 0);
Point nextControlLocation = parentDisplayRectangle.Location;
foreach (Control c in source)
{
if (!c.Visible) continue;
nextControlLocation.Offset(c.Margin.Left, c.Margin.Top);
c.Location = nextControlLocation;
if (c.AutoSize)
{
c.Size = c.GetPreferredSize(parentDisplayRectangle.Size);
}
nextControlLocation.Y = parentDisplayRectangle.Y;
nextControlLocation.X += c.Width + c.Margin.Right + parent.Padding.Horizontal;
}
return false;
}
}
}
One thing that took a while is that changing the location / size of one ToolStrip item will cause the layout to re-fire, with the controls reordered. So I take a copy of the controls before the layout loop.
And you cant use AddRange(...) to add items to the Custom Panel for some reason - need to Add(...) them one at a time.
hope that helps (it's based on MSDN LayoutEngine Example, fixed for ToolStripPanels)
Wyzfen
System.Windows.Forms.FlowLayoutPanel can do the job.
Just put the ToolStrip controls in it with correct order.
I think in your case you can get away with just setting the LayoutStyle on your ToolStrip to ToolStripLayoutStyle.HorizontalStackWithOverflow rather than need your own custom LayoutEngine.
I asked a different question on a similar topic on how to handle layout with dynamic items to have better control of the overflow.

Resources