Gluon Mobile Charm 5.0 Cannot Hide Layer - gluon

I have a loading gif for all backend requests. Prior to Charm 5.0.0, it worked fine in which the loading gif would show, backend would finish what it needed to, then the loading gif would be hidden. Now, the loading gif shows, but it doesn't hide.
addLayerFactory(LOADING_GIF, () -> new Layer() {
private final Node root;
private final double sizeX = getGlassPane().getWidth();
private final double sizeY = getGlassPane().getHeight();
{
ProgressIndicator loading = new ProgressIndicator();
loading.setRadius(50);
loading.setStyle("-fx-text-fill:white");
root = new StackPane(loading);
root.setStyle("-fx-background-color: rgba(0,0,0,0);");
getChildren().add(root);
this.setStyle("-fx-background-color:rgba(255,255,255,0.7)");
this.setShowTransitionFactory(v -> {
FadeInTransition ft = new FadeInTransition(v);
ft.setRate(2);
return ft;
});
}
#Override
public void show() {
this.setBackgroundFade(0.0);
super.show();
Layer pane = this;
Task<Integer> task = new Task<Integer>() {
#Override
protected Integer call() throws Exception {
int iterations = 0;
int max = DataService.readOutTime / 1000;
while (iterations <= max) {
Thread.sleep(1000);
iterations++;
}
Platform.runLater(new Runnable() {
#Override
public void run() {
if (pane.isVisible()) {
pane.setShowTransitionFactory(v -> {
FadeOutTransition ft = new FadeOutTransition(v);
ft.setRate(2);
return ft;
});
pane.hide();
MobileApplication.getInstance().showMessage("There was an error in sending your data.");
}
}
});
return iterations;
}
};
Thread thread = new Thread(task);
thread.start();
}
#Override
public void hide() {
this.setBackgroundFade(0.0);
super.hide();
}
#Override
public void layoutChildren() {
root.setVisible(isShowing());
if (!isShowing()) {
return;
}
root.resize(sizeX, sizeY);
resizeRelocate((getGlassPane().getWidth() - sizeX) / 2, (getGlassPane().getHeight() - sizeY) / 2, sizeX, sizeY);
}
});
I have a couple of utility methods that show and hide the loader:
public void showLoader() {
MobileApplication.getInstance().showLayer(App.LOADING_GIF);
}
public void hideLoader() {
MobileApplication.getInstance().hideLayer(App.LOADING_GIF);
}
Interestingly, the custom timeout I created (to hide the loader in case there is a stall in the backend) doesn't hide the layer either.

There is an issue with your code: you are overriding Layer::layoutChildren, but you are not calling super.layoutChildren().
If you check the JavaDoc:
Override this method to add the layout logic for your layer. Care should be taken to call this method in overriden methods for proper functioning of the Layer.
This means that you are getting rid of some important parts of the Layer control, such as animations, events and visibility control.
This should work:
#Override
public void layoutChildren() {
super.layoutChildren();
root.setVisible(isShowing());
if (!isShowing()) {
return;
}
root.resize(sizeX, sizeY);
resizeRelocate(getGlassPane().getWidth() - sizeX) / 2, getGlassPane().getHeight() - sizeY) / 2, sizeX, sizeY);
}
On a side note, for the hide transition, you should use setHideTransitionFactory.

So this is what I have done to solve this. From the Gluon Docs on the hide() method:
If this layer is showing, calling this method will hide it. If a hide transition is present, it is played before hiding the Layer. Care should be taken to call this only once LifecycleEvent.SHOWN has been fired.
Thus, I was realizing that the response from the backend was coming before the layer was fully shown. Thus, I modified the overridden hide() method as follows:
#Override
public void hide() {
if (this.isShowing()) {
this.setOnShown(e -> {
this.setBackgroundFade(0.0);
super.hide();
});
} else {
super.hide();
}
}
So if the layer is still in LifecycleEvent.SHOWING mode when being told to hide, make sure that it hides when it is shown. Otherwise it is already shown so hide it.

Related

Vaadin - run client side javascript after image fully loaded

I need to print a picture on client side. I used this as a template. My PrintUI looks like this:
#Override
protected void init(VaadinRequest request) {
Item item = ..get item ..
StreamResource imageStream = ... build image dynamically ...
Image image = new Image(item.getName(), imageStream);
image.setWidth("100%");
setContent(image);
setWidth("100%");
// Print automatically when the window opens
JavaScript.getCurrent().execute("setTimeout(function() {print(); self.close();}, 0);");
}
This works so far in IE but in chrome it opens the printing preview showing an empty page. The problem is that the image is loaded in some way that chrome does not wait for it and starts the printing preview immideatly.
To verify this, I tried: (setting a 5sec timeout)
JavaScript.getCurrent().execute("setTimeout(function() {print(); self.close();}, 0);");
Then it works in IE and Chrome, but its of course an ugly hack, and if the connection is slower than 5sec, then again it will fail.
In pure JS it would work like this, but Im not sure how to reference the element from vaadin in cient-side js. Any ideas?
You can use AbstractJavascriptExtension.
Example extension class:
#JavaScript({ "vaadin://scripts/connector/wait_for_image_load_connector.js" })
public class WaitForImageLoadExtension extends AbstractJavaScriptExtension {
private List<ImageLoadedListener> imageLoadedListeners = new ArrayList<>();
public interface ImageLoadedListener {
void onImageLoaded();
}
public void extend(Image image) {
super.extend(image);
addFunction("onImageLoaded", new JavaScriptFunction() {
#Override
public void call(JsonArray arguments) {
for (ImageLoadedListener imageLoadedListener : imageLoadedListeners) {
if (imageLoadedListener != null) {
imageLoadedListener.onImageLoaded();
}
}
}
});
}
public void addImageLoadedListener(ImageLoadedListener listener) {
imageLoadedListeners.add(listener);
}
}
and javascript connector (placed in wait_for_image_load_connector.js) with the waiting method you have linked:
window.your_package_WaitForImageLoadExtension = function() {
var connectorId = this.getParentId();
var img = this.getElement(connectorId);
if (img.complete) {
this.onImageLoaded();
} else {
img.addEventListener('load', this.onImageLoaded)
img.addEventListener('error', function() {
alert('error');
})
}
}
Then you can do something like that:
Image image = new Image(item.getName(), imageStream);
WaitForImageLoadExtension ext = new WaitForImageLoadExtension();
ext.extend(image);
ext.addImageLoadedListener(new ImageLoadedListener() {
#Override
public void onImageLoaded() {
JavaScript.eval("print()");
}
});
In your case, when calling print() is the only thing you want to do after the image is loaded, you can also do it without server-side listener by just calling it in the connector:
if (img.complete) {
print();
} else {
img.addEventListener('load', print)
img.addEventListener('error', function() {
alert('error');
})
}

How to know if Unity UI button is being held down?

I am using Unity 5.2 UI. I am working on a game for iOS. I have a custom keyboard. I want to add the functionality to the del/backspace key so that when i hold the del key for more than 2 secs, it deletes the whole word instead of a single letter, which it deletes on single clicks. How do I achieve that?
Using the UGUI event you'd create a script like the following and attach it to your button:
using UnityEngine;
using UnityEngine.EventSystems;
public class LongPress : MonoBehaviour, IPointerDownHandler, IPointerUpHandler {
private bool isDown;
private float downTime;
public void OnPointerDown(PointerEventData eventData) {
this.isDown = true;
this.downTime = Time.realtimeSinceStartup;
}
public void OnPointerUp(PointerEventData eventData) {
this.isDown = false;
}
void Update() {
if (!this.isDown) return;
if (Time.realtimeSinceStartup - this.downTime > 2f) {
print("Handle Long Tap");
this.isDown = false;
}
}
}

Java FX Textarea performance issue in .jar

I have a TextArea that I would like to be able to append characters or words to over a period of time. I use Timer from java.util and when I run application in Eclipse everthing works ok, but when I export application into .jar I have performance issue.
Here is video from Eclipse:
http://pl.tinypic.com/r/4ftw1f/8
Here is .jar:
http://pl.tinypic.com/r/6zmoon/8
And code:
#FXML
private TextArea textarea;
public void start(KeyEvent keyEvent)
{
if (keyEvent.getCode() == KeyCode.ENTER)
{
new Timer().schedule(
new TimerTask() {
int i;
#Override
public void run() {
textarea.appendText("hey" + i + "\n");
i++;
}
}, 0, 500);
}
}
Your code has threading issues: in Java 8 it will just throw IllegalStateExceptions as you are trying to update the UI from a background thread. You need
if (event.getCode() == KeyCode.ENTER)
{
new Timer().schedule(
new TimerTask() {
int i;
#Override
public void run() {
String message = "hey"+i+"\n";
Platform.runLater(() -> textArea.appendText(message));
i++;
}
}, 0, 500);
}
I don't know if that will fix your performance issue or not. Appending text to a text area essentially involves doing lots of string concatenation; eventually (as the text in the text area gets long) this is going to be prohibitive. You might want to use a virtualized control (such as ListView), depending on the functionality you need.

How to use events in GWT Frame

I would like to capture all events within a GWT frame. I've found several ways to do this, but they only return mousemove and mouseout events. I also need keypresses, input, etc. The goal is to capture the events and send them to another client by using websockets, and then replicate them on the other side (co-browsing).
I am using a page on the same domain within the frame.
public class ESinkFrame extends Frame implements EventListener {
public ESinkFrame(String src){
super(src);
DOM.sinkEvents(getElement(), Event.KEYEVENTS);
DOM.sinkEvents(getElement(), Event.MOUSEEVENTS);
}
public void onBrowserEvent(Event event) {
System.out.println( "sunk event: " + DOM.eventGetTypeString(event) );
}
}
And when I use it, I also try to attach a different way of grabbing the events.
ESinkFrame frame = new ESinkFrame("http://127.0.0.1:8888/other.html");
RootPanel.get().add(frame);
FrameElement frameElt = frame.getElement().cast();
Document frameDoc = frameElt.getContentDocument();
BodyElement body = frameDoc.getBody();
Element el = body.cast();
DOM.setEventListener(el, new EventListener()
{
public void onBrowserEvent(Event event)
{
Window.alert("test");
}
});
DOM.sinkEvents(el, Event.KEYEVENTS);
Event.addNativePreviewHandler(new NativePreviewHandler(){
public void onPreviewNativeEvent(NativePreviewEvent event) {
String eventName = event.getNativeEvent().getType();
if (event.isFirstHandler() /* && (event.getTypeInt() & Event.MOUSEEVENTS) == 0*/)
System.out.println("PreviewHandler: " + eventName);
}
});

Slowly initialized components that cause lack of user experience

This is the view that appears when I click a button on a previous view.
The text boxes, the smiling face image and the labels are predesigned created by xCode.
Please see the image and the code of the view to clear why all the view's components are initializing very slowly and getting ready to give the last shoot that is captured by me when it is finished to be totally loaded . Moreover, It is very slow when I type letters, the letters are appearing very slowly while I am typing with the keyboard that iOS provides on every touch on the text box.
The Code of The View;
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace IstanbulCity
{
public partial class AskForNAme : UIViewController
{
public delegate void AskForNAmeClosingDelegate (AskForNAme form);
public event AskForNAmeClosingDelegate AskForNAmeClosed;
NSObject obs1;
float scrollamount = 0.0f;
float bottomPoint = 0.0f;
float yOffset = 0.2f;
bool moveViewUp = false;
public AskForNAme () : base ("AskForNAme", null)
{
}
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(true);
obs1 = NSNotificationCenter.DefaultCenter.AddObserver (
"UIKeyboardDidShowNotification", KeyboardUpNotification);
this.tbOwnerMailAdress.ShouldReturn += TextFieldShouldReturn;
this.tbOwnerBirthDay.ShouldReturn += TextFieldShouldReturn;
this.uivGuguPhoto.Image = UIImage.FromFile ("image/fcuk.jpeg");
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(false);
obs1 = NSNotificationCenter.DefaultCenter.AddObserver (
"UIKeyboardDidShowNotification", KeyboardUpNotification);
this.tbOwnerMailAdress.ShouldReturn += TextFieldShouldReturn;
this.tbOwnerBirthDay.ShouldReturn += TextFieldShouldReturn;
this.uivGuguPhoto.Image = UIImage.FromFile ("image/fcuk.jpeg");
}
public override void ViewDidUnload ()
{
base.ViewDidUnload ();
// Clear any references to subviews of the main view in order to
// allow the Garbage Collector to collect them sooner.
//
// e.g. myOutlet.Dispose (); myOutlet = null;
ReleaseDesignerOutlets ();
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
// Return true for supported orientations
return (toInterfaceOrientation != UIInterfaceOrientation.PortraitUpsideDown);
}
void HandleIstanbulCityViewControllerClosed (babyAge form)
{
form.DismissModalViewControllerAnimated (true);
form = null;
}
partial void tbKadikoyHallEditDidEndOnExit (MonoTouch.Foundation.NSObject sender)
{
tbIstanbulName.ResignFirstResponder ();
}
private bool TextFieldShouldReturn (UITextField tf)
{
tf.ResignFirstResponder ();
if (moveViewUp) {
ScrollTheView (false);
}
return true;
}
private void KeyboardUpNotification (NSNotification notification)
{
ResetTheView ();
RectangleF r = UIKeyboard.BoundsFromNotification (notification);
if (this.tbOwnerMailAdress.IsEditing ) {
//Calculate the bottom of the Texbox
//plus a small margin...
bottomPoint = (this.tbOwnerMailAdress.Frame.Y + this.tbOwnerMailAdress.Frame.Height + yOffset);
//Calculate the amount to scroll the view
//upwards so the Textbox becomes visible...
//This is the height of the Keyboard -
//(the height of the display - the bottom
//of the Texbox)...
scrollamount = (r.Height - (View.Frame.Size.Height - bottomPoint));
}
else if (this.tbOwnerBirthDay.IsEditing)
{
bottomPoint = (this.tbOwnerBirthDay.Frame.Y + this.tbOwnerBirthDay.Frame.Height + yOffset);
scrollamount = (r.Height - (View.Frame.Size.Height - bottomPoint));
}
else
{
scrollamount = 0;
}
//Check to see whether the view
//should be moved up...
if (scrollamount > 0) {
moveViewUp = true;
ScrollTheView (moveViewUp);
} else
moveViewUp = false;
}
private void ResetTheView ()
{
UIView.BeginAnimations (string.Empty, System.IntPtr.Zero);
UIView.SetAnimationDuration (0.3);
RectangleF frame = View.Frame;
frame.Y = 0;
View.Frame = frame;
UIView.CommitAnimations ();
}
private void ScrollTheView (bool movedUp)
{
//To invoke a views built-in animation behaviour,
//you create an animation block and
//set the duration of the move...
//Set the display scroll animation and duration...
UIView.BeginAnimations (string.Empty, System.IntPtr.Zero);
UIView.SetAnimationDuration (0.3);
//Get Display size...
RectangleF frame = View.Frame;
if (movedUp) {
//If the view should be moved up,
//subtract the keyboard height from the display...
frame.Y -= scrollamount;
} else {
//If the view shouldn't be moved up, restore it
//by adding the keyboard height back to the original...
frame.Y += scrollamount;
}
//Assign the new frame to the view...
View.Frame = frame;
//Tell the view that your all done with setting
//the animation parameters, and it should
//start the animation...
UIView.CommitAnimations ();
}
}
}
The Recent Version - Still The Same User Experience' slow!
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace IstanbulCity
{
public partial class AskForNAme : UIViewController
{
public delegate void AskForNAmeClosingDelegate (AskForNAme form);
public event AskForNAmeClosingDelegate AskForNAmeClosed;
public AskForNAme () : base ("AskForNAme", null)
{
}
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
}
public override void ViewDidUnload ()
{
base.ViewDidUnload ();
// Clear any references to subviews of the main view in order to
// allow the Garbage Collector to collect them sooner.
//
// e.g. myOutlet.Dispose (); myOutlet = null;
ReleaseDesignerOutlets ();
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
// Return true for supported orientations
return (toInterfaceOrientation != UIInterfaceOrientation.PortraitUpsideDown);
}
void HandleIstanbulCityViewControllerClosed (babyAge form)
{
form.DismissModalViewControllerAnimated (true);
form = null;
}
}
}
This does not look to be initialization related. You are adding notifications from both ViewDidAppear and ViewWillAppear. You're also always calling ResetTheView, which does animations, on every keyboard notification (even if nothing else changed).
My guess is that you are calling ResetTheView way more often that you realize - and the continuous animations are killing the performance of your application.
You can confirm this by putting a Console.WriteLine, and maybe a counter, in the ResetTheView method.

Resources