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.
Related
I'm trying to get a clone of a raycasted object, and move the clone of the selected object in a <div>. The raycaster works just fine, but the CSS3DObject instance (the cloned 3d Object) is not shown in the scene. I have made another renderer (namely a CSS3DRenderer), in addition to the current WebGLRenderer, and basically my css3d code consists of this:
cssRenderer = new CSS3DRenderer();
cssRenderer.setSize(window.innerWidth, window.innerHeight);
cssRenderer.domElement.style.position = 'absolute';
cssRenderer.domElement.style.top = 0;
cssScene = new THREE.Scene();
document.body.appendChild(cssRenderer.domElement);
...
thumbnail = document.querySelector('.thumbnail');
thumbObject = new CSS3DObject(thumbnail);
....
targetClone = target.clone();
thumbObject.position.copy(targetClone.position);
cssScene.add(thumbObject);
and my render loop code, pertaining to this, is:
requestAnimationFrame(update);
renderer.render(scene, camera); // WebGLRenderer
cssRenderer.render(cssScene, camera); //CSS3DRenderer
My intention is to embed the cloned object (targetClone) in the CSS3DObject (thumbObject, which in actuality is an HTML element). The console shows no error, and I cannot see any reaction to this code whatsoever (except that the div travels to the center of model!) No matter if I select the div through traversing the DOM or making a fresh element by means of createElement, nothing is being embedded in the div. I suspect that everything is behind the scene(s). Any ideas how can I achieve this, and where I'm doing it wrong?
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.
Currently I have a project that is being used to draw rooms with lines and images that can be selected by the user by hitting a button representing what they want to add. I.E. if they want a shower a button for shower is pressed and an image appears in a pane. The shower can be resized and moved in the pane. The user also has the ability to use lines to draw objects or walls. The lines can be resized, rotated, or moved. I am now trying to get these objects to interact with each other. Say a user is using lines to make an object, and when the line comes near another object the line being moved snaps to the other object. I have found a 3rd party library that has SnapLineSnapResult but I don't see anything where someone has used it. Is this something that is desktop JavaFX usable, or is it a touch operation and does anyone have code to model or another solution?
SnapLineSnapResult
My code for line that would be useful if I can use this class is as follows:
line.setOnMouseDragged((MouseEvent event1) -> {
// in resize region
if (isInResizeZoneLine(line, event1)) {
// adjust line
line.setEndX(event1.getX());
}
// in movable region
else {
Point2D currentPointer = new Point2D(event1.getX(), event1.getY());
if (bound.getBoundsInLocal().contains(currentPointer)) {
/*--------*/ // potential place for if (near other object to be snapped)
double lineWidth = line.getEndX() - line.getStartX();
line.setStartY(currentPointer.getY());
line.setEndY(currentPointer.getY());
line.setStartX(currentPointer.getX() - lineWidth/2);
line.setEndX(currentPointer.getX() + lineWidth/2);
}
}
});
I would like to disable the default animation when I create new Tab.
Tab tabA = new Tab();
tabA.setText("Tab A");
tabPane.getTabs().add(tabA);
Is this possible?
Now, when Java 8 is finally out, one has the possibility to disable the animation, using CSS:
tabPane.setStyle("-fx-open-tab-animation: NONE; -fx-close-tab-animation: NONE;");
The default setting is "GROW".
Not easily. The animation logic is part of the TabPaneSkin:
From TabPane.java:
#Override protected Skin<?> createDefaultSkin() {
return new TabPaneSkin(this);
}
But as far as I know, there is no way to make small adjustments to the default skin (which is hidden in the com.sun.javafx.scene.control.skin package) but you'd have to implement a whole new skin.
I embed SVG graphics in my Flex application using
package MyUI
{
public class Assets
{
[Embed(source="/assets/pic.svg"]
[Bindable]
public static var svgPic:Class;
}
}
and then extending the Tree class with some of my own code, setting the icon upon adding a node to the data provider:
public class MyTree extends Tree
{
public function MyTree()
{
// ...
this.iconField = "svgIcon";
// ...
this.dataProvider = new ArrayCollection;
this.dataProvider.addItem({ /* ... */ svgIcon: MyUI.Assets.svgPic /* ... */ });
// ...
}
}
Now I have two things I want to do:
use the SVG graphics in multiple places in the app, scaling them to the appropriate size for each appearance, i. e. scale them to a proper icon size when using them in the tree
change the size of the icon at runtime, e. g. display a slightly larger icon for selected items or let an icon "pulse" as a response to some event
I read the Flex documentation on the 9-slice scaling properties in the Embed tag, but I think that's not what I want.
Edit:
I unsuccessfully checked the "similar questions" suggested by SO, among others this one:
Flex: Modify an embedded icon and use it in a button?
Subclass mx.controls.treeClasses.TreeItemRenderer and make it resize the icon to your desired dimensions, or create your own item renderer implementation by using the same interfaces as TreeItemRenderer. Set a custom item renderer with the itemRenderer property:
exampleTree.itemRenderer = new ClassFactory( ExampleCustomItemRendererClass );
The answer to this question might point you in the right direction, without knowing more about the trouble you're having:
Flex: Modify an embedded icon and use it in a button?
Hope it helps!