I'm trying to do some 3D work with Javafx (using the TornadoFX library) and am unable to set my PerspectiveCamera to the Scene as the Scene is returning null.
I launch my program like so:
class ThreeDTest : App(HomeView::class, ThreeDStyles::class) {
override fun start(stage: Stage) {
super.start(stage)
stage.show()
stage.scene.fill = Color.BLACK
val primaryScreenBounds = Screen.getPrimary().visualBounds
stage.maxWidth = 1920.0
stage.maxHeight = 1080.0
stage.minWidth = 1920.0
stage.minHeight = 1080.0
}
}
and then in HomeView class I have this:
class HomeView : View() {
override val root = stackpane {
val axisGroup = Xform()
val world = Xform()
val camera = PerspectiveCamera(true)
val cameraXform = Xform()
val cameraXform2 = Xform()
val cameraXform3 = Xform()
val cameraInitialDistance = -450.0
val cameraInitialXAngle = 70.0
val cameraInitialYAngle = 320.0
val cameraNearClip = 0.1
val cameraFarClip = 10000.0
//just a builder class to add properties to the camera
buildCamera(this, cameraXform, cameraXform2, cameraXform3, camera, cameraNearClip, cameraFarClip, cameraInitialDistance,
cameraInitialYAngle, cameraInitialXAngle)
buildAxes(axisGroup, world)
println(scene)
//returns null
scene.camera = camera
//this does not work as scene is null
}
Is there something I'm doing wrong? I have no idea why my Scene is null as I thought TornadoFX View was supposed to create a new Scene when it's initialized? Do I need to manually create it in the App class? I tried overriding the createPrimaryScene function, and also moving my code to an init function within the HomeView view– both with no success.
Any ideas? Pulling out hairs over here.
(Oh, and on an unrelated note, could I just say that having 3D support in TornadoFX would, quite frankly, make my entire life?)
The View is created before the scene is attached, so to manipulate scene, simply override onDock and perform your operations there. When onDock is called, the scene is attached.
Related
I have a Pane that contains some VBoxs and Lines. I want to bind the endpoints of the Lines to the "anchors" inside the VBoxs (basically I want to bind to the position of a Node nested arbitrarily deep in the VBox), but I can't figure out what value represents the Nodes position relative to the top Pane. I've tried layout properties, translate properties, as well as bounds in local and bounds in parent, and none of them seem to work. What am I missing?
(I can provide a code sample if needed, but I don't think it helps explain my problem any better since I can't get it to work.)
EDIT: I forgot to mention the VBox can be moved around the pane freely, which is why I need to bind the lines.
EDIT: Here's some source showing my progress. I can get the correct location, but it's not binding
public class Graph extends Application {
private double startX;
private double startY;
private ObjectBinding<Bounds> bounds;
private DoubleBinding tx;
private DoubleBinding ty;
#Override
public void start(Stage primaryStage) throws Exception {
Pane pane = new Pane();
Circle target = new Circle(5, Color.RED);
VBox node = wrap(target);
Line connector = new Line();
bounds = Bindings.createObjectBinding(() -> {
Bounds nodeLocal = target.getBoundsInLocal();
Bounds nodeScene = target.localToScene(nodeLocal);
Bounds nodePane = pane.sceneToLocal(nodeScene);
return nodePane;
},
target.boundsInLocalProperty(),
target.localToSceneTransformProperty(),
pane.localToSceneTransformProperty()
);
connector.setStartX(0);
connector.setStartY(0);
tx = Bindings.createDoubleBinding(() -> bounds.get().getMinX(), bounds);
ty = Bindings.createDoubleBinding(() -> bounds.get().getMinY(), bounds);
connector.endXProperty().bind(tx);
connector.endYProperty().bind(ty);
connector.setStroke(Color.BLACK);
pane.getChildren().add(node);
pane.getChildren().add(connector);
node.relocate(100, 100);
primaryStage.setScene(new Scene(pane, 300, 300));
primaryStage.show();
}
private VBox wrap(Circle target) {
VBox node = new VBox(new Label("Node"), new StackPane(new Rectangle(50, 50, Color.GRAY), target));
node.setOnMousePressed(event -> {
Node source = (Node) event.getSource();
startX = source.getBoundsInParent().getMinX() - event.getScreenX();
startY = source.getBoundsInParent().getMinY() - event.getScreenY();
});
node.setOnMouseDragged(event -> {
Node source = (Node) event.getSource();
double offsetX = event.getScreenX() + startX;
double offsetY = event.getScreenY() + startY;
source.relocate(offsetX, offsetY);
});
return node;
}
}
Given any Node node in a (meaning it has a parent or indirect ancestor) Pane pane, the basic idea is to do
ObjectBinding<Bounds> boundsInPaneBinding = Bindings.createObjectBinding(() -> {
Bounds nodeLocal = node.getBoundsInLocal();
Bounds nodeScene = node.localToScene(nodeLocal);
Bounds nodePane = pane.sceneToLocal(nodeScene);
return nodePane ;
}, node.boundsInLocalProperty(), node.localToSceneTransformProperty(),
pane.localToSceneTransformProperty());
Then boundsInPaneBinding is an ObservableValue<Bounds> that always contains the bounds of the Node in the Pane's coordinate system. So you can then do things like
line.startXProperty().bind(Bindings.createDoubleBinding(
() -> boundsInPaneBinding.get().getMinX(),
boundsInPaneBinding));
The tricky part here is making sure that the bindings don't get garbage collected prematurely. (See here for a discussion.) First, you need to retain a reference to the binding:
private ObjectBinding<Bounds> boundsInPaneBinding ;
and then (for reasons I can't quite figure out), the binding must actually evaluate the bound property directly:
boundsInPaneBinding = Bindings.createObjectBinding(() -> {
Bounds nodeLocal = node.getBoundsInLocal();
// note how this actually gets the value of localToSceneTransformProperty():
Bounds nodeScene = node.getLocalToSceneTransform().apply(nodeLocal);
Bounds nodePane = pane.sceneToLocal(nodeScene);
return nodePane ;
}, node.boundsInLocalProperty(), node.localToSceneTransformProperty(),
pane.localToSceneTransformProperty());
I'm using the mvvm approach to develop a barcode scanning app with xamarin. The main hurdle was that the 3rd party scanner object does not work in xaml. I used a ContentPage to create a simple logic-less c# code view which allows me to have a footer with buttons and a logo overlayed at the bottom of the scanner. My problem is that could not find any great best practices for binding items from your code view to your viewModel, as opposed binding a xaml view to a viewModel. Here is some of my view below.
public class BarcodeScannerPage : ContentPage
{
ZXingScannerView zxing;
BarcodeViewModel viewModel;
public BarcodeScannerPage() : base()
{
try
{
viewModel = new BarcodeViewModel();
BindingContext = viewModel;
zxing = new ZXingScannerView
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Options = new MobileBarcodeScanningOptions
{
TryHarder = true,
DelayBetweenContinuousScans = 3000
},
ScanResultCommand = viewModel.GetResult
};
var cancelButton = new Button
{
BackgroundColor = Color.Gray,
Text = "Cancel",
TextColor = Color.Blue,
FontSize = 15,
Command = viewModel.CancelButton
};
Binding cancelBinding = new Binding
{
Source = viewModel.CancelIsAvailable,
//Path = "ShowCancel",
Mode = BindingMode.OneWay,
};
cancelButton.SetBinding(IsVisibleProperty, cancelBinding);
var doneButton = new Button
{
BackgroundColor = Color.Gray,
Text = "Done",
TextColor = Color.Blue,
FontSize = 15,
Command = viewModel.DoneButton
};
Binding doneBinding = new Binding
{
Source = viewModel.DoneIsAvailable,
//Path = "ShowDone",
Mode = BindingMode.OneWay,
};
doneButton.SetBinding(Button.IsVisibleProperty, doneBinding);
When a barcode is scanned my command, GetResultCommand, sends the result to my BarcodeView model. I have created two Bools in my BarcodeView model named isDoneAvailable and isCancelAvailable. I want to bind these values to the Visibility property of the doneButton and cancelButton in my view. Right now the buttons are bound to whatever the bool values are at the creation of BarcodeViewModel, but they DO NOT update. I need to be able to control visibility from the GetResultCommand method of my BarcodeViewModel. Specifically, when a certain number of barcodes are scanned, I want to make the buttons appear and disappear. I have a feeling they don't update because the path is not set, but when I uncomment the path, the binding doesn't work at all. Any ideas what I've done wrong with the bindings of the buttons, or the correct way to set the Path to my bools in the viewModel? Here is some of my BarcodeViewModel code below.
public class BarcodeViewModel : INotifyPropertyChanged
{
public bool CancelIsAvailable { get { return _cancelIsAvailable; } set { _cancelIsAvailable = value; OnPropertyChanged("ShowCancel"); } }
public bool DoneIsAvailable { get { return _doneIsAvailable; } set { _doneIsAvailable = value; OnPropertyChanged("ShowDone"); } }
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
I still would like to know the correct way to get this binding to update but, I was able to work-around this issue by creating a button in my viewModel and referencing it in my view. Then when I dynamically updated the button in my viewModel, it also updated in my view.
Currently im doing my thesis work, and I'm making a component library with javafx.
But im dealing a problem that I wish I could change the icon that puts the tool scene builder imported .jar.
In Scene Builder 2.0 icons for custom controls from JAR files are set by private Collection<LibraryItem> makeLibraryItems(JarReport jarReport) in the private LibraryItem makeLibraryItem(Path path) function. Currently the source for that function is:
private Collection<LibraryItem> makeLibraryItems(JarReport jarReport) throws IOException {
final List<LibraryItem> result = new ArrayList<>();
final URL iconURL = ImageUtils.getNodeIconURL(null);
final List<String> excludedItems = library.getFilter();
for (JarReportEntry e : jarReport.getEntries()) {
if ((e.getStatus() == JarReportEntry.Status.OK) && e.isNode()) {
// We filter out items listed in the excluded list, based on canonical name of the class.
final String canonicalName = e.getKlass().getCanonicalName();
if (! excludedItems.contains(canonicalName)) {
final String name = e.getKlass().getSimpleName();
final String fxmlText = JarExplorer.makeFxmlText(e.getKlass());
result.add(new LibraryItem(name, UserLibrary.TAG_USER_DEFINED, fxmlText, iconURL, library));
}
}
}
return result;
}
As you can see the iconURL is not based on anything supplied by your control's JAR so it is currently not possible to supply an icon. This would require a change to Scene Builder (this could be done after all it's open source).
I know this question is a bit old, but hopefully this helps somebody.
A few days ago I started studying JavaFX, and came across the desire to perform 2 experiments. Firstly, I would like to know if it is possible to put an animated background behind an user interface. I've succeeded in creating an animated background, and now I'm having great difficulties to position some controls in the middle of my interface.
I'd like to introduce you 2 pictures of my program. The first demonstrates the undesirable result that I'm getting:
I believe this is my nodes tree:
This is the code of my application:
public class AnimatedBackground extends Application
{
// #########################################################################################################
// MAIN
// #########################################################################################################
public static void main(String[] args)
{
Application.launch(args);
}
// #########################################################################################################
// INSTÂNCIAS
// #########################################################################################################
private Group root;
private Group grp_hexagons;
private Rectangle rect_background;
private Scene cenario;
// UI
private VBox lay_box_controls;
private Label lab_test;
private TextArea texA_test;
private Button bot_test;
// #########################################################################################################
// INÍCIO FX
// #########################################################################################################
#Override public void start(Stage stage) throws Exception
{
this.confFX();
cenario = new Scene(this.root , 640 , 480);
this.rect_background.widthProperty().bind(this.cenario.widthProperty());
this.rect_background.heightProperty().bind(this.cenario.heightProperty());
stage.setScene(cenario);
stage.setTitle("Meu programa JavaFX - R.D.S.");
stage.show();
}
protected void confFX()
{
this.root = new Group();
this.grp_hexagons = new Group();
// Initiate the circles and all animation stuff.
for(int cont = 0 ; cont < 15 ; cont++)
{
Circle circle = new Circle();
circle.setFill(Color.WHITE);
circle.setEffect(new GaussianBlur(Math.random() * 8 + 2));
circle.setOpacity(Math.random());
circle.setRadius(20);
this.grp_hexagons.getChildren().add(circle);
double randScale = (Math.random() * 4) + 1;
KeyValue kValueX = new KeyValue(circle.scaleXProperty() , randScale);
KeyValue kValueY = new KeyValue(circle.scaleYProperty() , randScale);
KeyFrame kFrame = new KeyFrame(Duration.millis(5000 + (Math.random() * 5000)) , kValueX , kValueY);
Timeline linhaT = new Timeline();
linhaT.getKeyFrames().add(kFrame);
linhaT.setAutoReverse(true);
linhaT.setCycleCount(Animation.INDEFINITE);
linhaT.play();
}
this.rect_background = new Rectangle();
this.root.getChildren().add(this.rect_background);
this.root.getChildren().add(this.grp_hexagons);
// UI
this.lay_box_controls = new VBox();
this.lay_box_controls.setSpacing(20);
this.lay_box_controls.setAlignment(Pos.CENTER);
this.bot_test = new Button("CHANGE POSITIONS");
this.bot_test.setAlignment(Pos.CENTER);
this.bot_test.setOnAction(new EventHandler<ActionEvent>()
{
#Override public void handle(ActionEvent e)
{
for(Node hexagono : grp_hexagons.getChildren())
{
hexagono.setTranslateX(Math.random() * cenario.getWidth());
hexagono.setTranslateY(Math.random() * cenario.getHeight());
}
}
});
this.texA_test = new TextArea();
this.texA_test.setText("This is just a test.");
this.lab_test = new Label("This is just a label.");
this.lab_test.setTextFill(Color.WHITE);
this.lab_test.setFont(new Font(32));
this.lay_box_controls.getChildren().add(this.lab_test);
this.lay_box_controls.getChildren().add(this.texA_test);
this.lay_box_controls.getChildren().add(this.bot_test);
this.root.getChildren().add(this.lay_box_controls);
}
}
I've tried to make the use of a StackPane as the root of my scene graph, but also found an undesired result. Despite the controls have stayed in the center of the window, the circles begin to move in as they grow and shrink, making it appear that everything is weird.
The second thing I would like to know is if it is possible to customize the controls so they perform some animation when some event happens. Although we can change the appearance of controls using CSS, it's harder to create something complex. For example, when a control changes its appearance due to a change of state, the transition state change is not made in an animated way, but in an abrupt and static way. Is there a way to animate, for example, a button between its states? This would be done using the JavaFX API? Or would that be using CSS? Or would not be possible in any way?
Thank you for your attention.
after much struggle, I and some users of the Oracle community could resolve this issue. I see no need to repeat here all the resolution made by us, so I'll post the link so you can access the solution of the problem. I hope this benefits us all. Thanks for your attention anyway.
https://community.oracle.com/thread/2620500
I've got the following code being called in view the viewdidload method inside of my UIViewController.
Inside the appdelegate I have a UINavigationController which is instantiated with this aforementioned controller and in turn the UINavigationController is placed inside a UITabViewController which in turn is assigned as the rootviewcontroller.
Inside the controller I'm making an async web call to get the data to populate a table, if I use the loading view code to display an activity indicator I get the following warning in monotouch.
Applications are expected to have a root view controller at the end of application launch
public class LoadingView : UIAlertView
{
private UIActivityIndicatorView _activityView;
public void ShowActivity (string title)
{
Title = title;
this.Show();
// Spinner - add after Show() or we have no Bounds.
_activityView = new UIActivityIndicatorView (UIActivityIndicatorViewStyle.WhiteLarge);
_activityView.Frame = new RectangleF ((Bounds.Width / 2) - 15, Bounds.Height - 50, 30, 30);
_activityView.StartAnimating ();
AddSubview (_activityView);
}
public void Hide ()
{
DismissWithClickedButtonIndex (0, true);
}
}
Any pointers would be gratefully received.
EDIT : I'm already setting the root view controller.
window = new UIWindow (UIScreen.MainScreen.Bounds);
window.RootViewController = tabController;
Full appDelegate code :
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
// create a new window instance based on the screen size
window = new UIWindow (UIScreen.MainScreen.Bounds);
tabController = new UITabBarController();
jobsNavigationController = new UINavigationController(new JobsController());
jobsNavigationController.NavigationBar.BarStyle = UIBarStyle.Black;
jobsNavigationController.TabBarItem.Image = UIImage.FromFile("Images/briefcase.png");
jobsNavigationController.TabBarItem.Title = "Current Positions";
myAccountNavigationController = new UINavigationController(new LoginDialogViewController());
myAccountNavigationController.NavigationBar.BarStyle = UIBarStyle.Black;
myAccountNavigationController.TabBarItem.Image = UIImage.FromFile("images/man.png");
myAccountNavigationController.TabBarItem.Title = "My Account";
tabController.SetViewControllers(new UIViewController[] { jobsNavigationController,myAccountNavigationController,new SettingsDialogViewController()},false);
window.RootViewController = tabController;
// make the window visible
window.MakeKeyAndVisible ();
return true;
}
To avoid this warning (in iOS5) and keep iOS 4.x compatibility you can do the following inside your FinishedLaunching method:
if (UIDevice.CurrentDevice.CheckSystemVersion (5, 0))
window.RootViewController = navigation;
else
window.AddSubview (navigation.View);
Look here for a more complete sample.
window.AddSubview(tabcontroller.view);
Fixed the issue, odd I don't set the rootviewcontroller anymore.