Wrong resize of RadioButtons in JavaFX WebView - java

When I zoom in on a web page opened in a WebView (zoom factor < 1.0), most of the content gets scaled as expected, however there are some things that do not, like radio buttons or check boxes:
In the above image the first WebView is unzoomed and the second has a zoom factor of 0.5 though the radio buttons have (or have nearly) the same size.
Here is the code for this example:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class WebViewScalingDemo2 extends Application {
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("HTML");
stage.setWidth(800);
stage.setHeight(800);
Scene scene = new Scene(new Group());
final VBox root = new VBox();
String html = "<html><head><meta charset=\"utf-8\"><title>Radio-Buttons definieren</title><style>"+
"fieldset{"+
"border:none;"+
"}</style></head>"+
"<body><h1>Hier wird abkassiert!</h1><form action=\"#\"><p>Geben Sie Ihre Zahlungsweise an:</p>"+
"<fieldset><input type=\"radio\" id=\"mc\" name=\"Zahlmethode\" value=\"Mastercard\"><label for=\"mc\"> Mastercard</label><br>"+
"<input type=\"radio\" id=\"vi\" name=\"Zahlmethode\" value=\"Visa\"><label for=\"vi\"> Visa</label><br>"+
"<input type=\"radio\" id=\"ae\" name=\"Zahlmethode\" value=\"AmericanExpress\"><label for=\"ae\"> American Express</label>"+
"</fieldset></form></body></html>";
WebView webView1 = new WebView();
webView1.setPrefSize(300, 300);
webView1.setLayoutX(250);
webView1.setLayoutY(250);
webView1.getEngine().loadContent(html);
WebView webView2 = new WebView();
webView2.setPrefSize(300, 300);
webView2.setLayoutX(250);
webView2.setLayoutY(250);
webView2.getEngine().loadContent(html);
root.getChildren().addAll(webView1, webView2);
webView1.setZoom(1);
webView2.setZoom(0.5);
scene.setRoot(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
For me this is an issue because there are labels close to the right of the buttons, and if the zoom factor is too low, the label will be partially over the radio button.
I have two questions here:
Why are the radio buttons not scaled properly? They also stay the same size when using a zoom factor > 1.0.
Is there anything I can do about this? I was thinking along the lines of some CSS I could indject to force larger spacing when the zoom is low, but that would be dependent on the used zoom factor and it should be a solution that works for all radio buttons and check boxes, without the need to target them specially (by id or style class).

This is an issue with the native code, the rendering with webkit in javafx and is still present in Java 1.8.0_60.
The workaround is to apply -webkit-transform: scale(**your scale here**); on this elements on your own.
If you don't intend to change the zoomProperty dynamically I suggest adding CSS style:
input[type="checkbox"], input[type="radio"] {
-webkit-transform: scale(**your scale here**);
}
The other approach is with javascript and is good if you intend to change the zoomProperty dynamically:
(function($){
var scaleCheckboxesAndRadioButtons = function(scale) {
var $elements= $('input[type="checkbox"], input[type="radio"]');
$elements.css("-webkit-transform", "scale(" + scale + ")");
}
}(jQuery));
Than just add a listener on the zoomProperty of your webview and on change call the javascript function from your java code:
webView.zoomProperty().addListener((observable, oldValue, newValue) -> {
webView.getEngine().executeScript("scaleCheckboxesAndRadioButtons(" + newValue + ");");
});

Related

JavaFX ScrollPane receiving ScrollEvents on mouse enter

I have a JavaFX application with a ScrollPane that handles resizing of nodes upon scrollEvents. However when the JavaFX stage (or Window) is not focused, I get an odd behaviour that I think might be a JFX bug, though wondering if anyone has encountered or managed to resolve it.
To replicate the issue, if you lose focus on the JavaFX window and perform some scrolling using the mouse-wheel on another window (eg your browser), and then relatively quickly move your mouse back to re-enter the JavaFX window (without clicking, scrolling or focusing upon the JavaFX window) the JavaFX window receives a bunch of scrollEvents despite no mouse-wheel action being performed.
I'm wondering if anyone has encountered this and worked out a way to somehow filter these odd scrollEvents out as it results in some strange zooming action that is unexpected given the lack of mouse-wheel scrolling!
I'm using Java & JavaFX 17 (OpenJFX), see below sample application that demonstrates, thanks!
public class ScrollEventIssueApplication extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane borderPane = new BorderPane ();
borderPane .setPrefWidth(600);
borderPane .setPrefHeight(600);
Pane content = new Pane();
content.setPrefWidth(1000);
content.setPrefHeight(1800);
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setPrefWidth(700);
scrollPane.setPrefHeight(700);
content.setOnScroll(event -> {
System.out.println("Scroll event received: " + event.getDeltaY());
});
borderPane.setCenter(scrollPane);
Scene scene = new Scene(borderPane, 1800, 900);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
It appears that this may be a platform-dependent feature of the installed pointing device and driver. I could occasionally reproduce the effect on Mac OS X 12 with JavaFX 17, but only when I also accidentally raked an errant finger or two across the mouse's multi-touch surface.
For reference, I tested the following simpler variation. Note code to enumerate the OS and Java version numbers, as well as changes to the viewport dimensions in order to show the scroll bars when set to AS_NEEDED by default.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
/** #see https://stackoverflow.com/q/72485336/230513 */
public class ScrollTest extends Application {
private static final double W = 640;
private static final double H = 480;
#Override
public void start(Stage stage) {
Pane content = new StackPane();
content.getChildren().addAll(new Rectangle(W, H, Color.BLUE),
new Circle(Math.min(W, H) / 2, Color.LIGHTBLUE));
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setPrefViewportWidth(2 * W / 3);
scrollPane.setPrefViewportHeight(2 * H / 3);
content.setOnScroll(event -> System.out.println(
"dx dy: " + event.getDeltaX() + " " + event.getDeltaY()));
stage.setScene(new Scene(scrollPane));
stage.show();
}
public static void main(String[] args) {
System.out.println(System.getProperty("os.name")
+ " v" + System.getProperty("os.version")
+ "; Java v" + System.getProperty("java.version")
+ "; JavaFX v" + System.getProperty("javafx.runtime.version"));
launch(args);
}
}
I realised the issue was the mouse I was using (a Logitech MX Vertical Advanced Ergonomic) had a "smooth scrolling" option on the mouse wheel that meant to it would "continue" some small scroll events after the mouse wheel had stopped scrolling which were causing this issue. Turning off that option in the Logitech Options application resolved the issue.

How to get the X and Y location of a javaFX node for displaying a PopupControl?

I have created a JavaFX class that is similar to the DatePicker class but instead displays a popup with a way to choose a time. I am wanting to have the popup appear underneath the TextField and Button just as the DatePicker Popup does, but I am unable to find a way to get the X and Y coordinates of the Nodes. Any help would be appreciated.
You can get screen co-ordinates using: node.localToScreen(x,y).
The following code will locate (anchor) the top left corner of a pop-up window at the bottom center of a node on the pop-up owner window:
Point2D anchorPoint = node.localToScreen(
node.getWidth() / 2,
node.getHeight()
);
popup.setAnchorLocation(
PopupWindow.AnchorLocation.WINDOW_TOP_LEFT
);
popup.show(
node,
anchorPoint.getX(),
anchorPoint.getY()
);
Sample application:
Displays and hides a popup located at the bottom center of controlling button.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.stage.*;
public class Popcorn extends Application {
#Override
public void start(Stage stage) {
StackPane popupLayout = new StackPane(
new Label(
"Clarke's third law\nAny sufficiently advanced technology is indistinguishable from magic."
)
);
popupLayout.setStyle("-fx-opacity: 0.8; -fx-background-color: paleturquoise;");
popupLayout.setPadding(new Insets(10));
Popup popup = new Popup();
popup.getContent().add(
popupLayout
);
popupLayout.setOnMouseClicked(event -> popup.hide());
ToggleButton showPopupButton = new ToggleButton("Show popup");
showPopupButton.textProperty().bind(
Bindings.when(showPopupButton.selectedProperty())
.then("Hide popup")
.otherwise("Show popup")
);
showPopupButton.selectedProperty().addListener((observable, wasSelected, isSelected) -> {
if (isSelected) {
showPopup(popup, showPopupButton);
} else {
popup.hide();
}
});
popup.setOnShown(event -> showPopupButton.setSelected(true));
popup.setOnHidden(event -> showPopupButton.setSelected(false));
StackPane stageLayout = new StackPane(showPopupButton);
stageLayout.setPadding(new Insets(10));
stage.setScene(new Scene(stageLayout));
stage.show();
}
private void showPopup(Popup popup, Control ownerNode) {
Point2D anchorPoint = ownerNode.localToScreen(
ownerNode.getWidth() / 2,
ownerNode.getHeight()
);
popup.setAnchorLocation(
PopupWindow.AnchorLocation.WINDOW_TOP_LEFT
);
popup.show(
ownerNode,
anchorPoint.getX(),
anchorPoint.getY()
);
}
public static void main(String[] args) {
launch(args);
}
}
A note on popup anchors
The popup anchor point is just advice to the popup system on where to place the popup. The popup logic built into JavaFX is smart enough to know the size of the popup window and its initial location on the screen. If that means that there is not enough real estate left on the screen to display the popup at the suggested anchor point, the popup implementation will automatically adjust the anchor point to ensure that the popup will be entirely visible when first displayed. You can try this out by running the example code and placing the owner stage at the extreme bottom or right of the screen before trying to show the popup.

JavaFX- Button Height Doesn't Get Smaller?

.button
{
-fx-background-image: url("mapDefault.png");
-fx-pref-width: 10px;
-fx-pref-height: 10px;
}
My button looks like this, and theoretically, it should fit the image (10x10) exactly. However
It returns that the button is about 30px in height, despite that I changed it. The button is declared like
Button beachButton = new Button();
and does not have any text values in it.
Any possible causes to this increase/min-cap in height?
Any changes in text need to be taken into account, especially custom font. To accommodate for any picture sizes smaller than the font of the item, set the font size equal to 0px as well.
The offset of the icon(or text) from the buttons border may be the problem. A Button with text in default font type & size cannot get smaller in height than 25px (using setPrefHeight() or setStyle(...)). To make the height the same as other controls the padding could be reduced as in the following example:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SmallerButtons extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Label label1= new Label ("Label1");
Label label2= new Label ("Label2");
CheckBox checkBox1= new CheckBox ("CheckBox1");
CheckBox checkBox2= new CheckBox ("CheckBox2");
Button button1= new Button ("Button1");
Button button2= new Button ("Button2");
Button button3= new Button ("Button3");
button3.setOnAction(a-> System.out.println(label1.getHeight() + " / " +checkBox1.getHeight() + " / " +button1.getHeight()));
label1.setStyle("-fx-font-size:10");
button1.setPadding(new Insets(0,3,0,3)); // <----- this one works. Standard value is 5.0.
button1.setMaxWidth(200);
button2.setPrefHeight(17); // no effect
button3.setStyle("-fx-pref-height:35px"); // or also setPrefHeight(35)
VBox vb1=new VBox(label1,label2);
VBox vb2=new VBox(checkBox1,checkBox2);
VBox vb3=new VBox(button1, button2, button3);
vb3.setMaxWidth(80);
vb1.setSpacing(1);
vb2.setSpacing(1);
vb3.setSpacing(1);
HBox hb = new HBox(vb1,vb2,vb3);
hb.setSpacing(15);
Scene scene = new Scene(hb);
stage.setOnCloseRequest((e)->System.exit(0));
stage.setScene(scene);
stage.show();
}
}
One catch: With one buttons padding adjusted all other buttons need the padding explicitly set, too.

Why does disabled JavaFX TextArea has a different color than TextField

I am restyling a JavaFX application, but I have a problem with the :disabled styles. When I try to change the -fx-text-fill and -fx-opacity settings, textareas still get a slightly lighter text color than textfields. This is the style I got right now:
/*** Text Fields and Areas ***/
.text-field,
.text-area {
-fx-text-fill: #000;
-fx-opacity: 1.0;
}
.text-field:disabled,
.text-area:disabled {
-fx-text-fill: #000;
-fx-opacity: 0.5;
}
This is how the disabled components look in the program:
Screenshot from the JavaFX application
As you can see, the text color of the TextField is #7a7a7a which is 50% of #000. The TextArea however appear to have the color #c7c7c7 which is 25% of #000.
Does anyone know how I can get the same disabled color for textareas as for textfields?
What's going on
IMO the current behaviour is a bug and should be filed at http://bugreport.java.com (I have done this, unfortunately the Java bug reporting system does not provide any way to track this bug report unless it is accepted by the JavaFX team).
The issue is that the opacity modifier for the text in the text area is applied twice. The default TextArea skin is implemented as a TextArea control node, with an internal ScrollPane in it and, when the TextArea is disabled, the opacity of both is set to 0.4, so the text (and scroll bars in the scroll pane) have the disability opacity fading effect applied twice (which is wrong). You can see this by inspecting a disabled TextArea control in ScenicView.
Workaround
Explicitly set the disabled scroll-pane's opacity to 1 when it is contained within a text-input control.
.text-input > .scroll-pane:disabled {
-fx-opacity: 1;
}
Sample app:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.scenicview.ScenicView;
public class DisabilityAssistance extends Application {
#Override
public void start(Stage stage) throws Exception{
TextArea area = new TextArea("Text Area");
area.setDisable(true);
TextField field = new TextField("Text Field");
field.setDisable(true);
Scene scene = new Scene(new VBox(10, area, field));
stage.setScene(scene);
stage.show();
scene.getStylesheets().add(getClass().getResource(
"disability.css"
).toURI().toURL().toExternalForm());
ScenicView.show(stage.getScene());
}
public static void main(String[] args) {
launch(args);
}
}
Output (without CSS work-around applied):
Output (with CSS work-around applied):

Why doesn't TextArea occupy entire area of ScrollPane in JavaFX? How to add scroll bars to TextArea in JavaFX?

In Swing I was to add JTextArea into JScrollPane in order JTextArea to have scroll bars. When I do the same in JavaFX, the behavior is different.
This example
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;
public class SlideshowForSlipryPage2 extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("SlideshowForSlipryPage");
primaryStage.setScene(
new Scene(
new ScrollPane(
new TextArea() {{
setPromptText("[PROMPT 1]");
}}
)
, 300, 250
)
);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
gives
i.e. text area is wider than a window and shorter than it.
Why and how to fix?
This issue has nothing to do with the pref width of the text area.
You must set the wrap property to true so that the text will wrap onto two lines when the cursor hits the edge of the container. 2 ways:
1) Using scene builder is the most simple way. Simple click "Wrap Text" in the properties of the TextArea.
2) Call the following method of the TextArea:
textArea.setWrapText(true);
You have to call setFitToWidth(true) and setFitToHeight(true) on the ScrollPane, you can set those properties in the scene builder Layout tab after selecting the ScrollPane.
Try this:
textArea.setPrefSize( Double.MAX_VALUE, Double.MAX_VALUE );
and set scrollpane size same as parent like this:
scrollPane.prefWidthProperty().bind(<parentControl>.prefWidthProperty());
scrollPane.prefHeightProperty().bind(<parentConrol>.prefHeightProperty());
I hope it resolves your query !!!

Categories