My background image will not centre. I'm using BorderLayout.CENTER. Everything else is in the right position except for the background image which instead of being centred is a tad bit to the left. Any suggestions on how to fix it?
private JTextField tf;
private JLabel jl2;
public void window() {
ImageIcon ic = new ImageIcon("hangman.png");
JFrame gameFrame = new JFrame();
JPanel jp = new JPanel();
JPanel jpLets = new JPanel();
JPanel jpBlank = new JPanel();
JPanel blankLet = new JPanel();
JPanel imgPane = new JPanel();
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
blankLet.setLayout(new BoxLayout(blankLet, BoxLayout.X_AXIS));
panel2.setLayout(new BorderLayout());
imgPane.setLayout(new BorderLayout());
panel1.setLayout(new BorderLayout());
panel1.setOpaque(false);//!!
//jp.setBorder(BorderFactory.createTitledBorder(""));
tf = new JTextField(1);
JLabel img = new JLabel(ic, JLabel.CENTER);
JLabel jl = new JLabel("Enter a letter", JLabel.CENTER);
jl2 = new JLabel("Letters used: ", JLabel.CENTER);
JLabel jl3 = new JLabel("__ ", JLabel.CENTER);
jl.setFont(new Font("Rockwell", Font.PLAIN, 20));
tf.setFont(new Font("Rockwell", Font.PLAIN, 20));
jl2.setFont(new Font("Rockwell", Font.PLAIN, 20));
imgPane.add(img);
jp.add(jl);
jp.add(tf);
jpLets.add(jl2);
jpBlank.add(jl3);
blankLet.add(jpLets);
blankLet.add(jpBlank);
panel1.add(imgPane, BorderLayout.CENTER);
panel1.add(jp, BorderLayout.NORTH);
panel1.add(blankLet, BorderLayout.SOUTH);
panel2.add(panel1, BorderLayout.CENTER);
gameFrame.setTitle("Hangman");
gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameFrame.setIconImage(
new ImageIcon("Hangman-Game-grey.png").getImage());
gameFrame.setResizable(false);
gameFrame.add(panel2);
gameFrame.setSize(600, 600);
gameFrame.setLocationRelativeTo(null);
gameFrame.setVisible(true);
Two things you can try
Remove the BorderLayout from this imgPane.setLayout(new BorderLayout());
If one doesn't work try to add the label to the Panel instead of the imgPane
panel1.add(img, BorderLayout.CENTER);
Also, panel2 looks unnecessary. Get rid of that and just add panel1 to the frame
Try setting the alignment
img.setAlignmentX(JLabel.CENTER_ALIGNMENT);
img.setAlignmentX(JLabel.CENTER_ALIGNMENT);
panel1.add(img, BorderLayout.CENTER);
Here's the code I used to reconstruct the desired result
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
public class HangManAlign extends JPanel {
public HangManAlign() {
JLabel guess = new JLabel("Guess");
JTextField jtfGuess = new JTextField(3);
JLabel label = new JLabel(new ImageIcon("images/Xpo9R.png"));
JLabel bottom = new JLabel("Letters Used __ __ __ __ __ __ __ __ __");
bottom.setHorizontalAlignment(JLabel.CENTER);
JPanel topPanel = new JPanel();
topPanel.add(guess);
topPanel.add(jtfGuess);
setLayout(new BorderLayout());
add(topPanel, BorderLayout.NORTH);
add(label, BorderLayout.CENTER);
add(bottom, BorderLayout.SOUTH);
setBorder(new EmptyBorder(20, 20, 20, 20));
}
public static void createAndShowGui() {
JFrame frame = new JFrame();
frame.add(new HangManAlign());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I Copied your entire image and cut it down to size, so you can see that it's centered. Here's the image I used
Related
I am trying to get 9 digits to distribute evenly, in three rows, throughout a box of a defined size, without the numbers clumping up in the middle.
1 2 3
4 5 6
7 8 9
So far, the only consistent way that I have found to prevent the numbers from clumping up in the middle, is to add two extra spaces to the digit in the middle, one extra space character on each side (ex: " 2 ", " 5 " and " 8 "). Surely there is a better way to layout these numbers than this?
public class DemoCode extends Application {
// Private Class
class PossibleCellValue extends Text {
PossibleCellValue(String value) {
super(value);
setFont(FONT_FOR_POSSIBLE_VALUES);
}
}
// Private Class
class Cell extends VBox {
Cell() {
super();
this.setBorder(borderBlack);
this.setPrefSize(CELL_SIZE, CELL_SIZE);
PossibleCellValue guess1 = new PossibleCellValue("1");
PossibleCellValue guess2 = new PossibleCellValue(" 2 ");
PossibleCellValue guess3 = new PossibleCellValue("3");
PossibleCellValue guess4 = new PossibleCellValue("4");
PossibleCellValue guess5 = new PossibleCellValue(" 5 ");
PossibleCellValue guess6 = new PossibleCellValue("6");
PossibleCellValue guess7 = new PossibleCellValue("7");
PossibleCellValue guess8 = new PossibleCellValue(" 8 ");
PossibleCellValue guess9 = new PossibleCellValue("9");
HBox box1 = new HBox(guess1, guess2, guess3);
HBox box2 = new HBox(guess4, guess5, guess6);
HBox box3 = new HBox(guess7, guess8, guess9);
VBox vbox = new VBox(box1, box2, box3);
getChildren().add(vbox);
this.setAlignment(Pos.CENTER);
}
}
// Normal start method
public void start(Stage stage) throws Exception {
Scene scene = new Scene(new Cell());
stage.setScene(scene);
stage.setTitle("Demo Code");
stage.show();
} // DemoCode
public static void main(String[] args) {
Application.launch(args);
}
static protected final BorderStroke strokeBlack = new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN);
public static final Border borderBlack = new Border(strokeBlack);
private static final int FONT_SIZE_FOR_POSSIBLE_VALUES = 15;
private static final Font FONT_FOR_POSSIBLE_VALUES = Font.font("Courier", FontWeight.NORMAL, FONT_SIZE_FOR_POSSIBLE_VALUES);
static protected final int CELL_SIZE = 54;
} // DemoCode.java
You can use a TilePane or GridPane for your layout.
Here is an example using a TilePane:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.stream.IntStream;
public class GridViewer extends Application {
private static final int S = 3;
private static final double PANE_SIZE = 54 * S;
public void start(Stage stage) {
TilePane tiles = new TilePane(
0, 0,
IntStream.range(1, 10)
.mapToObj(this::createGridCell)
.toArray(Node[]::new)
);
tiles.setMinSize(TilePane.USE_PREF_SIZE, TilePane.USE_PREF_SIZE);
tiles.setMaxSize(TilePane.USE_PREF_SIZE, TilePane.USE_PREF_SIZE);
tiles.setPrefSize(PANE_SIZE, PANE_SIZE);
tiles.setPrefColumns(S);
tiles.setAlignment(Pos.BASELINE_CENTER);
tiles.setPrefTileHeight(PANE_SIZE / S);
tiles.setPrefTileWidth(PANE_SIZE / S);
StackPane root = new StackPane(tiles);
root.setPadding(new Insets(10));
stage.setScene(new Scene(root));
stage.show();
}
private Node createGridCell(int i) {
Label label = new Label("" + i);
label.setStyle("-fx-background-color: cyan; -fx-border-color: green;");
label.setPrefSize(PANE_SIZE / S, PANE_SIZE / S);
label.setAlignment(Pos.BASELINE_CENTER);
return label;
}
public static void main(String[] args) {
launch(args);
}
}
I have a own component (extended TextField). When I display the component in an AnchorPane the layout from the component is correctly displayed. But when I display the component in a TabPane then the first time when the component is shown the layout isn't correct rendered.
Screenshot:
Own component in an AnchorPane
Screenshot:
Own component in a TabPane
Here a MCVE:
import static javafx.scene.layout.Region.USE_PREF_SIZE;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.ActionEvent;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* #author Naoghuman
*/
public class ExtendedComponentsMCVE extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
ExtendedTextField extendedTextField = new ExtendedTextField();
// OnlyAnchorPane oap = new OnlyAnchorPane(extendedTextField);
// Scene scene = new Scene(oap, 800, 600);
TabAnchorPane tap = new TabAnchorPane(extendedTextField);
Scene scene = new Scene(tap, 800, 600);
primaryStage.setTitle("Demo Extended Components");
primaryStage.setScene(scene);
primaryStage.show();
}
// ExtendedTextField #######################################################
class ExtendedTextField extends HBox {
private BooleanProperty configureCheckBoxProperty;
private BooleanProperty configureLeftLabelProperty;
private BooleanProperty configureTopLabelProperty;
private CheckBox cbReadOnly;
private Label lLeft;
private Label lTop;
private TextField tfText;
private VBox vBox;
private String lastUserInput = ""; // NOI18N
ExtendedTextField() {
super();
this.init();
}
private void init() {
// vbox
super.setAlignment(Pos.BOTTOM_LEFT);
super.setStyle("-fx-background-color: lightgreen;");
// left label
configureLeftLabelProperty = new SimpleBooleanProperty(Boolean.TRUE);
lLeft = new Label("<left>"); // NOI18N
lLeft.setMaxHeight(USE_PREF_SIZE);
lLeft.setMinHeight(USE_PREF_SIZE);
lLeft.visibleProperty().bind(configureLeftLabelProperty);
lLeft.managedProperty().bind(configureLeftLabelProperty);
super.getChildren().add(lLeft);
// checkbox
configureCheckBoxProperty = new SimpleBooleanProperty(Boolean.TRUE);
cbReadOnly = new CheckBox();
cbReadOnly.setMaxHeight(USE_PREF_SIZE);
cbReadOnly.setMinHeight(USE_PREF_SIZE);
cbReadOnly.visibleProperty().bind(configureCheckBoxProperty);
cbReadOnly.managedProperty().bind(configureCheckBoxProperty);
super.getChildren().add(cbReadOnly);
// vbox
vBox = new VBox();
HBox.setHgrow(vBox, Priority.ALWAYS);
// top label
configureTopLabelProperty = new SimpleBooleanProperty(Boolean.TRUE);
lTop = new Label("<top>"); // NOI18N
lTop.visibleProperty().bind(configureTopLabelProperty);
lTop.managedProperty().bind(configureTopLabelProperty);
vBox.getChildren().add(lTop);
// textfield
tfText = new TextField(lastUserInput);
tfText.disableProperty().bind(cbReadOnly.selectedProperty().not());
lLeft.prefHeightProperty().bind(tfText.heightProperty());
cbReadOnly.prefHeightProperty().bind(tfText.heightProperty());
vBox.getChildren().add(tfText);
super.getChildren().add(vBox);
}
public void setCheckBoxSelected(Boolean selected) {
cbReadOnly.setSelected(selected);
if (selected) {
tfText.setText(lastUserInput);
}
else {
lastUserInput = tfText.getText();
tfText.setText(null);
}
}
public void setCheckBoxVisibleManaged(Boolean visible) {
configureCheckBoxProperty.setValue(visible);
}
public void setLeftLabelVisibleManaged(boolean selected) {
configureLeftLabelProperty.setValue(selected);
}
public void setTopLabelVisibleManaged(boolean selected) {
configureTopLabelProperty.setValue(selected);
}
}
// ExtendedTextField #######################################################
// OnlyAnchorPane ##########################################################
class OnlyAnchorPane extends AnchorPane {
OnlyAnchorPane(ExtendedTextField extendedTextField) {
super();
super.setStyle("-fx-background-color: BLANCHEDALMOND;");
// hbox
HBox hbox = new HBox();
hbox.setStyle("-fx-background-color: KHAKI;");
hbox.setSpacing(7.0d);
hbox.setPrefWidth(Double.MAX_VALUE);
// extendedTextField
VBox vbox = new VBox();
vbox.getChildren().add(extendedTextField);
HBox.setHgrow(vbox, Priority.ALWAYS);
hbox.getChildren().add(vbox);
// menu
MenuVBox menu = new MenuVBox(extendedTextField);
hbox.getChildren().add(menu);
AnchorPane.setBottomAnchor(hbox, 14d);
AnchorPane.setLeftAnchor(hbox, 14d);
AnchorPane.setTopAnchor(hbox, 14d);
AnchorPane.setRightAnchor(hbox, 14d);
super.getChildren().add(hbox);
}
}
// OnlyAnchorPane ##########################################################
// TabAnchorPane ###########################################################
class TabAnchorPane extends AnchorPane {
TabAnchorPane(ExtendedTextField extendedTextField) {
super();
super.setStyle("-fx-background-color: BLANCHEDALMOND;");
// tabpane
TabPane tp = new TabPane();
// tab
Tab t = new Tab("TextField");
t.setClosable(false);
tp.getTabs().add(t);
// hbox
HBox hbox = new HBox();
hbox.setStyle("-fx-background-color: KHAKI;");
hbox.setSpacing(7.0d);
hbox.setPrefWidth(Double.MAX_VALUE);
// extendedTextField
VBox vbox = new VBox();
vbox.getChildren().add(extendedTextField);
HBox.setHgrow(vbox, Priority.ALWAYS);
hbox.getChildren().add(vbox);
// menu
MenuVBox menu = new MenuVBox(extendedTextField);
hbox.getChildren().add(menu);
t.setContent(hbox);
AnchorPane.setBottomAnchor(tp, 14d);
AnchorPane.setLeftAnchor(tp, 14d);
AnchorPane.setTopAnchor(tp, 14d);
AnchorPane.setRightAnchor(tp, 14d);
super.getChildren().add(tp);
}
}
// TabAnchorPane ###########################################################
// MenuVBox ################################################################
class MenuVBox extends VBox {
MenuVBox(ExtendedTextField extendedTextField) {
super();
super.setStyle("-fx-background-color: HONEYDEW;");
super.setSpacing(7.0d);
super.setMaxWidth(200.0d);
super.setMinWidth(200.0d);
super.setPrefWidth(200.0d);
// show top label
CheckBox cb1 = new CheckBox("Show top label");
cb1.setSelected(true);
cb1.setOnAction((ActionEvent event) -> {
extendedTextField.setTopLabelVisibleManaged(cb1.isSelected());
});
super.getChildren().add(cb1);
// show left label
CheckBox cb2 = new CheckBox("Show left label");
cb2.setSelected(true);
cb2.setOnAction((ActionEvent event) -> {
extendedTextField.setLeftLabelVisibleManaged(cb2.isSelected());
});
super.getChildren().add(cb2);
// seperator
super.getChildren().add(new Separator());
// select checkbox
CheckBox cb3 = new CheckBox("Select checkbox");
cb3.setSelected(false);
cb3.setOnAction((ActionEvent event) -> {
extendedTextField.setCheckBoxSelected(cb3.isSelected());
});
super.getChildren().add(cb3);
// show checkbox
CheckBox cb4 = new CheckBox("Show checkbox");
cb4.setSelected(true);
cb4.setOnAction((ActionEvent event) -> {
extendedTextField.setCheckBoxVisibleManaged(cb4.isSelected());
});
super.getChildren().add(cb4);
}
}
// MenuVBox ################################################################
}
That is the problem of laying out maybe due to the fixed values you tried to give for related nodes:
lLeft.setMaxHeight(USE_PREF_SIZE);
lLeft.setMinHeight(USE_PREF_SIZE);
and
cbReadOnly.setMaxHeight(USE_PREF_SIZE);
cbReadOnly.setMinHeight(USE_PREF_SIZE);
This just a guess, but anyway you can manually request the renderer to layout the components just at the end of constructing of them via runLater():
class ExtendedTextField extends HBox {
...
...
private void init() {
...
Platform.runLater( ()->{
requestLayout();
});
}
...
}
BTW, I could not see any control that extends TextField ;)
In this case I'm a little confused as to why I can't use myFrame as the first parameter in the showMessageDialog function. Why doesn't this work?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BabyCalculator extends JFrame{
public BabyCalculator(){
//You set this up so that you can refer to the frame using the inner class below.
setDefaultCloseOperation(EXIT_ON_CLOSE);
setName("Baby Calculator");
setLayout(new GridLayout(3,0));
//add
JLabel addLabel = new JLabel("Amount to add:");
JTextField addField = new JTextField(10);
JButton addButton = new JButton("add");
addButton.addActionListener(new AddListener());
//multiply
JLabel multiplyLabel = new JLabel("Amount to multiply:");
JTextField multiplyField = new JTextField(10);
JButton multiplyButton = new JButton("multiply");
//total
JLabel totalLabel = new JLabel("Total");
JTextField totalField = new JTextField(10);
totalField.setEditable(false);
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
//Create Panels
JPanel topRow = new JPanel(new BorderLayout());
JPanel middleRow = new JPanel (new BorderLayout());
JPanel bottomRow = new JPanel (new FlowLayout());
//Add the top Row
topRow.add(addLabel,BorderLayout.WEST);
topRow.add(addField, BorderLayout.CENTER);
topRow.add(addButton, BorderLayout.EAST);
add(topRow);
middleRow.add(multiplyLabel,BorderLayout.WEST);
middleRow.add(multiplyField, BorderLayout.CENTER);
middleRow.add(multiplyButton, BorderLayout.EAST);
add(middleRow);
bottomRow.add(totalLabel);
bottomRow.add(totalField);
bottomRow.add(stopButton);
add(bottomRow);
pack();
setVisible(true);
}
public static void main (String[] args){
JFrame myFrame = new BabyCalculator();
}
public class AddListener implements ActionListener{
public void actionPerformed(ActionEvent e){
JOptionPane.showMessageDialog(myFrame, "You Clicked the add button");
}
}
//end class AddListener
public class StopListener implements ActionListener{//this is an inner class
public void actionPerformed(ActionEvent e){
JOptionPane.showMessageDialog(myFrame, "You Clicked the stop button");
}//end class StopListener
}
}
I know that this is an inner class, and I'm not exactly sure about the access privileges, but it seems like there should be some way to access the "myFrame" variable in the main function.
Add a declaration statement at the top within the BabyCalculator class as:
JFrame myFrame;
Or you could make it a public variable with:
public JFrame myFrame = ...
Is there a simple way to bind a concatenation of StringProperty objects?
Here is what I want to do:
TextField t1 = new TextField();
TextField t2 = new TextField();
StringProperty s1 = new SimpleStringProperty();
Stringproperty s2 = new SimpleStringProperty();
Stringproperty s3 = new SimpleStringProperty();
s1.bind( t1.textProperty() ); // Binds the text of t1
s2.bind( t2.textProperty() ); // Binds the text of t2
// What I want to do, theoretically :
s3.bind( s1.getValue() + " <some Text> " + s2.getValue() );
How can I do that?
You can do:
s3.bind(Bindings.concat(s1, " <some Text> ", s2));
Here's a complete example:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BindingsConcatTest extends Application {
#Override
public void start(Stage primaryStage) {
TextField tf1 = new TextField();
TextField tf2 = new TextField();
Label label = new Label();
label.textProperty().bind(Bindings.concat(tf1.textProperty(), " : ", tf2.textProperty()));
VBox root = new VBox(5, tf1, tf2, label);
Scene scene = new Scene(root, 250, 150);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I found this example of Internal Frames
http://docs.oracle.com/javase/tutorial/uiswing/components/internalframe.html
Is it possible to make the same internal Frames in JavaFX?
With JFXtras there is a Window control, where you can add content and handle the internal window behavior.
First you will need to put in your classpath the jfxtras library. They have some instructions where you can get the library. If you are using maven, just need to add:
<dependency>
<groupId>org.jfxtras</groupId>
<artifactId>jfxtras-labs</artifactId>
<version>2.2-r5</version>
</dependency>
Or download the library and put it into your project classpath, whatever.
Now I put a sample of the demo of the Window with a little difference, allowing generation of several windows.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import jfxtras.labs.scene.control.window.CloseIcon;
import jfxtras.labs.scene.control.window.MinimizeIcon;
import jfxtras.labs.scene.control.window.Window;
public class WindowTests extends Application {
private static int counter = 1;
private void init(Stage primaryStage) {
final Group root = new Group();
Button button = new Button("Add more windows");
root.getChildren().addAll(button);
primaryStage.setResizable(false);
primaryStage.setScene(new Scene(root, 600, 500));
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
// create a window with title "My Window"
Window w = new Window("My Window#"+counter);
// set the window position to 10,10 (coordinates inside canvas)
w.setLayoutX(10);
w.setLayoutY(10);
// define the initial window size
w.setPrefSize(300, 200);
// either to the left
w.getLeftIcons().add(new CloseIcon(w));
// .. or to the right
w.getRightIcons().add(new MinimizeIcon(w));
// add some content
w.getContentPane().getChildren().add(new Label("Content... \nof the window#"+counter++));
// add the window to the canvas
root.getChildren().add(w);
}
});
}
public double getSampleWidth() {return 600;}
public double getSampleHeight() {return 500;}
#Override
public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) {launch(args);}
}
In the original demo, the event code was in the init method, and no button was included. I add the button to create dynamically windows and adding them to the screen.
Here is a snapshot of the result of the application:
I totally recommend you try the demo of jfxtras. They have really great stuff. Hope it helps.
You can implement simple internal window themselves. Main idea, that InternalWindow class just skeleton, that has internal frame like functionality. You can apply any content to it.
1) Declare class
public class InternalWindow extends Region
2) You should be able to set content in window
public void setRoot(Node node) {
getChildren().add(node);
}
3) You should be able to bring window to front if many window exist
public void makeFocusable() {
this.setOnMouseClicked(mouseEvent -> {
toFront();
});
}
4) Now we need dragging functionality
//just for encapsulation
private static class Delta {
double x, y;
}
//we can select nodes that react drag event
public void makeDragable(Node what) {
final Delta dragDelta = new Delta();
what.setOnMousePressed(mouseEvent -> {
dragDelta.x = getLayoutX() - mouseEvent.getScreenX();
dragDelta.y = getLayoutY() - mouseEvent.getScreenY();
//also bring to front when moving
toFront();
});
what.setOnMouseDragged(mouseEvent -> {
setLayoutX(mouseEvent.getScreenX() + dragDelta.x);
setLayoutY(mouseEvent.getScreenY() + dragDelta.y);
});
}
5) Also we want able to resize window (I show only simple right-bottom resizing)
//current state
private boolean RESIZE_BOTTOM;
private boolean RESIZE_RIGHT;
public void makeResizable(double mouseBorderWidth) {
this.setOnMouseMoved(mouseEvent -> {
//local window's coordiantes
double mouseX = mouseEvent.getX();
double mouseY = mouseEvent.getY();
//window size
double width = this.boundsInLocalProperty().get().getWidth();
double height = this.boundsInLocalProperty().get().getHeight();
//if we on the edge, change state and cursor
if (Math.abs(mouseX - width) < mouseBorderWidth
&& Math.abs(mouseY - height) < mouseBorderWidth) {
RESIZE_RIGHT = true;
RESIZE_BOTTOM = true;
this.setCursor(Cursor.NW_RESIZE);
} else {
RESIZE_BOTTOM = false;
RESIZE_RIGHT = false;
this.setCursor(Cursor.DEFAULT);
}
});
this.setOnMouseDragged(mouseEvent -> {
//resize root
Region region = (Region) getChildren().get(0);
//resize logic depends on state
if (RESIZE_BOTTOM && RESIZE_RIGHT) {
region.setPrefSize(mouseEvent.getX(), mouseEvent.getY());
} else if (RESIZE_RIGHT) {
region.setPrefWidth(mouseEvent.getX());
} else if (RESIZE_BOTTOM) {
region.setPrefHeight(mouseEvent.getY());
}
});
}
6) Usage. First we construct all layout. Then apply it to InternalWindow.
private InternalWindow constructWindow() {
// content
ImageView imageView = new ImageView("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Cheetah4.jpg/250px-Cheetah4.jpg");
// title bar
BorderPane titleBar = new BorderPane();
titleBar.setStyle("-fx-background-color: green; -fx-padding: 3");
Label label = new Label("header");
titleBar.setLeft(label);
Button closeButton = new Button("x");
titleBar.setRight(closeButton);
// title bat + content
BorderPane windowPane = new BorderPane();
windowPane.setStyle("-fx-border-width: 1; -fx-border-color: black");
windowPane.setTop(titleBar);
windowPane.setCenter(imageView);
//apply layout to InternalWindow
InternalWindow interalWindow = new InternalWindow();
interalWindow.setRoot(windowPane);
//drag only by title
interalWindow.makeDragable(titleBar);
interalWindow.makeDragable(label);
interalWindow.makeResizable(20);
interalWindow.makeFocusable();
return interalWindow;
}
7) And how add window to layout
#Override
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
root.getChildren().add(constructWindow());
root.getChildren().add(constructWindow());
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
Result
Full code: gist
Upd about close button:
You can add method to InternalWindow
public void setCloseButton(Button btn) {
btn.setOnAction(event -> ((Pane) getParent()).getChildren().remove(this));
}
And when construct:
interalWindow.setCloseButton(closeButton);