Javafx equally spanned toggle button - javafx-8

I need to make a group of toggle button like the following, with the white background being the selected button, and two buttons take 50% width of the parent container. Two toggle buttons are place inside HBox. The styling
So far I tried, stuck like this.
.viewType .toggle-button {
-fx-padding: 0 2 0 2;
-fx-background-color: #000;
-fx-text-fill: white;
}
.viewType .toggle-button:selected {
-fx-padding: 0;
-fx-background-color: white;
-fx-text-fill: black;
-fx-border-wdith: 2;
-fx-border-color: black;
-fx-border-radius: 4;
}

You can set the buttons maxWidth to max double in java side. This will provide your buttons to same width in HBox. Hope it is useful:
btn1.setMaxWidth(Double.MAX_VALUE);
btn2.setMaxWidth(Double.MAX_VALUE);
You can check the following link for useful information related with sizing and aligning nodes:
Sizing and Aligning Nodes

If you want both buttons to have equal width, use a GridPane instead of a HBox, and use column constraints to make the two columns equal widths:
GridPane grid = new GridPane();
grid.getColumnConstraints().addAll(createCol(), createCol());
ToggleButton toggle1 = new ToggleButton("...");
toggle1.setMaxWidth(Double.MAX_VALUE);
ToggleButton toggle2 = new ToggleButton("...");
toggle2.setMaxWidth(Double.MAX_VALUE);
grid.addRow(0, toggle1, toggle2);
// ...
private ColumnConstraints createCol() {
ColumnConstraints col = new ColumnConstraints();
col.setPercentWidth(50);
col.setFillWidth(true);
return col ;
}
You can further control how big the grid pane is in its parent by configuring the parent (details depend on what type of pane is used for the parent).
SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class EqualSizedButtons extends Application {
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
grid.getStyleClass().add("viewType");
grid.getColumnConstraints().addAll(createCol(), createCol());
ToggleButton toggle1 = new ToggleButton("A");
toggle1.setMaxWidth(Double.MAX_VALUE);
ToggleButton toggle2 = new ToggleButton("This is really big button B");
toggle2.setMaxWidth(Double.MAX_VALUE);
grid.addRow(0, toggle1, toggle2);
new ToggleGroup().getToggles().addAll(toggle1, toggle2);
BorderPane root = new BorderPane();
root.setBottom(grid);
Scene scene = new Scene(root, 600, 600);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private ColumnConstraints createCol() {
ColumnConstraints col = new ColumnConstraints();
col.setPercentWidth(50);
col.setFillWidth(true);
return col ;
}
public static void main(String[] args) {
launch(args);
}
}

Related

how to add same background color for all pane in javafx?

I want to maintain single background color(black) for all panes, and for all views. i don't want write css for every view. i am using only vbox and hbox mostly. and very few table views. is there any easy way to write css once and apply to all. thank you in advance
You don't write a css for every view, you give every element the same style class.
Pane pane = new Pane();
pane.getStyleClass().add("bg-black-style");
Somewhere you need to add the stylesheet to the scene
scene.getStylesheets().add("css-file.css");
And in the css file
.bg-black-style {
-fx-background-color: black;
}
This way every thing that should look the same has it's style all in one place.
You can just use .pane in CSS class, and it will work for all the panes.
.pane{
-fx-background-color: black;
}
Same works with .button etc.
You can apply the style sheet to the entire application like this:
package hacks;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
import java.net.URL;
/**
* Created by BDay on 7/10/17.<br>
* <br>
* CssStyle sets the style for the entire project
*/
public class CssStyle extends Application {
private String yourCss = "YourResource.css";
public CssStyle() {
try {
Application.setUserAgentStylesheet(getCss()); //null sets default style
} catch (NullPointerException ex) {
System.out.println(yourCss + " resource not found");
}
}
private Button button = new Button("Button Text");
private TextArea textArea = new TextArea("you text here");
private ObservableList<String> listItems = FXCollections.observableArrayList("one", "two", "three");
private ListView listView = new ListView<String>(listItems);
private FlowPane root = new FlowPane(button, textArea, listView);
private Scene scene = new Scene(root);
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(scene);
primaryStage.show();
}
private String getCss() throws NullPointerException {
ClassLoader classLoader = getClass().getClassLoader();
URL resource = classLoader.getResource(yourCss);
String asString = resource.toExternalForm(); //throws null
return asString;
}
}

Javafx 8 drawing a line between translated nodes

How do you draw a line between the centers of translated nodes? Given for example the following code snippet:
public class Test extends Application{
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Circle circle1=new Circle(10, Color.GREEN);
root.getChildren().add(circle1);
Circle circle2=new Circle(10, Color.RED);
root.getChildren().add(circle2);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
circle1.setTranslateX(100);
Line line=new Line(circle1.getCenterX(), circle1.getCenterY(), circle2.getCenterX(), circle2.getCenterY());
root.getChildren().add(line);
}
public static void main(String[] args) {
launch(args);
}
}
Running this application will clearly show a red and a green circle. However, there won't be a line because each of the centers of the circles are at the coordinates (0,0). Nevertheless, the circles do not cover each other because one of the circles is translated. This doesn't work:
Line line=new Line(circle1.getCenterX()+circle1.getTranslateX(), circle1.getCenterY()+circle1.getTranslateY(), circle2.getCenterX()+circle2.getTranslateX(), circle2.getCenterY()+circle2.getTranslateY());
Finally, let's assume that there is an approach to draw a line connecting the centers of the two circles. If, after the line is drawn, I would invoke circle2.setTranslateX(50);, how do I ensure that the endpoint of the line on the side of circle2 moves accordingly?
A StackPane is a managed layout pane, meaning that it manages the positions of its child nodes (by default it centers them); the translation is applied after the StackPane positions the nodes. This is why the circles appear in different locations but the line is not where you expect. Using a Pane instead of a StackPane will make things work as you expect.
To keep the line in the correct position relative to the circles when the circles are repositioned dynamically, bind the startX, startY, endX, and endY properties, instead of just setting them.
import javafx.animation.Animation;
import javafx.animation.ParallelTransition;
import javafx.animation.SequentialTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;
public class LineConnectingCircles extends Application{
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Circle circle1=new Circle(10, Color.GREEN);
root.getChildren().add(circle1);
Circle circle2=new Circle(10, Color.RED);
root.getChildren().add(circle2);
// move circles so we can see them:
circle1.setTranslateX(100);
circle2.setTranslateY(50);
Line line = new Line();
// bind ends of line:
line.startXProperty().bind(circle1.centerXProperty().add(circle1.translateXProperty()));
line.startYProperty().bind(circle1.centerYProperty().add(circle1.translateYProperty()));
line.endXProperty().bind(circle2.centerXProperty().add(circle2.translateXProperty()));
line.endYProperty().bind(circle2.centerYProperty().add(circle2.translateYProperty()));
root.getChildren().add(line);
// create some animations for the circles to test the line binding:
Button button = new Button("Animate");
TranslateTransition circle1Animation = new TranslateTransition(Duration.seconds(1), circle1);
circle1Animation.setByY(150);
TranslateTransition circle2Animation = new TranslateTransition(Duration.seconds(1), circle2);
circle2Animation.setByX(150);
ParallelTransition animation = new ParallelTransition(circle1Animation, circle2Animation);
animation.setAutoReverse(true);
animation.setCycleCount(2);
button.disableProperty().bind(animation.statusProperty().isEqualTo(Animation.Status.RUNNING));
button.setOnAction(e -> animation.play());
BorderPane.setAlignment(button, Pos.CENTER);
BorderPane.setMargin(button, new Insets(10));
Scene scene = new Scene(new BorderPane(root, null, null, button, null), 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JScrollPane in Eclipse by using palette

I am trying to make a JFrame scrollable by using Palette. In Netbeans if I make a panel with dimensions (300, 500) and a ScrollPane with dimensions (200,200) then if I drag and drop the panel into the ScrollPane it creates automatically the bars.
In eclipse I tried it with the same way and I cannot make it. Moreover the final code in eclipse after the attempt is the following:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JScrollPane;
public class InsertWaterRawData extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
InsertWaterRawData frame = new InsertWaterRawData();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public InsertWaterRawData() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 577, 383);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(236, 87, 200, 200);
contentPane.add(scrollPane);
JPanel panel = new JPanel();
scrollPane.setViewportView(panel);
panel.setLayout(null);
}
}
Is there any way to make it with the palette ??
Thanks in advance
panel.setLayout(null);
Don't use a null layout!!!
The scrollbars will appear automatically when the preferred size of the panel is greater than the size of the scrollpane.
When you use a layout manager the preferred size of the panel will be calculated automatically as you add components to the panel.
If you are doing custom painting on your panel, then you need to override the getPreferredSize() method of your panel to return an appropriate size.
Based on the code you posted there is no need for scrollbars because no components have been added to the panel.

JavaFX: How to make a Node partially mouse transparent?

Simplified Problem:
Make one Node "A" that is on top of another Node "B" to be half transparent to MouseEvents, so the Events will reach the underlying Node "B". Both Nodes are of equal size but Node "A" has a half transparent background image so one half of Node "B" is visible.
Real Problem:
I have a menu of tabs. Each tab can be dragged to expand the corresponding menu layer. Therefore each tab layer is a Pane with a partially transparent background (basically a polygon) of which the transparent part should be also transparent to MouseEvents.
The illustration (which I can't post yet, see link: Illustration of tabs, the dark green line is the border of the green Pane) shows the basic principle: just imagine only the tabs are visible and the layer itself can be pulled to the right to view it's content.
So the question is, how do I make a region of a Node transparent to MouseEvents without making the whole Node transparent?
Thank you for your help!
Update:
To clarify the simple problem here is the corresponding code:
//Create parent group
Group root = new Group();
//Create linear gradient, so one side is transparent
Stop[] stops = new Stop[] { new Stop(0, Color.rgb(0, 255, 0, 0.0)), new Stop(1, Color.rgb(0, 255, 0, 1.0))};
LinearGradient lg1 = new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, stops);
//Create the rectangles
Rectangle A = new Rectangle(100, 50, lg1);
Rectangle B = new Rectangle(100,50, Color.RED);
//Add eventHandlers
A.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
System.out.println("Clicked A");
}
});
B.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
System.out.println("Clicked B");
}
});
root.getChildren().addAll(B, A);
//Add to Scene..
Hope this helps.
Consider the pickOnBounds property, it may help in your situation, but it is not clear to me without seeing your code attempt which fails for the simplified problem.
node.setPickOnBounds(true)
If pickOnBounds is true, then picking is computed by intersecting with the bounds of this node, else picking is computed by intersecting with the geometric shape of this node.
The code below demonstrates how this may be used by creating a square overlaid by an ImageView for an Image which contains tranparent pixels. If pickOnBounds is set to true for the ImageView, then, even if you click on the transparent pixels in the image, the ImageView will receive the mouseClick event. If pickOnBounds is set to false for the ImageView, then, even if you click on the transparent pixels in the image, the ImageView will not process the click and the click event will be received by the node behind the image.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class PickOnBoundsDemo extends Application {
public static void main(String[] args) { Application.launch(args); }
#Override public void start(Stage stage) {
final Rectangle back = new Rectangle(128, 128);
back.setFill(Color.FORESTGREEN);
final ImageView front = new ImageView("http://icons.iconarchive.com/icons/aha-soft/free-large-boss/128/Wizard-icon.png");
// icon: Linkware (Backlink to http://www.aha-soft.com required)
final StackPane pickArea = new StackPane();
pickArea.getChildren().addAll(
back,
front
);
final ToggleButton pickTypeSelection = new ToggleButton("Pick On Bounds");
final Label pickResult = new Label();
Bindings.bindBidirectional(front.pickOnBoundsProperty(), pickTypeSelection.selectedProperty());
front.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent t) {
pickResult.setText("Front clicked");
}
});
back.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent t) {
pickResult.setText("Back clicked");
}
});
VBox layout = new VBox(10);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
layout.getChildren().setAll(
pickArea,
new Label("Click inside the above area to test mouse picking."),
pickTypeSelection,
pickResult
);
layout.setAlignment(Pos.CENTER);
stage.setScene(new Scene(layout));
stage.show();
}
}

Javafx - Rotate node for display in landscape/portrait orientation on handheld device

The javafx application I am developing will eventually be running on a handheld device with 800x480 resolution. The application will typically be running in portrait mode, but for some features (e.g. displaying charts and tables), it will need to switch to landscape mode to better display the data.
My question is, is there a straight forward way to operate with nodes that are rotated by multiples of 90 degrees?
I can rotate the table by calling setRotate(), although this introduces several new issues:
To resize column widths when rotated, the user has to drag the column dividers left to right (orthogonal to the row of headers),
The table still expands its width/height to the size of its parent, although this doesn't work as well when rotated -90 degrees (or other multiples of 90).
The other constraint is that the chart content is contained in the center of a BorderPane, where the top and bottom of the BorderPane contain toolbars, which prevents rotating the entire scene.
Here is my SSCCE; please correct me if there are any problems with the code below.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TablePanelTrial extends Application {
BorderPane root = new BorderPane();
private boolean isRotated=false;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("Table Panel Trial");
final TablePanel tp = new TablePanel();
Button btnRotate = new Button("Rotate");
btnRotate.setOnAction(new EventHandler() {
#Override
public void handle(ActionEvent event) {
double r = -90;
if(isRotated){
r=0;
isRotated = !isRotated;
}
else{
r=-90;
tp.tv.setMinHeight(200);
isRotated = !isRotated;
}
tp.rotate(r);
}
});
root.setTop(btnRotate);
root.setCenter(tp.getVB());
primaryStage.setScene(new Scene(root, 480, 800));
primaryStage.show();
}
class TablePanel{
private VBox vbox = new VBox();
private TableView tv = new TableView();
private String[] labelVal = {"Column 1", "Element", "Difference", "File Name", "Report Number"};
public TablePanel(){
TableColumn column1 = new TableColumn(labelVal[0]);
TableColumn column2 = new TableColumn(labelVal[1]);
TableColumn column3 = new TableColumn(labelVal[2]);
TableColumn column4 = new TableColumn(labelVal[3]);
TableColumn column5 = new TableColumn(labelVal[4]);
tv.getColumns().addAll(column1, column2, column3, column4,column5);
vbox.getChildren().add(tv);
tv.setPrefHeight(2000);
}
public Pane getVB(){
return vbox;
}
public void rotate(double r){
vbox.setRotate(r);
}
}
}
Android does not perform a rotate function when the device goes to landscape mode, I think.
One has to redraw the layout again to display it.
With JavaFX, what you can do is add a Listener to the scene to listen for a change in width since that's what basically happens.
You can do it like this most probably:
scene.widthProperty().addListener((observable, oldValue, newValue) -> {
....initialize the changes to layout here....
);
});
May not be the best solution but I guess its something.