I'm trying to change the close icon background of the selected tab only, the problem is that it works when there is one tab, but when I add more nothing happens until I close the first one, here is what I did :
tabPane.lookup(".tab:selected:top").lookup(".tab-container").lookup(".tab-close-button").setStyle("-fx-background-color: red");
Now the problem is that when I select another tab and try to change it's color nothing happens, until I close the previous changed one.
Quick example:
I have 2 tabs with white close buttons,
I select tab1 : the color changed,
I select tab2 : nothing happens,
I close tab1 then select tab2 : tab2's close button changed.
The selected tab is correct because if I print those 2 lines for each tab:
System.out.println(tabPane.getSelectionModel().getSelectedItem());
System.out.println(tabPane.lookup(".tab:selected:top").lookup(".tab-container").lookup(".tab-close-button"));
here is the result :
javafx.scene.control.Tab#6d81182e <----tab1
TabPaneSkin$TabHeaderSkin$2#7d620380[styleClass=tab-close-button] <----tab1
//now I switch to tab2
javafx.scene.control.Tab#416ca04a <----tab2
TabPaneSkin$TabHeaderSkin$2#7d620380[styleClass=tab-close-button] <----this address should not be the same as the first one.
PS : I can't do it in CSS because the color should change dynamically, the line is executed inside a listener when I switch tab.
Thanks for the help!
CSS lookups are inherently reliant on implementation details of the rendering framework, and as such they can be unreliable and you should really avoid using them whenever possible. In particular, lookups will not work if a CSS pass has not been made on the scene graph (typically this happens during rendering), or if changes to the CSS state have occurred since the last CSS pass. I think what is happening with your code is that the listener is invoked before the CSS state has been updated with the new selection state, causing you to retrieve the wrong Tab from the .tab:selected lookup (though I'm by no means certain this is what's happening).
Note that you already have most of the dynamic behavior you need, because the "selected" pseudoclass will be updated dynamically.
The best approach for functionality like this is to define an external CSS file which covers all your possible styles, and then to programmatically modify CSS state using one of a number of mechanisms. A couple of ways to do this are:
Use "looked-up colors". These act like variable names in CSS.
Use custom pseudoclasses, which you can use as selectors in the CSS file, and whose state you can update programatically.
Here's an example using the first approach. Here we define a looked-up color in the CSS file called "selected-tab-color" which initially sets the close button to be blue.
tabpane.css:
.tab-pane {
selected-tab-color: blue ;
}
.tab:selected > .tab-container > .tab-close-button {
-fx-background-color: selected-tab-color ;
}
In Java code, we can update this simply with a call to tabPane.setStyle("selected-tab-color: red;"), which you can do, e.g., in an event handler (but really anywhere, as long as it's on the JavaFX application thread). In this example, we just list to the selected tab, and make one tab have a red close button, the others blue.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TabPaneTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1", new Label("Tab 1"));
Tab tab2 = new Tab("Tab 2", new Label("This tab has\na custom close button"));
Tab tab3 = new Tab("Tab 3", new Label("Tab 3"));
tabPane.getTabs().addAll(tab1, tab2, tab3);
tabPane.getSelectionModel().selectedItemProperty().addListener((obs, oldTab, newTab) -> {
if (newTab == tab2) {
tabPane.setStyle("selected-tab-color: red;");
} else {
tabPane.setStyle("selected-tab-color: blue;");
}
});
BorderPane root = new BorderPane(tabPane);
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("tabpane.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.setWidth(250);
primaryStage.setHeight(250);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
You can use this technique to turn the effect off entirely, by reverting to the default:
tabPane.setStyle("selected-tab-color: -fx-mark-color;");
however, this relies on knowing the details of the default CSS implementation (i.e. the name of the default color for the tab close button).
A slightly better way to turn the functionality on or off programmatically is to use a custom Pseudoclass. These work like any other CSS pseudoclass, and can have their state modified programmatically.
In this example, the color of the close button is red only if the containing tab pane has the warn-on-close pseudoclass set:
.tab-pane:warn-on-close .tab:selected > .tab-container > .tab-close-button {
-fx-background-color: red ;
}
And in the Java code here, we switch this on when tab 2 is selected, and off when the others are selected. Again, arbitrary logic is possible here.
import javafx.application.Application;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TabPaneTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1", new Label("Tab 1"));
Tab tab2 = new Tab("Tab 2", new Label("This tab has\na custom close button"));
Tab tab3 = new Tab("Tab 3", new Label("Tab 3"));
tabPane.getTabs().addAll(tab1, tab2, tab3);
PseudoClass warnOnClosePseudoClass = PseudoClass.getPseudoClass("warn-on-close");
tabPane.getSelectionModel().selectedItemProperty().addListener((obs, oldTab, newTab) ->
tabPane.pseudoClassStateChanged(warnOnClosePseudoClass, newTab == tab2));
BorderPane root = new BorderPane(tabPane);
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("tabpane.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.setWidth(250);
primaryStage.setHeight(250);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
Related
In some situations I need to completely hide the page change buttons in the pagination control, but the hidden area may have been occupied by a page, that is, extend it downwards.
In this example of a window with pagination, I'd like to hide the gray area and enlarge the sky-colored area to the hidden area.
How can I do this?
I tried this:
package javafxpagination;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Pagination;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestPagination extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Test pagination");
Pagination pagination = new Pagination();
pagination.setPageCount(4);
pagination.setMaxPageIndicatorCount(4);
pagination.setCurrentPageIndex(0);
pagination.setPageFactory((pageIndex) -> {
Pane pagePane = new Pane();
pagePane.setPrefWidth(600);
pagePane.setPrefHeight(400);
pagePane.setStyle("-fx-background-color: #DDF1F8;"); //sky color
Button button = new Button("Hide/show page buttons");
button.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
HBox paginationHBox = (HBox)pagination.lookup(".control-box");
Node paginationControl = pagination.lookup(".pagination-control");
paginationControl.setStyle("-fx-background-color: gray;");
paginationControl.setVisible(!paginationControl.isVisible()); //switch visibility
paginationControl.setManaged(paginationControl.isVisible());
paginationControl.minHeight(0.0);
paginationControl.prefHeight(0.0);
paginationControl.maxHeight(0.0);
}
});
pagePane.getChildren().add(button);
return pagePane;
});
VBox vBox = new VBox(pagination);
Scene scene = new Scene(vBox, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
but it did not hide gray area and not stretch the sky-colored page area.
Solution using your VBox layout manager
By adding this line to your application, the Pagination control will expand to fill the available space:
VBox.setVgrow(pagination, Priority.ALWAYS);
That answers the "how to occupy" portion of your question: "JavaFX - pagination: how to hide bottom (control) panel and occupy its area with a page?"
For the hiding, you are already accomplishing that, which is based on the info at:
JavaFX - setVisible hides the element but doesn't rearrange adjacent nodes
with the addition of the lookup,
Node paginationControl = pagination.lookup(".pagination-control")
that you are already doing, plus these lines:
paginationControl.setVisible(!paginationControl.isVisible());
paginationControl.setManaged(paginationControl.isVisible());
The lines to set the pagination control's minHeight, prefHeight, maxHeight to 0, are not needed and I wouldn't recommend keeping them.
Background on Layout Managers
Layouts are managed by layout managers. These layout managers have some default behavior, but sometimes you need to configure them or give them hints to achieve the layout behavior you want.
You have your Pagination control inside a VBox. If you look at the VBox documentation, it says:
If a vbox is resized larger than its preferred height, by default it will keep children to their preferred heights, leaving the extra space unused. If an application wishes to have one or more children be allocated that extra space it may optionally set a vgrow constraint on the child.
So, the default behavior, which is what you are using, is not to expand the managed content to fill additional available space (which is what you are seeing).
Alternate solution - replacing the pagination control
An alternative implementation is, when hiding, replace the Pagination control with the panel to be displayed rather than hiding the Pagination controls.
For example, remove the pagination control from the parent layout container (the vbox) and, in its place, add the panel to be displayed directly into the parent layout container.
To show the pagination again, you would do the opposite.
That is probably the solution that I would prefer for this kind of work.
For such a solution, you need to be mindful that a node can only be the child of a single parent at any one time and take that into account for your implementation. Also, the previous information about layout managers and setting Vgrow constraints on nodes still applies for this solution.
As the title basically. I have a node in JavaFX which I want to be displayed in front of all other nodes according to certain CSS rules. I do not want this to change the ordering of the nodes in my VBox which .toFront() appears to do. See this question.
Is this even possible?
EDIT: To clarify. The situation is the following. I have a VBox containing a bunch of tightly packed ImageViews. When I hover over one I want it to grow slightly to give it the feel that the image is being lifted off of the screen. But since the ImageViews are so tightly packed only the top edge grows (visibly). The bottom edge grows but is below the following image and cannot be seen.
EDIT 2: Upon request here is a screenshot of what I am doing.
The different colour gradients are ImageViews and as I hover over one it should grow as the top edge of the top gradient has in this image (look closely at the top right corner next to the X). However as is also visible in this image the bottom edge of this ImageView has become hidden by the next gradient in this VBox and the grow is not visible.
This sounds like the perfect situation for using the viewOrder property of Node added in Java 9. The viewOrder controls how Nodes are drawn in relation to other Nodes of the same Parent without changing the order of the Nodes in the child list. Here's the Javadoc:
Defines the rendering and picking order of this Node within its parent.
This property is used to alter the rendering and picking order of a
node within its parent without reordering the parent's children list.
For example, this can be used as a more efficient way to implement
transparency sorting. To do this, an application can assign the
viewOrder value of each node to the computed distance between that
node and the viewer.
The parent will traverse its children in decreasing viewOrder order.
This means that a child with a lower viewOrder will be in front of a
child with a higher viewOrder. If two children have the same
viewOrder, the parent will traverse them in the order they appear in
the parent's children list.
However, viewOrder does not alter the layout and focus traversal order
of this Node within its parent. A parent always traverses its children
list in order when doing layout or focus traversal.
Here's an example using this property:
import javafx.animation.ScaleTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.ArrayList;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
var box = new HBox(createRectangles(Color.DARKBLUE, Color.FIREBRICK, 25));
box.setAlignment(Pos.CENTER);
box.setPadding(new Insets(50, 20, 50, 20));
primaryStage.setScene(new Scene(box));
primaryStage.show();
}
private Rectangle[] createRectangles(Color start, Color end, int count) {
var list = new ArrayList<Rectangle>(count);
for (double i = 0; i < count; i++) {
var rect = new Rectangle(30, 60, start.interpolate(end, i / count));
var scaleTrans = new ScaleTransition(Duration.millis(250), rect);
scaleTrans.setFromX(1.0);
scaleTrans.setFromY(1.0);
scaleTrans.setToX(1.2);
scaleTrans.setToY(1.2);
rect.setOnMouseEntered(e -> {
scaleTrans.stop(); // <--- doesn't seem necessary*
scaleTrans.setRate(1.0);
rect.setViewOrder(-1.0);
scaleTrans.play();
});
rect.setOnMouseExited(e -> {
scaleTrans.stop(); // <--- doesn't seem necessary*
scaleTrans.setRate(-1.0);
rect.setViewOrder(0.0);
scaleTrans.play();
});
// *the "stop()"'s don't seem to be necessary. When I commented
// them out the animation still worked. In fact, the animation
// actually seems smoother in the situation where you move the
// mouse over and then away quickly (before the zoom-in completes).
list.add(rect);
}
return list.toArray(new Rectangle[0]);
}
}
It uses Rectangles instead of ImageViews but the concept is the same. When the mouse hovers over a Rectangle it sets the view order to be lower than the others and then plays a ScaleTransition to make it bigger. When the mouse exits it resets the view order back to 0 and then reverses the ScaleTransition.
Note: I used the var keyword which was added in Java 10.
And here is a GIF of the example in action:
Edit: Since you brought up CSS I went and checked if the view order could be set from a stylesheet. And it appears it can. Looking at the CSS Reference Guide there is a CSS property defined for Node named -fx-view-order.
Here is one such solution, creating a new stage to show the zoomed in image.
I do not set the proper coordinates in this sample, but this works as a proof of concept.
In a nutshell: capture the onMouseEntered and onMouseExited events and hide or show the new stage.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
FlowPane root = new FlowPane();
root.setHgap(5);
root.setVgap(5);
for (int i = 0; i < 25; i++) {
root.getChildren().add(getImagePane());
}
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
private VBox getImagePane() {
VBox pane = new VBox() {{
setAlignment(Pos.CENTER);
setPadding(new Insets(5));
setWidth(50);
setHeight(50);
setStyle("-fx-border-color: black");
}};
ImageView img = new ImageView("sample/imageGallery/cerulean.png") {{
setFitWidth(50);
setFitHeight(50);
setPreserveRatio(true);
Stage stage = zoomedStage(pane, this);
setOnMouseEntered(mouseEvent -> stage.show());
setOnMouseExited(mouseEvent -> stage.hide());
}};
pane.getChildren().add(img);
return pane;
}
private Stage zoomedStage(VBox parent, ImageView img) {
Stage stage = new Stage();
stage.setWidth(110);
stage.setHeight(110);
VBox pane = new VBox() {{
setAlignment(Pos.CENTER);
setPadding(new Insets(5));
setWidth(110);
setHeight(110);
setStyle("-fx-border-color: black");
}};
pane.setPickOnBounds(false);
ImageView zoomedImage = new ImageView(img.getImage());
zoomedImage.setFitHeight(100);
zoomedImage.setFitWidth(100);
pane.getChildren().add(zoomedImage);
stage.setScene(new Scene(pane));
stage.initStyle(StageStyle.UNDECORATED);
return stage;
}
}
From here, it should just be a matter or fixing the stage's coordinates to be centered over the image and then remove the stage's decoration.
Known Issues:
You will also need to handle the issue with the mouse cursor being blocked by the new stage. This will lead to a loop where the mouse is constantly entering and exiting the thumbnail of the image, causing the zoomed in stage to flicker.
From my point of view, you have a VBox with spacing 0 so each ImageView is tightly packed to each other so the glow effect is not well visible in every image. In that case you could just add a margin each time you want to select an ImageView in order to 'help' the glowing effect to appear.
For Java 8 :
Sadly, this can't be happened from a CSS cause the ImageView does not provide any rule for setting margin or padding. So you are more or less (in my opinion) bound to write that behaviour through code.
:
private Node createImageView(String imageLink) {
// Setting the image view
ImageView imageView = new ImageView(imageLink);
// setting the fit width of the image view
imageView.setFitWidth(400);
// Setting the preserve ratio of the image view
imageView.setPreserveRatio(true);
// Instantiating the Glow class
Glow glow = new Glow();
imageView.setOnMouseEntered(e -> {
// setting level of the glow effect
glow.setLevel(0.9);
// Adding a margin on TOP and Bottom side just to make the
// glowing effect visible
VBox.setMargin(imageView, new Insets(2,0,2,0));
});
imageView.setOnMouseExited(e -> {
// remove the glow effect
glow.setLevel(0.0);
// remove the margins
VBox.setMargin(imageView, new Insets(0));
});
// Applying bloom effect to text
imageView.setEffect(glow);
return imageView;
}
I'm trying to limit the number of characters which can be inserted in a JavaFX-8 TextField: when the limit is reached endLineReached() method is called.
I would like to set the maximum number of chars basing on size (the visible part on GUI) of the textfield. So the code I'm using is
textfield.textProperty().addListener((ov, oldValue, newValue) -> {
onEndLine(textfield.getText(), textfield.getPrefColumnCount());
});
public void onEndLine(String text, int prefColumnCount) {
if (text.length() > prefColumnCount) {
endLineReached();
}
}
The problem is that textfield.getPrefColumnCount() not work properly. Furthermore I'm not able to set prefColumnCount value programmatically because the textfield width could change every time the program is re-started (not during execution). The textfield is child of a JavaFX HBox.
If you want to limit the length of the text based solely on the visual width of the text field, that's a little tricky as it depends on many factors (the font, the width of the text field, the padding applied to the text field, in the case of a variable-width font the actual text that has been entered, etc).
Note this seems a strange thing to do (imo), because the user will be able to enter relatively long text if it happens to use narrow characters, but relatively short text if it happens to use wide characters (in this example below I can enter 26 "l"s but only 8 "m"s). Really the text the user is allowed to enter should be based on logical considerations (i.e. business rules), not visual ones. But maybe you have some unusual use-case for this.
You can use a text formatter on the text field to veto the addition of text. To check the width, create a new Text object with the new text, set the font to the same font as the text field, and then check its width. You need to account for padding in the text field as well. Note this is a little fragile, as you don't really know the implementation of the layout of the text field, but this appears to work ok, at least with the JDK version I have.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextInputControl;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class TextFieldNoScroll extends Application {
#Override
public void start(Stage primaryStage) {
TextField textField = new TextField();
textField.setTextFormatter(new TextFormatter<String>(change -> {
if (change.isAdded()) {
Insets textFieldInsets = change.getControl().getPadding();
double horizPadding = textFieldInsets.getLeft() + textFieldInsets.getRight() ;
Text newText = new Text(change.getControlNewText());
newText.setFont(((TextInputControl)change.getControl()).getFont());
double newTextWidth = newText.getBoundsInLocal().getWidth();
if (newTextWidth + horizPadding > change.getControl().getWidth()) {
return null ;
}
}
return change ;
}));
VBox root = new VBox(textField);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 120, 120);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
In the code below, the green rectangle is not removed on mouse click. However, if you resize the stage after the mouse click, the scene is repainted and the green rect vanishes.
If you set the green rectangle's size to 150/150, then some of it is immediately on top of the pane, and it vanishes immediately on mouse click.
Is this a JavaFX bug, or am I overlooking something?
How can I make the rectangle disappear on mouse click?
My environment: Windows 7 / Java 1.8.0 64-Bit Server VM build 25.0-b70.
package xschach.client;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main3 extends Application {
public static void main(String[] args) {
Application.launch(Main3.class.getName());
}
public Main3() {}
#Override
public void start(Stage stage) throws Exception {
Pane pane = new Pane();
Rectangle rect = new Rectangle(10, 10, 200, 200);
pane.getChildren().add(rect);
stage.setScene(new Scene(pane, 300, 300));
stage.show();
Rectangle rect2 = new Rectangle(100, 100, 50, 50);
rect2.setFill(Color.GREEN);
pane.getChildren().add(rect2);
pane.setOnMouseClicked(event -> pane.getChildren().remove(rect2));
}
}
It really seems to me a bug.
Doing some tests, what happens is the very last node (on top), no matter how many we have, that lays within the bounds of the first one, when it's removed, it is not visible to the scene graph, it's not marked as dirty, and no requestLayout() is called.
I've found also other workaround. Just allow some (minimal) transparency to the first child, and it will work...
rect.setFill(Color.web("000000FE"));
And you can always put this node behind the first one...
pane.setOnMouseClicked(event -> {
rect2.toBack();
pane.getChildren().remove(rect2);
});
Anyway, consider filing a bug to Jira.
I know it's a little bit late, but it might help someone.
If you use the method .clear() on the children list of the pane if forces the render of the view.
So this solution, quite extreme, I admit it, works :
pane.setOnMouseClicked(event -> {
pane.getChildren().clear();
pane.getChildren().add(rect);
});
I found another workaround. Move the node that is not erasing to the back with the toBack() method, and then move it back to its place with the toFront() method. This seems to activate the dirty mechanism and repaints the background node correctly.
I mean like in file managers, when you would click, drag the mouse, creating a rectangle selection and after the mouse released selection is created?
I could do that like this (pseude-code like):
onMousePressed:
setGestureStarted(true)
onMouseMoved:
if isGestureStarted:
changeRectangle(event.getX, event.getY)
onMouseReleased:
select(getSelectionRectange())
But I thought that it's pretty common behavior and maybe it's already in framework.
EDIT1:
I was trying to do zoomable linechart. And I actually came across library to do that.
It's pretty good, but could be better though.
Right now I'm considering the actual worth of javaFX in our web project, because I don't like how such thing as zoomable chart is not in the library. Probably would be better with javascript (except I should learn it first, but It shouldn't be that hard).
You would probably need to make your own implementation for this. I found your pseudo code is quiet good. If you like to select for any component then you need to first create a simple rectangular boundary which is easily possible by your pseudo code.
Now for finding out either your node is inside that boundary then you need to do iteration of all the nodes/children of certain Parent Object by using this function: Node Intersect check
I would suggest to use that function after the onMouseReleased or if you like to see things in realtime then it is preferable in onMouseMoved
Your question asks "Is there any implementation of rectangle selection in JavaFX?"
The answer is "yes".
SceneBuilder implements drag-select functionality.
SceneBuilder is open source, so take a look through the source if you are interested on how this behaviour is achieved in JavaFX by SceneBuilder.
SceneBuilderKit is the framework from which SceneBuilder is derived, its source is at the link I provided.
From the SceneBuilder release notes:
JavaFX Scene Builder Kit is an API that allows the integration of Scene Builder panels and functionalities directly into the GUI of a larger application, or a Java IDE, such as NetBeans, IntelliJ, and Eclipse.
where is documentation?
From the release notes:
The javafx_scenebuilder_kit_javadoc-2_0-ea--.zip file, which contains an API javadoc for the JavaFX Scene Builder Kit. You can download the zip file from http://www.oracle.com/technetwork/java/javafx/downloads/devpreview-1429449.html.
The javafx_scenebuilder_kit_samples-2_0-ea--.zip file, which contains the SceneBuilderHello sample application that shows a minimal Java source code example of how the Scene Builder Kit API can be used. This sample is delivered as a NetBeans project. It can be downloaded from http://www.oracle.com/technetwork/java/javafx/downloads/devpreview-1429449.html.
Perhaps after you investigate, SceneBuilder and SceneBuilderKit might not be what you are looking for. In which case, edit your question to make it more explicit and perhaps include source for your rectangle selection implementation attempt and more detail on your requirements (what you intending to select, an image showing how the feature works, etc).
yes, in jfxtras-labs project via:
MouseControlUtil.addSelectionRectangleGesture(Parent root, Rectangle rect)
or
MouseControlUtil.addSelectionRectangleGesture(Parent root, Rectangle rect, EventHandler<MouseEvent> dragHandler, EventHandler<MouseEvent> pressHandler, EventHandler<MouseEvent> releaseHandler)
more info: http://jfxtras.org/doc/8.0labs/jfxtras/labs/util/event/MouseControlUtil.html
Note that selection behavior is extremely application specific and the class above is just a helper class to help you with selection gesture implementations. In the end you have to implement selection behavior yourself.
For a more detailed and matured example of node selection in JavaFx see my other answer here.
Edit: Basic Demo
This is the basic usage. Note that it's just a demo and should NOT be considered final or production ready! For more complex implementation of selection behavior you should tailor it (mostly mouse handlers) on your own based on your application's specific requirements.
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
import jfxtras.labs.util.event.MouseControlUtil;
public class ShapeSelectionExample extends Application {
private List<Shape> selected = new ArrayList<>();
#Override
public void start(Stage primaryStage) {
final Group shapesGroup = new Group();
final AnchorPane root = new AnchorPane(shapesGroup);
// Add whatever shapes you like...
Rectangle shape1 = new Rectangle(200, 20, 50, 50);
Rectangle shape2 = new Rectangle(300, 60, 50, 50);
Circle shape3 = new Circle(100, 100, 30);
shapesGroup.getChildren().addAll(shape1, shape2, shape3);
final Rectangle selectionRect = new Rectangle(10, 10, Color.TRANSPARENT);
selectionRect.setStroke(Color.BLACK);
EventHandler<MouseEvent> mouseDragHanlder = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
for (Node shape : shapesGroup.getChildren()) {
handleSelection(selectionRect, (Shape) shape);
}
}
};
// Add selection gesture
MouseControlUtil.addSelectionRectangleGesture(root, selectionRect, mouseDragHanlder, null, null);
primaryStage.setScene(new Scene(root, 400, 300));
primaryStage.show();
}
private void handleSelection(Rectangle selectionRect, Shape shape) {
if(selectionRect.getBoundsInParent().intersects(shape.getBoundsInParent())) {
shape.setFill(Color.RED);
if(!this.selected.contains(shape))
this.selected.add(shape);
} else {
shape.setFill(Color.BLACK);
this.selected.remove(shape);
}
System.out.println("number of selected items:" + this.selected.size());
}
public static void main(String[] args) {
launch(args);
}
}
This is how the result would look like:
You could also write mouse press and release handlers (currently null in this code) to handle selection behavior while mouse button is pressed or released (which is different to mouse drag).