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

Jacob Schoen picture Jacob Schoen · Jun 20, 2012 · Viewed 24.7k times · Source

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();
    }
}

Answer

jewelsea picture jewelsea · Jun 20, 2012

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.