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.
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.
??? 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!!!
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 !!!
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 !