Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you set the style for a JavaFX ContextMenu using css?

I am trying to change the style of a ContextMenu item using a seperate CSS file. I looked at the caspian.css section and found the following definitions:

  • .context-menu
  • .context-menu .separator
  • .context-menu .scroll-arrow
  • .context-menu .scroll-arrow:hover
  • .context-menu:show-mnemonics .mnemonic-underline

I copied those over exactly to my css file and changed just the background color values as a test:

.context-menu {
    -fx-skin: "com.sun.javafx.scene.control.skin.ContextMenuSkin";
    -fx-background-color: #006699;
    -fx-background-insets: 0, 1, 2;
    -fx-background-radius: 0 6 6 6, 0 5 5 5, 0 4 4 4;
/*    -fx-padding: 0.666667em 0.083333em 0.666667em 0.083333em;  8 1 8 1 */
    -fx-padding: 0.333333em 0.083333em 0.666667em 0.083333em; /* 4 1 8 1 */
}

.context-menu .separator {
    -fx-padding: 0.0em 0.333333em 0.0em 0.333333em; /* 0 4 0 4 */
}

.context-menu .scroll-arrow {
    -fx-padding: 0.416667em 0.416667em 0.416667em 0.416667em; /* 5 */
    -fx-background-color: #006699;
}

.context-menu .scroll-arrow:hover {
    -fx-background: -fx-accent;
    -fx-background-color: #006699;
    -fx-text-fill: -fx-selection-bar-text;
}

.context-menu:show-mnemonics .mnemonic-underline {
    -fx-stroke: -fx-text-fill;
}

This obviously does not work or I would not be here. It seems to have no effect no matter what values I change.

I opened up JavaFX Scene Builder to take a look (side note I used this as a last resort, as I find it pretty clumsy to use). I noticed under the Styleable Parts of the css section for the context menu that is lists CSSBridge[context-menu] which seems odd. Other things like Label have Label[label].

Can anybody explain to me what is going on here, as it appears to be ignoring my css file for context menus and using the default values in caspian.css?


Attaching sample FXML file, css and java code to run.

Sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.net.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>

<AnchorPane fx:id="myroot" xmlns:fx="http://javafx.com/fxml">
  <children>
    <Label text="Right click for options">
      <contextMenu>
        <ContextMenu>
          <items>
            <MenuItem text="Help" />
            <MenuItem text="Me" />
          </items>
        </ContextMenu>
      </contextMenu>
    </Label>
  </children>
  <stylesheets>
    <URL value="@contextcolor.css" />
  </stylesheets>
</AnchorPane>

contextcolor.css

.root {
  -fx-background-color: cornsilk; 
  -fx-padding: 10;
}

.context-menu {
  -fx-background-color: #006699;
  -fx-text-fill: white;
}

.menu-item .label {
  -fx-text-fill: yellow;
}

.menu-item:focused .label {
  -fx-text-fill: white;
}

Test.java

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Test extends Application {

    public static void main(String[] args) {
        Application.launch(Test.class, args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        System.out.println(com.sun.javafx.runtime.VersionInfo.getVersion());

        Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));

        stage.setScene(new Scene(root));
        stage.show();
    }
}
like image 493
Jacob Schoen Avatar asked Jun 20 '12 20:06

Jacob Schoen


People also ask

Can I use CSS in JavaFX?

CSS in JavaFXJavaFX provides you the facility of using CSS to enhance the look and feel of the application. The package javafx. css contains the classes that are used to apply CSS for JavaFX applications.

How do I load CSS in JavaFX?

The first way is to use the full folder path to the file: scene. getStylesheets(). add("/chapter6/basics/style.

What is CSS JavaFX?

CSS styles are applied to nodes in the JavaFX scene graph in a way similar to the way CSS styles are applied to elements in the HTML DOM. Styles are first applied to the parent, then to its children. The code is written such that only those branches of the scene graph that might need CSS reapplied are visited.


2 Answers

Here is a simple example of styling a JavaFX context menu via css.

Tested on WinXPsp3, Jdk7u6b14ea, JavaFX 2.2b12.

java app

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class ContextColor extends Application {
  public static void main(String[] args) { launch(args); }
  @Override public void start(Stage stage) {
    Label label = new Label("Right click for options");
    label.setContextMenu(new ContextMenu(new MenuItem("Help"), new MenuItem("Me")));
    Scene scene = new Scene(label);
    scene.getStylesheets().add(ContextColor.class.getResource("contextcolor.css").toExternalForm());
    stage.setScene(scene);
    stage.show();
  }
}

css stylesheet

/** contextcolor.css
 *   place in same folder as ContextColor.java
 *   ensure your build system copies this file to the ContextColor.class output directory on build
 */
.root {
  -fx-background-color: cornsilk; 
  -fx-padding: 10;
}

.context-menu {
  -fx-background-color: #006699;
  -fx-text-fill: white;
}

.menu-item .label {
  -fx-text-fill: yellow;
}

.menu-item:focused .label {
  -fx-text-fill: white;
}

I couldn't tell you the exact reason why your css styling did not function as you expect. Some possible reasons are:

  1. You are not loading it correctly.
  2. Your css file is not copied to your output path.
  3. Your css file is otherwise corrupted or syntactically incorrect.
  4. You are using an earlier version of JavaFX which has difficulty styling context menus from css.

Update

Looking at the complete code in your question where the css file is loaded via fxml, I can reproduce your issue where the context menu is not styled. If, instead of setting the stylesheet in the fxml, I set the stylesheet on the scene in code (as in my test app), then it all works fine.

The difference when the css is set via fxml is that the fxml is not setting the stylesheet on the scene, but instead on the parent root node of the scene. If in the code I add the stylesheet to the parent rather than the scene, then I end up with the same behaviour from the code implementation as the fxml. So this is not really an issue with fxml per se, but rather it is in issue with the inheritance rules of the JavaFX 2.2 css processing. IMO, the css processing is wrong - the styling should be the same whether the stylesheet has been set on the scene or on the root node of the scene.

I advise filing a bug against the JavaFX runtime controls at http://javafx-jira.kenai.com with your test case and a link back to this StackOverflow question and the JavaFX team will resolve the issue in due time.

As a workaround, just set your stylesheet on the scene in code for now.


Update

Root cause for this issue appears to be RT-19435: popup control not styled be parent's style sheet declarations.

like image 124
jewelsea Avatar answered Oct 18 '22 19:10

jewelsea


import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.geometry.Insets;

/**
 *
 * @author nidhi.a.agrawal
 */
public class Context extends Application {

    @Override
    public void start(Stage stage) throws Exception {
      //  Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));

        TextArea notification = new TextArea();
        ContextMenu contextMenu = new ContextMenu();

        Node itemIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_createnew.png")));
        Node con_test_hierIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_Configure_Test _Hierachy.png")));
        Node cutIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_cut.png")));
        Node copyIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_copy.png")));
        Node pasteIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_paste.png")));
        Node insertIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_insert.png")));
        Node editIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_edit.png")));
        Node renameIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_rename.png")));
        Node deleteIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_delete.png")));
        Node tagIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_tag.png")));
        Node refreshIcon = new ImageView(new Image(getClass().getResourceAsStream("icon_refresh.png")));


        MenuItem sap_new = new MenuItem("Create New Sap System", itemIcon);
        MenuItem con_test_hier = new MenuItem("Configure Test Hierarchy", con_test_hierIcon);
        MenuItem cut = new MenuItem("Cut", cutIcon);
        MenuItem copy = new MenuItem("Copy", copyIcon);
        MenuItem paste = new MenuItem("Paste", pasteIcon);
        MenuItem insert = new MenuItem("Insert", insertIcon);
        MenuItem edit = new MenuItem("Edit", editIcon);
        MenuItem rename = new MenuItem("Rename", renameIcon);
        MenuItem delete = new MenuItem("Delete", deleteIcon);
        MenuItem tag = new MenuItem("Tag", tagIcon);
        MenuItem refresh = new MenuItem("Refresh", refreshIcon);

         contextMenu.getItems().addAll(sap_new,con_test_hier,cut,copy,paste,insert,edit,rename,delete,tag,refresh);
notification.setContextMenu(contextMenu);
 Group root = new Group();
        root.getChildren().add(notification);
         Scene scene = new Scene(root);
          scene.getStylesheets().add(Context.class.getResource("contextcolor.css").toExternalForm());
        stage.setScene(scene);
        stage.show();

       }
public static void main(String[] args) {
        launch(args);
    }
}

For these menu I have set syles like this:---

.

root {
  -fx-background-color: cornsilk; 
  -fx-padding: 10;
}

.context-menu {
  -fx-background-color: #C8CFD7;
  -fx-border-color: #BBC2CC;
  -fx-text-fill: white;
}


.menu-item .label {
  -fx-fill:red;
    -fx-font-family:tahoma;
    -fx-font-size: 12px;;
    -fx-stroke:#C8CFD7;
    -fx-strok-width:.25;
}

.menu-item:focused .label {
  -fx-text-fill: white;
}
.menu-item{
    -fx-border-color: #DDDEE2 transparent #B9C0C8 transparent;

    -fx-border-width:0.50px;

}
.menu-item:focused {
     -fx-background: -fx-accent;
     -fx-background-color: #7092BE;
     -fx-text-fill: -fx-selection-bar-text;
}

it is running fine I am able to change background color,border color,setting icons etc.

like image 29
Nidhi Avatar answered Oct 18 '22 20:10

Nidhi