Gluon ScrollPane refresh issue - javafx

I have a messaging application running in Android which has the setup like in setup of the screen
the order is as below
<View>
<BorderPane>
<center>
<ScrollPane>
<content>
<VBox> //issue is here
</content>
<ScrollPane>
<center>
<bottom>
<TextField>
<bottom>
</BorderPane>
</View>
When I add children to VBox with
VBox.getChildren().add(TextLabel);
The ScrollPane gets new VBox and shows that on the screen.
However when i add more children that what current screen can fit i scroll to end of the ScrollPane by setting vvalueProperty();
ScrollPane.vvalueProperty().bind(VBox.heightProperty());
(Above code is essential to recreate the issue)
This works perfectly fine when running it on computer but on mobile i have this weird issue where scrollPane drops VBox when i add more children than what can be fit on the screen. And when i click on the VBox area the screen refreshes and i get the desired content on the screen
Video demonstrating ScrollBar issue in gluon
For convenience i have set following color code
ScrollBar - Red
VBox - Blue
As an alternative to binding I also tried
ScrollBar.setVvalue(1.0);
setVvalue() did not have same issue but this on the other hand was not showing the last message in the view.
Right now i have tried all possible combinations including replacing VBox with FlowPane and observed same behavior.

I can reproduce your issue on an Android device. Somehow, as discussed in the comments above, the binding of the vertical scroll position is causing some race condition.
Instead of trying to find out the cause of that issue, I'd rather remove the binding and propose a different approach to get the desired result: the binding is a very strong condition in this case.
When you try to do in the same pass this:
vBox.getChildren().add(label);
scrollPane.setVvalue(vBox.getHeight());
you already noticed and mentioned that the scroll position wasn't properly updated and you were missing the last item added to the vBox.
This can be explained easily: when you add a new item to the box, there is a call to layoutChildren() that will take some time to be performed. At least it will take another pulse to get the correct value.
But since you try to set immediately the vertical scroll position, the value vBox.getHeight() will still return the old value. In other words, you have to wait a little bit to get the new value.
There are several ways to do it. The most straightforward is with a listener to the box's height property:
vBox.heightProperty().addListener((obs, ov, nv) ->
scrollPane.setVvalue(nv.doubleValue()));
As an alternative, after adding the item to the box, you could use:
vBox.getChildren().add(label);
Platform.runLater(() -> scrollPane.setVvalue(vBox.getHeight()));
But this doesn't guarantee that the call won't be done immediately. So it is better to do a PauseTransition instead, where you can control the timing:
vBox.getChildren().add(label);
PauseTransition pause = new PauseTransition(Duration.millis(30));
pause.setOnFinished(f -> scrollPane.setVvalue(vBox.getHeight()));
pause.play();
As a suggestion, you could also do a nice transition to slide in the new item.
Alternative solution
So far, you are using an ScrollPane combined with a VBox to add a number of items, allowing scrolling to the first item on the list but keeping the scroll position always at the bottom so the last item added is fully visible. While this works fine (with my proposal above to avoid the binding), you are adding many nodes to a non virtualized container.
I think there is a better alternative, with a ListView (or better a CharmListView that will allow headers). With the proper CellFactory you can have exactly the same visuals, and you can directly scroll to the last item. But the main advantage of this control is its virtualFlow, that will manage for you a limited number of nodes while you have many more items added to a list.
This is just a short code snippet to use a ListView control for your chat application:
ListView<String> listView = new ListView<>();
listView.setCellFactory(p -> new ListCell<String>() {
private final Label label;
{
label = new Label(null, MaterialDesignIcon.CHAT_BUBBLE.graphic());
label.setMaxWidth(Double.MAX_VALUE);
label.setPrefWidth(this.getWidth() - 60);
label.setPrefHeight(30);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null && ! empty) {
label.setText(item);
label.setAlignment(getIndex() % 2 == 0 ? Pos.CENTER_LEFT : Pos.CENTER_RIGHT);
setGraphic(label);
} else {
setGraphic(null);
}
}
});
setCenter(listView);
and to add a new item and scroll to it, you just need:
listView.getItems().add("Text " + (listView.getItems().size() + 1));
listView.scrollTo(listView.getItems().size() - 1);
Of course, with the proper styling you can remove the lines between rows, and create the same visuals as with the scrollPane.

Related

How to replace down arrow with text in a combobox in JavaFX

I'm trying to remove the down arrow from a combobox. All the solutions I have found just make the arrow disappear, for example this one.
Is there a way to remove completely the space where the arrow appears and fill the box just with the text of the selected choice?
If you want to completely elimnate the arrow & arrow button space, you can try with the below custom ComboBox.
The below code is setting the arrow button and arrow nodes size to 0 and asking to rerender the comboBox. The null check is to let this changes apply only once.
public class MyComboBox<T> extends ComboBox<T>{
Region arrowBtn ;
#Override
protected void layoutChildren() {
super.layoutChildren();
if(arrowBtn==null){
arrowBtn= (Region)lookup(".arrow-button");
arrowBtn.setMaxSize(0,0);
arrowBtn.setMinSize(0,0);
arrowBtn.setPadding(new Insets(0));
Region arrow= (Region)lookup(".arrow");
arrow.setMaxSize(0,0);
arrow.setMinSize(0,0);
arrow.setPadding(new Insets(0));
// Call again the super method to relayout with the new bounds.
super.layoutChildren();
}
}
}
UPDATE :
Based on the suggestion of #kleopatra, we can get the same behaviour using css as well (without the need to create a new class for ComboBox).
.combo-box .arrow-button,
.combo-box .arrow{
-fx-max-width:0px;
-fx-min-width:0px;
-fx-padding:0px;
}
The below image will tell you the difference of a normal combox box and this custom combo box. The left one is the normal comboBox, you can see the list cell when inspecting with ScenicView. The right one is the custom one. The list cell is completely occupied suppressing the arrow space.

Default javafx tableview scroll bar position

I am having tableview at my .fxml file like below,
<TableView fx:id="tblViewer" prefHeight="340.0" prefWidth="800.0"
styleClass="tableview" stylesheets="#ClsMain.css" />
I have select box of tables. When I am selecting any table all the data with columns populated into tableview. If columns are more, than tableview automatically render the horizontal scroll bar to it same for vertical scroll bar.
Problem is when I scroll to right end and if I am changing the dropdown selection it is not let me go to the left side. And some of the columns are truncated from the left. Like showing in the image.
If you have the solution then help me out please.
Kind Regards.
Updated:
Here I am binding the tableview and the data
private void initClsTable() {
System.out.println("in inintClsTable");
Bindings.bindContentBidirectional(tblViewer.getColumns(), getViewModel().getColumnList());
Bindings.bindContentBidirectional(tblViewer.getItems(), getViewModel().getDataList());
}
Here my selection taken place: (When user changes the selection)
private void subscribechoosenTableDataConfig() {
this.columnsList.clear();
this.dataList.clear();
System.out.println("subscribechoosenTableDataConfig");
if (choosenTableDataConfig.get() != null && choosenTableDataConfig.getValue().getTableName() != null
&& choosenTableDataConfig.getValue().getTableNumber() != 0) {
clsManager.tell(new ClsManager.ClsTableDataRequest(eventHandler, choosenTableDataConfig.getValue()
.getTableName(), choosenTableDataConfig.getValue().getTableNumber()), ActorRef.noSender());
}
}
By removing these two lines I have what I wanted.
this.columnsList.clear();
this.dataList.clear();
Many thanks to ItachiUchiha

Restricting the OptionGroup width in Vaadin

I have a couple of OptionGroups with very long captions that run across the width of the page, which looks very bad. I tried restricting the width of the OptionGroup using setWidth and via CSS, and also tried restricting the width of the parent container; all without effect.
So I made a grid layout with an option group in the first column (spanning all rows), and individual labels for the captions in the second column (one per row). However, in case the captions span multiple lines (which they do in my case), this leads to the radio buttons / checkboxes no longer being aligned to the captions. (Regrettably, I'm not allowed to post images.) For instance,
(o) This is a multiline
(o) caption
This is another multiline
caption
I resolved this by creating one OptionGroup per label, and adding each option group in the first column:
(o) This is a multiline
caption
(o) This is another multiline
caption
Clearly, in case of radio buttons, this means multiple buttons can be selected at the same time, since they are no longer linked via a single OptionGroup. Therefore, I registered listeners which, each time a button is selected, de-select all other buttons. And this brings me to my problem; since this "unchecking" is done at the server side, there will unavoidably be some lag, meaning that for some time, multiple radio buttons will appear selected at the client side.
Any ideas on how to resolve this? I only started working with Vaadin recently, so I'm far from an expert. Is there some simple way of restricting the caption width (some magical undocumented CSS class), or do I need to extend / adapt the client-side widget implementation?
Thanks,
William
What you need is FlexibleOptionGroup add-on.
Here is an example implementation:
#Override
protected void init(VaadinRequest request) {
Container cont = new IndexedContainer();
cont.addContainerProperty("caption", String.class, "");
// very long strings in the following setValue() methods
cont.getContainerProperty(cont.addItem(), "caption").setValue("I have...");
cont.getContainerProperty(cont.addItem(), "caption").setValue("So I ma...");
FlexibleOptionGroup fog = new FlexibleOptionGroup(cont);
fog.setCaption("FlexibleOptionGroup:");
fog.setItemCaptionPropertyId("caption");
fog.setMultiSelect(true); // force using CheckBoxes
VerticalLayout fogLayout = new VerticalLayout();
Iterator<FlexibleOptionGroupItemComponent> iter;
iter = fog.getItemComponentIterator();
while(iter.hasNext()) {
// OptionGroupItem part (CheckBox or RadioButton)
FlexibleOptionGroupItemComponent fogItemComponent = iter.next();
// CustomComponent part
Label caption = new Label(fogItemComponent.getCaption());
caption.setWidth(400, Unit.PIXELS);
fogLayout.addComponent(new HorizontalLayout(fogItemComponent, caption));
}
setContent(fogLayout);
}
The above code produces:

tripleplay Animator add layer

I have the following (pseudo) code
root = _iface.createRoot(...)
Label l = new Label("hello world");
anim = Animator.create();
anim.delay(1500).then().add(root.layer, l.layer);
anim.delay(1000).then().action(new Runnable() {
public void run() {
// root.add(l);
System.out.println("it works");
}
});
the it work's line gets printed ok, so I assume I'm updating the animation right, but the label is never added to the scene!
If I uncomment the root.add(l) inside the Runnable it works as expected (the label is added after 1 second), but it doesn't get added with anim.delay(1500).then().add(root.layer, l.layer);
any idea whay I'm doing wrong?
You can't just add the layer of a TPUI Widget to another layer and expect the Widget to render properly. A widget must be added to its parent via Group.add.
The animation code you are using is more designed for animating raw PlayN layer's than UI elements. UI elements are generally laid out using a LayoutManager which controls where the layer is positioned. If you tried to animate the layer directly, you would confuse the layout manager and generally mess everything up.
That said, it's pretty safe to animate the Root of the interface, because that anchors a whole UI into the PlayN scene graph.
If you really want to try what you're doing above, don't use Animator.add use:
action(new Runnable() {
root.add(l);
});
(like you have above) which properly adds the Label to the Root, and triggers validation and rendering of the Label.

Scroll flex spark datagroup to maximum amount programmatically

I have a spark skinnable component which contains a datagroup with images. The datagroup is scrolled by hovering the mouse over it. Everything works fine except one thing: after I change the datagroup provider, I need to scroll down automatically. The problem is the images are not loaded immediately after I set the provider so (contentHeight - height) does not yet represent the actual maximum scrolling position. Is there an easy way of telling the datagroup to scroll down as its content loads? Because the workaround seems to be not so straightforward.
This is the code for scrolling(thumbnailStrip is my datagroup):
private function thumbnailStrip_mouseMoveHandler(evt:MouseEvent):void {
var fr:Number = (thumbnailStrip.contentHeight - thumbnailStrip.height) / thumbnailStrip.height;
var scroll:Number = fr * evt.stageY - fr * this.y;
var ms:Number = maxScroll();
if(scroll > ms) scroll = ms;
thumbnailStrip.verticalScrollPosition = scroll;
}
private function maxScroll():Number {
return thumbnailStrip.contentHeight - thumbnailStrip.height;
}
Thanks,
Calin
thumbnailStrip.layout.verticalScrollPosition += thumbnailStrip.layout.getVerticalScrollPositionDelta(NavigationUnit.END);
This may have to run a few times to get all the way to the bottom.It's supposed to return the difference between the current scroll position and the "end" of the scroll position. As things load, I'd just keep calling this in a "callLater".
btw, there's a bug for this: http://bugs.adobe.com/jira/browse/SDK-25740 not sure if it's fixed in 4.5, ugly workaround here: http://flexponential.com/2011/02/13/scrolling-to-the-bottom-of-a-spark-list/

Resources