I have an object I want to drag around the screen with the mouse in Processing. I set acquired to true on mouse down over the object, and to false on mouse up, thus:
void mousePressed() {
if (overThing()) {
acquired = true;
}
}
void mouseReleased() {
acquired = false;
}
I then query acquired in my update(), and drag the object if it is true.
void update() {
\\ other stuff...
if (acquired) {
\\ drag thing code ...
}
}
This all works fine in Processing. mouseReleased() gets called whether I release the mouse inside or outside the active window.
However, when I move the code to Chrome, using processing.js (v1.4.8), mouseReleased() is not called if I release the mouse outside the canvas (whether the mouse is still over the web page, or outside the browser window). So when I return the (now unclicked) mouse to the canvas, the object is still getting dragged around.
I tried including a test of mousePressed in update(), but that also returns true in these circumstances.
Any help on what I need to do to make mouse state changes outside the canvas visible with processing.js?
I don't know about Processing specifically, but releasing mouse buttons outside a widget is a common issue in GUI development.
I suspect that you have no way of knowing the precise time when the mouse is released outside the widget, but you do have two options:
Set acquired = false in mouseOut(), as #Kevin suggests.
I assume there is some type of mouseEntered() method in Processing, and also some way of knowing if the mouse button is currently pressed (either a global variable, or an event object passed to mouseEntered()). You can catch the mouse entered event, check if the mouse has been released, and set acquired = false then.
Like so:
void mouseEntered() {
if (mouse button is pressed) {
acquired = false;
}
}
Edit: From your comments, #Susan, it seems like there is a bug in processing.js, where mousePressed is not set to false if the mouse button is released outside the canvas. One thing pointing to this being a bug is that the mouse movement example on the processing website also shows this behaviour.
Depending upon how much control you have over the website this is going on, and how much effort you want to go to, you could fix the bug yourself by writing some javascript (separate from your processing code):
Define a mouseUp() event on the page <body>, to catch all mouse release events on the page.
In the mouseUp() event, check if the event comes from your Processing control. (There is probably an event object passed to the mouseUp() function, and you might have to give your Processing control an ID to identify it)
If the event doesn't come from your Processing control, then fire a mouseUp event yourself, on the Processing control. This should (hopefully!) trigger a mouse event inside your Processing code.
I'm not sure what Processing will make of the mouse (x,y) position being outside its control when it handles the event you send it. You might want to set a flag on the event object (assuming you can add extra data to the event object) to say "don't use the (x,y) position of this event - it's outside the control".
Edit2: It was easier than I thought! Here is the JavaScript code to detect the mouse being released outside of the Processing canvas and send the mouseReleased event to the canvas. I've tested it on the mouse movement example from the Processing website, and it fixes the bug.
It uses jQuery (although it could be re-written to not use jQuery), and it assumes your Processing canvas has the ID "processingCanvas":
$(':not(processingCanvas)').mouseup(function(){
Processing.getInstanceById('processingCanvas').mouseReleased();
});
To use this code, include it anywhere in your page (in a JavaScript file or in <script> tags) and make sure you have the jQuery library included before this code.
The Processing object allows JavaScript to call any functions defined in your Processing code. Here I've used it to call Processing's built in mouseReleased() function, but if you wanted to call a custom function to handle the mouse-released-outside state differently, then you could.
You should use the mouseOut() function to detect when the mouse leaves the sketch:
void mouseOut() {
acquired = false;
}
More info in the reference here.
Related
I have two shapes in my canvas using CreateJS. In each shape I included a hit area with the own shape with a mouseover listener. Two shapes are one above the other. When I click into the shape, I received the two callbacks. It's possible to get only the callback to the visible shapes?
enter image description here
Similar to the DOM, the way mouse interaction works is to bubble up the display list, which excludes elements that are not part of the hierarchy chain of the event target.
This means siblings, or elements of other display lists that are underneath will not receive event handlers (which is what you described), and you will not receive mouse events for elements that are not the target of the mouse event.
However, you can wire up your own interaction fairly easily using getObjectsUnderPoint, which tells you what is under the mouse.
stage.on("click", handleClick);
function handleClick(event) {
var list = stage.getObjectsUnderPoint(event.localX, event.localY);
for (var i=0, l=list.length; i<l; i++) {
console.log(list[i]);
}
}
Here is a quick sample: http://jsfiddle.net/y8jhb26x/
Note that you can add the mouse event to any container you want to constrain what objects will trigger this check (I just used stage), but when you call getObjectsUnderPoint, it will return anything under the mouse. If you want to only check items in that container, you can use the contains method to filter out unwanted children:
for (var i=0, l=list.length; i<l; i++) {
if (someContainer.contains(list[i])) {
console.log(list[i]);
}
}
You can also use arguments on getObjectsUnderPoint to filter out items with mouse handlers, or respect the mouseChildren/mouseEnabled property, which is how actual mouse interaction works.
getObjectsUnderPoint method
mouse interaction code
Hope that helps!
I'm new here!
I searched internet a lot for my question, but I didn't found anything - or I'm really thinking wrong.
I program on VB.NET since 2 years, and on XNA since 6 months. I built a game and an editor for the game, and they are running great.
The question i about my editor (for an RPG game), and I'll try to explain at my best.
I have a main form with menustrips on top and a big picturebox covering the entire form, a picbox that is binded to the Game1 object when it start with the command Run().
The Game1 object handles two classes, that are basically panels that it draws on the picbox of the main form: a tileset panel in the left down the tabpage, and a map panel on the right. This works perfectly.
The problem is when for the first time yesterday I tried to draw with XNA on a form. I have multiple forms to manage NPCs, equipment, conditions, events, variables, etc and in the event form, I have a tabpage that manages map teleport events. On this tabpage I have a list of maps and a picbox where I want to draw a small view of the selected map. For this, I created of course a minimap panel with it's own draw and update methods.
...but of course, the minimap appears on the main form on the normal map.
I tried to change in real time the DeviceWindowHandle, but I failed... apparently, it changes only during the Run()
I tried to create a new game object and binding him to the event teleport form, but in the moment of lunching the Run() of this object, the debugger stops saying that I cannot launch more that one game loop in a thread.
I can't believe that XNA doesn't let to draw multiple things on different forms... and I can not pause the main loop from the event form (which is called from the NPC form) to start the minimap loop!
I think that is something really easy that unfortunately I don't know...
I'm getting crazy and lost... what I can do?
Please help me, thanks!!
Here's an example of what I commented (Sorry it's in C# but I don't really write VB.Net. Translating it should be pretty straight forward though):
private MainGame mainGame;
private ToolboxGame toolbox1;
private ToolboxGame toolbox2;
// And this should be put in some Form Initialization method:
Task.Factory.StartNew(() => {
this.mainGame = new MainGame(imgEditorPictureBox.Handle)
this.mainGame.Run();
}
Task.Factory.StartNew(() => {
this.toolbox1 = new ToolboxGame(toolbox1PictureBox.Handle)
this.toolbox1.Run();
}
Task.Factory.StartNew(() => {
this.toolbox2 = new ToolboxGame(toolbox2PictureBox.Handle)
this.toolbox2.Run();
}
Something like that should do it. Obviously whenever you "move" variables from one "game" to another, keep in mind that they run on different threads, so anywhere you use it, you'll need to
lock (dummyObject)
{
// Use data
}
to make sure you're not accessing it in one game, while the other is trying to set it.
Locking in VB.Net: Is there a lock statement in VB.NET?
You'll obviously need to come up with some smart infrastructure to get this working smoothly, but since you've made a game and editor before, I'm sure this should not prove a humongous challenge.
All you want show to the player you need draw in the game window. You have one Game with one GraphicsDevice and by default all you draw will be rendered on the game window. But you can call GraphicsDevice.SetRenderTarget method to change render target. Call it with RenderTarget2D object as parameter and anithing you will draw after this will be rendered to that render target.
Next you need call GraphicsDevice.SetRenderTarget(null) to set game window as render target again.
There is my (uncompleted yet) custom GUI realization for XNA. I hope it can help you.
Update
class Game1 : Game
{
GraphicsDevice graphicsDevice;
SpriteBatch spriteBatch;
public RenderTarget2D MinimapRenderBuffer;
public RenderTarget2D AnotherRenderBuffer1;
public RenderTarget2D AnotherRenderBuffer2;
public EventHandler RenderBuffersUpdated;
void Initialize()
{
// Better initialize them only once, don't do it in Draw method
this.MinimapRenderBuffer = new RenderTarget2D(this.graphicsDevice, 100, 100); // any size you want
this.AnotherRenderBuffer1 = new RenderTarget2D(this.graphicsDevice, 50, 50);
this.AnotherRenderBuffer2 = new RenderTarget2D(this.graphicsDevice, 500, 500);
}
void Draw()
{
this.graphicsDevice.SetRenderTarget(this.MinimapRenderBuffer);
// draw minimap to MinimapRenderBuffer
this.graphicsDevice.SetRenderTarget(this.AnotherRenderBuffer1);
// draw whatewer to AnotherRenderBuffer1
this.graphicsDevice.SetRenderTarget(this.AnotherRenderBuffer2);
// draw whatewer to AnotherRenderBuffer2
this.graphicsDevice.SetRenderTarget(null);
// now draw to screen
if (this.RenderBuffersUpdated != null)
{
RenderBuffersUpdated(null, null);
}
}
}
And use rendertargets in your editor when event raised. And you can convert them to bitmaps.
At the bottom of the durandal docs for dialogs / modals (http://durandaljs.com/documentation/Showing-Message-Boxes-And-Modals.html) there's some syntax for repositioning a dialog. The dialog I have gets gradually bigger as the user selects stuff, so every time a selection is made, I'd like to call reposition(). I tried following along with this:
vm.compositionComplete = function (child, parent, context) {
var addEditDialog = dialog.getDialog(context.model); // resolves
// whenever something is selected:
addEditDialog.context.reposition(vm); // no method 'reposition'
}
But I get an error - there is no function reposition. What am I doing wrong?
You can set up a custom dialog context that responds to a reposition message (using Durandal's app.trigger()). You would trigger the message upon some event in your dialog (such as, as you say, the user's selecting stuff).
Also in that custom dialog context, create a method call reposition. In the activate or attached handler of that custom dialog context, subscribe to the message you use to trigger a reposition.
I'm advocating a messaging approach because you may wish to isolate your "selection viewModel" in its own viewModel, and then compose it into your custom dialog context. With this approach, your selection viewModel and your custom dialog context would be loosely bound. That way, you could use your selection viewModel elsewhere in your code (and even have some other viewModel, instead of the custom dialog context, respond to the reposition message).
I registered to the OnSizeChanged event of my Page, like that:
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
ApplicationViewState myViewState = ApplicationView.Value;
if (myViewState == ApplicationViewState.Snapped)
{
Windows.UI.ViewManagement.ApplicationView.TryUnsnap();
}
}
I'm tyring to set the application-view to Filled/Portrait state when the user trying (manually....) to resize it to snapped view.
but the TryUnsnap method fails and it stays in snapped state...
Help!
Thanks.
To understand TryUnsnap() we need to understand the 2 types of Windows 8 events:
Programmatic events
Programmatic events do not require the user to do anything. For example the Loaded event of a Page or the Tick event of a Timer.
User-initiated events
User-initiated events require the user to do something. For example the Click event of a Button or the Tapped event of a Control.
The important part
Depending on the type of event, only certain Windows 8 APIs can be called. Adding a Secondary Tile, for example. And (as you might have guessed) un-Snapping an app.
That means you can call those APIs all you want from programmatic events but they will never deliver the results you desire. Unsnap in the StateChanged event, and it will fail for this reason. Unsnap in the Button.Click event, and it will succeed for this reason.
The rationale behind this behavior is the user experience. If the app can change it's 'orientation' on the user without the user's interaction then the behavior of the app becomes both confusing and unpredictable. Windows 8 is a pro-user operating system. When you discover developer 'constraints', 99% of the time it is this philosophy behind it.
Let me demonstrate:
If you attached to the StateChanged event, your code would look like this:
this.ApplicationViewStates.CurrentStateChanged += (s, args) =>
{
System.Diagnostics.Debug.WriteLine("After StateChanged: {0}", this.ApplicationViewStates.CurrentState.Name);
if (this.ApplicationViewStates.CurrentState == this.Snapped)
{
System.Diagnostics.Debug.WriteLine("Before Unsnap: {0}", this.ApplicationViewStates.CurrentState.Name);
Unsnap();
}
};
However, the resulting output (in the debugger) would look like this:
After StateChanged: FullScreenLandscape
After StateChanged: Snapped
Before Unsnap: Snapped
After TryUnsnap: Snapped
This is frustrating for a developer who does not understand the difference between programmatic and user-initiated events in Windows 8. The API appears to "not work" when, in fact, it works perfectly. Just not like they want it to.
If you attached to the Click event, your code would look like this:
MyButton.Click += (s, args) =>
{
System.Diagnostics.Debug.WriteLine("After Button.Click: {0}", this.ApplicationViewStates.CurrentState.Name);
if (this.ApplicationViewStates.CurrentState == this.Snapped)
{
System.Diagnostics.Debug.WriteLine("Before Unsnap: {0}", this.ApplicationViewStates.CurrentState.Name);
Unsnap();
}
};
Then, the resulting output would look like this:
After Button.Click: Snapped
Before Unsnap: Snapped
After TryUnsnap: Snapped
After StateChanged: FullScreenLandscape
This gets you what you want, but it brings up an important point. See how After TryUnsnap the state REMAINS "Snapped"? The transition of a Visual State from one to another is not a synchronous event. Calling for a change takes an unpredictable amount of time. It's probably done with a dispatch post, but I would have to check to be sure.
Having said all that, the state does change. And, after the change the CurrentStateChanged event is raised and you can handle the new Snapped state. By the way, it does not matter if there is another snapped app, this works either way.
The MSDN docs say it only works when it is in the foreground. This is pretty stupid since user interaction can't occur on a background app, and background apps have their threads suspended anyway. But, to be fair to MSDN, this API does not work when your app is in the background - whatever that's worth.
I hope this helps clear it up.
And now to your question:
You want to go from Snapped to Portrait? Of course in Portrait, Snapped is not possible so this is not a possibility for you to code. You want to go from Snapped to Filled as soon as the app is snapped. The event raised from the Snapped action is a programmatic event. As a result, you have to lure the user into doing something in your UI first. So, no you can't do what you are asking. You can't Unsnap() until the user interacts with your app somehow (like a button click event).
Oh, and here's the Unsnap() method if you wanted to reference all my code. I am not doing anything special, but you might be interested:
void Unsnap()
{
if (Windows.UI.ViewManagement.ApplicationView.TryUnsnap())
// successfully unsnapped
System.Diagnostics.Debug.WriteLine("After TryUnsnap: {0}", this.ApplicationViewStates.CurrentState.Name);
else
// un-successfully unsnapped
System.Diagnostics.Debug.WriteLine("After TryUnsnap: {0}", this.ApplicationViewStates.CurrentState.Name);
}
Have a great day and best of luck!
var CurrentSnappedState = ApplicationView.Value;
if (CurrentSnappedState == ApplicationViewState.Snapped && !ApplicationView.TryUnsnap())
{
return;
}
Should do the trick. Remember that you can still snap the page, but when you try to do anything in the snapped page you will be redirected to the fullview.
I have to port some legacy code, that uses modal dialog boxes all over the place to Metro/WinRT (using C++/CX). Because these dialog boxes provide their own message loop (using DialogBoxParam()), the calling code will wait until the user has clicked a button on the message box.
I'm currently trying to write a replacement for the old message box class, that uses XAML and the popup control. To reproduce the same behavior, I have to wait in the calling thread, but also have to keep the UI responsive. I've found out, that CoreDispatcher::ProcessEvents() can be used in a loop, to keep processing events (yeah I realize that this isn't very beautiful, but I don't want to change all of our legacy code to a new threading model). However I'm running into an issue that keeps crashing my app.
Here is a minimal example that reproduces the issue (just create a XAML app and wire this to a button):
void CPPXamlTest::MainPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
bool cancel = false;
auto popup = ref new Popup();
auto button = ref new Button();
button->Content = "Boom";
auto token = (button->Click += ref new RoutedEventHandler([&cancel] (Object ^, RoutedEventArgs ^) { cancel = true; }));
popup->Child = button;
popup->IsOpen = true;
while (!cancel)
{
Window::Current->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
}
popup->IsOpen = false;
button->Click -= token;
}
This seems to work well for the first one or two tries of opening and closing the popup, using the two buttons. After a few tries however, the application will instantly crash deep in Windows.UI.Xaml.dll, while trying to dereference a null pointer. I can also reproduce this in C# (with practically the same code).
Does anyone have an idea, what is going on in here? Or a suggestion for an alternative approach?
If anyone is interested: I asked the same question a few days later on the MSDN forums and got a response there from a Microsoft employee:
http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/11fa65e7-90b7-41f5-9884-80064ec6e2d8/
Apparently the problem here is the nested message loop that is caused by calling ProcessEvents inside an event handler. It seems that this is not supported by WinRT, but instead of failing in a well-defined manner, this will or may cause a crash.
Alas this was the best and only answer I could find, so I ended up working around the problem, by dispatching the event handler (and a lot of other code) into another thread. I could then emulate the waiting behavior of DialogBox()/DialogBoxParam() (outside the main thread), by waiting on an event that was signaled when the user clicked/tapped a button on my XAML "dialog" popup.
A workaround that works fine for me is to replace the line:
Window::Current->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
with:
auto myDispatchedHandler = ref new DispatchedHandler([&](){
Window::Current->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
});
dispatcher->RunAsync(CoreDispatcherPriority::Normal,myDispatchedHandler);
For more info see this post at MSDN.