I'm I wrong to think that when resizing a window for a JavaFX-based application, the configured minimum size for a column width constraint should be used instead of the preferred size?
The program is pretty simple, from a book: JavaFX 9 By Example, with some minor changes to work in Eclipse with JavaFX 15.
First a picture showing the desired outcome. I've set the fxml ColumnConstraints prefWidth value to 50, which is the same as the minWidth.
Now, the same application is launched with a single change: the prefWidth is set to 150 instead of 50. When making the horizontal window size smaller with the mouse, you can see that the application is not using the minWidth setting (minWidth was not changed). I didn't shrink the horizontal to its minimum, to make it more visually obvious that the contents are being clipped.
Following are the two additional classes in this project (am skipping displaying the module-info). But I don't think there is anything here that affects the gist of the question.
package com.jfxbe;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class FXMLContactFormX extends Application {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
stage.setTitle("FXMLContactForm ");
Parent root = null;
try {
root = FXMLLoader.load(getClass().getResource("ContactFormX.fxml"));
} catch (IOException e) {
e.printStackTrace();
}
Scene scene = new Scene(root, 380, 150, Color.WHITE);
stage.setScene(scene);
stage.setMinWidth(200);
stage.setMinHeight(150);
stage.show();
}
}
package com.jfxbe;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
public class ContactFormXController {
#FXML private TextField firstNameField;
#FXML private TextField lastNameField;
#FXML private Button saveButton;
#FXML
protected void saveContactAction(ActionEvent event) {
System.out.println("Saving the following information: ");
System.out.println("First Name: " + firstNameField.getText());
System.out.println(" Last Name: " + lastNameField.getText());
}
}
Am I missing something in how column constraints work? Is there documentation that explains what is going on with the minWidth being ignored or not?
Related
I would like to apply two or three different styles to text in a single cell in a TableView.
For example, I'd like the single cell to have text formatted like this:
Edge of the Sun [EP] (Disc 2)
I'd really like to do it with colors, as well.
I know how to apply styling to the entire cell, but I don't even know where to start for applying style to part of the text.
Putting the data in different columns isn't a viable option.
Below's a quick example that
uses TextFlow to style parts of a text
implements a custom TableCell that has a TextFlow as its graphics and updates the text parts as appropriate
Note that there is a slight visual glitch: the prefHeight of the flow seems to return the accumulated height of the lines as if they were wrapped even if they aren't, thus making the row height oversized. As a quick hack, the computePrefHeight is overridden to force a single line - with the drawback that the other line/s simply disappear if the column width is decreased. Pretty sure there's something better but too lazy to further dig ;)
import java.util.Locale;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
/**
*
*/
public class TableFormattedCell extends Application {
public static class MyCell extends TableCell<Locale, Locale> {
private TextFlow flow;
private Label displayName;
private Label displayLanguage;
public MyCell() {
displayName = new Label();
displayName.setStyle("-fx-font-weight: bold");
displayLanguage = new Label();
displayLanguage.setStyle("-fx-font-style: italic; -fx-text-fill: darkviolet");
flow = new TextFlow(displayName, displayLanguage) {
#Override
protected double computePrefHeight(double width) {
// quick hack to force into single line ...
// there must be something better ..
return super.computePrefHeight(-1);
}
};
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(flow);
}
#Override
protected void updateItem(Locale item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
displayName.setText("");
displayLanguage.setText("");
} else {
displayName.setText(item.getDisplayName() + " ");
displayLanguage.setText(item.getDisplayLanguage());
}
}
}
private Parent getContent() {
TableView<Locale> table = new TableView<>(FXCollections.observableArrayList(
Locale.getAvailableLocales()));
TableColumn<Locale, String> countryCode = new TableColumn<>("CountryCode");
countryCode.setCellValueFactory(new PropertyValueFactory<>("country"));
TableColumn<Locale, String> language = new TableColumn<>("Language");
language.setCellValueFactory(new PropertyValueFactory<>("language"));
table.getColumns().addAll(countryCode, language);
TableColumn<Locale, Locale> local = new TableColumn<>("Locale");
local.setCellValueFactory(c -> new SimpleObjectProperty<>(c.getValue()));
local.setCellFactory(e -> new MyCell());
table.getColumns().addAll(local);
BorderPane pane = new BorderPane(table);
return pane;
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent(), 800, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TableFormattedCell.class.getName());
}
I'm trying to resize the caret in a TextField without modifying the font size, but I can not do it.
Does anyone know how to do that?
Thanks!
I have no idea why you would want to do this. And this probably won't give you exactly what you want, but it does change the size of the caret. Note that the big caret will still be clipped to be within the bounds of the containing TextField.
The small line between the m and the a is the small caret. Image transfer into stackoverflow reduced clarity so it is hard to see:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BiglyCaret extends Application {
#Override
public void start(Stage stage) throws Exception {
TextField bigCaretTextField = new TextField("Big Caret");
bigCaretTextField.setSkin(
new TextFieldCaretControlSkin(
bigCaretTextField,
2
)
);
TextField smallCaretTextField = new TextField("Small Caret");
smallCaretTextField.setSkin(
new TextFieldCaretControlSkin(
smallCaretTextField,
0.5
)
);
TextField normalTextField = new TextField("Standard Caret");
VBox layout = new VBox(
10,
bigCaretTextField,
smallCaretTextField,
normalTextField
);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
TextFieldCaretControlSkin.java
Note, this uses a private API class in Java 8, so it won't work for Java 9. In Java 9, the TextFieldSkin will become public API (move to a javafx package instead of a com.sun package), so it will probably work in Java 9 if you change the package.
import com.sun.javafx.scene.control.skin.TextFieldSkin;
import javafx.scene.control.TextField;
public class TextFieldCaretControlSkin extends TextFieldSkin {
public TextFieldCaretControlSkin(TextField textField, double scale) {
super(textField);
setScale(scale);
}
private void setScale(double scale) {
caretPath.setScaleX(scale);
caretPath.setScaleY(scale);
}
}
This answer was based on this answer:
Hide input caret of TextField in JavaFX8
So I do not get any errors when I execute my code.
I also have setup controller in my fxml and code that I post below is of controller and main class.Since I already posted fxml in previous post.
package realEstateApplication;
import javafx.application.Application;
//import javafx.fxml.FXMLLoader;
//import javafx.scene.Parent;
//import javafx.scene.Scene;
import javafx.stage.Stage;
import realEstateApplication.controllers.loginViewController;
public class Main extends Application {
public Main() {
System.out.println("Main invoked ... \n");
}
#Override
public void start(Stage primaryStage) throws Exception{
// Parent loginRoot = FXMLLoader.load(getClass().getResource("views/loginView.fxml"));
loginViewController loginMVC = new loginViewController();
// Parent login = FXMLLoader.load(getClass().getResource("views/loginView.fxml"));
/* Parent admin = FXMLLoader.load(getClass().getResource("views/adminView.fxml"));
Parent registerAdmin = FXMLLoader.load(getClass().getResource("views/registerAdminView.fxml"));
Parent registerCustomer = FXMLLoader.load(getClass().getResource("views/registerCustomerView.fxml"));
Parent registerCompany = FXMLLoader.load(getClass().getResource("views/registerCompanyView.fxml"));
Parent registerFlat = FXMLLoader.load(getClass().getResource("views/registerFlatView.fxml"));*/
// Stage firstStage = new Stage();
// firstStage.setTitle("Login");
// firstStage.setScene(new Scene(loginRoot, 600, 400));
//firstStage.show();
/*Stage secondaryStage = new Stage();
secondaryStage.setTitle("Administrator");
secondaryStage.setScene(new Scene(admin, 600, 399));
secondaryStage.show();
Stage ternaryStage = new Stage();
ternaryStage.setTitle("For new admins");
ternaryStage.setScene(new Scene(registerAdmin, 600, 400));
ternaryStage.show();
Stage forthStage = new Stage();
forthStage.setTitle("New customers register here");
forthStage.setScene(new Scene(registerCustomer, 600, 400));
forthStage.show();
Stage pentaStage = new Stage();
pentaStage.setTitle("Register new company here");
pentaStage.setScene(new Scene(registerCompany, 600, 400));
pentaStage.show();
Stage hexaStage = new Stage();
hexaStage.setTitle("Create flats here");
hexaStage.setScene(new Scene(registerFlat, 600, 400));
hexaStage.show();*/
}
public static void main(String[] args) {
launch(args);
}
}
Below I have included my controller code:
package realEstateApplication.controllers;
import javafx.fxml.FXML;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Modality;
import javafx.stage.Stage;
/**
* Created by PriteshJ on 4/16/16.
*/
public class loginViewController extends Application {
#FXML private TextField username_tField;
#FXML private PasswordField password_tField;
#FXML private Button login_Button;
#FXML private Button signUp_Button;
public loginViewController() {
System.out.println("Login Controller constructor invoked ...\n");
}
#Override
public void start(Stage loginStage) throws Exception {
Parent loginRoot = FXMLLoader.load(getClass().getResource("views/loginView.fxml"));
if(loginRoot == null)
System.out.println("something weird happened .... ");
loginStage.setTitle("Login");
loginStage.setScene(new Scene(loginRoot, 600, 400));
loginStage.initModality(Modality.WINDOW_MODAL);
loginStage.show();
}
}
(So I tried to pass a reference to Parent variable which points to my fxml file which is within scope of start method since my Main class extends Application (javafx), that gave me NullPointerException.
Tried to make it static but that didn't work either.
Then tried approach I posted now , it gives no errors but neither it works.)
Well I am trying to think from MVC perspective in JavaFX. So I got a loginViewController which I rely to completely own login.fxml and do any operations on it. I got a main class where JavaFX application starts but it can only construct my view from an object of my viewController. I went through lots of examples, but so far I never came across an example that shows how to do above stuff that I want to.
Looking forward to suggestions.
EDIT:
I have an alert box that pops up if the user clicks "Delete" for removing an item in a ListView. It works, but I would like it to pop over the original stage. It showed up in my first monitor. Is there any way to set the position of the alert when it's shown?
Note, the "owner" is in a different class, and I created everything with Scenebuilder/FXML. I cannot figure out how to get initOwner() to work. Here is the "Main" class:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Assignment_5 extends Application {
public Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("Assignment_5.fxml"));
primaryStage.setTitle("Plant Pack");
primaryStage.setScene(new Scene(root, 1200, 500));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is the working code within the Controller class. It's not necessary to implement the modality of this alert, but it would be a nice addition to make it more convenient. I simply don't know how to pass the main Window from the Main class to this:
protected void handleDeleteButtonClick(ActionEvent event) {
Alert alertBox = new Alert(Alert.AlertType.CONFIRMATION, "Confirm Delete", ButtonType.OK, ButtonType.CANCEL);
alertBox.setContentText("Are you sure you want to delete this " + plantType.getValue().toString().toLowerCase() + "?");
alertBox.showAndWait();
if(alertBox.getResult() == ButtonType.OK) {
int selectedPlant = plantList.getSelectionModel().getSelectedIndex();
observablePlantList.remove(selectedPlant);
}
else {
alertBox.close();
}
}
I understand this is fairly new, so it's difficult to find many resources. If anyone knows any info I may have missed, please let me know. Thanks for any help offered.
I am using Java 8 with IntelliJ 14.1.5.
As #jewelsea suggests, setting the modality and owner for the alert box will assure that the alert will appear over the stage, even if the stage is moved.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class DeleteAlertDemo extends Application {
Stage owner;
ObservableList<String> observablePlantList;
ListView<String> plantList;
protected void handleDeleteButtonClick(ActionEvent event) {
String item = plantList.getSelectionModel().getSelectedItem();
Alert alertBox = new Alert(Alert.AlertType.CONFIRMATION, "Confirm Delete",
ButtonType.OK, ButtonType.CANCEL);
alertBox.setContentText("Are you sure you want to delete this "
+ item.toLowerCase() + "?");
alertBox.initModality(Modality.APPLICATION_MODAL); /* *** */
alertBox.initOwner(owner); /* *** */
alertBox.showAndWait();
if (alertBox.getResult() == ButtonType.OK) {
int selectedPlant = plantList.getSelectionModel().getSelectedIndex();
observablePlantList.remove(selectedPlant);
} else {
alertBox.close();
}
}
#Override
public void start(Stage primaryStage) {
owner = primaryStage; /* *** */
Button deleteBtn = new Button();
deleteBtn.setText("Delete");
deleteBtn.setOnAction(this::handleDeleteButtonClick);
observablePlantList = FXCollections.observableArrayList("Begonia",
"Peony", "Rose", "Lilly", "Chrysanthemum", "Hosta");
plantList = new ListView<>(observablePlantList);
plantList.getSelectionModel().select(0);
BorderPane root = new BorderPane();
root.setCenter(plantList);
root.setRight(deleteBtn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Delete Alert Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
How do I make a vertical ButtonBar in JavaFX 8?
I want exactly the same concepts of the ButtonBar component, but ButtonBar is horizontal, I want vertical. All the buttons must be same size.
If I use a VBox I have to set the width of the buttons and of the VBox manually.
Is there a simpler way to do that without having to set widths?
You need two things: to set fillWidth to true on the VBox, and to set maxWidth on each button. The most convenient approach is probably a simple subclass of VBox:
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
public class VerticalButtonBar extends VBox {
public VerticalButtonBar() {
setFillWidth(true);
}
public void addButton(Button button) {
button.setMaxWidth(Double.MAX_VALUE);
getChildren().add(button);
}
}
and then you can do things like
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class VerticalButtonBarExample extends Application {
#Override
public void start(Stage primaryStage) {
VerticalButtonBar bar = new VerticalButtonBar();
bar.addButton(new Button("A"));
bar.addButton(new Button("Button"));
BorderPane root = new BorderPane();
root.setLeft(bar);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
which looks like
See Adding a custom component to SceneBuilder 2.0 if you want to make the VerticalButtonBar visible to SceneBuilder.
As far as I can see, ButtonBar has no way to change this, so there is nothing SceneBuilder can do about it.
You might want to file a feature request for ButtonBar: http://bugs.java.com/