This commit is contained in:
crschnick
2026-01-21 14:55:28 +00:00
parent a8c3b5dfea
commit 6cc9e4ab6b
5 changed files with 32 additions and 82 deletions
@@ -15,6 +15,12 @@ import java.util.function.Consumer;
public abstract class BaseRegionBuilder<T extends Region, B extends BaseRegionBuilder<T, B>> extends AbstractRegionBuilder<T, B> {
public BaseRegionBuilder() {
apply(t -> {
BindingsHelper.preserve(t, this);
});
}
public B hgrow() {
apply(t -> HBox.setHgrow(t, Priority.ALWAYS));
return self();
@@ -1,81 +0,0 @@
# Compound Components
As a basis, JavaFX nodes are created and manage via comps (compound components).
## Principles
#### A comp is a Node/Region factory, not just another fancy wrapper for existing classes
It is advantageous to define a certain component to be a factory
that can create an instances of a JavaFX Node each time it is called.
By using this factory architecture, the scene contents can
be rebuilt entirely by invoking the root component factory.
#### A comp should produce a transparent representation of Regions and Controls
In JavaFX, using skins allows for flexibility when generating the look and feel for a control.
One limitation of this approach is that the generated node tree is not very transparent
for developers who are especially interested in styling it.
This is caused by the fact that a skin does not expose the information required to style
it completely or even alter it without creating a new Skin class.
A comp should be designed to allow developers to easily expose as much information
about the produced node tree structure using the CompStructure class.
In case you don't want to expose the detailed structure of your comp,
you can also just use a very simple structure.
#### A comp should produce a Region instead of a Node
In practice, working with the very abstract node class comes with its fair share of limitations.
It is much easier to work with region instances, as they have various width and height properties.
Since pretty much every Node is also a Region, the main focus of comps are regions.
In case you are dealing with Nodes that are not Regions, like an ImageView or WebView,
you can still wrap them inside for example a StackPane to obtain a Region again that you can work with.
#### The generation process of a comp can be augmented
As comps are factories, any changes that should be applied to all produced
Node instances must be integrated into the factory pipeline.
This can be achieved with the Augment class, which allows you
to alter the produced node after the base factory has finished.
#### Properties used by Comps should be managed by the user, not the Comp itself
This allows Comps to only be a thin wrapper around already existing
Observables/Properties and gives the user the ability to complete control the handling of Properties.
This approach is also required for the next point.
#### A comp should not break when used Observables are updated from a thread that is not the platform thread
One common limitation of using JavaFX is that many things break when
calling any method from another thread that is not the platform thread.
While in many cases these issues can be mitigated by wrapping a problematic call in a Platform.runLater(...),
some problematic instances are harder to fix, for example Observable bindings.
In JavaFX, there is currently no way to propagate changes of an Observable
to other bound Observables only using the platform thread, when the original change was made from a different thread.
The FxComps library provides a solution with the PlatformThread.sync(...) methods and strongly encourages that
Comps make use of these methods in combination with user-managed properties
to allow for value changes for Observables from any thread without issue.
## Hot reload
The reason a Comp is designed to be a factory is to allow for hot
reloading your created GUI in conjunction with the hot-reload functionality in your IDE:
````java
void setupReload(Scene scene, BaseRegionBuilder<?,?> content) {
var contentR = content.build();
scene.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode().equals(KeyCode.F5)) {
var newContentR = content.build();
scene.setRoot(newContentR);
event.consume();
}
});
}
````
If you for example bind your IDE Hot Reload to F4 and your Scene reload listener to F5,
you can almost instantly apply any changes made to your GUI code without restarting.
You can also implement a similar solution to also reload your stylesheets and translations.
@@ -124,7 +124,7 @@ public class BitwardenPasswordManager implements PasswordManager {
var password = login.required("password");
return new CredentialResult(user.isNull() ? null : user.asText(), InPlaceSecretValue.of(password.asText()));
} catch (Exception ex) {
ErrorEventFactory.fromThrowable(ex).handle();
ErrorEventFactory.fromThrowable(ex).expected().handle();
return null;
}
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 John Hendrikx
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+4
View File
@@ -0,0 +1,4 @@
name=fx-builders
version=0.4
license=MIT License
link=https://github.com/int4-org/FX