Make WebView ignore scene CSS in JavaFX - java

When I load a site / html using javafx.scene.web.WebView that site seems to be affected by my scene custom styling. A minimal example to demonstrate the issue.
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
VBox root = new VBox();
primaryStage.setScene(new Scene(root, 900, 900));
String css = Main.class.getResource("/test.css").toExternalForm();
primaryStage.getScene().getStylesheets().add(css);
WebView webView = new WebView();
root.getChildren().add(webView);
webView.getEngine().load("http://google.pl");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
test.css
.text-area,
.text-field {
-fx-background-color: red;
}
This results in
Ultimately I wish for a method like webview.getEngine().dontInheritStyles()of course there is none and I couldn't find any method of doing it otherwise. Tried:
webView.getStylesheets().clear();
webView.setStyle(null);
webView.getStyleClass().clear();
none of them worked. One way that I think could make this work (haven't tried it yet tho) would be to open the webview in a sub window which doesn't use the same scene, however I want the webview to be embeded in my existing application view so that option would be my last resort and I rather avoid it.

You can use some kind of hack, such as a combination of JavaFX and Swing.
You have two classes:
JFXPanel - which allows you to embed a JavaFX control into Swing
SwingNode - which allows you to embed a Swing control into JavaFX
You can combine the use of the JFXPanel and SwingNode classes in the wrapper class:
public class Styleless<T extends Parent> extends StackPane {
private T root;
public Styleless(T root) {
this.root = root;
JFXPanel jfxPanel = new JFXPanel();
jfxPanel.setScene(new Scene(root));
SwingNode swingNode = new SwingNode();
swingNode.setContent(jfxPanel);
getChildren().add(swingNode);
}
public T getRoot() {
return root;
}
}
And you can use it like this:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
VBox root = new VBox();
primaryStage.setScene(new Scene(root, 900, 900));
String css = Main.class.getResource("/test.css").toExternalForm();
primaryStage.getScene().getStylesheets().add(css);
WebView webView = new WebView();
webView.getEngine().load("http://google.pl");
Styleless<WebView> webViewStyleless = new Styleless<>(webView);
root.getChildren().add(webViewStyleless);
VBox.setVgrow(webViewStyleless, Priority.ALWAYS);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

You can also use style classes in your CSS to manage which elements get styles applied to them. Define a 'myapplication' class in your css, and add that class to your root node.
test.css
/* any text fields inside a container with the myapplication style class */
.myapplication > .text-field{
-fx-background-color: red;
}
/* any text areas inside a container with the myapplication style class */
.myapplication > .text-area{
-fx-background-color: red;
}
Main.java
...
root.getStyleClass().add("myapplication");
TextField txtField = new TextField("Application text field");
root.getChildren().addAll(webView, txtField);
...
Using the '>' css selector (https://www.w3schools.com/cssref/css_selectors.asp) this way will apply the style to text fields whose parent has the style class 'myapplication'. When a WebView is created, it has the style class 'web-view' (https://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html#webview)
While there is no real way to prevent style values from being inherited, style classes are not added to children.

Related

How do I change the color of an anchorpane from the controller class?

For a GUI I'm making the minimize, maximize, close buttons similar to that of Spotify by using an anchorpane for each 'button'. I created the buttons via scenebuilder and they are loaded to the class via fxml. I can't figure out how to call a particular instance of an anchorpane in the controller class to change its background color when the mouse enters or exits.
public static Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setScene(new Scene(root, 1280, 800));
...
this.primaryStage = primaryStage;
etc.
is how the UI class is set up
I expect the color of the anchorpane to change when the mouse enters the bounds, but as of now I have no idea how to call it.
Solved it, had to set an fxid to the anchorpane and then initiate it in the controller class after an #FXML.
Kinda like this:
#FXML
Anchorpane someButton;
#FXML
public void makeButtonWhite(MouseEvent event)
{
someButton.setStyle("-fx-background-color: #ffffff");
}
The most convenient way of doing this imho is using a stylesheet to assign the background.
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" stylesheets="#style.css">
<children>
<AnchorPane prefWidth="30" prefHeight="20" styleClass="my-button"/>
</children>
</AnchorPane>
style.css (same directory as fxml)
/* default style */
.my-button {
-fx-background-color: blue;
}
/* style when mouse is inside the region */
.my-button:hover {
-fx-background-color: red;
}
This allows you to easily add the style to multiple Regions; you simply need to add the style class it (styleClass="my-button").
the code you need for starting this must be the starting class
public class Class extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(this.getClass().getResource("/Folder/File.fxml"));
loader.setController(yourControllerClass());
//only if you do something with the controller class afterwards
YourControllerClass controller = loader.setController();
Parent parent = loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(parent);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
you know the code method in scenebuilder there you can set on mouse entered and exited for a button, a label etc. then if for example the button with the id "closeButton" and the OnMouseEntered "closeButtonOnEntered" and the OnMouseExited "closeButtonOnExited"
this will be the code you need
public yourcontrollerclass {
#FXML
Button closeButton;
#FXML
private void closeButtonOnEntered() {
//sets button red
button.setStyle("-fx-background-color: #ff0000");
}
#FXML
private void closeButtonOnExited() {
//sets button your first color
button.setStyle("-fx-background-color: transparent");
}
}
this can be done with nearly every thing in sceneBuilder
I hope i could help you(sorry for my bad english)

JavaFX - How to change scene using a scene from another class?

I have the following class which has a button.
public class GUI extends Application {
private BorderPane mainLayout = new BorderPane();
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Main Menu");
FlowPane layout = new FlowPane();
Button button = new Button("Click");
layout.getChildren().addAll(button);
mainLayout.setTop(layout);
Scene scene = new Scene(mainLayout, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
If I have another class with a scene, how can I update the GUI class to show the scene by pressing the button?
The preferred mechanism would probably be to get the stage dynamically from a trigger event, for example:
button.setOnAction(event -> {
Scene newScene = // ... commands which define the new scene.
Stage stage = ((Node) event.getTarget()).getScene().getStage();
// or alternatively, just:
// Stage stage = button.getScene().getStage();
stage.setScene(newScene);
});
An alternative is to provide a static accessor to the main stage in the Application.
Change your GUI class to add an accessor for the stage:
public class GUI extends Application {
private static Stage guiStage;
public static Stage getStage() {
return guiStage;
}
#Override
public void start(Stage primaryStage) {
guiStage = primaryStage;
// other app initialization logic . . .
}
}
In your class which needs to change the scene for the GUI stage to a new scene, invoke:
Scene newScene = // ... commands which define the new scene.
GUI.getStage().setScene(newScene);
Using a static accessor in this specific instance is generally OK, because you can only have a single Application instance launched for a given JVM execution. The only real drawback is that you have a coded dependency between the class creating your new scene and your Application class. But, for some application types, this won't matter.

How to change styling for disabled ListView in JavaFX?

I'm trying to make a disabled ListView look the same as a non-disabled ListView.
I looked it up and found that you have to set the opacity to 1 and have tried a number of approaches, with and without stylesheets, but none seem to work. I have tried:
listView.setStyle("-fx-opacity: 1.0;");
.listView:disabled {
-fx-opacity: 1.0;
}
.listView .list-cell {
-fx-opacity: 1.0;
}
.listView .list-cell:disabled {
-fx-opacity : 1.0;
}
I have also tried setting the background-color to white but this makes the text invisble for some reason, even though no other color does that. How would I go about doing this? Thanks.
Setting the opacity in the disabled pseudo-class (as you mentioned) should be enough:
.list-view:disabled {
-fx-opacity: 1;
}
just dont forget to add the stylesheet to the Scene:
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
As an example:
public class Main extends Application {
#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());
ListView<String> lw = new ListView<>();
lw.setItems(FXCollections.observableArrayList("dog", "cat", "whale"));
lw.setDisable(true);
root.setCenter(lw);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
and the CSS selector is contained in the file application.css and this file placed in the same directory as the Application class file.
Note: If you would like to have the selection also to look like the same, you can define the following selectors also:
.list-cell:disabled,
.list-cell:disabled,
.list-view:disabled {
-fx-opacity: 1;
}

JavaFX load one of fxml file depends on window

I'm totally new in JavaFX... Until now, for my basic app I had all in one class - Main class. Now I want to extend my app and do this in proper way, so I would like to make controllers. As far as I know, I need to put package of my controller in root element in FXML file.
In my case it is AnchorPane element and I put something like this:
fx:controller="hr.controller.MainWindowController"
And in this controller should be all injections for ID's and methods, right? By injections I mean #FXML annotation. But how I connect this with my Main class? What should be in Main class? I know (I thinks so :P) that Main should extends Application class. So it contains this method:
#Override
public void start(Stage stage) {}
What's more and what I said in the beginning I also want be able to load/open new window after some action. Let's say that I have file mainWindow.fxml with above reference to the controller. The second file window2.fxml should be loaded after action on button in the 1st window.
Could you tell me, how should I achive this?
UPDATE!
Main class:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("mainWindow.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root));
primaryStage.setResizable(false);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
MainWindowController:
public class MainWindowController{
#FXML
private Button btnSave;
#FXML
private MenuBar menuBar;
#FXML
private MenuItem exit;
#FXML
public void handleAction(ActionEvent event) throws IOException {
Stage stage = null;
Parent root = null;
if(event.getSource()==exit){
stage = (Stage) menuBar.getScene().getWindow();
root = FXMLLoader.load(getClass().getResource("window2.fxml"));
} else {
stage =(Stage) btnSave.getScene().getWindow();
root = FXMLLoader.load(getClass().getResource("mainWindow.fxml"));
}
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
Is this the proper way? It works how I want to, but I'm not sure if it should looks like this :)

How to return to code after I'm done with JavaFX scene

I'm launching my JavaFX scene as:
Applicaiton.launch(Main.class);
form my java code.
How to return to my code after I'm done with JavaFX!
Example:
public String Method()
{
Stirng s = "MyName";
Application.launch(Main.class);//here I lauch JavaFX scene
s.trim();//how to come back here after I'm done with that scene.
}
It's not supposed to work that way. You start by extending javafx.application.Application, then the entry point is start(Stage), which you must override. That method is the place where you have to set up the Scene for your stage, build the layout with Node's (buttons, layout managers, text fields, checkboxes), and register event handlers. You can access startup parameters with getParameters().
The application can be launched by providing the usual main() that calls launch(), a facility of JavaFX. So the minimal JavaFX application looks like:
public class MyApp extends Application {
#Override
public void start(Stage primaryStage) {
VBox root = new VBox();
root.getChildren().setAll(new Label("Hello world!"));
Scene scene = new Scene(root, 600, 400);
// Add widgets and set up event handlers
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String... args) {
launch(args);
}
}
Platform.exit(); will do it click here for javafx2.2 documentation

Categories