1

i am trying to modify color of MenuButton arrow , using JavaFX code not by CSS.

i found it inside caspian.css :

.menu-button > .arrow-button > .arrow {
    -fx-background-insets: 1 0 -1 0, 0;
    -fx-background-color: -fx-mark-highlight-color, -fx-mark-color;
    -fx-padding: 0.25em; /* 3 */
    -fx-shape: "M 0 -3.5 v 7 l 4 -3.5 z";
}

i tried to use something like that :

menubutton.lookup(".arrow");

but it throw NullPointerException

and when i do that :

System.out.println(this.getStyleClass().toString());

it out put that only: menu-button only.

so can any one give me the way to modify it using Java without using CSS ??

Jason4Ever
  • 1,439
  • 4
  • 23
  • 43
  • Have you tried lookup with ".arrow"? – nablex Mar 10 '14 at 12:00
  • lol , sorry , that already what i tried , yes but it didn't work – Jason4Ever Mar 10 '14 at 12:10
  • When are you doing the lookup? Lookups will only work once css has been applied, which is typically after the menu has been displayed. – James_D Mar 10 '14 at 12:20
  • If the statement `menubutton.lookup("arrow")` **throws** `NullPointerException` it means that `menubutton` is `null`. It may be that you are executing this code **before** the FXML is loaded and stuff initialized. Can you show more code, in particular the place where this line is called? – Nikos Paraskevopoulos Mar 10 '14 at 12:36
  • @Jason4Ever, @James_D: I noticed that `lookup()` will indeed return the correct thing if called *after* `primaryStage.show()` (i.e. the answer from James_D), but it does return `null` if called before. – Nikos Paraskevopoulos Mar 10 '14 at 12:45
  • I guess the other point to make here is that lookups are inherently non-robust. The preferred way to do this is using CSS. – James_D Mar 10 '14 at 12:53
  • Or, of course, by changing the value of -fx-mark-color for the menuButton. Which I should have seen half and hour ago. Answer updated accordingly. – James_D Mar 10 '14 at 12:57

2 Answers2

7

This works:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class RedMenuButton extends Application {

    @Override
    public void start(Stage primaryStage) {
        final StackPane root = new StackPane();
        final MenuButton menuButton = new MenuButton("Menu");
        menuButton.getItems().addAll(new MenuItem("Item 1"), new MenuItem("Item 2"), new MenuItem("Item 3"));
        root.getChildren().add(menuButton);

        final Scene scene = new Scene(root, 250, 150);
        primaryStage.setScene(scene);
        primaryStage.show();

        menuButton.lookup(".arrow").setStyle("-fx-background-color: red;");
    }

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

Update: But this is a better solution (which I probably would have got first time if daylight savings hadn't messed with my sleep ;)).

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class RedMenuButton extends Application {

    @Override
    public void start(Stage primaryStage) {
        final StackPane root = new StackPane();
        final MenuButton menuButton = new MenuButton("Menu");
        menuButton.getItems().addAll(new MenuItem("Item 1"), new MenuItem("Item 2"), new MenuItem("Item 3"));
        root.getChildren().add(menuButton);

        menuButton.setStyle("-fx-mark-color: red");

        final Scene scene = new Scene(root, 250, 150);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
-1

There are several ways to do this, here are four. The code is Jython with JavaFX. You can edit this code to meet your needs.


First, the enum, for context.

public enum URLBarArrowConstants {
     //URLBarArrow Constants
     BYCSS_AND_SHAPE,
     BYCSS_AND_NO_SHAPE,
     NOCSS_AND_SHAPE,
     NOCSS_AND_NO_SHAPE;
}

Second, the css files, for context.

EG #1

/*ComboBox's Arrow is a Region.*/
.combo-box .arrow-button .arrow {
     -fx-shape: "...";
     -fx-scale-shape: true;
     -fx-position-shape: true;
}

EG#2

/*ComboBox's Arrow is a Region.*/
.combo-box .arrow-button .arrow {
    /*Setting either of these two will do.*/
     -fx-background-color: transparent; 
     -fx-opacity: 0.0;  
}

/*ComboBox's Arrow Button is a Stack Pane.*/
.combo-box .arrow-button{
    -fx-background-position: center;
    -fx-background-repeat: no-repeat;
    -fx-background-image: url("..<file>.png");
}

The method, in my main file.

def setCustomURLBarArrow(self, url_bar, scene, URLBarArrowConstant):
    from javafx.scene.paint import Paint
    from javafx.scene.shape import Shape, SVGPath, FillRule

Don't configure the ComboBox Arrow by CSS, instead, do it programmatically and change the Regions SVG Shape

if URLBarArrowConstant == URLBarArrowConstants.NOCSS_AND_SHAPE:

    #SVG Object
    previous_url_bar = SVGPath()

    #SVG Path
    previous_url_bar.setContent("...") # edit this 

    #SVG Fill Rule
    previous_url_bar.setFillRule(FillRule.NON_ZERO)

    #Set Fill -- 
    previous_url_bar.setFill(Paint.valueOf(Color.web("...").toString())) //edit here

    #Apply CSS Sheet
    url_bar.applyCss()

    #Set Region's Shape
    arrow_region = url_bar.lookup(".arrow").setShape(previous_url_bar)

Configure the ComboBox Arrow by CSS and change the Regions SVG Shape

elif URLBarArrowConstant == URLBarArrowConstants.BYCSS_AND_SHAPE:
    #Apply Stylesheet for URL Bar
    scene.getStylesheets().add(File("..<file>.css").toURI().toString()) //edit here

Configure the ComboBox Arrow by CSS but instead, merely hide the arrow by setting the transparency/opacity values and set a background.

elif URLBarArrowConstant == URLBarArrowConstants.BYCSS_AND_NO_SHAPE:
    #Apply Stylesheet for URL Bar
    scene.getStylesheets().add(File("..<file>.css").toURI().toString()) //edit here

Don't configure the ComboBox Arrow by CSS, instead, do it programmatically and merely hide the arrow by setting the transparency/opacity values and set a background.

elif URLBarArrowConstant == URLBarArrowConstants.NOCSS_AND_NO_SHAPE:

    from javafx.scene.paint import Paint
    from javafx.scene.layout import CornerRadii
    from javafx.scene.layout import Background, BackgroundSize, BackgroundImage, BackgroundPosition, BackgroundRepeat, BackgroundFill

    #Apply CSS Sheet
    url_bar.applyCss()

    #Grab Arrow(Region), ArrowButton(StackPane) ComboBox properties
    arrow_region = url_bar.lookup(".arrow")
    arrow_button = url_bar.lookup(".arrow-button")

    #Either Set Opacity to 0 or set background color to transparent.
    arrow_region.setOpacity(0.0)
    arrow_region.setBackground( Background( array(BackgroundFill, [BackgroundFill( Paint.valueOf(Color.TRANSPARENT.toString()), CornerRadii.EMPTY, Insets.EMPTY)]) ) )

    #Set a Background Image for the .arrow-button StackPane.
    arrow_button.setBackground(Background( array(BackgroundImage, [BackgroundImage( Image( String(File('..<file>.png').toURI().toString()), True) , BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER, BackgroundSize.DEFAULT)] ) ) )       //if you want, edit this
Adrian David
  • 1
  • 1
  • 3