java - how to shake a login dialog in javaFX/8 -


how make javafx/8 dialog box shake more elegantly whenever user input wrong login name/password pair?.

since dialog in java8u40 not have one, set out make 1 myself. however, doesn't enough.

what's wrong it? can help? there better way in doing it?


public void logindialog() {     // create custom dialog.     dialog<pair<string, string>> dialog = new dialog<>();     dialog.settitle("mars simulation project");     dialog.setheadertext("log in");     dialog.setcontenttext("enter username , password : ");     dialog.initmodality(modality.none);     // set button types.     buttontype loginbuttontype = new buttontype("login", buttondata.ok_done);     dialog.getdialogpane().getbuttontypes().addall(loginbuttontype, buttontype.cancel);      // create username , password labels , fields.     gridpane grid = new gridpane();     grid.sethgap(10);     grid.setvgap(10);     grid.setpadding(new insets(20, 150, 10, 10));      textfield tfplayer = new textfield();     tfplayer.setprompttext("e.g. m03j");     passwordfield tfpassword = new passwordfield();     tfpassword.setprompttext("xxxx");      button defaultpwb = new button("use default");     button guestb = new button("as guest");      defaultpwb.setonaction(event -> {         tfpassword.settext("msp0");     } );      guestb.setonaction(event -> {         tfplayer.settext("guest_");         tfpassword.settext("msp0");     } );      grid.add(new label("player name :"), 0, 0);     grid.add(tfplayer, 1, 0);     grid.add(guestb, 2, 0);     grid.add(new label("password :"), 0, 1);     grid.add(tfpassword, 1, 1);     grid.add(defaultpwb, 2, 1);      // enable/disable login button depending on whether username entered.     node loginbutton = dialog.getdialogpane().lookupbutton(loginbuttontype);     loginbutton.setdisable(true);      // validation (using java 8 lambda syntax).     tfplayer.textproperty().addlistener((observable, oldvalue, newvalue) -> {         loginbutton.setdisable(newvalue.trim().isempty());     } );      dialog.getdialogpane().setcontent(grid);      // request focus on player name field default.     platform.runlater(() -> tfplayer.requestfocus());      // convert result player name /host address pair when login     // button clicked.     dialog.setresultconverter(dialogbutton -> {         if (dialogbutton == loginbuttontype) {             return new pair<>(tfplayer.gettext(), tfpassword.gettext());         }         return null;     } );      optional<pair<string, string>> result = dialog.showandwait();      result.ifpresent(input -> {         playername = tfplayer.gettext();         logger.info("player " + input.getkey() + " connecting server @ " + serveraddressstr);          try {             dialog.show();             makecontact(serveraddressstr);             // obtain client id             boolean issuccessful = sendregister();              if (issuccessful) {                 dialog.close();                 // establish chat...              } else {                 // shake dialog or send alert inform user                 // player name not valid                 dialogearthquakecenter dec = new dialogearthquakecenter(dialog);                 dec.starttimer();                  try {                     system.out.println("start sleeping ");                     thread.sleep(2000);                     system.out.println("done sleeping ");                 }                 catch (interruptedexception e) {}                  logindialog();             }          } catch (exception e) {             e.printstacktrace();         }     } ); 

so far, problem hit button "login", dialog close default.

therefore have use dialog.show() make show again.

[edit] this, however, still cannot prevent momentary gap happening (seeing dialog disappear , reappear).

after that, create instance of dialogearthquakecenter in order shake dialog.

note dialogearthquakecenter below direct modification of original :

https://github.com/gigiigig/java-chat/blob/master/tag/facebookchatcore_original/src/facebookchat/ui/common/dialogearthquakecenter.java

import java.awt.event.actionevent; import java.awt.event.actionlistener; import javax.swing.timer; import javafx.animation.keyframe; import javafx.animation.timeline; import javafx.application.platform; import javafx.scene.control.dialog; import javafx.util.duration; import javafx.util.pair;  public class dialogearthquakecenter {  public static final int shake_distance = 10; public static final double shake_cycle = 50; public static final int shake_duration = 500; public static final int shake_update = 5;  private dialog<pair<string, string>> dialog; private int x, y; private long starttime; private timer shaketimer; private final double two_pi = math.pi * 2.0; private timeline timeline;  public dialogearthquakecenter(dialog<pair<string, string>> parent) {     dialog = parent; }  /**  * creates , starts timer  *  * @return scene  */ public void starttimer() {     x = (int) dialog.getx();     y = (int) dialog.gety();     starttime = system.currenttimemillis();     // set earth time text update     timeline = new timeline(new keyframe(duration.millis(shake_duration), ae -> startnudging()));     //timeline.setcyclecount(javafx.animation.animation.indefinite);     timeline.play(); }  public void startnudging() {     x = (int) dialog.getx();     y = (int) dialog.gety();     starttime = system.currenttimemillis();     shaketimer = new timer(shake_update, new actionlistener() {         public void actionperformed(actionevent e) {             shake();         }     });      shaketimer.start(); }  public void shake() {     // calculate elapsed time     long elapsed = system.currenttimemillis() - starttime;     //system.out.println("elapsed " + elapsed);     // use sin calculate x-offset     double waveoffset = (elapsed % shake_cycle) / shake_cycle;     double angle = waveoffset * two_pi;     // offset x-location amount     // proportional sine, shake_distance     int shakenx = (int) ((math.sin(angle) * shake_distance) + x);      platform.runlater(() -> {         //dialog.hide();         dialog.setx(shakenx);         //system.out.println("set shakenx " + shakenx);         dialog.sety(y);         dialog.show();     });      //try {thread.sleep(20);}     //catch (interruptedexception ex) {}      // should stop timer     if (elapsed >= shake_duration) {         stopshake();      } }  public void stopshake() {     shaketimer.stop();     platform.runlater(() -> {         timeline.stop();         dialog.close();     }); } } 

i did notice controlsfx dialog has shake() method.

does know if works ?

see https://code.google.com/p/mqtt-spy/source/browse/mqtt-spy/src/main/java/org/controlsfx/dialog/customdialogs.java?r=6ec0240e4e64d1b8cc2b59bc77cd5902a68e0c81

thanks comments!

there's way can add transition once user has click on login button using dialog api, before window closed.

using dialog.show() instead of dialog.showandwait()`, trick trapping click action on button, consume event, , perform required logic.

dialog.initmodality(modality.application_modal); dialog.show();              loginbutton.addeventfilter(eventtype.root,      e->{         if(e.geteventtype().equals(actionevent.action)){                             e.consume();             // (hardcoded) login validation             boolean issuccessful = false;             if (issuccessful) {                 dialog.close();             }             else {                 // perform animation , close dialog (or other action)                 shaketransition anim = new shaketransition(dialog.getdialogpane(), t->dialog.close());                 anim.playfromstart();             }         }     }); 

for shake animation, i've modified shaketransition jasper potts, in order move dialog window, @jewelsea pointed out:

/**  * animate shake effect on given node  *  * based on cachedtimelinetransition, transition uses timeline internally  * , turns speed caching on animated node during animation. *  * https://github.com/fxexperience/code/blob/master/fxexperiencecontrols/src/com/fxexperience/javafx/animation/cachedtimelinetransition.java *  * , shaketransition *  * https://github.com/fxexperience/code/blob/master/fxexperiencecontrols/src/com/fxexperience/javafx/animation/shaketransition.java *  * @author jasper potts */ class shaketransition extends transition {      private final interpolator web_ease = interpolator.spline(0.25, 0.1, 0.25, 1);     private final timeline timeline;     private final node node;     private boolean oldcache = false;     private cachehint oldcachehint = cachehint.default;     private final boolean usecache=true;     private final double xini;      private final doubleproperty x = new simpledoubleproperty();      /**     * create new shaketransition     *      * @param node node affect     */     public shaketransition(final node node, eventhandler<actionevent> event) {         this.node=node;         statusproperty().addlistener((ov, t, newstatus) -> {            switch(newstatus) {                case running:                    starting();                    break;                default:                    stopping();                    break;            }         });          this.timeline= new timeline(                 new keyframe(duration.millis(0), new keyvalue(x, 0, web_ease)),                 new keyframe(duration.millis(100), new keyvalue(x, -10, web_ease)),                 new keyframe(duration.millis(200), new keyvalue(x, 10, web_ease)),                 new keyframe(duration.millis(300), new keyvalue(x, -10, web_ease)),                 new keyframe(duration.millis(400), new keyvalue(x, 10, web_ease)),                 new keyframe(duration.millis(500), new keyvalue(x, -10, web_ease)),                 new keyframe(duration.millis(600), new keyvalue(x, 10, web_ease)),                 new keyframe(duration.millis(700), new keyvalue(x, -10, web_ease)),                 new keyframe(duration.millis(800), new keyvalue(x, 10, web_ease)),                 new keyframe(duration.millis(900), new keyvalue(x, -10, web_ease)),                 new keyframe(duration.millis(1000), new keyvalue(x, 0, web_ease))             );         xini=node.getscene().getwindow().getx();         x.addlistener((ob,n,n1)->(node.getscene().getwindow()).setx(xini+n1.doublevalue()));          setcycleduration(duration.seconds(1));         setdelay(duration.seconds(0.2));         setonfinished(event);     }       /**     * called when animation starting     */     protected final void starting() {         if (usecache) {             oldcache = node.iscache();             oldcachehint = node.getcachehint();             node.setcache(true);             node.setcachehint(cachehint.speed);         }     }      /**     * called when animation stopping     */     protected final void stopping() {         if (usecache) {             node.setcache(oldcache);             node.setcachehint(oldcachehint);         }     }      @override      protected void interpolate(double d) {         timeline.playfrom(duration.seconds(d));         timeline.stop();     } } 

and javafx application using login dialog:

@override public void start(stage primarystage) {     button btn = new button();     btn.settext("show login dialog");     btn.setonaction(mevent -> {          // create custom dialog.         dialog<pair<string, string>> dialog = new dialog<>();         dialog.settitle("mars simulation project");         dialog.setheadertext("log in");         dialog.setcontenttext("enter username , password : ");         dialog.initmodality(modality.none);         // set button types.         buttontype loginbuttontype = new buttontype("login", buttondata.ok_done);         dialog.getdialogpane().getbuttontypes().addall(loginbuttontype, buttontype.cancel);          // create username , password labels , fields.         gridpane grid = new gridpane();         grid.sethgap(10);         grid.setvgap(10);         grid.setpadding(new insets(20, 150, 10, 10));          textfield tfplayer = new textfield();         tfplayer.setprompttext("e.g. m03j");         passwordfield tfpassword = new passwordfield();         tfpassword.setprompttext("xxxx");          button defaultpwb = new button("use default");         button guestb = new button("as guest");         defaultpwb.setonaction(event -> {             tfpassword.settext("msp0");         } );          guestb.setonaction(event -> {             tfplayer.settext("guest_");             tfpassword.settext("msp0");         } );          grid.add(new label("player name :"), 0, 0);         grid.add(tfplayer, 1, 0);         grid.add(guestb, 2, 0);         grid.add(new label("password :"), 0, 1);         grid.add(tfpassword, 1, 1);         grid.add(defaultpwb, 2, 1);          // enable/disable login button depending on whether username entered.         node loginbutton = dialog.getdialogpane().lookupbutton(loginbuttontype);         loginbutton.setdisable(true);          // validation (using java 8 lambda syntax).         tfplayer.textproperty().addlistener((observable, oldvalue, newvalue) -> {             loginbutton.setdisable(newvalue.trim().isempty());         } );          dialog.getdialogpane().setcontent(grid);          // request focus on player name field default.         platform.runlater(() -> tfplayer.requestfocus());          dialog.initmodality(modality.application_modal);         dialog.show();                      loginbutton.addeventfilter(eventtype.root,              e->{                 if(e.geteventtype().equals(actionevent.action)){                     e.consume();                     // (hardcoded) login validation                     boolean issuccessful = false;                     if (issuccessful) {                         dialog.close();                     }                     else {                         shaketransition anim = new shaketransition(dialog.getdialogpane(), t->dialog.close());                         anim.playfromstart();                     }                 }             });     });      stackpane root = new stackpane();     root.getchildren().add(btn);      scene scene = new scene(root, 300, 250);      primarystage.settitle("shaky login dialog");     primarystage.setscene(scene);     primarystage.show(); }