What does Gluon SceneBuilder look for in JAR files? - javafx

Using Clojure, I'm trying to write a custom JavaFX component for use in Gluon SceneBuilder, to be loaded up from a .jar file. That is, I'd like to point SceneBuilder to the .jar with my custom class, and have the thing show up in the list of draggable items on the left.
I can make the visual structure show up with FXML only, but I'd like to include some behavior as well.
After doing the (:gen-class) stuff in my Clojure source, running lein uberjar, and using the fx:root construct in the FXML, I'm able to use the resulting class in a modified version of the official Java example.
When I instantiate my custom class in the CustomControlExample, I see evidence of the Clojure init code running (via printlns and other stuff in the graphics).
So my custom class appears to work normally. It has two constructors -- with and without a String argument, and extends from HBox. I can verify these when I view the resulting .class file in NetBeans and also using JarExplorer. The class has a ton more stuff in it, due to being a Clojure constructed class, but it has at least the same number and type of constructors as the example.
The problem is my custom component does not appear in the SceneBuilder when I import the uberjar file.
So the question is: What exactly does SceneBuilder need to see in the class to make it appear as a custom draggable component?
Here is the relevant portion of my one source file (it includes a utility library for dealing with starting up the FX runtime).
src/toyui/GridSettingsPane.clj:
(ns toyui.GridSettingsPane
(:gen-class
:extends javafx.scene.layout.HBox
:post-init post-init
:init init
:constructors {[] []
[String] []})
(:use [jfxutils.core :exclude [-main]]))
(defn -init
([]
(-init "unnamed-init"))
([name]
(println "hi from -init")
[[] []]))
(defn -post-init
([this]
(-post-init this "unnamed-post-init"))
([this name]
(println "hi from -post-init")
(jfxutils.core/app-init)
(let [loader (javafx.fxml.FXMLLoader. (clojure.java.io/resource "GridSettingsPane.fxml"))]
(.setRoot loader this)
(.setController loader this)
(.load loader)
loader))))

I discovered that SceneBuilder was using some real-time class loading. Clojure defaults to *use-context-classloader* = true, so I was able to get the class to load, sort of, by modifying the scenebuilder code a little as in my comment above. However it still was having some trouble with finding some type of resource. I figured it was still probably due to class paths and the like.
In the process I also discovered that the SceneBuilder dynamically creates a little FXML file (stream, actually), which says, among other things, <?import toyui.GridSettingsPane?>, and the runs the regular FXML loader on that stream.
So I went and modified FXMLExample to import my Clojure class from within the .fxml file and it worked.
So the conclusion is yes, my class will work as-is in the FXML when running from a regular program, but for some reason doesn't load up when the jar explorer is looking.

Related

How to make Flow understand code written for Node.js?

I'm just getting started with Flow, trying to introduce it into an existing Node codebase.
Here are two lines Flow complains about:
import Module from 'module';
const nodeVersion = Number(process.versions.node.split('.')[0]);
The warnings about these lines are, respectively:
module. Required module not found
call of method `split`. Method cannot be called on possibly null value
So it seems like Flow isn't aware of some things that are standard in a Node environment (e.g. process.versions.node is guaranteed to be a string, and there is definitely a Node builtin called module).
But then again, Flow's configuration docs suggest it's Node-aware by default. And I have plenty of other stuff like import fs from 'fs'; which does not cause any warning. So what am I doing wrong?
Module fs works as expected because Flow comes with built-in definitions for it, see declare module "fs" here: https://github.com/facebook/flow/blob/master/lib/node.js#L624
Regarding process.versions.node, you can see in the same file that the versions key is typed as a map of nullable strings, with no mention of the specific node property: versions : { [key: string] : ?string };. So you'll need to either make a PR to improve this definition, or adjust your code for the possibility of that value being null.
I guess the answer about module "module" is obvious now – there are no built-in definitions for that module in Flow in lib/node.js. You could write your own definitions, and optionally send a PR with them to the Flow team. You can also try searching github for these, someone might have done the work already.
That lib directory is very useful by the way, it has Flow definitions for DOM and other stuff as well.

QML map binding v2

I'm complete noob in Qt, so my question may sound too stupid, but I really need help. I know C++ a little and that's it.
So, my task is to write a C++ program which reads INI-alike (format is not very important) file
height=20
width=15
To make it clear, I have no idea what properties will be defined in this file, names or types are unknown to me at compile time.
After that program loads QML file (I can do this) and injects loaded file data (have no idea how to do this) as JavaScript object, for instance named "Settings", so that QML property bindings will use it like
Rectangle {
width: Settings.width
height: Settings.height
}
So the question is: How can I inject read data as JavaScript object into QML so that QML property binding will use it?
One way to do it would be to write a QObject wrapper around QSettings and expose an instance of it to QML, another would be to use the Settings QML Type. I am sure there are others.

QML map binding

I'm complete noob in Qt, so my question may sound too stupid, but I really need help. I know C++ a little and that's it.
So, my task is to write a C++ program which reads INI-alike file (I can do this, but not sure of most correct/Qt way)
height=20
width=15
or
height=int:20
width=int:15
if properties should be strongly typed. File format is not very important. To make it clear, I have no idea what properties will be defined in this file, names or types are unknown to me at compile time.
after that program loads QML file (I can do this) and injects loaded file data (have no idea how to do this) as JavaScript object, for instance named "Settings", so that QML property bindings will use it like
Rectangle {
width: Settings.width
}
So the questions are:
What is most correct/qt-style way to read INI-alike file?
How can I inject read data as JavaScript object into QML so that QML property binding will use it?
First: The most Qt-style way is using QSettings class:
QSettings *settings = new QSettings("G:/options1.ini",QSettings::IniFormat);
qDebug()<< "height" <<settings->value("height").toInt();
qDebug()<< "width" <<settings->value("width").toInt();
My file:
height=20
width=15
Output:
height 20
width 15
See description of this class. It is really helpful thing.
http://qt-project.org/doc/qt-4.8/qsettings.html

How do I change Closure Compiler compile options not exported to command line?

I found that some options in CompilerOption are not exported to the command line.
For example, alias all strings is available in the Closure Compiler's Java API CompilerOption but I have no idea how set this in the command line.
I know I can create a new java class, like:
Compiler c = new Compiler();
ComppilerOptions opt = new ComppilerOptions();
opt.setAliasAllString(true);
c.compile(.....);
However I have to handle the command line args myself.
Any simple idea?
============================
In order to try the alias all string option, I write a simple command line application based on compiler.jar.
However I found that, the result I got when open the alias all string is not what I expected.
For example:
a["prototype"]["say"]=function(){
var a="something string";
}
Given the above code, the something string will be replaced by a variable like this:
var xx="something string";
....
var a=xx;
....
This is fine, but how about the string "say"? How does the closure compiler know this should be aliased(replace it use variable) or exported(export this method)?
This is the compiled code now:
a.prototype.say=function(){....}
It seems that it export it.
While I want this:
var a="prototype",b="say",c="something string";
xx[a][b]=function(){.....}
In fact, this is the google_map-like compilation.
Is this possible?
Not all options are available from the command line - this includes aliasAllStrings. For some of them you have the following options:
Build a custom version of the compiler
Use the Java API (see example).
Use plovr
Getting the same level of compression and obfuscation as the Maps API requires code written specifically for the compiler. When properly written, you'll see property and namespace collapsing, prototype aliasing and a whole host of others. For an example of the style of code that will optimize that way, take a look at the Closure Library.
Modifying http://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/javascript/jscomp/CompilationLevel.java?r=706 is usually easy enough if you just want to play with something.
Plovr (a Closure build tool) provides an option called experimental-compiler-options, which is documented as follows:
The Closure Compiler contains many options that are only available programmatically in Java. Many of these options are experimental or not finalized, so they may not be a permanent part of the API. Nevertheless, many of them will be useful to you today, so plovr attempts to expose these the experimental-compiler-options option. Under the hood, it uses reflection in Java, so it is fairly hacky, but in practice, it is a convenient way to experiment with Closure Compiler options without writing Java code.

In Qt, how to setCodecForCStrings globally (in header)?

I'm developing a bunch of Qt applications in C++ and they all use some modules (translation units) for common functionality that use Qt as well.
Whenever I convert a C string (implicit conversion) or C++ string object (fromStdString()) to a QString object, I expect the original data to be UTF-8 encoded and vice versa (toStdString()).
Since the default is Latin-1, I have to set the codec "manually" (in the init procedure of every one of my programs) to UTF-8:
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
Not all of my modules have an init procedure. The ones containing a class do (I can put this line in the class constructor), but some modules just contain a namespace with lots of functions. So there's no place for setCodecForCStrings(). Whenever I convert from/to a QString implicitly (from within one of my modules), I rely on the codec being already set by the init procedure of the main program, which seems to be a rather bad solution.
Is there a reliable way to set the codec to UTF-8 in my modules, or will I just have to be very careful not to use implicit conversions at all (in my modules at least) and write something like std::string(q.toUtf8().constData()) instead of q.toStdString()?
This can be done using a class definition for an automatically instantiated singleton-similar class having some init code in its constructor:
class ModuleInit {
static ModuleInit *instance;
public:
ModuleInit() {
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
}
};
ModuleInit * ModuleInit::instance = new ModuleInit(); // put this line in .cpp!
Putting this code anywhere into any project will set the text codec to UTF-8. You can, however, overwrite the text codec in the main() method, since the code above is executed even before the main() method.
With "anywhere" I of course mean places where it is syntactically allowed to. Put the class definition in some header file, and the instantiation in the appropriate .cpp file.
You can even put the whole code in one .cpp file, say moduleinit.cpp which you compile and link, but never explicitly use (there is no header file you could include...). This will also avoid accidental instantiations except the very first one and you will not have any problems with duplicate class names.
Note that you can't set the codec for C-strings in one particular file. Setting the codec using QTextCodec::setCodecForCString will set it application-wide!

Resources