So I was making a UI in JavaFX and was using Regions and setting the shape to an SVG path and style to turn it onto an icon.
SVGPath userIcon = new SVGPath();
userIcon.setContent("M55,27.5C55,12.337,42.663,0,27.5,0S0,12.337,0,27.5c0,8.009,3.444,15.228,8.926,20.258l-0.026,0.023l0.892,0.752c0.058,0.049,0.121,0.089,0.179,0.137c0.474,0.393,0.965,0.766,1.465,1.127c0.162,0.117,0.324,0.234,0.489,0.348c0.534,0.368,1.082,0.717,1.642,1.048c0.122,0.072,0.245,0.142,0.368,0.212c0.613,0.349,1.239,0.678,1.88,0.98c0.047,0.022,0.095,0.042,0.142,0.064c2.089,0.971,4.319,1.684,6.651,2.105c0.061,0.011,0.122,0.022,0.184,0.033c0.724,0.125,1.456,0.225,2.197,0.292c0.09,0.008,0.18,0.013,0.271,0.021C25.998,54.961,26.744,55,27.5,55c0.749,0,1.488-0.039,2.222-0.098c0.093-0.008,0.186-0.013,0.279-0.021c0.735-0.067,1.461-0.164,2.178-0.287c0.062-0.011,0.125-0.022,0.187-0.034c2.297-0.412,4.495-1.109,6.557-2.055c0.076-0.035,0.153-0.068,0.229-0.104c0.617-0.29,1.22-0.603,1.811-0.936c0.147-0.083,0.293-0.167,0.439-0.253c0.538-0.317,1.067-0.648,1.581-1c0.185-0.126,0.366-0.259,0.549-0.391c0.439-0.316,0.87-0.642,1.289-0.983c0.093-0.075,0.193-0.14,0.284-0.217l0.915-0.764l-0.027-0.023C51.523,42.802,55,35.55,55,27.5z M2,27.5C2,13.439,13.439,2,27.5,2S53,13.439,53,27.5c0,7.577-3.325,14.389-8.589,19.063c-0.294-0.203-0.59-0.385-0.893-0.537l-8.467-4.233c-0.76-0.38-1.232-1.144-1.232-1.993v-2.957c0.196-0.242,0.403-0.516,0.617-0.817c1.096-1.548,1.975-3.27,2.616-5.123c1.267-0.602,2.085-1.864,2.085-3.289v-3.545c0-0.867-0.318-1.708-0.887-2.369v-4.667c0.052-0.519,0.236-3.448-1.883-5.864C34.524,9.065,31.541,8,27.5,8s-7.024,1.065-8.867,3.168c-2.119,2.416-1.935,5.345-1.883,5.864v4.667c-0.568,0.661-0.887,1.502-0.887,2.369v3.545c0,1.101,0.494,2.128,1.34,2.821c0.81,3.173,2.477,5.575,3.093,6.389v2.894c0,0.816-0.445,1.566-1.162,1.958l-7.907,4.313c-0.252,0.137-0.502,0.297-0.752,0.476C5.276,41.792,2,35.022,2,27.5z M42.459,48.132c-0.35,0.254-0.706,0.5-1.067,0.735c-0.166,0.108-0.331,0.216-0.5,0.321c-0.472,0.292-0.952,0.57-1.442,0.83c-0.108,0.057-0.217,0.111-0.326,0.167c-1.126,0.577-2.291,1.073-3.488,1.476c-0.042,0.014-0.084,0.029-0.127,0.043c-0.627,0.208-1.262,0.393-1.904,0.552c-0.002,0-0.004,0.001-0.006,0.001c-0.648,0.16-1.304,0.293-1.964,0.402c-0.018,0.003-0.036,0.007-0.054,0.01c-0.621,0.101-1.247,0.174-1.875,0.229c-0.111,0.01-0.222,0.017-0.334,0.025C28.751,52.97,28.127,53,27.5,53c-0.634,0-1.266-0.031-1.895-0.078c-0.109-0.008-0.218-0.015-0.326-0.025c-0.634-0.056-1.265-0.131-1.89-0.233c-0.028-0.005-0.056-0.01-0.084-0.015c-1.322-0.221-2.623-0.546-3.89-0.971c-0.039-0.013-0.079-0.027-0.118-0.04c-0.629-0.214-1.251-0.451-1.862-0.713c-0.004-0.002-0.009-0.004-0.013-0.006c-0.578-0.249-1.145-0.525-1.705-0.816c-0.073-0.038-0.147-0.074-0.219-0.113c-0.511-0.273-1.011-0.568-1.504-0.876c-0.146-0.092-0.291-0.185-0.435-0.279c-0.454-0.297-0.902-0.606-1.338-0.933c-0.045-0.034-0.088-0.07-0.133-0.104c0.032-0.018,0.064-0.036,0.096-0.054l7.907-4.313c1.36-0.742,2.205-2.165,2.205-3.714l-0.001-3.602l-0.23-0.278c-0.022-0.025-2.184-2.655-3.001-6.216l-0.091-0.396l-0.341-0.221c-0.481-0.311-0.769-0.831-0.769-1.392v-3.545c0-0.465,0.197-0.898,0.557-1.223l0.33-0.298v-5.57l-0.009-0.131c-0.003-0.024-0.298-2.429,1.396-4.36C21.583,10.837,24.061,10,27.5,10c3.426,0,5.896,0.83,7.346,2.466c1.692,1.911,1.415,4.361,1.413,4.381l-0.009,5.701l0.33,0.298c0.359,0.324,0.557,0.758,0.557,1.223v3.545c0,0.713-0.485,1.36-1.181,1.575l-0.497,0.153l-0.16,0.495c-0.59,1.833-1.43,3.526-2.496,5.032c-0.262,0.37-0.517,0.698-0.736,0.949l-0.248,0.283V39.8c0,1.612,0.896,3.062,2.338,3.782l8.467,4.233c0.054,0.027,0.107,0.055,0.16,0.083C42.677,47.979,42.567,48.054,42.459,48.132z");
//BookNow Icon
SVGPath bookNowIcon = new SVGPath();
bookNowIcon.setContent("m435.710938 117.226562-6.925782-4 12-20.796874 6.925782 4 12-20.800782c3.1875-5.515625 4.050781-12.074218 2.398437-18.226562-1.648437-6.152344-5.679687-11.394532-11.199219-14.574219l-13.863281-8c-5.515625-3.1875-12.070313-4.050781-18.222656-2.398437-6.152344 1.652343-11.394531 5.679687-14.578125 11.199218l-12 20.800782 6.929687 4-12 20.796874-6.929687-4-12.246094 21.28125v-106.296874h-320c-26.5.027343-47.9726562 21.503906-48 48v352c.0273438 26.5 21.5 47.972656 48 48h320v-213.703126l59.710938-103.421874 13.855468 8-24 41.597656 13.859375 8 32-55.421875zm-6.640626-68.503906 13.859376 8c1.835937 1.0625 3.179687 2.8125 3.730468 4.863282.546875 2.050781.257813 4.234374-.804687 6.074218l-4 6.929688-27.710938-16 4-6.929688c2.207031-3.828125 7.097657-5.144531 10.925781-2.9375zm-14.613281 28.519532 12.472657 7.203124-12 20.796876-13.859376-8 12-20.796876zm-366.457031-61.03125h304v24h-312v16h312v24h-304c-17.671875 0-32-14.324219-32-32 0-17.671876 14.328125-32 32-32zm304 416h-296v-296h-16v294.863281c-14.085938-3.636719-23.945312-16.316407-24-30.863281v-316.320313c6.738281 6.0625 15.0625 10.085937 24 11.601563v24.71875h16v-24h296v38.007812l-116.65625 202.058594-4.289062 71.429687 59.714843-39.429687 61.230469-106.058594zm-101.039062-89.09375 26.078124 15.054687-28.09375 18.542969zm34.894531 1.664062-6.925781-4 108-187.058594-13.859376-8-108 187.058594-3.460937-2-3.464844-2 128-221.695312 27.710938 16zm0 0");
//Records Icon
SVGPath recordsIcon = new SVGPath();
recordsIcon.setContent("m72.808594 48h-64.960938c-4.332031.003906-7.84374975 3.515625-7.847656 7.847656v384.304688c.00390625 4.332031 3.515625 7.84375 7.847656 7.847656h288.3125c4.332032-.003906 7.84375-3.515625 7.847656-7.847656v-384.304688c-.003906-4.332031-3.515624-7.84375-7.847656-7.847656h-64.953125c-3.828125 18.613281-20.199219 31.976562-39.199219 32h-80c-19-.023438-35.371093-13.386719-39.199218-32zm215.199218 360h-272v-16h272zm0-32h-272v-16h272zm0-32h-272v-16h272zm0-32h-272v-16h272zm0-32h-272v-16h272zm-183.160156-175.960938c3.742188.394532 6.699219 3.34375 7.113282 7.082032l5.382812 48.4375 11.808594-19.679688c1.640625-2.726562 4.746094-4.222656 7.902344-3.804687s5.761718 2.664062 6.640624 5.726562l8.90625 31.136719 15.8125-47.464844c1.023438-3.074218 3.796876-5.230468 7.027344-5.460937 3.230469-.226563 6.28125 1.511719 7.726563 4.414062l13.785156 27.574219h91.054687v16h-96c-3.046874.015625-5.839843-1.699219-7.199218-4.425781l-7.359375-14.726563-17.890625 53.679688c-1.136719 3.304687-4.261719 5.511718-7.757813 5.472656-3.507812-.074219-6.558593-2.425781-7.519531-5.800781l-10.9375-38.269531-14.519531 24.191406c-1.769531 2.941406-5.214844 4.425781-8.566407 3.691406-3.351562-.730469-5.863281-3.523438-6.242187-6.933594l-4-35.789062-20.480469 61.4375c-1.050781 3.152344-3.9375 5.324218-7.253906 5.464844h-.273438c-3.199218 0-6.089843-1.90625-7.351562-4.847657l-21.921875-51.144531h-26.726563v-16h32c3.199219 0 6.089844 1.90625 7.351563 4.847656l15.710937 36.65625 25.34375-76c1.195313-3.5625 4.695313-5.832031 8.433594-5.464844zm0 0");
...More like that...
//Set Region Shapes
profileSVGRegion.setShape(userIcon);
bookNowSVGRegion.setShape(bookNowIcon);
recordsSVGRegion.setShape(recordsIcon);
...More like that...
The problem is that it was too recursive(had like 15 icons) and I decided to write a function to do it,
Region createRegionIcon(String svgData, String color){
Region svgRegion = new Region();
SVGPath icon = new SVGPath();
icon.setContent(svgData);
svgRegion.setShape(icon);
svgRegion.setStyle("-fx-background-color: " + color);
return svgRegion;
}
but it just wasn't doing what I expected. When I set the regions equal to the function, nothing would happen.
addressSVGRegion = createRegionIcon("Some random SVGPath data", "#123456");
You have to set the size of the ´Region´. Try modifying your method to:
private final double width = 50;
private final double height = 50;
private static Region createRegionIcon(String svgData, String color) {
SVGPath icon = new SVGPath();
icon.setContent(svgData);
Region svgRegion = new Region();
svgRegion.setShape(icon);
svgRegion.setMinSize(width, height);
svgRegion.setPrefSize(width, height);
svgRegion.setMaxSize(width, height);
svgRegion.setStyle("-fx-background-color: " + color);
return svgRegion;
}
Working example:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.shape.SVGPath;
import javafx.stage.Stage;
public class App extends Application {
private static final double WIDTH = 50;
private static final double HEIGHT = 50;
private final String svgData1 = "M55,27.5C55,12.337,42.663,0,27.5,0S0,12.337,0,27.5c0,8.009,3.444,15.228,8.926,20.258l-0.026,0.023l0.892,0.752c0.058,0.049,0.121,0.089,0.179,0.137c0.474,0.393,0.965,0.766,1.465,1.127c0.162,0.117,0.324,0.234,0.489,0.348c0.534,0.368,1.082,0.717,1.642,1.048c0.122,0.072,0.245,0.142,0.368,0.212c0.613,0.349,1.239,0.678,1.88,0.98c0.047,0.022,0.095,0.042,0.142,0.064c2.089,0.971,4.319,1.684,6.651,2.105c0.061,0.011,0.122,0.022,0.184,0.033c0.724,0.125,1.456,0.225,2.197,0.292c0.09,0.008,0.18,0.013,0.271,0.021C25.998,54.961,26.744,55,27.5,55c0.749,0,1.488-0.039,2.222-0.098c0.093-0.008,0.186-0.013,0.279-0.021c0.735-0.067,1.461-0.164,2.178-0.287c0.062-0.011,0.125-0.022,0.187-0.034c2.297-0.412,4.495-1.109,6.557-2.055c0.076-0.035,0.153-0.068,0.229-0.104c0.617-0.29,1.22-0.603,1.811-0.936c0.147-0.083,0.293-0.167,0.439-0.253c0.538-0.317,1.067-0.648,1.581-1c0.185-0.126,0.366-0.259,0.549-0.391c0.439-0.316,0.87-0.642,1.289-0.983c0.093-0.075,0.193-0.14,0.284-0.217l0.915-0.764l-0.027-0.023C51.523,42.802,55,35.55,55,27.5z M2,27.5C2,13.439,13.439,2,27.5,2S53,13.439,53,27.5c0,7.577-3.325,14.389-8.589,19.063c-0.294-0.203-0.59-0.385-0.893-0.537l-8.467-4.233c-0.76-0.38-1.232-1.144-1.232-1.993v-2.957c0.196-0.242,0.403-0.516,0.617-0.817c1.096-1.548,1.975-3.27,2.616-5.123c1.267-0.602,2.085-1.864,2.085-3.289v-3.545c0-0.867-0.318-1.708-0.887-2.369v-4.667c0.052-0.519,0.236-3.448-1.883-5.864C34.524,9.065,31.541,8,27.5,8s-7.024,1.065-8.867,3.168c-2.119,2.416-1.935,5.345-1.883,5.864v4.667c-0.568,0.661-0.887,1.502-0.887,2.369v3.545c0,1.101,0.494,2.128,1.34,2.821c0.81,3.173,2.477,5.575,3.093,6.389v2.894c0,0.816-0.445,1.566-1.162,1.958l-7.907,4.313c-0.252,0.137-0.502,0.297-0.752,0.476C5.276,41.792,2,35.022,2,27.5z M42.459,48.132c-0.35,0.254-0.706,0.5-1.067,0.735c-0.166,0.108-0.331,0.216-0.5,0.321c-0.472,0.292-0.952,0.57-1.442,0.83c-0.108,0.057-0.217,0.111-0.326,0.167c-1.126,0.577-2.291,1.073-3.488,1.476c-0.042,0.014-0.084,0.029-0.127,0.043c-0.627,0.208-1.262,0.393-1.904,0.552c-0.002,0-0.004,0.001-0.006,0.001c-0.648,0.16-1.304,0.293-1.964,0.402c-0.018,0.003-0.036,0.007-0.054,0.01c-0.621,0.101-1.247,0.174-1.875,0.229c-0.111,0.01-0.222,0.017-0.334,0.025C28.751,52.97,28.127,53,27.5,53c-0.634,0-1.266-0.031-1.895-0.078c-0.109-0.008-0.218-0.015-0.326-0.025c-0.634-0.056-1.265-0.131-1.89-0.233c-0.028-0.005-0.056-0.01-0.084-0.015c-1.322-0.221-2.623-0.546-3.89-0.971c-0.039-0.013-0.079-0.027-0.118-0.04c-0.629-0.214-1.251-0.451-1.862-0.713c-0.004-0.002-0.009-0.004-0.013-0.006c-0.578-0.249-1.145-0.525-1.705-0.816c-0.073-0.038-0.147-0.074-0.219-0.113c-0.511-0.273-1.011-0.568-1.504-0.876c-0.146-0.092-0.291-0.185-0.435-0.279c-0.454-0.297-0.902-0.606-1.338-0.933c-0.045-0.034-0.088-0.07-0.133-0.104c0.032-0.018,0.064-0.036,0.096-0.054l7.907-4.313c1.36-0.742,2.205-2.165,2.205-3.714l-0.001-3.602l-0.23-0.278c-0.022-0.025-2.184-2.655-3.001-6.216l-0.091-0.396l-0.341-0.221c-0.481-0.311-0.769-0.831-0.769-1.392v-3.545c0-0.465,0.197-0.898,0.557-1.223l0.33-0.298v-5.57l-0.009-0.131c-0.003-0.024-0.298-2.429,1.396-4.36C21.583,10.837,24.061,10,27.5,10c3.426,0,5.896,0.83,7.346,2.466c1.692,1.911,1.415,4.361,1.413,4.381l-0.009,5.701l0.33,0.298c0.359,0.324,0.557,0.758,0.557,1.223v3.545c0,0.713-0.485,1.36-1.181,1.575l-0.497,0.153l-0.16,0.495c-0.59,1.833-1.43,3.526-2.496,5.032c-0.262,0.37-0.517,0.698-0.736,0.949l-0.248,0.283V39.8c0,1.612,0.896,3.062,2.338,3.782l8.467,4.233c0.054,0.027,0.107,0.055,0.16,0.083C42.677,47.979,42.567,48.054,42.459,48.132z";
private final String svgData2 = "m435.710938 117.226562-6.925782-4 12-20.796874 6.925782 4 12-20.800782c3.1875-5.515625 4.050781-12.074218 2.398437-18.226562-1.648437-6.152344-5.679687-11.394532-11.199219-14.574219l-13.863281-8c-5.515625-3.1875-12.070313-4.050781-18.222656-2.398437-6.152344 1.652343-11.394531 5.679687-14.578125 11.199218l-12 20.800782 6.929687 4-12 20.796874-6.929687-4-12.246094 21.28125v-106.296874h-320c-26.5.027343-47.9726562 21.503906-48 48v352c.0273438 26.5 21.5 47.972656 48 48h320v-213.703126l59.710938-103.421874 13.855468 8-24 41.597656 13.859375 8 32-55.421875zm-6.640626-68.503906 13.859376 8c1.835937 1.0625 3.179687 2.8125 3.730468 4.863282.546875 2.050781.257813 4.234374-.804687 6.074218l-4 6.929688-27.710938-16 4-6.929688c2.207031-3.828125 7.097657-5.144531 10.925781-2.9375zm-14.613281 28.519532 12.472657 7.203124-12 20.796876-13.859376-8 12-20.796876zm-366.457031-61.03125h304v24h-312v16h312v24h-304c-17.671875 0-32-14.324219-32-32 0-17.671876 14.328125-32 32-32zm304 416h-296v-296h-16v294.863281c-14.085938-3.636719-23.945312-16.316407-24-30.863281v-316.320313c6.738281 6.0625 15.0625 10.085937 24 11.601563v24.71875h16v-24h296v38.007812l-116.65625 202.058594-4.289062 71.429687 59.714843-39.429687 61.230469-106.058594zm-101.039062-89.09375 26.078124 15.054687-28.09375 18.542969zm34.894531 1.664062-6.925781-4 108-187.058594-13.859376-8-108 187.058594-3.460937-2-3.464844-2 128-221.695312 27.710938 16zm0 0";
private final String svgData3 = "m72.808594 48h-64.960938c-4.332031.003906-7.84374975 3.515625-7.847656 7.847656v384.304688c.00390625 4.332031 3.515625 7.84375 7.847656 7.847656h288.3125c4.332032-.003906 7.84375-3.515625 7.847656-7.847656v-384.304688c-.003906-4.332031-3.515624-7.84375-7.847656-7.847656h-64.953125c-3.828125 18.613281-20.199219 31.976562-39.199219 32h-80c-19-.023438-35.371093-13.386719-39.199218-32zm215.199218 360h-272v-16h272zm0-32h-272v-16h272zm0-32h-272v-16h272zm0-32h-272v-16h272zm0-32h-272v-16h272zm-183.160156-175.960938c3.742188.394532 6.699219 3.34375 7.113282 7.082032l5.382812 48.4375 11.808594-19.679688c1.640625-2.726562 4.746094-4.222656 7.902344-3.804687s5.761718 2.664062 6.640624 5.726562l8.90625 31.136719 15.8125-47.464844c1.023438-3.074218 3.796876-5.230468 7.027344-5.460937 3.230469-.226563 6.28125 1.511719 7.726563 4.414062l13.785156 27.574219h91.054687v16h-96c-3.046874.015625-5.839843-1.699219-7.199218-4.425781l-7.359375-14.726563-17.890625 53.679688c-1.136719 3.304687-4.261719 5.511718-7.757813 5.472656-3.507812-.074219-6.558593-2.425781-7.519531-5.800781l-10.9375-38.269531-14.519531 24.191406c-1.769531 2.941406-5.214844 4.425781-8.566407 3.691406-3.351562-.730469-5.863281-3.523438-6.242187-6.933594l-4-35.789062-20.480469 61.4375c-1.050781 3.152344-3.9375 5.324218-7.253906 5.464844h-.273438c-3.199218 0-6.089843-1.90625-7.351562-4.847657l-21.921875-51.144531h-26.726563v-16h32c3.199219 0 6.089844 1.90625 7.351563 4.847656l15.710937 36.65625 25.34375-76c1.195313-3.5625 4.695313-5.832031 8.433594-5.464844zm0 0";
#Override
public void start(Stage stage) {
Region svgShape1 = createRegionIcon(svgData1, "blue");
Region svgShape2 = createRegionIcon(svgData2, "red");
Region svgShape3 = createRegionIcon(svgData3, "green");
HBox pane = new HBox();
pane.setAlignment(Pos.CENTER);
pane.setSpacing(10);
pane.setStyle("-fx-background-color: white;");
pane.getChildren().addAll(svgShape1, svgShape2, svgShape3);
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.show();
}
private static Region createRegionIcon(String svgData, String color) {
SVGPath icon = new SVGPath();
icon.setContent(svgData);
Region svgRegion = new Region();
svgRegion.setShape(icon);
svgRegion.setMinSize(WIDTH, HEIGHT);
svgRegion.setPrefSize(WIDTH, HEIGHT);
svgRegion.setMaxSize(WIDTH, HEIGHT);
svgRegion.setStyle("-fx-background-color: " + color);
return svgRegion;
}
public static void main(String[] args) {
launch();
}
}
Output:
Regions can be shaped in CSS using the -fx-shape CSS property
Using this technique, the explicit creation of an SVGPath is not necessary.
See moderna.css in the JavaFX source for many examples of applying shapes to regions.
If you wanted two regions to have the same shape, you could do that by applying the same style class to different instances of Region.
The shaped regions can be colored by setting the -fx-background-color.
The shaped regions won't be seen unless they have a non-zero size. The region size can be managed via min/max/pref height/width settings (either in java code, FXML, or CSS). These settings provide instructions to the layout pane enclosing the region on how to size that region.
Here is an example of using -fx-shape to shape regions. The code is based on that in Oboe's answer, just adapted to use an external style sheet and slightly different coloring.
region-styling.css
.layout-pane {
-fx-spacing: 10;
-fx-background-color: white;
}
.icon {
-fx-min-width: 50;
-fx-pref-width: 50;
-fx-max-width: 50;
-fx-min-height: 50;
-fx-pref-height: 50;
-fx-max-height: 50;
}
.user-icon {
-fx-shape: "M55,27.5C55,12.337,42.663,0,27.5,0S0,12.337,0,27.5c0,8.009,3.444,15.228,8.926,20.258l-0.026,0.023l0.892,0.752c0.058,0.049,0.121,0.089,0.179,0.137c0.474,0.393,0.965,0.766,1.465,1.127c0.162,0.117,0.324,0.234,0.489,0.348c0.534,0.368,1.082,0.717,1.642,1.048c0.122,0.072,0.245,0.142,0.368,0.212c0.613,0.349,1.239,0.678,1.88,0.98c0.047,0.022,0.095,0.042,0.142,0.064c2.089,0.971,4.319,1.684,6.651,2.105c0.061,0.011,0.122,0.022,0.184,0.033c0.724,0.125,1.456,0.225,2.197,0.292c0.09,0.008,0.18,0.013,0.271,0.021C25.998,54.961,26.744,55,27.5,55c0.749,0,1.488-0.039,2.222-0.098c0.093-0.008,0.186-0.013,0.279-0.021c0.735-0.067,1.461-0.164,2.178-0.287c0.062-0.011,0.125-0.022,0.187-0.034c2.297-0.412,4.495-1.109,6.557-2.055c0.076-0.035,0.153-0.068,0.229-0.104c0.617-0.29,1.22-0.603,1.811-0.936c0.147-0.083,0.293-0.167,0.439-0.253c0.538-0.317,1.067-0.648,1.581-1c0.185-0.126,0.366-0.259,0.549-0.391c0.439-0.316,0.87-0.642,1.289-0.983c0.093-0.075,0.193-0.14,0.284-0.217l0.915-0.764l-0.027-0.023C51.523,42.802,55,35.55,55,27.5z M2,27.5C2,13.439,13.439,2,27.5,2S53,13.439,53,27.5c0,7.577-3.325,14.389-8.589,19.063c-0.294-0.203-0.59-0.385-0.893-0.537l-8.467-4.233c-0.76-0.38-1.232-1.144-1.232-1.993v-2.957c0.196-0.242,0.403-0.516,0.617-0.817c1.096-1.548,1.975-3.27,2.616-5.123c1.267-0.602,2.085-1.864,2.085-3.289v-3.545c0-0.867-0.318-1.708-0.887-2.369v-4.667c0.052-0.519,0.236-3.448-1.883-5.864C34.524,9.065,31.541,8,27.5,8s-7.024,1.065-8.867,3.168c-2.119,2.416-1.935,5.345-1.883,5.864v4.667c-0.568,0.661-0.887,1.502-0.887,2.369v3.545c0,1.101,0.494,2.128,1.34,2.821c0.81,3.173,2.477,5.575,3.093,6.389v2.894c0,0.816-0.445,1.566-1.162,1.958l-7.907,4.313c-0.252,0.137-0.502,0.297-0.752,0.476C5.276,41.792,2,35.022,2,27.5z M42.459,48.132c-0.35,0.254-0.706,0.5-1.067,0.735c-0.166,0.108-0.331,0.216-0.5,0.321c-0.472,0.292-0.952,0.57-1.442,0.83c-0.108,0.057-0.217,0.111-0.326,0.167c-1.126,0.577-2.291,1.073-3.488,1.476c-0.042,0.014-0.084,0.029-0.127,0.043c-0.627,0.208-1.262,0.393-1.904,0.552c-0.002,0-0.004,0.001-0.006,0.001c-0.648,0.16-1.304,0.293-1.964,0.402c-0.018,0.003-0.036,0.007-0.054,0.01c-0.621,0.101-1.247,0.174-1.875,0.229c-0.111,0.01-0.222,0.017-0.334,0.025C28.751,52.97,28.127,53,27.5,53c-0.634,0-1.266-0.031-1.895-0.078c-0.109-0.008-0.218-0.015-0.326-0.025c-0.634-0.056-1.265-0.131-1.89-0.233c-0.028-0.005-0.056-0.01-0.084-0.015c-1.322-0.221-2.623-0.546-3.89-0.971c-0.039-0.013-0.079-0.027-0.118-0.04c-0.629-0.214-1.251-0.451-1.862-0.713c-0.004-0.002-0.009-0.004-0.013-0.006c-0.578-0.249-1.145-0.525-1.705-0.816c-0.073-0.038-0.147-0.074-0.219-0.113c-0.511-0.273-1.011-0.568-1.504-0.876c-0.146-0.092-0.291-0.185-0.435-0.279c-0.454-0.297-0.902-0.606-1.338-0.933c-0.045-0.034-0.088-0.07-0.133-0.104c0.032-0.018,0.064-0.036,0.096-0.054l7.907-4.313c1.36-0.742,2.205-2.165,2.205-3.714l-0.001-3.602l-0.23-0.278c-0.022-0.025-2.184-2.655-3.001-6.216l-0.091-0.396l-0.341-0.221c-0.481-0.311-0.769-0.831-0.769-1.392v-3.545c0-0.465,0.197-0.898,0.557-1.223l0.33-0.298v-5.57l-0.009-0.131c-0.003-0.024-0.298-2.429,1.396-4.36C21.583,10.837,24.061,10,27.5,10c3.426,0,5.896,0.83,7.346,2.466c1.692,1.911,1.415,4.361,1.413,4.381l-0.009,5.701l0.33,0.298c0.359,0.324,0.557,0.758,0.557,1.223v3.545c0,0.713-0.485,1.36-1.181,1.575l-0.497,0.153l-0.16,0.495c-0.59,1.833-1.43,3.526-2.496,5.032c-0.262,0.37-0.517,0.698-0.736,0.949l-0.248,0.283V39.8c0,1.612,0.896,3.062,2.338,3.782l8.467,4.233c0.054,0.027,0.107,0.055,0.16,0.083C42.677,47.979,42.567,48.054,42.459,48.132z";
-fx-background-color: blue;
}
.notepad-icon {
-fx-shape: "m435.710938 117.226562-6.925782-4 12-20.796874 6.925782 4 12-20.800782c3.1875-5.515625 4.050781-12.074218 2.398437-18.226562-1.648437-6.152344-5.679687-11.394532-11.199219-14.574219l-13.863281-8c-5.515625-3.1875-12.070313-4.050781-18.222656-2.398437-6.152344 1.652343-11.394531 5.679687-14.578125 11.199218l-12 20.800782 6.929687 4-12 20.796874-6.929687-4-12.246094 21.28125v-106.296874h-320c-26.5.027343-47.9726562 21.503906-48 48v352c.0273438 26.5 21.5 47.972656 48 48h320v-213.703126l59.710938-103.421874 13.855468 8-24 41.597656 13.859375 8 32-55.421875zm-6.640626-68.503906 13.859376 8c1.835937 1.0625 3.179687 2.8125 3.730468 4.863282.546875 2.050781.257813 4.234374-.804687 6.074218l-4 6.929688-27.710938-16 4-6.929688c2.207031-3.828125 7.097657-5.144531 10.925781-2.9375zm-14.613281 28.519532 12.472657 7.203124-12 20.796876-13.859376-8 12-20.796876zm-366.457031-61.03125h304v24h-312v16h312v24h-304c-17.671875 0-32-14.324219-32-32 0-17.671876 14.328125-32 32-32zm304 416h-296v-296h-16v294.863281c-14.085938-3.636719-23.945312-16.316407-24-30.863281v-316.320313c6.738281 6.0625 15.0625 10.085937 24 11.601563v24.71875h16v-24h296v38.007812l-116.65625 202.058594-4.289062 71.429687 59.714843-39.429687 61.230469-106.058594zm-101.039062-89.09375 26.078124 15.054687-28.09375 18.542969zm34.894531 1.664062-6.925781-4 108-187.058594-13.859376-8-108 187.058594-3.460937-2-3.464844-2 128-221.695312 27.710938 16zm0 0";
-fx-background-color: green;
}
.cardiac-arrest-icon {
-fx-shape: "m72.808594 48h-64.960938c-4.332031.003906-7.84374975 3.515625-7.847656 7.847656v384.304688c.00390625 4.332031 3.515625 7.84375 7.847656 7.847656h288.3125c4.332032-.003906 7.84375-3.515625 7.847656-7.847656v-384.304688c-.003906-4.332031-3.515624-7.84375-7.847656-7.847656h-64.953125c-3.828125 18.613281-20.199219 31.976562-39.199219 32h-80c-19-.023438-35.371093-13.386719-39.199218-32zm215.199218 360h-272v-16h272zm0-32h-272v-16h272zm0-32h-272v-16h272zm0-32h-272v-16h272zm0-32h-272v-16h272zm-183.160156-175.960938c3.742188.394532 6.699219 3.34375 7.113282 7.082032l5.382812 48.4375 11.808594-19.679688c1.640625-2.726562 4.746094-4.222656 7.902344-3.804687s5.761718 2.664062 6.640624 5.726562l8.90625 31.136719 15.8125-47.464844c1.023438-3.074218 3.796876-5.230468 7.027344-5.460937 3.230469-.226563 6.28125 1.511719 7.726563 4.414062l13.785156 27.574219h91.054687v16h-96c-3.046874.015625-5.839843-1.699219-7.199218-4.425781l-7.359375-14.726563-17.890625 53.679688c-1.136719 3.304687-4.261719 5.511718-7.757813 5.472656-3.507812-.074219-6.558593-2.425781-7.519531-5.800781l-10.9375-38.269531-14.519531 24.191406c-1.769531 2.941406-5.214844 4.425781-8.566407 3.691406-3.351562-.730469-5.863281-3.523438-6.242187-6.933594l-4-35.789062-20.480469 61.4375c-1.050781 3.152344-3.9375 5.324218-7.253906 5.464844h-.273438c-3.199218 0-6.089843-1.90625-7.351562-4.847657l-21.921875-51.144531h-26.726563v-16h32c3.199219 0 6.089844 1.90625 7.351563 4.847656l15.710937 36.65625 25.34375-76c1.195313-3.5625 4.695313-5.832031 8.433594-5.464844zm0 0";
-fx-background-color: red;
}
I set some items like height and width in the CSS.
If using FXML for layout, you may prefer to set the layout info like height and width there. If you do so, you could set the min and max values for the width and height all to -Infinity. This value is a convention in JavaFX for a sentinel value that uses the preferred width for other values. Such a setting is not possible with CSS, so, here I just set those values explicitly to the preferred size values.
RegionStyling.java
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.stage.Stage;
public class RegionStyling extends Application {
#Override
public void start(Stage stage) {
HBox layout = new HBox();
layout.setAlignment(Pos.CENTER);
layout.getStyleClass().add("layout-pane");
layout.getChildren().addAll(
createIcon("user-icon"),
createIcon("notepad-icon"),
createIcon("cardiac-arrest-icon")
);
Scene scene = new Scene(layout);
scene.getStylesheets().add(
RegionStyling.class.getResource(
"region-styling.css"
).toExternalForm()
);
stage.setScene(scene);
stage.show();
}
private static Region createIcon(String styleClass) {
Region icon = new Region();
icon.getStyleClass().addAll("icon", styleClass);
return icon;
}
public static void main(String[] args) {
launch();
}
}
Related
I've managed to move the camera using mouse drag. However, the problem is that once I've moved the camera and released the mouse press, the camera returns back to the origin.
I want the camera to remain in the changed position instead so that when I use the mouse again to move the camera, it moves from that position to wherever instead of from the origin.
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Sphere;
public class Main extends Application {
private static final int WIDTH = 900;
private static final int HEIGHT = 600;
//Tracks drag starting point for x and y
private double anchorX, anchorY;
#Override
public void start(Stage primaryStage) {
Sphere sphere = new Sphere(50);
Group group = new Group();
group.getChildren().add(sphere);
Camera camera = new PerspectiveCamera();
Scene scene = new Scene(group, WIDTH, HEIGHT);
scene.setFill(Color.SILVER);
scene.setCamera(camera);
sphere.setTranslateX(WIDTH / 2);
sphere.setTranslateY(HEIGHT / 2);
initMouseControl(scene, camera, primaryStage, sphere);
primaryStage.setTitle("Solar System Simulator");
primaryStage.setScene(scene);
primaryStage.show();
}
private void initMouseControl(Scene scene, Camera camera, Stage stage, Sphere sphere) {
scene.setOnMousePressed(event -> {
//Save start points
anchorX = event.getSceneX();
anchorY = event.getSceneY();
});
scene.setOnMouseDragged(event -> {
camera.setTranslateY(anchorY - event.getSceneY());
camera.setTranslateX(anchorX - event.getSceneX());
});
stage.addEventHandler(ScrollEvent.SCROLL, event -> {
sphere.setTranslateZ(sphere.getTranslateZ() + event.getDeltaY());
});
}
public static void main(String[] args) {
launch(args);
}
}
I've tried googling to find a solution but couldn't find much on camera movement with javafx
First of all, your scene does not have depth buffer enabled . These constructors enable 3d features in a scene instance Scene(Parent root,double width,double height,boolean depthBuffer) and Scene(Parent root,double width,double height,boolean depthBuffer,SceneAntialiasing antiAliasing) both with depthBufer boolean set to true. Second ,if you want to translate a node ; translate it getting its current coordinates and then add new coords . that will avoid reset coords . I divided the dragged result by 10 because it brought too high values
public class Main extends Application {
private static final int WIDTH = 900;
private static final int HEIGHT = 600;
//Tracks drag starting point for x and y
private double anchorX, anchorY;
#Override
public void start(Stage primaryStage) {
Sphere sphere = new Sphere(50);
Group group = new Group();
group.getChildren().add(sphere);
Camera camera = new PerspectiveCamera();
// for 3d Scene constructor must have zbuffer= true and SceneAntialiasing
Scene scene = new Scene(group, WIDTH, HEIGHT, true, SceneAntialiasing.BALANCED);
scene.setFill(Color.SILVER);
scene.setCamera(camera);
sphere.setTranslateX(WIDTH / 2);
sphere.setTranslateY(HEIGHT / 2);
initMouseControl(scene, camera, primaryStage, sphere);
primaryStage.setTitle("Solar System Simulator");
primaryStage.setScene(scene);
primaryStage.show();
}
private void initMouseControl(Scene scene, Camera camera, Stage stage, Sphere sphere) {
scene.setOnMousePressed(event -> {
//Save start points
anchorX = event.getSceneX();
anchorY = event.getSceneY();
});
// translating camera from its current coords pluss event
scene.setOnMouseDragged(event -> {
camera.setTranslateY(camera.getTranslateY() + ((anchorY - event.getSceneY()) / 10));
camera.setTranslateX(camera.getTranslateX() + ((anchorX - event.getSceneX()) / 10));
});
stage.addEventHandler(ScrollEvent.SCROLL, event -> {
sphere.setTranslateZ(sphere.getTranslateZ() + event.getDeltaY());
});
}
public static void main(String[] args) {
launch(args);
}
}
I don't know in how far Google can help you fix your logic problems. I did no further analysis of your code but at least each time when you press the mouse button your camera position is reset to zero by design. Your changes are not cumulative.
Okay, this is driving me crazy. The documentation is pretty weak, the example application from Oracle is very weird, with a huge convoluted helper class, and even the questions about it on here have no answers!
I've largely followed and simplified this tutorial, but instead of rotating the object, I'm trying to rotate the camera, so when you drag the mouse, it should orbit the camera.
However, though I have confirmed via console logs and debugging that the event handlers are being called, and everything seems to have the right values, my rotates just never happen! What am I missing?
Furthermore, I can't manage to move the camera at all, even the (commented out) translateX and the like don't work either, so I am quite stumped, but can't get the axis to look like anywhere but the upper left corner!
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.transform.Rotate;
public class RotateCameraExample extends Group {
private double anchorX, anchorY;
private double anchorAngleX = 0;
private double anchorAngleY = 0;
private DoubleProperty angleX = new SimpleDoubleProperty(0);
private DoubleProperty angleY = new SimpleDoubleProperty(0);
Camera camera;
Group axes;
public RotateCameraExample() {
axes = buildAxes();
getChildren().add(axes);
camera = new PerspectiveCamera(true);
camera.setFarClip(6000);
camera.setNearClip(0.01);
//camera.translateYProperty().set(300); // this doesn't do anything! why?
getChildren().add(camera);
initMouseControl();
}
private void initMouseControl() {
Rotate xRotate = new Rotate(0, Rotate.X_AXIS);
Rotate yRotate = new Rotate(0, Rotate.Y_AXIS);
camera.getTransforms().addAll(xRotate, yRotate);
xRotate.angleProperty().bind(angleX);
yRotate.angleProperty().bind(angleY);
setOnMousePressed(event -> {
anchorX = event.getSceneX();
anchorY = event.getSceneY();
anchorAngleX = angleX.get();
anchorAngleY = angleY.get();
});
setOnMouseDragged(event -> {
angleX.set(anchorAngleX - (anchorY - event.getSceneY()));
angleY.set(anchorAngleY + anchorX - event.getSceneX());
});
}
private Group buildAxes() {
final Box xAxis = new Box(1200, 10, 10);
final Box yAxis = new Box(10, 1200, 10);
final Box zAxis = new Box(10, 10, 1200);
xAxis.setMaterial(new PhongMaterial(Color.RED));
yAxis.setMaterial(new PhongMaterial(Color.GREEN));
zAxis.setMaterial(new PhongMaterial(Color.BLUE));
Group axisGroup = new Group();
axisGroup.getChildren().addAll(xAxis, yAxis, zAxis);
return axisGroup;
}
}
Here can see that the axis is visible in the upper left, and I want it to remain at (0, 0, 0) while moving the camera around it.
Here is the Application launch code, which is clearly not the issue:
public class TestApp extends Application {
#Override
public void start(Stage stage) throws IOException {
RotateCameraExample g = new RotateCameraExample();
Scene scene = new Scene(g, 800, 800, Color.BLACK);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Instead of adding the camera to the children of the Group,
getChildren().add(camera);
You should set the scene's camera.
scene.setCamera(g.camera);
You will immediately see the axes at the center of the screen. Similarly, mouse handler(s) should be applied to the scene. You can then update the group's transforms in your scene's handler(s).
As an example, the variation below alters the camera's rotation in response to mouse scroll events. Note how the vertical mouse scroll affects rotation about the X axis, while horizontal mouse scroll affects rotation about the Y axis. The same gestures also translate the group as a whole. An assortment of keyboard commands enable one to rotate the camera around the Z axis, dolly along the Z axis, and reset the scene.
You can translate and rotate about points on a circle, as illustrated here; in contrast, this related example animates the rotation of an object about a pivot.
import javafx.application.Application;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/a/69260181/230513
*/
public class RotateCameraExample extends Application {
private static class RotateCamera extends Group {
private final Camera camera;
private final Rotate xRotate = new Rotate(0, Rotate.X_AXIS);
private final Rotate yRotate = new Rotate(0, Rotate.Y_AXIS);
private final Rotate zRotate = new Rotate(0, Rotate.Z_AXIS);
public RotateCamera() {
buildAxes();
camera = new PerspectiveCamera(true);
camera.setFarClip(6000);
camera.setNearClip(0.01);
camera.setTranslateZ(-2000);
camera.getTransforms().addAll(xRotate, yRotate, zRotate);
}
private void buildAxes() {
final Box xAxis = new Box(1200, 10, 10);
final Box yAxis = new Box(10, 1200, 10);
final Box zAxis = new Box(10, 10, 1200);
xAxis.setMaterial(new PhongMaterial(Color.RED));
yAxis.setMaterial(new PhongMaterial(Color.GREEN));
zAxis.setMaterial(new PhongMaterial(Color.BLUE));
Group axisGroup = new Group();
axisGroup.getChildren().addAll(xAxis, yAxis, zAxis);
this.getChildren().add(axisGroup);
}
}
#Override
public void start(Stage stage) {
RotateCamera g = new RotateCamera();
Scene scene = new Scene(g, 800, 800, Color.BLACK);
scene.setCamera(g.camera);
stage.setScene(scene);
stage.show();
scene.setOnScroll((final ScrollEvent e) -> {
g.xRotate.setAngle(g.xRotate.getAngle() + e.getDeltaY() / 10);
g.yRotate.setAngle(g.yRotate.getAngle() - e.getDeltaX() / 10);
g.setTranslateX(g.getTranslateX() + e.getDeltaX());
g.setTranslateY(g.getTranslateY() + e.getDeltaY());
});
scene.setOnKeyPressed((KeyEvent e) -> {
KeyCode code = e.getCode();
switch (code) {
case LEFT:
g.zRotate.setAngle(g.zRotate.getAngle() + 10);
break;
case RIGHT:
g.zRotate.setAngle(g.zRotate.getAngle() - 10);
break;
case UP:
g.setTranslateZ(g.getTranslateZ() - 100);
break;
case DOWN:
g.setTranslateZ(g.getTranslateZ() + 100);
break;
case HOME:
g.xRotate.setAngle(0);
g.yRotate.setAngle(0);
g.zRotate.setAngle(0);
g.setTranslateX(0);
g.setTranslateY(0);
g.setTranslateZ(0);
break;
default:
break;
}
});
}
public static void main(String[] args) {
launch();
}
}
I'm building an small application with JavaFX to visualize circuit boards. The application includes the possibility of scaling and shifting. When I display small texts, they are distorted, as you can see on the screenshots below. Only when I have zoomed far enough into the scene will the text be displayed correctly again. Does anyone have a solution to this problem? I included the example code from which the screenshots are taken.
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
addMouseEventHandler(root);
Pane pane1 = new Pane();
Text text1 = new Text(400, 400, "Text 1");
text1.setFont(new Font(5));
pane1.getChildren().addAll(text1);
root.getChildren().add(pane1);
primaryStage.setTitle("Text scaling problem");
primaryStage.setScene(new Scene(root, 800, 800));
primaryStage.show();
}
private void addMouseEventHandler(Pane root) {
// add scroll handling to zoom in and out
root.setOnScroll((event) ->
{
double factor = event.getDeltaY() > 0 ? 1.1 : 0.9;
root.setScaleX(root.getScaleX() * factor);
root.setScaleY(root.getScaleY() * factor);
});
// add drag handling
root.setOnMousePressed((mouseEvent) ->
{
startMouseX = mouseEvent.getSceneX();
startMouseY = mouseEvent.getSceneY();
startTranslationX = root.getTranslateX();
startTranslationY = root.getTranslateY();
});
root.setOnMouseDragged((mouseEvent) ->
{
double movedX = startMouseX - mouseEvent.getSceneX();
double movedY = startMouseY - mouseEvent.getSceneY();
double transX = startTranslationX - movedX;
double transY = startTranslationY - movedY;
root.setTranslateX(transX);
root.setTranslateY(transY);
});
}
correct text
vs
distorted text
The correct text is scrolled one more step than the distorted.
i am not sure if anything can be done about scaling of text inside of the text node. but if you separate the characters in different text nodes it will keep its shape. you might wanna try Canvas and manually draw using GraphicsContext2D.
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class DistortedText extends Application{
private double startMouseX, startMouseY;
private double startTranslationX, startTranslationY;
#Override
public void start(Stage primaryStage) {
HBox box = new HBox(new Text("T"),new Text("e"),new Text("x"),new Text("T"),new Text(" "),new Text("1"));
Pane root = new Pane(box);
addMouseEventHandler(root,box);
primaryStage.setTitle("Text scaling problem");
primaryStage.setScene(new Scene(root, 800, 800));
primaryStage.show();
}
private void addMouseEventHandler( Pane container, Node node) {
// add scroll handling to zoom in and out
container.setOnScroll((event) ->
{
double factor = event.getDeltaY() > 0 ? 1.1 : 0.9;
node.setScaleX(node.getScaleX() * factor);
node.setScaleY(node.getScaleY() * factor);
});
// add drag handling
container.setOnMousePressed((mouseEvent) ->
{
startMouseX = mouseEvent.getX();
startMouseY = mouseEvent.getY();
startTranslationX = node.getTranslateX();
startTranslationY = node.getTranslateY();
});
container.setOnMouseDragged((mouseEvent) ->
{
double movedX = startMouseX - mouseEvent.getX();
double movedY = startMouseY - mouseEvent.getY();
double transX = startTranslationX - movedX;
double transY = startTranslationY - movedY;
node.setTranslateX(transX);
node.setTranslateY(transY);
});
}
public static void main(String[] args){
launch( args);
}
}
I have a pane that layouts its child nodes customly by overriding the layoutChildren method. It simplified looks like this:
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
public class MyPane extends Pane {
private Label lblTitel;
private Button btnStart;
public MyPane(Runnable sceneChanger) {
this.lblTitel = new Label("Title");
lblTitel.setFont(Font.font(16));
this.btnStart = new Button("Change Scene");
btnStart.setOnAction(e -> sceneChanger.run());
getChildren().addAll(lblTitel, btnStart);
}
#Override
protected void layoutChildren() {
lblTitel.setLayoutX(getWidth() / 2 - lblTitel.getWidth() / 2);
lblTitel.setLayoutY(4);
btnStart.setLayoutX(getWidth() / 2 - btnStart.getWidth() / 2);
btnStart.setLayoutY(getHeight() - 4 - btnStart.getHeight());
super.layoutChildren();
}
}
Displaying that Pane as root node of a scene of a stage works fine. But when I change the scene and then change it back again, the panes size doesn't fit the stages viewport anymore.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class App extends Application {
private Stage window;
private Scene firstScene;
private Scene secondScene;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage window) {
this.window = window;
this.firstScene = new Scene(new MyPane(() -> window.setScene(secondScene)));
this.secondScene = new Scene(new SecondPane());
window.setScene(firstScene);
window.setWidth(800);
window.setHeight(600);
window.show();
}
private class SecondPane extends Pane {
public SecondPane() {
Button btnBack = new Button("back");
btnBack.setOnAction(e -> window.setScene(firstScene));
getChildren().add(btnBack);
}
}
}
After changing back to the first scene the pane is far to large, you can see that by the title being more on the right side than in the horizontal center. As soon as I change the size of the window, the panes size perfectly fits the stages viewport again and everything is fine. So I added a custom increase of size to the window right after I switch scenes.
Platform.runLater(() -> window.setWidth(window.getWidth() + 1));
It works in most of the cases, but its a ridiculous solution and doesn't work when the stage is e.g. maximized.
Is there any way to make a customly layouted pane fit into the stages viewport after setting its scene to the stage, that works in all cases?
First, note that the layout you give in the example code can readily be accomplished using existing layout panes. Something like
BorderPane myPane = new BorderPane();
myPane.setTop(lblTitel);
myPane.setBottom(btnStart);
BorderPane.setAlignment(lblTitel, Pos.CENTER);
BorderPane.setAlignment(btnStart, Pos.CENTER);
BorderPane.setMargin(lblTitle, new Insets(4, 0, 0, 0));
BorderPane.setMargin(btnStart, new Insets(0, 0, 4, 0));
should give you the layout you want.
In general, you should always prefer the built-in layouts to a custom layout.
If you find you really do need a custom pane, then there is a lot more work to do than simply laying out the child nodes. Your pane needs to override the methods to determine its resizable range, which means overriding the methods computeMin/Pref/MaxWidth() and computeMin/Pref/MaxHeight(). You should also determine the content bias by overriding getContentBias(). The layoutChildren() method should determine the size of the child nodes as well as their positions. It should do this by determining their preferred sizes and using those if possible, and using a size in the min-max range for each otherwise. You can determine those sizes by calling prefWidth(), prefHeight() etc on the child nodes.
Finally, to ensure the custom layout properly respects borders and padding, you should account for any insets in the layout calculations. The utility methods snappedTop/Right/Bottom/LeftInsets() give you the values for the insets coming from borders or padding.
Here's an example which works for me: this assumes both child nodes have HORIZONTAL content bias (which makes sense here), so I pass -1 to the methods computing the width, and the computed width to the methods computing the height.
import javafx.geometry.Orientation;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;
public class MyPane extends Pane {
private Label lblTitel;
private Button btnStart;
public MyPane(Runnable sceneChanger) {
// updated to test for long text, wrapping, etc.
this.lblTitel = new Label("This is a really long title that might end up having to span multiple lines of text");
this.lblTitel.setWrapText(true);
this.lblTitel.setTextAlignment(TextAlignment.CENTER);
lblTitel.setFont(Font.font(16));
this.btnStart = new Button("Change Scene");
btnStart.setOnAction(e -> sceneChanger.run());
getChildren().addAll(lblTitel, btnStart);
}
#Override
public Orientation getContentBias() {
return Orientation.HORIZONTAL ;
}
#Override
public double computePrefWidth(double height) {
return Math.max(lblTitel.prefWidth(height), btnStart.prefWidth(height));
}
#Override
public double computeMinWidth(double height) {
return Math.max(lblTitel.minWidth(height), btnStart.minWidth(height));
}
#Override
public double computeMaxWidth(double height) {
return Math.max(lblTitel.maxWidth(height), btnStart.maxWidth(height));
}
#Override
public double computePrefHeight(double width) {
return lblTitel.prefHeight(width) + btnStart.prefHeight(width) + 8 ;
}
#Override
public double computeMinHeight(double width) {
return lblTitel.minHeight(width) + btnStart.minHeight(width) + 8 ;
}
#Override
public double computeMaxHeight(double width) {
return lblTitel.maxHeight(width) + btnStart.maxHeight(width) + 8 ;
}
#Override
protected void layoutChildren() {
double usableWidth = getWidth() - snappedLeftInset() - snappedRightInset() ;
double usableHeight = getHeight() - snappedTopInset() - snappedBottomInset() ;
double lblTitleWidth = lblTitel.prefWidth(-1);
if (lblTitleWidth > usableWidth) {
lblTitleWidth = Math.max(lblTitel.minWidth(-1), usableWidth);
}
double lblTitleHeight = lblTitel.prefHeight(lblTitleWidth);
if (lblTitleHeight > usableHeight) {
lblTitleHeight = Math.max(lblTitel.prefHeight(lblTitleWidth), usableHeight);
}
double lblTitleX = snappedLeftInset() + usableWidth / 2 - lblTitleWidth / 2 ;
lblTitel.resizeRelocate(lblTitleX, 4 + snappedTopInset(), lblTitleWidth, lblTitleHeight);
double btnStartWidth = btnStart.prefWidth(-1);
if (btnStartWidth > usableWidth) {
btnStartWidth = Math.max(btnStart.minWidth(-1), usableWidth);
}
double btnStartHeight = btnStart.prefHeight(btnStartWidth);
if (btnStartHeight > usableHeight) {
btnStartHeight = Math.max(btnStart.minHeight(btnStartWidth), usableHeight);
}
double btnStartX = snappedLeftInset() + usableWidth / 2 - btnStartWidth / 2 ;
double btnStartY = snappedTopInset() + getHeight() - 4 - btnStartHeight ;
btnStart.resizeRelocate(btnStartX, btnStartY, btnStartWidth, btnStartHeight);
}
}
I trying to build map which consist of regions(states) and when mouse entered to some region, I need handle it. Have many png images which represent each region separately. I blend my images and got what I want, but I can't handle some region.
For instance:
It's a first region img
This is a second region img
As result I got:
Code:
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("view/MapView.fxml"));
Pane root = loader.load();
primaryStage.setTitle("Map");
primaryStage.setScene(new Scene(root, 700, 700));
primaryStage.show();
//First region
File file = new File("src/res/img/region1.png");
Canvas canvas = new Canvas(700, 700);
canvas.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, event -> System.out.println("Region 1"));
GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
graphicsContext.drawImage(new Image(file.toURI().toString()), 0, 0);
root.getChildren().add(canvas);
//Second region
file = new File("src/res/img/region2.png");
canvas = new Canvas(700, 700);
canvas.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, event -> System.out.println("Region 2"));
graphicsContext = canvas.getGraphicsContext2D();
graphicsContext.drawImage(new Image(file.toURI().toString()), 0, 0);
root.getChildren().add(canvas);
}
In console I got always "Region 2".
Please give me tips for research. Thanks in advance!
You can use an ImageView and setPickOnBounds for that.
Example code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class LayersWithMouseEvents extends Application {
#Override
public void start(Stage primaryStage) {
// root
StackPane root = new StackPane();
// create layers
Pane region1Layer = new Pane();
Pane region2Layer = new Pane();
// add layers
root.getChildren().addAll(region1Layer, region2Layer);
// load images
ImageView region1ImageView = new ImageView( new Image( getClass().getResource("region1.png").toExternalForm()));
ImageView region2ImageView = new ImageView( new Image( getClass().getResource("region2.png").toExternalForm()));
// add images
region1Layer.getChildren().add(region1ImageView);
region2Layer.getChildren().add(region2ImageView);
// mouse handler
region1Layer.setOnMousePressed(e -> System.out.println("Region 1: " + e));
region2Layer.setOnMousePressed(e -> System.out.println("Region 2: " + e));
// this is the magic that allows you to click on the separate layers, but ONLY(!) as long as the layer is transparent
region1Layer.setPickOnBounds(false);
region2Layer.setPickOnBounds(false);
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
In the future please create an MCVE with focus on complete. Nobody wants to waste their time in order to make your incomplete example work.
To my opinion using a canvas here is not the right approach. If you would define your regions as polygons and add them to the scene graph you can attach a listener to each of these polygons and then react accordingly if the mouse is over some region. Maybe this is also possible with image views but I have never tried wether a transparent region of an image is also mouse transparent which seems to be necessary in that case. For a program of mine I used polygons and it works nicely.
The image data can be used to determine the color of the pixels under the mouse cursor. If the pixel is not fully transparent, then the cursor is on that region.
To retrieve this information you need to use a listener for the mouse moved event. For simplicity you can use a property with a listener attached to trigger the region enter/leave events:
The following example assumes you keep references to the images used for the regions named image1 and image2:
PixelReader reader1 = image1.getPixelReader();
PixelReader reader2 = image2.getPixelReader();
SimpleIntegerProperty region = new SimpleIntegerProperty(-1);
region.addListener((observable, oldValue, newValue) -> {
if (newValue.intValue() < 0) {
System.out.println("region left");
} else {
System.out.println("Region " + (newValue.intValue() + 1));
}
});
canvas.setOnMouseMoved(event -> {
int x = (int) event.getX();
int y = (int) event.getY();
if (x < image1.getWidth() && y < image1.getHeight() && reader1.getColor(x, y).getOpacity() != 0) {
region.set(0);
} else if (x < image2.getWidth() && y < image2.getHeight() && reader2.getColor(x, y).getOpacity() != 0) {
region.set(1);
} else {
region.set(-1);
}
});
Also there is no need to create multiple Canvas to paint the images.