Cherry is a modern, powerfull and simple C++/Lua library for building cross-platform application or cool user interfaces.
Whether youβre a beginner or an experienced developer, Cherry makes it easy to create real, efficient, and professional interfaces.
Important: Cherry is part of Infinite's initiative to create free, independent, and ethical technology and computing. Cherry is a library powering many parts of this project, but maintaining libraries like Cherry is a time-consuming and costly process. If you appreciate Cherry, you can support us on our funding page if you can.
- Main goals
- Use cases & Compatibility
- Features
- Dear ImGui native compatibility
- Lua Scripting
- Sound engine
- Drawing API
- Images/Textures
- Fonts
- I/O API
- Hooks
- Translation and accessibility
- Themes builder
- Components & Widgets builder
- Customization abilities
- Network fetch
- Advanced docking
- Markdown renderer
- Choose your features
- Multiple Dear Imgui Context
- Compute shaders
- KMS/DRM Rendering
- Debug tools
- Builtin components
- Builtin themes
- Used concepts
- Get started
- Projects using Cherry
- Choices and style
- Special Thanks
Cherry philosophy is to provide a powerfull library to make applications and interfaces, it's avoids heavy web technologies and bloated solutions, focusing instead on being simple yet complete, powered by ImGui, Vulkan, and SDL.
Whether youβre a beginner or an experienced developer, Cherry makes it easy to create real, efficient, and professional interfaces.
- Cherry is perfect to create tools with a complete interface quickly.
- Cherry is capable of making advanced and complexe user interfaces for applications or softwares.
| Technology | Windows | Linux | Linux DRM | macOS | iOS | Android | Web |
|---|---|---|---|---|---|---|---|
| Vulkan | β | β | π§ | β | β | β | β |
β
: Fully compatible
π§ : Not fully tested, partially incompatible but planned
β : Not planned, incompatible (for the moment...)
The following section will show you a little bit about the main features of Cherry. You will see some short videos, GIFs, or even examples to explain more about Cherry.
Cherry has native compatibility with Dear ImGui! That means you can directly add Dear ImGui code into your Cherry application rendering, and it will work just perfectly.
// Rendered inside a Cherry application
// All the Dear ImGui native code will work natively !
void Render() {
static bool show_demo_window = true;
static bool show_another_window = false;
static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
static ImGuiIO &io = ImGui::GetIO();
{
static float f = 0.0f;
static int counter = 0;
ImGui::Begin("Hello, world!");
ImGui::Text("This is some useful text.");
ImGui::Checkbox("Demo Window", &show_demo_window);
ImGui::Checkbox("Another Window", &show_another_window);
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
ImGui::ColorEdit3("clear color", (float *)&clear_color);
if (ImGui::Button("Button"))
counter++;
ImGui::SameLine();
ImGui::Text("counter = %d", counter);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::End();
}
if (show_another_window) {
ImGui::Begin("Another Window", &show_another_window);
ImGui::Text("Hello from another window!");
if (ImGui::Button("Close Me"))
show_another_window = false;
ImGui::End();
}
}Result :
In fact, Cherry uses Dear ImGui to render interfaces internally. We added many abstractions to allow you to create interfaces without Dear ImGui, just with simple high-level features, components, or widgets. But it is highly modular.
Cherry has an entire Lua scripting engine. You don't want to create interfaces with C++? You can script your applications and tools with Lua. In fact, the Lua scripting engine of Cherry embeds most of Cherry's logic and drawing features such as capturing I/O, playing sounds, drawing rectangles, or creating widgets and components.
Cherry programming with C++ = (code, compilation, wait, run and see)
Cherry programming with Lua = (code, ... that's it! Just see the result)
Preview :
luascripting.mp4
Cherry embeds a sound engine powered by miniaudio. Cherry adds more layers to create channels and run sounds or music easily.
Simple approach :
// Play a sound
CherryApp.PlaySound(CherryPath("resources/audio/tick.wav"));Advanced approach :
// Create and populate sound channels
CherryApp.CreateChannel("SFX");
CherryApp.GetChannel("SFX")->AddSound(CherryPath("resources/audio/tick.wav"));
CherryApp.GetChannel("SFX")->AddSound(CherryPath("resources/audio/tack.wav"));
// Play once
CherryApp.GetChannel("SFX")->PlayTick();
// Start Loop
CherryApp.GetChannel("SFX")->Play();
// Stop Loop
CherryApp.GetChannel("SFX")->Stop();Cherry has a high-level and simple-to-use drawing API, available in Lua and C++. With it, you can draw shapes and primitive forms, and bring life to your interfaces.
Simple example using C++ drawing API
void Render() {
float x = 50.0f;
float y = 75.0f;
float w = 90.0f;
float h = 20.0f;
std::string color = "#B1FF31";
CherryDrawing::RectWindow(Cherry::Vec2(x, y), Cherry::Vec2(w, h), color);
}Result :
More advanced example using C++ drawing API
This is the examples/concepts/drawing example of this repository ! You can check the code here
Cherry embeds a high-level and secure image and texture loader, compatible with multiple contexts of Dear ImGui.
Example using images in components :
CherryStyle::AddMarginX(50.0f);
CherryStyle::AddMarginY(50.0f);
CherryKit::ImageLocal(CherryPath("resources/images/infinite.png"), 100.0f, 50.0f);
CherryKit::ButtonText("Click me !");
CherryKit::ButtonText("And me !");
CherryKit::ButtonTextImage("Infinityyy", CherryPath("resources/images/settings.png"));You can also use Cherry to get native Dear ImGui textures :
This is how :
ImTextureID t = Cherry::GetTexture(CherryPath("resources/images/infinite.png"));Cherry supports font loading and setup. You can easily add new fonts and bring them to your interfaces.
Cherry support TTF and OTF files
void Render() {
Cherry::PushFont("rocket");
CherryKit::TitleOne("Hello font !!!");
Cherry::PopFont();
Cherry::PushFont("chunky");
CherryKit::TitleOne("Hello font !!!");
Cherry::PopFont();
Cherry::PushFont("jetbrainsmono");
CherryKit::TitleOne("Hello font !!!");
Cherry::PopFont();
}
CherryApplication CherryMain(int argc, char **argv) {
Cherry::ApplicationSpecification config;
auto app = new CherryApplication(config);
// Add OTF files
app->AddFont("rocket", CherryPath("rocket.otf"), 80.0f);
app->AddFont("chunky", CherryPath("chunky.otf"), 80.0f);
// Add TTF files
app->AddFont("jetbrainsmono", CherryPath("jetbrainsmono.ttf"), 80.0f);
return app;
}Cherry embeds an Inputs/Outputs API to quickly get user actions, key presses, mouse actions, etc.
if (CherryApp.IsKeyPressed(Cherry::CherryKey::Z)) {
// code here
}
if (CherryApp.IsKeyPressed(Cherry::CherryKey::CTRL)) {
// code here
}
if (CherryApp.IsMouseClicked(0)) {
// left click
}
if (CherryApp.IsMousePressed(0)) {
// left mouse button pressed
}Cherry gives you the possibility of creating hooks in your code. A hook is an independant process that will be calculated during frames or every second. Hooks can process animation data, compute shader results, etc.
The following example will calculate an active RGB color, and display the color in the text.
// Before render
auto foo = CherryHook({
static float hue = 0.0f;
hue += 0.01f;
if (hue > 1.0f)
hue = 0.0f;
float r = 0.f, g = 0.f, b = 0.f;
float h = hue * 6.0f;
int sector = (int)h;
float f = h - sector;
float q = 1.0f - f;
float t = f;
switch (sector % 6)
{
case 0: r = 1.f; g = t; b = 0.f; break;
case 1: r = q; g = 1.f; b = 0.f; break;
case 2: r = 0.f; g = 1.f; b = t; break;
case 3: r = 0.f; g = q; b = 1.f; break;
case 4: r = t; g = 0.f; b = 1.f; break;
case 5: r = 1.f; g = 0.f; b = q; break;
}
int R = (int)(r * 255.0f);
int G = (int)(g * 255.0f);
int B = (int)(b * 255.0f);
char hex[8];
snprintf(hex, sizeof(hex), "#%02X%02X%02X", R, G, B);
self->SetData("color", hex);
});
Cherry::CreateHook("rgb_channel", CherryHookSecond, foo);
// Render
void Render() {
std::string color = Cherry::GetHookData(CherryID("rgb_channel"), "color");
}There is the result :
The following example will create a hook that counts the number of clicks on the button "btn".
// Before render
auto foo = CherryHook({
static int i = 0;
i++;
self->SetData("clicked", std::to_string(i));
});
auto cond = CherryHookCond({
if (CherryApp.GetComponent(CherryID("btn")).GetDataAs<bool>("isClicked")) {
return true;
}
return false;
});
Cherry::CreateHook("btn_number_of_clicks", CherryHookFrame, foo, cond);
// Render
void Render() {
CherryKit::ButtonText(CherryID("btn"), "button");
std::string val = Cherry::GetHookData(CherryID("btn_number_of_clicks"), "clicked");
}Cherry has an advanced translation and locales system, allowing you to adapt your software, tool, or app to many languages in the world.
This is the format Cherry uses for locales. Everything is in a JSON file.
{
"locales": [
{"hello_world":"Hello world !"}
]
}And there is how we interact with locales files !
void Render() {
// You can change the selected language dynamically
if (CherryKit::ButtonText("Change for English").GetDataAs<bool>("isClicked")) {
CherryApp.SetLocale("en");
}
if (CherryKit::ButtonText("Change for Spanish").GetDataAs<bool>("isClicked")) {
CherryApp.SetLocale("es");
}
if (CherryKit::ButtonText("Change for French").GetDataAs<bool>("isClicked")) {
CherryApp.SetLocale("fr");
}
// Get the traduction with topic ID with Cherry::GetLocale
CherryKit::TitleOne(Cherry::GetLocale("hello_world"));
}
CherryApplication *Cherry::CreateApplication(int argc, char **argv) {
Cherry::ApplicationSpecification config;
config.SetMainRenderCallback(Render);
auto app = new CherryApplication(config);
// Register locales here
app->AddLocale("en", Cherry::GetPath("resources/locales/en.json"));
app->AddLocale("fr", Cherry::GetPath("resources/locales/fr.json"));
app->AddLocale("es", Cherry::GetPath("resources/locales/es.json"));
// Set default language
app->SetDefaultLocale("en");
app->SetLocale("en");
return app;
}And this is the result :
The theme builder gives you the ability to easily craft your own themes, and gives your users the opportunity to customize your tool as they wish!
// Cherry gives you the ability to create your own themes with custom properties, style parameters and customized behaviors !
class YellowThemeCherry : public Cherry::Theme {
public:
YellowThemeCherry() {
SetName("yellow");
SetProperty("color_window_bg", "#888815");
SetProperty("color_child_bg", "#888815");
SetProperty("color_framebg", "#888815");
}
};This is the result of the theme example of this repository :
Warning
This feature is still in active development, bugs can occur
The component builder gives you another ability: create your own replicable and invokable components. After crafting your own widgets in Lua or C++, you can call and invoke custom versions of them in your renderings.
To see how to manage components, check the example of managing components and widgets.
To see how create your own replicable components, use the component factory and save time with your creations, check the example of components creation. You can also check all advanced features on the advanced version of the component creation example.
The entire context, windows, and rendering methods are fully customizable. You can control the behavior of a window, and define whether you want the native desktop environment title bar or your own custom one.
While creating your application main implementation, you can define the sizes, behaviors, default states, customizations, settings, etc...
For more informations, please check how to create and configure your application
CherryApplication CherryMain(int argc, char **argv) {
Cherry::ApplicationSpecification config;
// Render mode (4 differents render modes availables, including docking, simple rendering, windowed rendering)
config.SetRenderMode(Cherry::WindowRenderingMethod::SimpleRender);
// Setup default and minimum window sizes
config.SetMinimumHeight(200);
config.SetMinimumWidth(100);
config.SetDefaultHeight(450);
config.SetDefaultWidth(450);
// Custom bar customizations
config.SetCustomTitlebar(true);
config.DisableDesktopTitleBar();
// Set rendering scales
config.SetGlobalScale(1.25f);
config.SetFontGlobalScale(1.15f);
// Icon functions
config.SetFavIconPath(Cherry::GetPath("resources/imgs/icon.png"));
config.SetIconPath(Cherry::GetPath("resources/imgs/icon.png"));
// Themes functions
config.AddTheme(CherryThemes::DarkVortex());
// Enable audio services
config.UseAudio();
return new CherryApplication(config);
}You can call theses configurations for the main application window, or even additional window.
For example, this is how create another window from your first one after starting your application :
if (CherryKit::ButtonText("Spawn window with custom titlebar").GetDataAs<bool>("isClicked")) {
auto ap = CherryKit::WindowSimple("Custom titlebar", RenderCustomTitleBar);
Cherry::ApplicationSpecification spec;
spec.SetName("Custom titlebar");
spec.SetRenderMode(Cherry::WindowRenderingMethod::SimpleWindow);
spec.SetUniqueAppWindowName("Custom titlebar");
spec.SetMinimumHeight(200);
spec.SetMinimumWidth(100);
spec.SetDefaultHeight(450);
spec.SetDefaultWidth(450);
spec.SetCustomTitlebar(true);
spec.DisableWindowManagerTitleBar = true;
ap->AttachOnNewWindow(spec);
Cherry::AddAppWindow(ap);
}There are built-in network features to fetch files from URLs, primarily for the purpose of downloading images from the web.
void Render() {
// Net components from the Cherry Kit
CherryKit::ImageHttp("https://infinite.si/assets/in_projects.c64028e7.png");
// Or fetch as Dear ImGui texture directly
auto texture = Cherry::GetTexture(Cherry::GetHttpPath("https://infinite.si/assets/in_projects.c64028e7.png"));
CherryGUI::Image(texture, 240.0f, 150.0f);
}Result :
Cherry will automaticly put images, medias or http files into a cache in temporary folder to avoid making too many fetches and preserve resources.
Cherry can show advanced dockspaces with multiple context-sharing abilities, giving you the possibility to create complex applications with multiple windows and dockspaces that can share ImGui windows between each other.
This is a short video showing the Docking features of Cherry (from Vortex):
To enable docking, set the RenderMode of Cherry to DockingWindows. Then, every app window of Cherry will be docked into a Dockspace.
CherryApplication CherryMain(int argc, char **argv) {
Cherry::ApplicationSpecification config;
config.SetRenderMode(Cherry::WindowRenderingMethod::DockingWindows);
return new CherryApplication(config);
}Warning
This feature is still in active development, bugs can occur
There is a built-in markdown parser and renderer, allowing you to read and render Markdown files. Ideal for large information files or feature documentation in your applications.
Note
We currently developing this feature. (maybe fully available for 1.7 ?)
Cherry gives you the possibility to choose what you want to use. If you don't want the markdown renderer, audio engine, or sound. You can disable them. By default, nothing is enabled; you need to enable each feature you want directly from the CMakeLists.txt file of your project.
By default, only the minimal render engine and some basic utilities are included. Cherry is not bloated by default.
This is how you can configure your custom Cherry into your projects :
# ---------------------------------------------------------------------------
# Cherry UI Library
# ---------------------------------------------------------------------------
option(CHERRY_STATIC "Link Cherry statically" OFF)
option(CHERRY_DEBUG "Enable Cherry debug tools" ON)
option(CHERRY_IMGUI_EXPORT "Disable workspace saves" ON)
option(CHERRY_PACKAGE "Enable packaging features with pyinstaller" OFF)
option(CHERRY_MULTIAPP "Use multiple apps in one executable" ON)
option(CHERRY_ENABLE_NET "Enable net features" OFF)
option(CHERRY_ENABLE_AUDIO "Enable audio features" OFF)
option(CHERRY_ENABLE_MARKDOWN "Enable markdown parsing and render" ON)
option(CHERRY_ENABLE_SCRIPTING "Enable script engine" ON)
option(CHERRY_ENABLE_KIT "Enable base component kit" ON)
option(CHERRY_ENABLE_LINUXDRM "Enable Linux Direct Rendering Manager" OFF)
option(CHERRY_ENABLE_NODES "Enable nodal systems" OFF)
option(CHERRY_ENABLE_WORKSPACESAVES "Enable workspace saves" OFF)
option(CHERRY_ENABLE_COMPUTE_SHADERS "Enable compute shaders" OFF)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/cherry cherry_build)Cherry natively supports multiple Dear ImGui contexts. That is not the only point, each context in your app can communicate with the others!
In the following example, we can see multiple Dear ImGui contexts in the same application from our example :
Note
This is an advanced use of Cherry, you need to be comfortable with GLSL, Cherry, Dear ImGui, Vulkan and core shader concepts.
Cherry supports compute shaders. A compute shader is not necessarily for rendering itself but more for side calculations you can give to the GPU. You can for example calculate custom textures, calculate Perlin noise, or apply complex mathematical problems.
This is an example from the noises compute shader example :
This is another example from the textures compute shader example :
Note
This is an advanced use of Cherry, you need to be comfortable with Linux, KMS, Direct Rendering Manager and Vulkan.
Cherry gives you the opportunity to create your own low-level renderers, without using a desktop manager or a compositor, just Cherry!
With that feature, you can make your own desktop environments and your own system UIs.
This is an example of the use of Cherry using KMS/DRM :
From this Bluesky post
"The first render of Cherry with vulkan and imgui on Linux KMS/DRM ! Yep, no desktop environment, no compositor, just Cherry."
Warning
This feature is still in active development, bugs can occur
If enabled, Cherry embeds native debug tools, including a component inspector, a console, and some debug utilities (similar to the DevTools of some web browsers).
To enable debug tools, simply turn CHERRY_DEBUG to ON on your cmake, then you have access to the CherryApp.ToggleDebugTools() to show/hide devtools.
You can also trigger devtools with F12 key like a web browser.
This a view of devtools :
Cherry provides an incredible starting set of ready-to-use components such as buttons, sliders, widgets, inputs, etc.
To access the built-in "Kit", simply enable CHERRY_ENABLE_KIT in your CMake file.
Then, you have access to a ton of ready-to-use components and style effects.
CherryKit:: // Access to components
// Exemple :
CherryKit::SeparatorText("Camera");
// You can also use the component directly :
if(CherryKit::SeparatorText("Camera").GetDataAs<bool>("isClicked")) {
// Code when the button is clicked
}You can go on main/kit/components to see all components definitions.
Cherry provides 8 themes of your choice, but you can create your own.
To access the built-in themes, simply enable CHERRY_ENABLE_KIT in your CMake file.
Then, you have access to 8 basic themes.
// You can call built-in themes with CherryThemes::
CherryApplication CherryMain(int argc, char **argv) {
Cherry::ApplicationSpecification config;
config.AddTheme(CherryThemes::Dark());
config.AddTheme(CherryThemes::Light());
config.AddTheme(CherryThemes::DarkColorfull());
// Other settings like render mode, windows etc...
return new CherryApplication(config);
}Immediate mode graphical user interfaces (or ImGui) rendering is a paradigm where the rendering data is stored functionally by the user. It guarantees that all rendering data is refreshed on each frame, following a fully functional approach. This method is ideal for building highly reactive applications, especially when real-time data needs to be displayed, perfect for monitoring or dashboard-style apps!
Static rendering is a common and powerful concept in application rendering. It involves creating a component and reserving it to be rendered and refreshed only when needed. While it is not immediate-mode, it allows for extremely high performance while still ensuring functional behavior and responsiveness.
Semi-static rendering is a hybrid between ImGui-style and static rendering. The graphical part is rendered using ImGui to guarantee instant visuals and highly reactive interactions. Meanwhile, the data and functional part of the component is handled statically, ensuring both low- and high-level persistence.
Note
Semi-static rendering is one of Cherryβs unique features.
Warning
The only dependance you need to compile Cherry is the VulkanSDK installed & ready. Please refer to the https://www.lunarg.com/vulkan-sdk/ website to see how install Vulkan on your system (compatible on Linux & Windows).
πͺ On Microsoft Windows
git clone https://github.com/infiniteHQ/Cherry cd Cherry git submodule update --init --recursive cd examples/hello build.bat .\build\bin\hello.exe
π§ On Linux based distributions
git clone https://github.com/infiniteHQ/Cherry cd Cherry git submodule update --init --recursive cd examples/hello bash build.sh ./build/bin/hello
Congratulations! If everything is working well, you can now start in the Cherry environment. Please follow our documentation. If you encounter any issues, feel free to ask your question or reach out to us on Discord!
Cherry is already used in real-world projects, such as:
You have a project using Cherry ? Come talk about that on our Discord, why not showing it here ?
Cherry follows the Infinite C++ style convention for coding style. Concerning naming, we wanted to keep Dear ImGui's conventions (PascalCase for functions and methods), primarily to maintain consistency across UI codebases and avoid making ImGui-style coding more complex.
- For all contributors of SDL2
- For all contributors of Vulkan/VulkanSDK
- For all contributors of STB
- For the log library of gabime and contributors (https://github.com/gabime/spdlog)
- For the json library of nlohmann and contributors (https://github.com/nlohmann/json)
- For the amazing immediate mode interface ImGui from Ocornut and contributors (https://github.com/ocornut/imgui)
- For the work of from "Studio Cherno" on Walnut (https://github.com/StudioCherno/Walnut)