JavaFX Custom Control – Nest Thermostat Part 3

Hi,

After some discussions with my colleagues, I decided today to show that the css approach is not the only one that can be used to create a custom control. Of course it allows to offer some look and feel extension point, but the same approach (working with svand transfering to JavaFX) can be used using code API.

Here is the graphic initialization method where I have replaced the css initialization code (now commented out) by call to the JavaFX objects API.

    private void initGraphcis() {
	// frame = new Region();
	// frame.getStyleClass().setAll("frame");
	frame = new Circle();
	frame.setFill(FRAME_FILL);
 
	shadow = new DropShadow();
	shadow.setBlurType(BlurType.ONE_PASS_BOX);
	shadow.setColor(Color.rgb(0, 0, 0, 0.4));
	frame.setEffect(shadow);
 
	frame1 = new Circle();
	// frame1.getStyleClass().setAll("frame1");
	frame1.setFill(FRAME1_FILL);
	frame1.setStroke(FRAME1_STROKE);
	frame1.setStrokeWidth(2.0);
 
	frame2 = new Circle();
	// frame2.getStyleClass().setAll("frame2");
	frame2.setFill(FRAME2_FILL);
	frame2.setStroke(FRAME2_STROKE);
 
	frame3 = new Circle();
	// frame3.getStyleClass().setAll("frame3");
	frame3.setFill(Color.web("#c44f1a"));
 
	line = new SVGPath();
	// line.getStyleClass().setAll("line");
	line.setContent("M 0.75,1.806272 C 0.75,1.806272 67.422114,-2.659598 118.06708,1.085452 130.59357,2.011752 166.81696,11.039202 185.35089,11.189052 206.02921,11.356252 242.24677,2.052122 255.84883,1.085452 304.58057,-2.377808 372.89963,1.806272 372.89963,1.806272");
	line.setFill(Color.web("#ffffff00"));
	line.setStroke(Color.web("#4d4d4d"));
	line.setStrokeWidth(1.5);
 
	line1 = new SVGPath();
	// line.getStyleClass().setAll("line1");
	line1.setContent("M 0.75,1.806272 C 0.75,1.806272 67.422114,-2.659598 118.06708,1.085452 130.59357,2.011752 166.81696,11.039202 185.35089,11.189052 206.02921,11.356252 242.24677,2.052122 255.84883,1.085452 304.58057,-2.377808 372.89963,1.806272 372.89963,1.806272");
	line.setFill(Color.web("#ffffff00"));
	line.setStroke(Color.web("#141414"));
	line.setStrokeWidth(1.5);
 
	lightEffect = new Ellipse();
	lightEffect.setFill(Color.rgb(255, 255, 255, 0.7));
	lightEffect.setEffect(new BoxBlur(90, 90, 5));
	lightEffect.setCache(true);
 
	getChildren().setAll(frame, frame1, frame2, frame3, line, line1, lightEffect );
    }

And the management of the nodes size is as follow (tha same is to be done for the css approach anyway).

    private void resize() {
	size = getWidth() < getHeight() ? getWidth() : getHeight();
	// frame.setPrefSize(size, size);
	frame.setRadius(size / 2.0);
	frame.setTranslateX(size / 2.0);
	frame.setTranslateY(size / 2.0);
 
	frame1.setRadius(frame1Ratio * size / 2.0);
	frame1.setTranslateX(size / 2.0);
	frame1.setTranslateY(size / 2.0);
	shadow.setOffsetX(size * shadowXOffset);
	shadow.setOffsetY(size * shadowYOffset);
	shadow.setRadius(size * shadowSizeOffset);
	shadow.setSpread(0.099);
 
	frame2.setRadius(frame2Ratio * size / 2.0);
	frame2.setTranslateX(size / 2.0);
	frame2.setTranslateY(size / 2.0);
 
	frame3.setRadius(frame3Ratio * size / 2.0);
	frame3.setTranslateX(size / 2.0);
	frame3.setTranslateY(size / 2.0);
 
	final double scaleRatio = size / initialSize;
	line1.setScaleX(scaleRatio);
	line1.setScaleY(scaleRatio);
	final double lineWidth = line1.getBoundsInLocal().getWidth();
	line1.setTranslateX(size / 2.0 - lineWidth / 2.0);
	line1.setTranslateY(size * 408.72054 / initialSize);
 
	line.setScaleX(scaleRatio);
	line.setScaleY(scaleRatio);
	line.setTranslateX(size / 2.0 - lineWidth / 2.0);
	line.setTranslateY(size * 410.08419 / initialSize);
 
	lightEffect.setRotate(lightEffectRotate);
	lightEffect.setTranslateX(lightEffectXRatio * size);
	lightEffect.setTranslateY(lightEffectYRatio * size);
	lightEffect.setRadiusX(lightEffectXRadiusRatio * size);
	lightEffect.setRadiusY(lightEffectYRadiusRatio * size);
    }

Next step will be to mix the two approach to offer both working methods for users. Ones will be able to use the css approach to customize the Nest representation, others will be able to use the good code API approach. It will allow me to see what are the impacts on the code (is it heavy or not to mix both approach) and if I shall make a choice between css and code API before going further to keep my code as simple as possible.

LoNee

JavaFX Custom Control – Nest Thermostat Part 2

I’ts been a while since I started to create the Nest thermostat FX custom control ! So last time, as suggested by Gerrit Grunwald I took some time to reproduce the Nest thermostat design with inkscape, as a first step to build a JavaFX version of it.

Today I’d like to share with you the mistakes I made when trying to create it in JavaFX, and also the final result.

First of all, I started to use the css file in order to make the background, that is composed in my inkscape version of three circle with three linear gradients andtwoa strokes. As suggested by Gerrit I made it by using only one Region and style it using CSS.

.nest{}
 
.nest .frame {
  -fx-background-radius		: 1024px;
  -fx-background-insets		: 1, 4, 20;
  -fx-background-color 		: linear-gradient(from 27.1% 6.5% to 77.35% 91%,  
                                      	#e8e8e8 0%, 
                                      	#c6c6c6 50%, 
                                      	#a6a6a6 100%),
                                  linear-gradient(from 27.1% 6.5% to 77.35% 91%, 
                                      	#fdfdfd 0%,
  					#ededed 3.552646%,
  					#d7d7d7 7.277831%,
  					#d2d2d2 11.973317%,
  					#c7c7c7 18.269639%,
  					#c1c1c1 25.449407%,
  					#b0b0b0 32.21809%,
  					#999999 37.210315%,
  					#868686 43.145844%,
  					#747474 49.577036%,
  					#5c5c5c 55.667913%,
  					#5a5a5a 61.299348%,
  					#5e5e5e 68.340749%,
  					#676767 76.115692%,
  					#706e6f 82.365692%,
  					#838383 88.148153%,
  					#959595 93.637025%,
  					#a8a8a8 100%),
  				linear-gradient(from 27.1% 6.5% to 77.35% 91%, 
                                      	#1c1715 0%, 
                                       	#181818 50%, 
                                       	#3a3a3a 100%); 
  -fx-border-radius		: 1024px;
  -fx-border-insets		: 0, 5, 20;
  -fx-border-width		: 0, 2, 1;
  -fx-border-color	 	: transparent, 
  				  linear-gradient(from 27.1% 6.5% to 77.35% 91%, 
                                    	#d5d5d5 0%, 
                                     	#747474 50%, 
                                     	#8f8f8f 100%), 
                                  #212121;
}

The final result was good, but when I resized my control I had bad result since the -fx-background-insets and -fx-border-width are absolute pixel values. You can see my problem below.

Nest-Mistake1
It took some time to me to figure out how to correct this. At the end the only idea I had was to make three different regions and use the svg path values. It would correct the -fx-background-insets pixel values, but not the stroke ones.

.nest{}
 
.nest .frame {
  -fx-shape			: "m 519.18435,179.4957 a 266.9594,266.9594 0 1 1 -0.72682,-2.0685";
  -fx-background-insets	        : 1;
  -fx-background-color 	        : linear-gradient(from 27.1% 6.5% to 77.35% 91%,  
                                     	#e8e8e8 0%, 
                                      	#c6c6c6 50%, 
                                       	#a6a6a6 100%);
}
 
.nest .frame1 {
  -fx-shape			: "m 514.32688,181.18013 a 261.81818,261.81818 0 1 1 -0.71283,-2.02866";
  -fx-background-color 	        : linear-gradient(from 27.1% 6.5% to 77.35% 91%, 
                                    	#fdfdfd 0%,
					#ededed 3.552646%,
					#d7d7d7 7.277831%,
					#d2d2d2 11.973317%,
					#c7c7c7 18.269639%,
					#c1c1c1 25.449407%,
					#b0b0b0 32.21809%,
					#999999 37.210315%,
					#868686 43.145844%,
					#747474 49.577036%,
					#5c5c5c 55.667913%,
					#5a5a5a 61.299348%,
					#5e5e5e 68.340749%,
					#676767 76.115692%,
					#706e6f 82.365692%,
					#838383 88.148153%,
					#959595 93.637025%,
					#a8a8a8 100%);      						
  -fx-border-width		: 2;
  -fx-border-color	 	: linear-gradient(from 27.1% 6.5% to 77.35% 91%, 
                                      	#d5d5d5 0%, 
                                      	#747474 50%, 
                                       	#8f8f8f 100%);
}
 
.nest .frame2 {
  -fx-shape			: "m 497.45305,187.03163 a 243.95858,243.95858 0 1 1 -0.66421,-1.89028";
  -fx-background-color 	        : linear-gradient(from 27.1% 6.5% to 77.35% 91%, 
                                     	#1c1715 0%, 
                                      	#181818 50%, 
                                      	#3a3a3a 100%); 
  -fx-border-width		: 1;
  -fx-border-color	 	: #212121;
}

Anyway when I tried it, here is the result I had.
nestmistake2.1

 

??? What is this ??? why am I only seeing one circle here… Well I have to say I took me some time to understand it was the fx-scale-shape and fx-position-shape attributes whose default value is true. Here it the definition of this two css attributes

-fx-position-shape <boolean> true If true means the shape centered within the region’s width and height, otherwise the shape is positioned at its source position. Has no effect if a shape string is not specified.
-fx-scale-shape <boolean> true If true means the shape is scaled to fit the size of the region, otherwise the shape is at its source size, and its position depends on the value of the position-shape property. Has no effect if a shape string is not specified.

So I only have to set the scale value to false then…  But when I did this, the shape into the region did not resized correctly when changing my control size… Argh!!!nestmistake2.2

Well, eventually I tried scaling the region and setting its prefSize when the control was resized. So I used the code snippet below :

private void resize() {
   size = getWidth() < getHeight() ? getWidth() : getHeight();
   final double scaleRatio = size / initialSize;
   frame.setPrefSize(size, size);
   frame.setScaleX(scaleRatio);
   frame.setScaleY(scaleRatio);
 
   frame1.setPrefSize(size, size);
   frame1.setScaleX(scaleRatio);
   frame1.setScaleY(scaleRatio);
 
   frame2.setPrefSize(size, size);
   frame2.setScaleX(scaleRatio);
   frame2.setScaleY(scaleRatio);
}

 It worked !!! Nst-Mistake3

Well, I do not know if this is the right way to do it, but it is the only one I found so far… Anyone using other tricks to solve the problems I identified in this post, share it loud on the comment section !!

The ticks and currentCursor and targetCursor are done using my RadialMenuItem from RadialFX. For now It did not allow me to customize it though css, but now that I start to understand how it works, next step is to enhance the RadialFx RadialMenuItem with css attributes.

To conclude here is a little video of the result I have today.

Code will soon be available on JFXtras too !

LoNee

JavaFX Custom Control – Nest Thermostat Part 1

Two weeks ago, I decided to spend some time watching some of the JavaOne talks about JavaFX, thanks to this excellent post from Hendrik Ebbers. I have to say I’ve learned so much already, just watching theses video (even if I have not finished yet) !

Gerrit’s Talk on “Use the Force, Luke” or Tips and Tricks for Using the Capabilities of JavaFX boost me to link my (basic) inkscape and design skill to my JavaFX knowledge. I decided I will take some time trying to create a complete custom JavaFX control as described by Gerrit in its talk using inkscape and CSS. It will be I think a good way to learn CSS :)

Here is the real life object I will try to reproduce in JavaFX. This is a Thermostat created by Nest.

nest

So as described by Gerrit, I first made an Inkscape prototype to understand how the image is built. First import the image in inkscape and then start by the external part of the image and go to the center details. I used a lot the object alignment and distribution as well as the duplicate (Ctrl+d) and Gradient with color picker. On the next image you can see the steps I had and the final image I built.

neststeps

The final result is close from the original even if I did not managed to do some of the effects. For example I had some trouble with the external circle gradient that is not symmetric. So I decided to get the brighter part and make it symmetric ! Here is a full comparison between my inkscape version (left) and the original one (right) :

nestredo

I have to say that importing the gradient by getting the real image in Inscakep and picking color with the color pick tool was really a simple and powerful way to reproduce the light effects. Thanks again to Gerrit for this really instructive talk.

Next step is to import the global shape in JavaFX using CSS mostly, as presented in the Talk.

For those who are interested in making custom control you should really click here.

LoNee

UPDATE 1 : I’ve reworked my inkscape version because something was not right about the light effect, and I’ve found, my external circle gradient was not aligned with the light direction, it was orthogonal with it !!! Here is the new screenshot :)

nestredo2

UPDATE 2 : For those willing to take a look at the inkscape file, here it is !

Leap Motion & JavaFX – Radial Menu – DrawKidFX

Today I’d like to share with you my last try on radial menu. I decided to inspire from the Win 8 radial menu to create a color chooser menu, very simple for now. Here is the original video

http://www.youtube.com/watch?v=UUe56Hb5Z_w

It did not take long to create the simple color chooser menu. I wanted to use it with my new X-mas gift that is a LeapMotion. So I decided to create a drawing program for my kids. Here is the result for now :

The gesture part is the following :

  • One finger pointed and the position along z axis is negative -> drawing 
  • One finger pointed and the position along z axis is positive -> moving cursor
  • One finger pointed and the position along z axis transitioned from positive to negative value -> new drawing line
  • One hand opened (at least 4 fingers) the color chooser menu is displayed
  • One hand opened the hand position is used to move the cursor (and choose a color by going through one)

Some lesson learned when integrating the leap motion with Java(FX) :

  • Do not rely on the LeapMotion threading model. It creates a NEW Thread to notify each frame and it did crash my JVM very often… I hope LeapMotion guys will offer update that change this performance problem
  • Instead, Poll it at the rate you desire, using for example the ExecutorServices API and the created controller API. No crash anymore…
  • Take care to perform the JavaFX update into the JavaFX Thread using Platform.runLater()
  • You can use the AWT Robot to move the mouse
  • Checking the gesture stability is not that easy. I hope LeapMotion guys will offer us update that includes new gesture support !

Code available very soon on github https://github.com/MrLoNee ! in project LeapDrawKidFX.

LoNee

JavaFX – Radial Menu – Movie inspiration

Hi,

A quick post to talk about my last radial menu trial.

This one is a remake of the menu you can see on the top left corner of this movie web site http://www.loopermovie.com/site/

It took me some time to understand how I could make the text at the right place, correctly aligned and looking smooth. If you need some information on this, do not hesitate to contact me. The secret is some trigonometry notions, some transformation matrix skill and a little bit of JavaFX API knowledge :)

I still have some problem with the animation that is not as smooth as on the site. I think I made too many parallel animations. I will have to try to rationalize it see if it improves the smoothness of the menu animations.

Anyway here is the video :

It also allows me to announce the creation of a JavaFX library dedicated to radial menu. It will offer the base radial menu item and many built-in example of radial menu. It shall allow coder to modify for their need the content of the built-in example. Stay in touch to have more information on this.

Next try ? Maybe Windows 8 radial menu…

LoNee