Open Source
FluentX Declarative
A concept-level C++ UI framework for lightweight native Windows apps, built on bare Win32 and Direct2D with a fluent, chainable builder API. Frozen, kept as an idea archive.
Install
git clone https://github.com/AmashOnBlitz/FluentX-Declarative-DEPRECATEDFluentX Declarative is a UI framework I started to see if native Windows apps could be described the way modern UI toolkits do it: you nest widgets inside each other and chain calls to configure them, instead of creating windows and handling messages by hand. The goal was lightweight native apps with no Qt, no OpenGL and no heavy runtime, only Win32 and Direct2D.
It is frozen. It took more time and attention than I can give as a student, so I stopped. The repo is now an idea archive and a design reference for a later library, FluentX Imperative, which I may build one day but am not promising. The code is MIT licensed, so take anything useful from it.
What it looks like
You include one header, FXLib.h, and describe the whole app as one nested
expression. This is trimmed from the demo in main.cpp:
#include "FXLib.h"
#include <iostream>
using namespace FluentX::engine;
using namespace FluentX::app;
using namespace FluentX::UI;
int main() {
FXEngine engine;
return engine.run(
FXCreateApplication()
.name("Hello FluentX")
.vendorName("Amash Shafi Jami")
.version("1.0 Beta")
.rootWindow(
FXCreateWindow()
.child(
FXCreatePaddingWidget()
.child(
FXCreateHStack()
.spacing(20)
.addChild(
FXCreateButton()
.onClick([](FXButton&) {
std::cout << "Clicked!" << std::endl;
})
.build()
)
.addChild(
FXCreateButton()
.style(FXCreateButtonStyle().rounded(20).build())
.build()
)
.build()
)
.build()
)
.build()
)
.build()
);
}
Every widget has a FXCreateX() function that returns a builder. Call setters on
it, then finish with .build(). The nesting in the code is the nesting on screen.
What was built
The framework went from first file to the last widget in about four weeks, from 31 December 2025 to 27 January 2026. By then it had:
- Application and engine.
FXCreateApplication()holds the name, vendor, version and root window.FXEngine::run()creates the window, lays everything out, starts rendering and runs the message loop. - Window. Title, icon, position, size, min and max size, resizable on or off, and a transparent window with an opacity from 0 to 255. Icons can be
.icoor.bmpdirectly, or.png,.jpgand.jpegthrough GDI+. - Buttons. A normal button, a rounded button, and a style object with
rounded(radius)and aVintagevariant. Buttons takeonClickcallbacks and min and max sizes. - Layout containers.
HStack(row),VStack(column) andZStack(layers), withspacing. - Helpers.
Spacerfor empty space,PaddingWidgetfor padding around a child, andRectangle. - Image and text edit. An image widget, and a base class for text editing.
- Colour.
FXColorand a palette header with a set of basic colours. - Structs. Small types for size, coordinates, padding and a light source.
- Registrar. A service that gives every built widget a unique ID and keeps a registry of them.
Widgets can be built three ways. .build() returns a unique_ptr to put inside a
parent, .build(idVar) also hands you the widget’s ID, and .buildVariable()
gives a plain value you can keep and reuse.
How it works
Widget base. Every widget derives from FXWidgetBase. It carries a type tag, an
ID, a size (default 200 by 100), min and max size, a visible flag, and two virtual
functions: isContainer() and layoutChildren(). Containers override them so
the layout code can walk the tree without knowing every widget type.
FX_USE_DEFAULT is the “no limit” value for a size.
Layout. The engine walks the widget tree once and builds a flat layout tree: a list of (rectangle, widget) pairs, each with a flag saying whether it is worth rendering. Stacks split the space between children, and min and max sizes clamp the result.
Window layer. FXPWindow wraps the raw Win32 window. It registers the class,
creates the window and runs the message loop. The window procedure is a static
function that stores a pointer to the FXPWindow object in the window’s user
data, so the real handler is an ordinary member function. It handles resize,
move and min/max tracking. If min size is bigger than max size, it shows an
error box instead of starting.
Rendering. Drawing goes through Direct2D. A singleton (FXD2D) owns a
multi-threaded factory and one render target per window, with support for
premultiplied alpha. Solid brushes are cached by widget ID in a sorted list that
is searched with a binary search, so a widget does not create a new brush every
frame. A platform UI service ties the layout tree to the renderer, and there is a
separate platform layer for button behaviour.
Namespaces. FluentX::engine, app, UI, layout, platform, Registrar
and InternalEngines, each with its own set of headers. Structure.md in the
repo lists every file with the date it was added.
Where it was left
- Working: every widget in the repo works: buttons, rounded buttons, the three stacks, spacer, padding, rectangle, image and the text edit base. So do the application, engine, window (including transparency and icons), colours and the Direct2D layer.
- Stopped at a checkpoint: I did not abandon it half-built. I stopped after reaching a point where everything that exists runs, and left it there.
- Not added: a frameless window (the code is commented out in the window layer), and the widgets you would expect in a full toolkit that I never started, such as labels, checkboxes and sliders.
- Debug output:
run()still prints the window size and every element’s layout to the console, so it builds as a console app, not a hidden-window one. - Version: the
1.0 Betain the demo is just the demo app’s version string. The library itself never had a release.
Build it
You need Windows and a C++17 compiler with the Windows SDK headers. The repo is
a flat folder of headers and .cpp files, so you can compile every .cpp in it
together. Check main.cpp for the demo, or replace it with your own.
The .cpp files are main.cpp, FXEngine.cpp, FXHStack.cpp, FXVStack.cpp,
FXZStack.cpp, FXPaddingWidget.cpp, FXRectangle.cpp and FXRegistrar.cpp.
Bare g++ command, listing every file and library yourself:
g++ -std=c++17 main.cpp FXEngine.cpp FXHStack.cpp FXVStack.cpp FXZStack.cpp FXPaddingWidget.cpp FXRectangle.cpp FXRegistrar.cpp -o FluentXDemo.exe -ld2d1 -lgdiplus -lgdi32 -luser32 -lole32
Convenient command, picking up every .cpp in the folder, with warnings and
optimisation on. In Git Bash or MSYS2:
g++ -std=c++17 -O2 -Wall *.cpp -o FluentXDemo.exe -ld2d1 -lgdiplus -lgdi32 -luser32 -lole32
In PowerShell, which does not expand *.cpp for you:
g++ -std=c++17 -O2 -Wall (Get-ChildItem *.cpp).Name -o FluentXDemo.exe -ld2d1 -lgdiplus -lgdi32 -luser32 -lole32
Add -static -static-libgcc -static-libstdc++ if you want an exe that runs on a
machine without the MinGW runtime DLLs. Do not add -mwindows, because the
console prints are how you see the layout output. If you edit one file often,
compile each .cpp to an .o with -c and link them at the end so only the
changed file is rebuilt.
If you use Visual Studio instead, make a Console App, add all the files, set the
language standard to C++17 and link d2d1.lib and gdiplus.lib.
Notes
- Windows only. It is built on Win32, Direct2D and GDI+.
- Concept-level and frozen. The widgets that exist work, but the set is small, and there is no support.
- Widgets are owned by their parent through
unique_ptr, while the registrar keeps raw pointers to them, so do not use a widget after its parent is gone. - The repo name ends in
-DEPRECATEDon purpose, because I am no longer maintaining it. Build a real app on it only if you are happy to maintain it yourself. - Free to use under the MIT license. Reuse the ideas, fork it, or finish it if you want to.