JavaFX synchronize Scroll pane content changes and its Vvalue - javafx

I have a Scrollpane containing 5 Titledpanes(Categories) each containing 2 images. Height of each TitledPane is 200 in expanded state and 25 in collapsed state.
Here is the image :
I have a text field in which I can enter the number of the Image to be selected.
For example, If I enter "4", the scroll pane scrolls to the Image-4 in Category-2 as shown below.
I am able to achieve this by calculating the Vvalue of the scroll pane based on the height to be scrolled and total height of the content inside the scrollpane.
Here is the issue
In the above example, if Category-2 is collapsed, I have to "Expand" it and then move to Image-4.
I am doing this by using the following code algorithm :
// Let the height of the scroll pane content with TitledPane-2 in collapsed state be (ht_ScrollPaneBefore = 825) and ScrollPane view port height be (ht_Viewport = 180). Let the ht of the scrollpane after expansion of TitledPane-2 be ht_ScrollPaneAfter which is calculated in step 3 in the following algorithm.
{
....
1. Expand TitledPane-2
2. Call applyCss() and layout() on the TitledPane to make the Expansion effective.(As addressed in the other query : https://stackoverflow.com/questions/26152642/get-the-height-of-a-node-in-javafx-generate-a-layout-pass)
3. Get the height of the total Content(ht_TotalContent) of the scroll pane with the expanded TitledPane-2.(ht_ScrollPaneAfter = 1000)
4. ****** Calculate the Vvalue based on (ht_TotalContent).(let it be Vvalue = 0.56) *****
5. Set the Vvalue to the scrollpane.
....
}
Issue
Since the expansion of the Titledpane is not yet applied "Visually" on the view, the Vvalue(0.52) is getting applied relative to ht_ScrollpaneBefore(800) which is leading to an incorrect scrolling ht as shown in the "Actual output" below.
Actual Output:
Expected:
Note: The ht of the scrollpane after the expansion of the TitledPane-2 is obtained successfully using the procedure described in the link: Get the height of a node in JavaFX (generate a layout pass). The Vvalue has been calculated based on ht_ScrollPaneAfter only not on ht_ScrollPaneBefore. The only problem here is the application of Vvalue is going wrong as the scroll pane view is not yet updated with the expanded TitledPane-2.
Code Sample :
private void scrollToImage() {
int imageNum = Integer.parseInt(textField.getText()); // Number entered in text field
int categoryNum = imageNum / 2 + imageNum % 2; // Which category the image number belongs to.
Bounds scrollViewBounds = mscr.localToScene(mscr.getBoundsInLocal()); // viewport bounds.
Platform.runLater(() -> {
System.out.println("Before: " + mvbx.getHeight());
titledPaneList[categoryNum - 1].setExpanded(true);
mvbx.applyCss();
mvbx.layout();
});
double scrollHt[] = {0.0};
//ht of the categories before selected image category.
for (int idx = 0; idx < (categoryNum - 1); idx++) {
scrollHt[0] += titledPaneList[idx].getHeight();
}
// check if the image is first/second image in the category.
int imageIdxInCategory = (imageNum % 2 == 0) ? 1 : 0;
// If the selected image is second image in the category,
// add the titlebar and first image ht to the scroll ht.
if (imageIdxInCategory > 0) {
scrollHt[0] += 25 //Title bar ht.
+ titledPaneList[categoryNum - 1].getInsets().getTop() // titled pane top insets
+ ((VBox) titledPaneList[categoryNum - 1].getContent()).getChildren().get(0).getBoundsInLocal().getHeight(); // ht of the first image
// Note: Since, my titledpane contains a vbox which contains the
// image number label and imageview,
// I am calculating the ht of the first vbox her in the above code.
}
Platform.runLater(() -> {
System.out.println("After: " + mvbx.getHeight());
double d_ScrollHtPerPix = 1
/ (mvbx.getHeight()
- (scrollViewBounds.getHeight() - mscr.getInsets().getTop() - mscr.getInsets().getBottom()));
mscr.setVvalue(scrollHt[0] * d_ScrollHtPerPix);
});
}
}
Output :
When the category of the selected image is collapsed,
Before : 825
After : 825
As u see, the height of the scrollpane content is same even after expanding the collapsed category.

Related

Get visible elements from AnchorPane

I'm developing a JavaFX application where the user is able to zoom and drag the elements (all contained in a AnchorPane). Some of those elements are simple lines and I need to have something like a ruler that has different parent to stick on the screen on the same position even if the user zooms or drags the mentioned AnchorPane. I got almost everything working but I have one problem, I need to know which lines from the AnchorPane are visible to the user (as if the user zooms and drags the AnchorPane, some of the lines are not visible anymore). Here's what I tried (not working...)
private List<Double> getVisibleVerticalLinesXCoordonate() {
List<Double> xCoordonatesOfVisibleVerticalLines = new ArrayList<>();
List<Node> visibleNodes = new ArrayList<>();
Bounds bounds = rulerParent.getBoundsInLocal();
Bounds paneBounds = rulerParent.localToScene(bounds);
for (Node n : gridVerticalLines) {
Bounds nodeBounds = n.getBoundsInParent();
if (paneBounds.intersects(nodeBounds)) {
visibleNodes.add(n);
}
}
for (Node node : visibleNodes) {
Bounds newBounds = getRelative(node);
xCoordonatesOfVisibleVerticalLines.add(newBounds.getMinX());
}
System.out.println(Arrays.asList(xCoordonatesOfVisibleVerticalLines));
return xCoordonatesOfVisibleVerticalLines;
}
private Bounds getRelative(Node node) {
return rulerParent.sceneToLocal(node.localToScene(node.getBoundsInLocal()));
}
So, the rulerParent is what is fixed on the screen (is not zooming or dragging at all). After I have the visible lines from the AnchorPane I get the x coordinates of the lines relative to rulerParent - so I can align the ruler lines with the lines in the AnchorPane.
The problem is that this is not returning the actual visible lines...
I don't need to be able to see the whole line to consider it visible, that's why I'm using intersect...if any part of a line is visible, I need it.
It's about how you handle the zoom and dragging actions try to use ViewPort instead of scaling as with ViewPort you can know the translation X and Y coordinates of the ViewPort which is the visible X and Y coordinates of the AnchorPane

JavaFX get minimum width/height/size of a Pane before it starts clipping

FFor the sake of simplicity it is better to show you the problem and then describe it
I plan to set the minimum width of the window to the minimum size possible before the pane (groupPane in this example) starts being clipped
how can i calculate that width beforehand.
the fxml section :
the current calculation method:
public void setupMinStageSizeInStandardMode(Stage primaryStage, Scene scene)
{
// the thickness of the border of the window
ObjectBinding<Insets> insets = Bindings.createObjectBinding(() ->
new Insets(scene.getY(),
primaryStage.getWidth()-scene.getWidth() - scene.getX(),
primaryStage.getHeight()-scene.getHeight() - scene.getY(),
scene.getX()),
scene.xProperty(),
scene.yProperty(),
scene.widthProperty(),
scene.heightProperty(),
primaryStage.widthProperty(),
primaryStage.heightProperty()
);
//i am trying to get the grid to the smallest size here
controlsGrid.setMaxWidth(0);
controlsGrid.setPrefWidth(0);
// then i am trying to calculate the wanted size
double minStageWidth = controlsGrid.getWidth() +insets.get().getLeft() + insets.get().getRight();
double minStageHeight = controlsGrid.getHeight() +insets.get().getTop() + insets.get().getBottom();
System.out.println(minStageWidth);
System.out.println(minStageHeight);
//then return the grid to it's intended size
controlsGrid.setMaxWidth(Region.USE_PREF_SIZE);
controlsGrid.setPrefWidth(Region.USE_PREF_SIZE);
primaryStage.setMinHeight(minStageHeight);
primaryStage.setMinWidth(minStageWidth);
}
Please help. i am losing sleep over this...

Forcing layout change when Stage is resized in JavaFX

I ran into a possible bug in JavaFX, but having short time, I am looking for a workaround.
I have a bunch of windows containing a chart and a toolbar:
public void createGui() {
root = new VBox();
ToolBar toolbar = new ToolBar();
Button btnConfig = new Button(bundle.getString("chartWindow.menu.configure"));
btnConfig.setOnAction(e -> doChartConfig());
toolbar.getItems().addAll(btnConfig);
chartPane = new VBox();
root.getChildren().setAll(toolbar, chartPane);
VBox.setVgrow(chartPane, Priority.ALWAYS);
scene = new Scene(root);
setScene(scene);
updateChart(); // Creates a chart
}
I have to resize them from code. (Tiling the open charts.) The simplified tiling code is:
// bounds is the user available area of the monitors(s)
double windowWidth = bounds.getWidth() / tileRule.columns;
double windowHeight = bounds.getHeight() / tileRule.rows;
int r = 0;
int c = 0;
for (Window w : sel) {
w.setWidth(windowWidth);
w.setHeight(windowHeight);
w.setX(bounds.getMinX() + c * windowWidth);
w.setY(bounds.getMinY() + r * windowHeight);
c++;
if (c >= tileRule.getColumns()) {
c = 0;
r++;
if (r >= tileRule.rows) {
break;
}
}
}
When I do this, the windows are perfectly arranged:
However, as it is visible, some of the windows contents aren't resized with the window (Now, accidentally, they are the last 3, but it is not always the case. Sometimes there are more, and they are not always the last ones.)
It is clearly visible, that the scene is the one that isn't resized with the window.
As soon as I resize the window manually, the controls are layed out well.
I trled a bunch of things to hack this:
Calling the requestLayout method manually
Removing the root element from the scene and adding again
Removing the scene from the stage and adding again
Adding 100 ms delay between the resize operations
binding the root elements width to the window width (minus border size)
None of the above helped. (Yes, not even adding the scene again! It caused the most spectacular result, because the contents are resized to the window size, but with streaching its content.)
Does anyone have any other idea how to hack this bug?
I'm using Java 8u74.

javafx : Scroll Pane bounds and its visible content bounds are not matching even when the scroll Vvalue = 0

I am printing the bounds of the scroll pane and its visible contents by using the following code:
Bounds scrollBounds = scrollPane.localToScene(scrollPane.getBoundsInParent());
System.out.println("ScrollPane Bounds: " + scrollBounds);
if (scrollPane.getContent() instanceof Parent)
{
Node n = (Node)scrollPane.getContent();
Bounds contentBounds = n.localToScene(n.getBoundsInParent());
System.out.println("Content Bounds: " + contentBounds);
}
It gives the followint output :
ScrollPane Bounds: BoundingBox [minX:1.0, minY:30.0, minZ:0.0, width:862.0, height:505.0, depth:0.0, maxX:863.0, maxY:535.0, maxZ:0.0]
Content Bounds: BoundingBox [minX:2.0, minY:6.0, minZ:0.0, width:989.0, height:1296.0, depth:0.0, maxX:991.0, maxY:1302.0, maxZ:0.0]
If you observe the minY of the scroll pane and its contents, the start of the scrollPane content is 24 units ahead of the start of the scroll pane itself.(Though the scroll pane's Vvalue = 0, meaning scroll bar is at the top).
Also the minY of the Content is varying with the size of the
window.(when window size is minimum it is 0 and when maximized it is
around 20)
How is it possible? or Am I getting the bounds in a wrong way?

Adjustment of contents in FlowPane

I have a flowpane in center and i applied a slider effect which gets invoke on a click of button on the right (so slider moves from right to left when expanded). I have followed JewelSea slider tutorial mentioned here Slider
Now i have two different flowpanes in two different nodes. Both the flowpane contains array of labels but the only difference is, One flowpane contains scrollbar and is contained in TitlePane while the other is without scrollbar and no titlepane.
So now if i click on slider the contents in the flowpane(without scrollbar & titlepane) gets automatically adjusted but its not the same case with the flowpane containing scrollbar.
Here is relevant code for flowpane with scrollbar-
public void loadCase() {
ScrollPane s = null;
if (!homeController.mainTabPane.getTabs().contains(testTab)) {
int app = 0;
if (appareaList.size() > 0) {
FlowPane fpTestmoduleContainer = new FlowPane();
FlowPane example = new FlowPane();
for (ApplicationAreas appttribute : appareaList) {
appTestTitledPane[app] = new TitledPane();
appTestTitledPane[app].setText(appttribute.getApplication_name());
appTestTitledPane[app].setPrefSize(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE);
/*Module loop start*/
fpTestmoduleContainer.setHgap(10);
fpTestmoduleContainer.setVgap(10);
// fpTestmoduleContainer.setPrefSize(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE);
List<TestModuleAttribute> testmoduleList = WSData.getTestingModuleList(appttribute.getApplication_id());
ArrayList<Label> listTestlbs = new ArrayList<Label>(testmoduleList.size());
System.out.println("testmoduleList.size()" + testmoduleList.size());
int i = 0;
for (TestModuleAttribute testmattribute : testmoduleList) {
listTestlbs.add(new Label());
listTestlbs.get(i).setText(testmattribute.getModule_name());
listTestlbs.get(i).setAlignment(Pos.CENTER);
listTestlbs.get(i).setTextAlignment(TextAlignment.CENTER);
listTestlbs.get(i).setWrapText(true);
listTestlbs.get(i).setPrefSize(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE);
listTestlbs.get(i).setId(testmattribute.getFxnode_css());
Image imgInstalled = new Image(getClass().getResourceAsStream("/upgradeworkbench/View/Icons/ok.png"));
listTestlbs.get(i).setGraphic(new ImageView(imgInstalled));
listTestlbs.get(i).setContentDisplay(ContentDisplay.BOTTOM);
Tooltip testtp = new Tooltip();
testtp.setText("Total No. Of test Cases :" + testmattribute.getTest_case());
testtp.setWrapText(true);
listTestlbs.get(i).setTooltip(testtp);
addModuleMouseClickListener(listTestlbs.get(i), testmattribute.getModule_name(), testmattribute.getFxnode_css(), testmattribute.getTest_case());
i = i + 1;
}
s = new ScrollPane();
s.setContent(fpTestmoduleContainer);
fpTestmoduleContainer.setPrefWidth(1500);
fpTestmoduleContainer.getChildren().addAll(listTestlbs);
//appTestTitledPane[app].setContent(fpTestmoduleContainer[app]);
listTestlbs.clear();
app = app + 1;
}
appareaTestmoduleContainer.getPanes().addAll(appTestTitledPane);
appareaTestmoduleContainer.setExpandedPane(appTestTitledPane[0]);
testTab.setText("Test Cases Wizard");
testTab.setText("Testing Application Foot Print");
//mainTab.setClosable(true);
// testTab.getContent().setVisible(true);
HBox hb = new HBox();
testTab.setContent(s);
}
}
}
Image of slider working as expected - before sliding
After sliding (without scrollbar) the 4 modules get to the next row as space is occupied by the slider
After adding scrollpane and embedding flowpane inside it. Slider overlaps the flowpane contents as shown
I want to know why the scrollbar causing issue in auto adjustment of contents inside the flowpane and how can i fix it ?
Here the width of your scrollpane is fixed. And then so is the width of the flow pane.You need to change the size of your scrollpane so that its content gets reset.
Use the following code.
scroll[app].setFitToHeight(true);
scroll[app].setFitToWidth(true);
This code will set the size of scrollpane according to the view. The flowpane will also adjust accordingly then.

Resources