Title in AppBar grows too large - javafx

On some views I show the name of a selected item as the title of the AppBar. But when the name is very long, the VBoxcontaining the title label grows to the prefWidth of the label. You can see the result on the first picture.
As a workaround I add a styleClass to the appBar, which sets the maxWidth for the label.
view.setOnShown(e -> getAppBar().getStyleClass().add("myStyleClass"));
Although it's working, it's bad practice to set the maxWidth like that, because it can't be adjusted to different screen sizes.
How can I achieve the result on the second picture, and make the title's width adapt to different screen sizes?

A quick and dirty workaround to set the max width of the label based on the scene dimensions could be, on a View subclass:
#Override
protected void updateAppBar(AppBar appBar) {
appBar.setTitleText("Very very very long title");
((Label) appBar.getTitle()).maxWidthProperty().bind(appBar.getScene().widthProperty().subtract(100));
}
where the amount to subtract will depend on the number of buttons (nav icon, action icons or menu button) you have in that view.

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.

Resizing ImageViews

I have a GridPane (4x5), all it's cells have as child an AnchorPane which cointains an ImageView. I need to resize the image so it fully cover the cell as soon as the gridPane (and thus it's cells) change size.
I managed to resize the image correctly when the size of the cell grows, but when the cell gets tinier the image doesn't resize back.
This leads into partially covering images of the confinant cells.
Can anyone explain what i'm doing wrong or give me the instruction to implement a proper resize?
This is my code:
ImageView image = new ImageView("/dice/" + draftList.get(i) + ".png");
AnchorPane pane = ((AnchorPane)(gridpane.getChildren().get(i)));
pane.getChildren().add(image);
fitToParent(image,pane);
//method in the same class
private void fitToParent(ImageView image, AnchorPane pane) {
image.fitWidthProperty().bind(pane.widthProperty());
image.fitHeightProperty().bind(pane.heightProperty());
}
You can try to use the setPreserveRatio(boolean) function of the ImageView class to true. This will allow you to keep the aspect ratio constant.
Eg:
ImageView iv = new ImageView(/*file path*/);
iv.setPreserveRatio(true);
Src: https://docs.oracle.com/javase/8/javafx/api/javafx/scene/image/ImageView.html
Other than this you can also try to limit the resizable property to false or set the min width and height so that the image is not partially covered
Src: https://docs.oracle.com/javase/8/javafx/api/javafx/scene/layout/Region.html#resize-double-double-

Gluon ScrollPane refresh issue

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.

JavaFX buttons with same size

I have these buttons with different size:
Image
How I can make all buttons with same with size?
It depends on layout where the button is located. For example, if you add all the buttons into GridPane or BorderPane, you have to specify each button width to correspond to certain variable. In the following example I wrap all buttons inside VBox, set VBox preference width and tie up all buttons minimum width to it:
VBox vBox = new VBox();
vBox.setPrefWidth(100);
Button btn1 = new Button("Short");
Button btn2 = new Button("Super Long Button");
btn1.setMinWidth(vBox.getPrefWidth());
btn2.setMinWidth(vBox.getPrefWidth());
vBox.getChildren().addAll(btn1, btn2);
It is also worth to mention that there are two ways to specify the button size. You can do it in the java code or specify it in javafx .fxml file. The above method is an example for java code implementation.
You can also unclamp a button's maximum dimensions so it will grow to fill the available space (unlike most nodes, by default a button node has it's max size clamped to it's preferred size so it doesn't usually grow to fill available space). An Oracle tutorial on Tips for Sizing and Aligning Nodes explains this in more detail.
VBox vBox = new VBox();
Button btn1 = new Button("Short");
Button btn2 = new Button("Super Long Button");
btn1.setMaxWidth(Double.MAX_VALUE);
btn2.setMaxWidth(Double.MAX_VALUE);
vBox.getChildren().addAll(btn1, btn2);
using css you can override the preferred width of all buttons like
.button {
-fx-pref-width: 200px;
}
or create your own style class for certain button groups and add the style to the button like:
css:
.my-special-button {
-fx-pref-height: 28px;
-fx-pref-width: 200px;
}
and then set the style to your button with either
fxml:
styleClass="my-special-button"
or in java
myButton.getStyleClass().add("my-special-button");

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:

Resources