How to make a button appear to have been clicked or selected? (JavaFX2)
Asked Answered
D

3

14

When user presses a button in my JavaFX2.2 UI, a dark blue halo appears to show it was clicked. During the course of events, my program may want to 'unclick' it to show it is no longer selected.

I expected a button.setSelected(false); I remember Swing used to have this, but there isn't such a call in JavaFx. This article discusses drop shadows but I want to simply use the same appearance used when a button is clicked which isn't really a drop shadow. setEffect(new DropShadow()) Using setStyle() is ok, but what values to use?

Deepsea answered 4/4, 2013 at 18:45 Comment(0)
G
26

Update March 2023

Changed links to reference modern versions of documents. These are JavaFX 8 for Oracle tutorials as those are the latest they provided and JavaFX 20 for API documents and reference guides.

Some parts of the example may still use or rely on obsolete features of JavaFX which were relevant at the time the answer was originally created, so you might not get the exact outcome shown in the sample images, but, in general, most parts of the answer are still relevant today.


I think for your description, that you actually want a ToggleButton rather than a standard Button.

There is a nice Oracle tutorial on ToggleButtons.

enter image description here


Addressing Items from the Question

dark blue halo appears to show it was clicked

The blue halo is actually a focus ring indicating that a control has focus. To focus a control, you can invoke the requestFocus method.

During the course of events, my program may want to 'unclick' it to show it is no longer selected.

To remove focus from a control, you can call requestFocus on another control to focus on that control instead.

I expected a button.setSelected(false);

To have a button which can toggle between a selected and unselected state, use a ToggleButton. A ToggleButton has a setSelected method.

setEffect(new DropShadow()) Using setStyle() is ok, but what values to use

css style values for drop shadow effects are define in the JavaFX CSS Reference Guide.

It is visually equivalent to define the drop shadow effect in code via setEffect or via css with setStyle or applying a style class from a stylesheet. Of the three approaches, I would never recommend the setStyle approach, but only the css from stylesheet or the setEffect from code approach.


Related

Note there is an additional related property - armed:

Indicates that the button has been "armed" such that a mouse release will cause the button's action to be invoked. This is subtly different from pressed. Pressed indicates that the mouse has been pressed on a Node and has not yet been released. arm however also takes into account whether the mouse is actually over the button and pressed.

A button in an armed state has a slightly different look than one which is not in an armed state. Most programs never need to interact with the armed state of buttons.


Sample for styling the selected state

The standard way to differentiate a selected ToggleButton from one which is not Selected is to darken it to give it a depth style effect and make it appear further away when it has been pushed. Here is the standard toggle button css for JavaFX 20:

.toggle-button:selected {
        -fx-background-color:
            -fx-shadow-highlight-color,
            linear-gradient(to bottom, derive(-fx-outer-border, -20%), -fx-outer-border),
            linear-gradient(to bottom,
                    derive(-fx-color, -22%) 0%,
                    derive(-fx-color, -13%) 20%,
                    derive(-fx-color, -11%) 50%);
    -fx-background-insets: 0 0 -1 0, 0, 1;
}
.toggle-button:selected:focused {
    -fx-background-color:
        -fx-focus-color,
        linear-gradient(to bottom,
            derive(-fx-color, -22%) 0%,
            derive(-fx-color, -13%) 20%,
            derive(-fx-color, -11%) 50%),
        -fx-faint-focus-color,
        linear-gradient(to bottom,
            derive(-fx-color, -22%) 0%,
            derive(-fx-color, -13%) 20%,
            derive(-fx-color, -11%) 50%);
    -fx-background-insets: -0.2, 1, -1.4, 2.6;
    -fx-background-radius: 3, 2, 4, 0;
}

You can override this default behavior by defining your own stylesheet that provides an alternate definition for the selected state. The sample below will ensure that the selected state display is much more subtle than the standard, only darkening the selected state color a small fraction rather than a lot.

toggle-plain Unselected

toggle-selected Selected

Associated css:

/**
 * file: colored-toggle.css
 *   Place in same directory as ColoredToggle.java.
 *   Have your build system copy this file to your build output directory.
 **/

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

.toggle-button {
  -fx-color: paleturquoise;
}

.toggle-button:selected {
    -fx-background-color:
        -fx-shadow-highlight-color,
        linear-gradient(to bottom, derive(-fx-color,-22%) 0%, derive(-fx-color,-15%) 100%),
        linear-gradient(to bottom, derive(-fx-color,-15%) 0%, derive(-fx-color,-10%) 50%, derive(-fx-color,-8%) 98%, derive(-fx-color,-12%) 100%);
}

.toggle-button:selected:focused {
    -fx-background-color:
        -fx-focus-color,
        linear-gradient(to bottom, derive(-fx-color,-22%) 0%, derive(-fx-color,-15%) 100%),
        linear-gradient(to bottom, derive(-fx-color,-15%) 0%, derive(-fx-color,-10%) 50%, derive(-fx-color,-8%) 98%, derive(-fx-color,-12%) 100%);
}

Source file:

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ColoredToggle extends Application {
  public static void main(String[] args) { Application.launch(ColoredToggle.class, args); }
 
  @Override public void start(Stage stage) {
    ToggleButton visibilityControl = new ToggleButton("Winterfell");

    VBox layout = new VBox(10);
    layout.setAlignment(Pos.CENTER);
    layout.getChildren().setAll(visibilityControl);

    layout.getStylesheets().add(getClass().getResource("colored-toggle.css").toExternalForm());
    
    stage.setScene(new Scene(layout));
    stage.show();
  }
}
Grazia answered 4/4, 2013 at 21:31 Comment(6)
I shall change to use a ToggleButton. My apologies for the delay in replying.Deepsea
I changed my buttons to toggle button and tried the following, but setting the button color removed the dark blue halo - it did not appear even when I requested focus. Any suggestions appreciated. String activeButtonStyle = "-fx-background-color:PaleTurquoise;"; b.setStyle(activeButtonStyle); b.requestFocus();Deepsea
Use -fx-base: paleturquoise; instead of -fx-background-color: paleturquoise;Grazia
I tried it but it looks very dark gray blue - not pale turquoise blue.Deepsea
The point of making a button appear selected is to change it's style from the default. The default mechanism to do this is to darken the button. I added some sample code and styles to the answer which demonstrates only darkening the selected style a little bit rather than a lot.Grazia
I meant that the default would turn to pale turquoise with the dark blue halo when selected. When unselected, it would appear just like a normal toggle button. Sorry for not explaining clearly.Deepsea
N
4

From a css stylesheet this command also works. There is no need for a toggle button. Once you press a different button, focus changes to the new one.

.button:focused{
    -fx-background-color: gray;
Niko answered 9/2, 2017 at 15:37 Comment(0)
I
1

I'm using JavaFx13, I used css to achieve my active button state. Here is my code(Only some part).

Controller:

@FXML
private BorderPane bp;

@FXML
private Button mnu_list;

@FXML
private Button mnu_address;

private Button activeMenu;

// This is my Constructor
public DashboardController() {
    this.activeMenu = null;
}

public void initialize() {

    mnu_list.setOnMouseClicked(
            actionEvent -> this.setActiveMenu(mnu_list, ListController.class)
    );

    mnu_address.setOnMouseClicked(
            actionEvent -> this.setActiveMenu(mnu_address, AddressController.class)
    );
}

private void setActiveMenu(Button s, Class c) {
    if (c != null) {
        // I'm using FXWeaver. Here I'm setting a new fxml to borderpane.
        bp.setCenter(this.fxWeaver.loadView(c));
    }
    if (this.activeMenu != null) {
        this.activeMenu.getStyleClass().removeAll("active");
    }
    s.getStyleClass().add("active");
    this.activeMenu = s;
}

CSS:

.btn_menu{
    -fx-background-radius: 0px;
    -fx-cursor: hand;
    -fx-alignment: center-left;
}
.btn_accent_color{
    -fx-background-color: #7c4dff;
    -fx-text-fill: #FFFFFF;
}
.btn_accent_color.active{
    -fx-background-color: #aa3bfe;
    -fx-text-fill: #FFFFFF;
}

Note: In .fxml file - I have set all menu button class as btn_menu, btn_accent_color.

Info: Here I'm toggleing (adding and removing) a css-class to a button. (Same as we do in Website using js)

Ieyasu answered 21/2, 2021 at 8:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.