I want to create drop down menu like this:
I want when I place the mouse over the text to see combo box which I can use to select a value. When I remove the mouse I want to see simple Label. How I can do this?
Unhovered:
On Hover:
On Click and Choose:
On Choice Complete:
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class Hoverboard extends Application {
public class TextChooser extends StackPane {
private Label label = new Label();
private ComboBox<String> combo = new ComboBox<>();
public TextChooser(String... options) {
StackPane.setAlignment(label, Pos.CENTER_LEFT);
StackPane.setAlignment(combo, Pos.CENTER_LEFT);
label.textProperty().bind(
combo.getSelectionModel().selectedItemProperty()
);
label.visibleProperty().bind(
combo.visibleProperty().not()
);
label.setPadding(new Insets(0, 0, 0, 9));
combo.getItems().setAll(options);
combo.getSelectionModel().select(0);
combo.setVisible(false);
label.setOnMouseEntered(event -> combo.setVisible(true));
combo.showingProperty().addListener(observable -> {
if (!combo.isShowing()) {
combo.setVisible(false);
}
});
combo.setOnMouseExited(event -> {
if (!combo.isShowing()) {
combo.setVisible(false);
}
});
getChildren().setAll(label, combo);
}
}
#Override
public void start(Stage stage) throws Exception {
TextChooser textChooser = new TextChooser(
"xyzzy", "frobozz", "foobar"
);
VBox layout = new VBox(textChooser);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch(Hoverboard.class);
}
}
Here is also css style version: https://github.com/varren/JavaFX-CSS-Styled-ComboBox-Demo
A little bit different from the default one, but you can play with css to get what you want. Default styles can be found in jxrt.jar!/com/sun/javafx/scene/control/skin/caspian/caspian.css
CSS
#changed{
-fx-background-color: transparent;
}
#changed .arrow,
#changed .arrow-button{
-fx-background-color: transparent;
}
/* this part is from default stiles fxrt.jar!/com/sun/javafx/scene/control/skin/caspian/caspian.css */
#changed:hover{
-fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;
-fx-background-radius: 5, 5, 4, 3;
-fx-background-insets: 0 0 -1 0, 0, 1, 2;
-fx-padding: 0;
}
#changed:showing > .arrow-button {
-fx-color: -fx-pressed-base;
}
#changed:hover > .arrow-button > .arrow{
-fx-background-insets: 1 0 -1 0, 0;
-fx-background-color: -fx-mark-highlight-color, -fx-mark-color;
-fx-padding: 0.166667em 0.333333em 0.166667em 0.333333em; /* 2 4 2 4 */
-fx-shape: "M 0 0 h 7 l -3.5 4 z";
}
JAVA
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
HBox root = new HBox();
primaryStage.setTitle("Combo Box Style From Css");
ComboBox combobox = new ComboBox<String>(FXCollections.observableArrayList("One", "Two", "Three"));
combobox.getSelectionModel().select(0);
combobox.setId("changed");
ComboBox normalCombobox = new ComboBox<String>(FXCollections.observableArrayList("One", "Two", "Three"));
normalCombobox.getSelectionModel().select(0);
root.getChildren().addAll(combobox, normalCombobox);
Scene scene = new Scene(root, 300, 275);
scene.setFill(Color.WHITE);
String css = Main.class.getResource("styles.css").toExternalForm();
scene.getStylesheets().clear();
scene.getStylesheets().add(css);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
DEMO
Two potential approaches:
Try to use CSS to modify the look of the ComboBox so it looks like a normal text field; hide the arrow and border, and re-show them on :hover. You'll want to lookup the CSS reference for ComboBox: http://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html#combobox
Use a normal TextField, and display a border (and arrow) on :hover. Attach a mouse-listener to the TextField to display a PopupControl on mouse click. Put a ListView inside the PopupControl so it behaves like a ComboBox. You'll need to create a class that implements Skin for your popup control. You should be able to find some examples on the web.
Related
I see a strange appearance of the text contained in a TextArea aftrer doing some changes of TextArea content and style.
With the simplified code shown below I reproducibly see this when I click the button 4 times:
But this is what I expected to see:
Note: If I then click into the TextArea I see the expected result.
What can can be done to get the expected result?
Note that I need to set textarea min/max width and height to get a nice appearance of the content.
Of course I could set it to a bigger value, but that would destroy the look that is required.
I tried setCache as proposed here but that did not work.
I have JavaFX-8 on Windows 8.1. I would also be interested what results are seen in newer versions.
EDIT
With JavaFX-13 the result is:
The text seems to be moved to the right instead of centered as specified in the css (and also to the bottom). I had ecpected that the text is postioned the same as on initial start of the application.
CSS:
.text-area-centered *.text {
-fx-text-alignment: center ;
}
.text-area-centered .scroll-pane {
-fx-hbar-policy: NEVER;
-fx-vbar-policy: NEVER;
}
Java:
public class Main extends Application {
private static final BackgroundFill blackBGF = new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY);
private static final BackgroundFill whiteBGF = new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY);
private static double textareaXY = 50;
private TextArea textarea = new TextArea();
private int clickNo = 1;
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
VBox vb = new VBox();
root.setCenter(vb);
Button b = new Button("ClickMe");
b.addEventHandler(ActionEvent.ACTION, this::OnClickButton);
vb.getChildren().add(b);
vb.getChildren().add(textarea);
textarea.setEditable(false);
textarea.getStyleClass().add("text-area-centered");
textarea.setBackground(new Background(blackBGF));
textarea.setMinHeight(textareaXY);
textarea.setMaxHeight(textareaXY);
textarea.setMinWidth(textareaXY);
textarea.setMaxWidth(textareaXY);
textarea.setFont(new Font("Courier New",10));
textarea.setText("1 2 3\n4 5 6\n7 8 9");
primaryStage.show();
}
private void OnClickButton(ActionEvent event)
{
if(clickNo == 1)
{
textarea.setText("7");
textarea.setFont(new Font("Courier New Bold",24));
}
else if(clickNo == 2)
{
Region region = ( Region ) textarea.lookup( ".content" );
region.setBackground(new Background(blackBGF));
textarea.setStyle("-fx-text-inner-color: white;");
}
else if(clickNo == 3)
{
Region region = ( Region ) textarea.lookup( ".content" );
region.setBackground(new Background(whiteBGF));
textarea.setStyle("-fx-text-inner-color: black;");
}
else if(clickNo == 4)
{
textarea.setText("1 2 3\n4 5 6\n7 8 9");
textarea.setFont(new Font("Courier New",10));
}
clickNo++;
}
public static void main(String[] args) {
launch(args);
}
}
https://stackoverflow.com/a/10615258/529411
I would like to add a background color to my tabpane dynamically (depending on certain conditions). How can I achieve this from code? One option is to assign he tab a specific ID which has the associated CSS, but in my case the color can be dynamically chosen by the user.
Also, I'm curious how to apply the styles in code when dealing with a hierarchy of components.
You can assign the background color to be a looked-up color in the CSS file:
.tab-pane > .tab-header-area > .tab-header-background {
-fx-background-color: -fx-outer-border, -fx-text-box-border, my-tab-header-background ;
}
Now in code you can set the value of the looked-up color whenever you need to:
tabPane.setStyle("my-tab-header-background: blue ;");
SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.stage.Stage;
public class DynamicTabHeaderBackground extends Application {
private static final String TAB_HEADER_BACKGROUND_KEY = "my-tab-header-background" ;
#Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
tabPane.setStyle(TAB_HEADER_BACKGROUND_KEY+": blue ;");
tabPane.getTabs().addAll(new Tab("Tab 1"), new Tab("Tab 2"));
tabPane.getSelectionModel().selectedIndexProperty().addListener((obs, oldIndex, newIndex) -> {
if (newIndex.intValue() == 0) {
tabPane.setStyle(TAB_HEADER_BACKGROUND_KEY+": blue ;");
} else {
tabPane.setStyle(TAB_HEADER_BACKGROUND_KEY+": green ;");
}
});
Scene scene = new Scene(tabPane, 400, 400);
scene.getStylesheets().add("dynamic-tab-header.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
with dynamic-tab-header.css containing the CSS code above.
Update
If you have multiple tab panes, you might want to consider the following variant of the CSS file:
.tab-pane {
my-tab-header-background: derive(-fx-text-box-border, 30%) ;
}
.tab-pane > .tab-header-area > .tab-header-background {
-fx-background-color: -fx-outer-border, -fx-text-box-border,
linear-gradient(from 0px 0px to 0px 5px, -fx-text-box-border, my-tab-header-background) ;
}
This basically emulates the default behavior, but allows you to modify the background on any particular tab pane by calling the tabPane.setStyle(...) code as before.
Trying to customize a floating TabPane by adding a CSS file just like in the example below. The new style does not appear for floating tabpane only - for a regular pane the style appears as expected.
public class FloatingTabPaneTest extends Application
{
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(Stage stage)
{
Parent root = createContentPane();
root.getStylesheets().add(getClass().getResource("/floatingTabPane.css").toExternalForm());
Scene scene = new Scene(root, 1000, 800);
stage.setScene(scene);
stage.setTitle(getClass().getSimpleName());
stage.show();
}
private Parent createContentPane()
{
TabPane tabPane = new TabPane();
tabPane.getStyleClass().add(TabPane.STYLE_CLASS_FLOATING);
addTab(tabPane, "Tab 1", new StackPane());
addTab(tabPane, "Tab 2", new StackPane());
addTab(tabPane, "Tab 3", new StackPane());
tabPane.getSelectionModel().selectLast();
return new BorderPane(tabPane);
}
private void addTab(TabPane tabPane, String name, Node content)
{
Tab tab = new Tab();
tab.setText(name);
tab.setContent(content);
tabPane.getTabs().add(tab);
}
}
FloatingTabPane.css:
.tab-pane.floating > .tab-header-area > .tab-header-background {
-fx-border-color: red;
-fx-border-width: 2;
-fx-background-color: cyan;
}
This is pretty interesting. The issue is because the headerBackground visibility is set to false, if you are on FLOATING style class.
If you search inside TabPaneSkin, you will find :
if (isFloatingStyleClass()) {
headerBackground.setVisible(false); // <---- Imp part
} else {
headerBackground.resize(snapSize(getWidth()), snapSize(getHeight()));
headerBackground.setVisible(true);
}
Since its visibility is set to false, your best shot is to do your changes on the tab-header-area instead of tab-header-background
.tab-pane.floating > .tab-header-area {
-fx-border-color: red;
-fx-border-width: 2;
-fx-background-color: cyan;
}
This will leave a thin red line on the tabs, but this is better than having no style at all ;)
So I have this code:
final StackPane g = new StackPane();
final ProgressIndicator p1 = new ProgressIndicator();
p1.setPrefSize(100, 100);
p1.progressProperty().addListener(new ChangeListener<Number>() {
#SuppressWarnings("rawtypes")
#Override
public void changed(ObservableValue ov, Number oldVal, Number newVal) {
if (p1.getProgress() < 0.3) {
p1.setStyle("-fx-progress-color: red;");
} else if (p1.getProgress() < 0.65) {
p1.setStyle("-fx-progress-color: orange;");
} else {
p1.setStyle("-fx-progress-color: green;");
}
}
});
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.setAutoReverse(true);
final KeyValue kv = new KeyValue(p1.progressProperty(), 1);
final KeyFrame kf1 = new KeyFrame(Duration.millis(3000), kv);
timeline.getKeyFrames().add(kf1);
g.getChildren().addAll(p1); //veil
g.setAlignment(Pos.CENTER);
Then I add the stack pane to a scene and the scene to the stage.
I have two stages, the initial stage with all my things and this additional stage with the progress indicator. Here is the problem: the stage that contains the progress indicator has a white squared(box) as background that fits perfect to the other stage, because usually has the same white background. But when the initial background is not white I can see the white background of the progress indicator stage. So here is my question: How can I make this background transparent? I tried the BlendMode class, but this doesn't seem to work. Any ideas?
Thanks in advance!
Use an external style sheet with
.progress-indicator .indicator {
-fx-background-color: transparent ;
}
Here's a complete example (I used the external style sheet to manage the colors too, but this will work anyway):
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.css.PseudoClass;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ProgressIndicatorTest extends Application {
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
ProgressIndicator pi = new ProgressIndicator();
Task<Void> counter = new Task<Void>() {
#Override
public Void call() throws Exception {
for (int i = 1; i <= 100; i++) {
Thread.sleep(50);
updateProgress(i, 100);
}
return null;
}
};
pi.progressProperty().bind(counter.progressProperty());
pi.progressProperty().addListener((obs, oldProgress, newProgress) -> {
PseudoClass warning = PseudoClass.getPseudoClass("warning");
PseudoClass critical = PseudoClass.getPseudoClass("critical");
if (newProgress.doubleValue() < 0.3) {
pi.pseudoClassStateChanged(warning, false);
pi.pseudoClassStateChanged(critical, true);
} else if (newProgress.doubleValue() < 0.65) {
pi.pseudoClassStateChanged(warning, true);
pi.pseudoClassStateChanged(critical, false);
} else {
pi.pseudoClassStateChanged(warning, false);
pi.pseudoClassStateChanged(critical, false);
}
});
pi.setMaxSize(Control.USE_PREF_SIZE, Control.USE_PREF_SIZE);
root.setStyle("-fx-background-color: antiqueWhite;");
root.getChildren().add(pi);
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().add("progress.css");
primaryStage.setScene(scene);
primaryStage.show();
new Thread(counter).start();
}
public static void main(String[] args) {
launch(args);
}
}
progress.css:
.progress-indicator .indicator {
-fx-background-color: transparent ;
}
.progress-indicator {
-fx-progress-color: green ;
}
.progress-indicator:critical {
-fx-progress-color: red ;
}
.progress-indicator:warning {
-fx-progress-color: orange ;
}
Try this for css (based on a bugfix for Java 8u40):
.progress-indicator:indeterminate > .spinner {
/** Applying to undo styling from .spinner, reported in RT-37965 */
-fx-background-color: transparent;
-fx-background-insets: 0;
-fx-background-radius: 0;
}
For me, none of the above answers worked.
I finally got it working by:
progressIndicator.getScene().getRoot().setStyle("-fx-background-color: transparent");
If you are using a scene, make the scene transparent by also doing:
scene.setFill(null);
Is it possible to change css for a JavaFX application while it is running?
The effect I am looking for is changing skins or themes at the click of a button.
The UI is in an FXML file if that makes any difference.
I have tried
Scene.getStylesheets()
.add(getClass().getResource(skinFileName).toExternalForm());
which has no effect.
thanks
It should have the effect. Try this full demo code:
public class CssThemeDemo extends Application {
private String theme1Url = getClass().getResource("theme1.css").toExternalForm();
private String theme2Url = getClass().getResource("theme2.css").toExternalForm();
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
final Scene scene = new Scene(root, 300, 250);
System.out.println("scene stylesheets: " + scene.getStylesheets());
scene.getStylesheets().add(theme1Url);
System.out.println("scene stylesheets: " + scene.getStylesheets());
final Button btn = new Button("Load Theme 1");
btn.getStyleClass().add("buttonStyle");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
scene.getStylesheets().remove(theme2Url);
System.out.println("scene stylesheets on button 1 click: " + scene.getStylesheets());
if(!scene.getStylesheets().contains(theme1Url)) scene.getStylesheets().add(theme1Url);
System.out.println("scene stylesheets on button 1 click: " + scene.getStylesheets());
}
});
final Button btn2 = new Button("Load Theme 2");
btn2.getStyleClass().add("buttonStyle");
btn2.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
scene.getStylesheets().remove(theme1Url);
System.out.println("scene stylesheets on button 2 click: " + scene.getStylesheets());
if(!scene.getStylesheets().contains(theme2Url)) scene.getStylesheets().add(theme2Url);
System.out.println("scene stylesheets on button 2 click: " + scene.getStylesheets());
}
});
ComboBox<String> comboBox = new ComboBox<String>(FXCollections.observableArrayList("Just", "another", "control"));
root.getChildren().add(VBoxBuilder.create().spacing(10).children(btn, btn2, comboBox).build());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
theme1 css:
.root{
-fx-font-size: 14pt;
-fx-font-family: "Tahoma";
-fx-base: #DFB951;
-fx-background: #A78732;
-fx-focus-color: #B6A678;
}
.buttonStyle {
-fx-text-fill: #006464;
-fx-background-color: #DFB951;
-fx-border-radius: 20;
-fx-background-radius: 20;
-fx-padding: 5;
}
theme2 css:
.root{
-fx-font-size: 16pt;
-fx-font-family: "Courier New";
-fx-base: rgb(132, 145, 47);
-fx-background: rgb(225, 228, 203);
}
.buttonStyle {
-fx-text-fill: red;
-fx-background-color: lightcyan;
-fx-border-color: green;
-fx-border-radius: 5;
-fx-padding: 3 6 6 6;
}
Note the same named CSS selectors in both theme1 and theme2 css files.
You can also try this piece of code(simple and truly illustrative) :
A Container for it: I chose BorderPane.
Add a main Scene for your application.
A Menu Bar with a set of items depending on the look of your application.
And item on the Menu bar.
BorderPane rootPane = new BorderPane();
Parent content = FXMLLoader.load(getClass().getResource("sample.fxml"));
rootPane.setCenter(content);
Scene scene = new Scene(root, 650, 550, Color.WHITE);
// Menu bar
MenuBar menuBar = new MenuBar();
// file menu
Menu fileMenu = new Menu("_File");
MenuItem exitItem = new MenuItem("Exit");
exitItem.setAccelerator(new KeyCodeCombination(KeyCode.X, KeyCombination.SHORTCUT_DOWN));
exitItem.setOnAction(ae -> Platform.exit());
fileMenu.getItems().add(exitItem);
menuBar.getMenus().add(fileMenu);
// Look and feel menu
Menu themeMenu = new Menu("_Theme");
themeMenu.setMnemonicParsing(true);
menuBar.getMenus().add(themeMenu);
rootPane.setTop(menuBar);
MenuItem theme1 = new MenuItem("Theme 1");
theme1.setOnAction(ae -> {
scene.getStylesheets().clear();
setUserAgentStylesheet(null);
scene.getStylesheets()
.add(getClass()
.getResource("theme1.css")
.toExternalForm());
});
MenuItem theme2 = new MenuItem("Theme 2");
theme2.setOnAction(ae -> {
scene.getStylesheets().clear();
setUserAgentStylesheet(null);
scene.getStylesheets()
.add(getClass()
.getResource("theme2.css")
.toExternalForm());
});
themeMenu.getItems()
.addAll(theme1,
theme2);
primaryStage.setScene(scene);
primaryStage.show();
Supposed that you have your two CSS files in the folder of the class
where you will call this code with the corresponding name theme1.css
and theme2.css.
Now you can switch between two themes while your application is running.