Skip to content

Commit f523507

Browse files
committed
feat: Implement TUIKLoader for dynamic UI loading from .tuik files; add example and update documentation
1 parent a3a5de5 commit f523507

5 files changed

Lines changed: 145 additions & 35 deletions

File tree

CMakeLists.txt

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,33 @@ include_directories(external/json)
3030
# PROJECT STRUCTURE
3131
# =============================================================================
3232

33+
# -----------------------------------------------------------------------------
34+
# Automatic generation of ui_<project_name>.h headers from .tuik files declared by the user
35+
# -----------------------------------------------------------------------------
36+
# Example usage in the project's CMakeLists.txt:
37+
# set(TUIKIT_UI examples/simple_ui.tuik path/to/other_ui.tuik)
38+
if(NOT DEFINED TUIKIT_UI)
39+
set(TUIKIT_UI "${CMAKE_SOURCE_DIR}/examples/example_2.tuik")
40+
endif()
41+
set(EMBEDDED_HEADERS "")
42+
foreach(TUIK_FILE ${TUIKIT_UI})
43+
get_filename_component(TUIK_NAME ${TUIK_FILE} NAME_WE)
44+
get_filename_component(TUIK_DIR ${TUIK_FILE} DIRECTORY)
45+
set(VAR_NAME "${TUIK_NAME}_tuik")
46+
set(HEADER_FILE "${TUIK_DIR}/${TUIK_NAME}_tuik.h")
47+
add_custom_command(
48+
OUTPUT ${HEADER_FILE}
49+
COMMAND ${CMAKE_COMMAND} -E make_directory ${TUIK_DIR}
50+
COMMAND xxd -i -n ${VAR_NAME} ${TUIK_FILE} > ${HEADER_FILE}
51+
DEPENDS ${TUIK_FILE}
52+
COMMENT "Generating ${HEADER_FILE} from ${TUIK_FILE} with variable ${VAR_NAME}"
53+
VERBATIM
54+
)
55+
list(APPEND EMBEDDED_HEADERS ${HEADER_FILE})
56+
endforeach()
57+
add_custom_target(generate_embedded_headers ALL DEPENDS ${EMBEDDED_HEADERS})
58+
59+
3360
# Headers
3461
set(TUIKIT_HEADERS
3562
include/tuikit.h
@@ -140,7 +167,7 @@ install(DIRECTORY include/tuikit/core/ DESTINATION include/tuikit/core FILES_MAT
140167
install(DIRECTORY include/tuikit/layouts/ DESTINATION include/tuikit/layouts FILES_MATCHING PATTERN "*.h")
141168
install(DIRECTORY include/tuikit/widgets/ DESTINATION include/tuikit/widgets FILES_MATCHING PATTERN "*.h")
142169

143-
# Installation de la librairie
170+
# Library installation
144171
install(TARGETS tuikit
145172
EXPORT TUIFrameworkTargets
146173
LIBRARY DESTINATION lib
@@ -149,7 +176,7 @@ install(TARGETS tuikit
149176
INCLUDES DESTINATION include
150177
)
151178

152-
# Configuration pour find_package
179+
# Configuration for find_package
153180
install(EXPORT TUIFrameworkTargets
154181
FILE TUIFrameworkTargets.cmake
155182
NAMESPACE TUIFramework::
@@ -229,22 +256,22 @@ include(CPack)
229256
# HELPER SCRIPTS
230257
# =============================================================================
231258

232-
# Script de build rapide
259+
# Quick build script
233260
# configure_file(
234261
# "${CMAKE_CURRENT_SOURCE_DIR}/scripts/build.sh.in"
235262
# "${CMAKE_CURRENT_BINARY_ROOT}/build.sh"
236263
# @ONLY
237264
# )
238265

239-
# Script d'installation
266+
# Installation script
240267
# configure_file(
241268
# "${CMAKE_CURRENT_SOURCE_DIR}/scripts/install.sh.in"
242269
# "${CMAKE_CURRENT_BINARY_ROOT}/install.sh"
243270
# @ONLY
244271
# )
245272

246273
# =============================================================================
247-
# INFORMATIONS DE BUILD
274+
# BUILD INFORMATION
248275
# =============================================================================
249276

250277
message(STATUS "=================================")
@@ -258,3 +285,4 @@ message(STATUS "Build tests: ${BUILD_TESTS}")
258285
message(STATUS "Build docs: ${BUILD_DOCS}")
259286
message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}")
260287
message(STATUS "=================================")
288+

README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Based on [FTXUI](https://github.com/ArthurSonzogni/FTXUI), TUIKit is a modern C+
1010
* **Event Handling:** Manage user interactions through a signal/slot-like mechanism or callbacks.
1111
* **Automatic Layouts:** Employ flexible layout managers (`TUIVBox`, `TUIHBox`) for responsive UI design.
1212
* **Advanced Theming:** Customize colors, borders, and styles to create visually appealing interfaces.
13+
* **UI Definition Files:** Support for defining UI layouts in JSON-like `.tuik` files, allowing dynamic loading and modification of interfaces.
1314

1415
## ✨ Implemented Components (Current State)
1516

@@ -106,6 +107,75 @@ int main() {
106107
return app.exec();
107108
}
108109
```
110+
111+
---
112+
113+
## 🆕 Example: Embedding a `.tuik` UI file
114+
115+
TUIKit supports embedding JSON UI definitions (`.tuik` files) directly into your binary using CMake and `xxd`.
116+
For each `.tuik` file listed in `TUIKIT_UI`, a header `<name>_tuik.h` is generated with a binary variable.
117+
118+
**Example `.tuik` file (`examples/simple_ui.tuik`):**
119+
```json
120+
{
121+
"class": "TUIVBoxLayout",
122+
"children": [
123+
{ "class": "TUILabel", "properties": { "text": "Hello from .tuik!" } },
124+
{ "class": "TUIButton", "properties": { "text": "Click Me" }, "name": "btn" }
125+
]
126+
}
127+
```
128+
129+
**C++ usage:**
130+
```cpp
131+
#include "tuikit.h"
132+
#include "examples/simple_ui_tuik.h"
133+
#include <nlohmann/json.hpp>
134+
135+
using namespace TUIKIT;
136+
137+
int main() {
138+
TUIApp app("TUI .tuik Example");
139+
140+
// Load UI from embedded binary resource
141+
std::string tuik_str(reinterpret_cast<const char*>(simple_ui_tuik), simple_ui_tuik_len);
142+
nlohmann::json tuik_json = nlohmann::json::parse(tuik_str);
143+
144+
TUIKLoader loader;
145+
auto main_ui_widget = loader.createWidgetFromJson(tuik_json);
146+
app.setMainWidget(main_ui_widget);
147+
148+
// Connect a slot to the button named "btn"
149+
auto btn = std::dynamic_pointer_cast<TUIButton>(loader.getWidget("btn"));
150+
if (btn) {
151+
connect(btn, [&] { /* action */ });
152+
}
153+
154+
return app.exec();
155+
}
156+
```
157+
158+
**CMake integration:**
159+
```cmake
160+
find_program(XXD_EXE xxd REQUIRED)
161+
set(TUIKIT_UI "examples/simple_ui.tuik")
162+
foreach(TUIK_FILE ${TUIKIT_UI})
163+
get_filename_component(TUIK_NAME ${TUIK_FILE} NAME_WE)
164+
get_filename_component(TUIK_DIR ${TUIK_FILE} DIRECTORY)
165+
set(VAR_NAME "${TUIK_NAME}_tuik")
166+
set(HEADER_FILE "${TUIK_DIR}/${TUIK_NAME}_tuik.h")
167+
add_custom_command(
168+
OUTPUT ${HEADER_FILE}
169+
COMMAND ${CMAKE_COMMAND} -E make_directory ${TUIK_DIR}
170+
COMMAND ${XXD_EXE} -i -n ${VAR_NAME} ${TUIK_FILE} > ${HEADER_FILE}
171+
DEPENDS ${TUIK_FILE}
172+
COMMENT "Generate ${HEADER_FILE} from ${TUIK_FILE} with variable ${VAR_NAME} (xxd required)"
173+
VERBATIM
174+
)
175+
list(APPEND EMBEDDED_HEADERS ${HEADER_FILE})
176+
endforeach()
177+
add_custom_target(generate_embedded_headers ALL DEPENDS ${EMBEDDED_HEADERS})
178+
```
109179
<!--
110180
## 🗺️ Development Roadmap
111181

examples/example_2.cpp

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,54 @@
22
#include <iostream>
33
#include <string>
44
#include <fstream>
5+
#include "example_2_tuik.h"
6+
#include <nlohmann/json.hpp>
7+
8+
// Ensure the following extern declarations match those in your generated simple_ui_tuik.h
59

610
using namespace TUIKIT;
711

8-
int main() {
12+
int main()
13+
{
914
std::ofstream cerr_log("debug_log_example2.txt");
10-
std::streambuf* old_cerr_buf = std::cerr.rdbuf();
15+
std::streambuf *old_cerr_buf = std::cerr.rdbuf();
1116
std::cerr.rdbuf(cerr_log.rdbuf());
1217

1318
TUIApp app("TUIKIT UI Loader Example");
1419

15-
// Load UI from .tuik file
16-
auto main_ui_widget = app.loadUI("../examples/simple_ui.tuik");
20+
// Load UI from binary resource
21+
std::string tuik_str(reinterpret_cast<const char *>(example_2_tuik), sizeof(example_2_tuik));
22+
nlohmann::json tuik_json = nlohmann::json::parse(tuik_str);
23+
TUIKLoader loader;
24+
auto main_ui_widget = loader.createWidgetFromJson(tuik_json);
1725

18-
if (!main_ui_widget) {
19-
std::cerr << "Failed to load UI from simple_ui.tuik" << std::endl;
20-
std::cout << "Failed to load UI from simple_ui.tuik" << std::endl;
26+
if (!main_ui_widget)
27+
{
28+
std::cerr << "Failed to load UI from embedded JSON" << std::endl;
2129
return 1;
2230
}
2331

2432
app.setMainWidget(main_ui_widget);
2533

26-
// Get named widgets
27-
auto name_input = std::dynamic_pointer_cast<TUITextField>(app.getWidget("name_input"));
28-
auto submit_button = std::dynamic_pointer_cast<TUIButton>(app.getWidget("submit_button"));
29-
auto output_label = std::dynamic_pointer_cast<TUILabel>(app.getWidget("output_label"));
34+
// Get named widgets from loader
35+
auto name_input = std::dynamic_pointer_cast<TUITextField>(loader.getWidget("name_input"));
36+
auto submit_button = std::dynamic_pointer_cast<TUIButton>(loader.getWidget("submit_button"));
37+
auto output_label = std::dynamic_pointer_cast<TUILabel>(loader.getWidget("output_label"));
3038

31-
if (name_input && submit_button && output_label) {
32-
connect(submit_button, [&] {
33-
output_label->setText("Hello, " + name_input->text() + "!");
34-
});
35-
} else {
39+
if (name_input && submit_button && output_label)
40+
{
41+
connect(submit_button, [&]
42+
{ output_label->setText("Hello, " + name_input->text() + "!"); });
43+
}
44+
else
45+
{
3646
std::cerr << "Error: Could not find all required widgets by name." << std::endl;
3747
}
3848

39-
app.setOnExit([&] {
49+
app.setOnExit([&]
50+
{
4051
std::cerr << "Application is exiting. Goodbye from example_2!" << std::endl;
41-
std::cerr.rdbuf(old_cerr_buf);
42-
});
52+
std::cerr.rdbuf(old_cerr_buf); });
4353

4454
int result = app.exec();
4555

include/tuikit/core/TUIKLoader.h

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,21 @@
3333
#include <string>
3434
#include <vector>
3535

36-
namespace TUIKIT {
36+
namespace TUIKIT
37+
{
3738

38-
class TUIKLoader {
39-
public:
40-
TUIKLoader();
41-
std::shared_ptr<TUIWidget> load(const std::string& filepath);
42-
std::shared_ptr<TUIWidget> getWidget(const std::string& name);
39+
class TUIKLoader
40+
{
41+
public:
42+
TUIKLoader();
43+
std::shared_ptr<TUIWidget> load(const std::string &filepath);
44+
std::shared_ptr<TUIWidget> getWidget(const std::string &name);
45+
std::shared_ptr<TUIWidget> createWidgetFromJson(const nlohmann::json &widget_json);
4346

44-
private:
45-
std::map<std::string, std::shared_ptr<TUIWidget>> named_widgets_;
47+
private:
48+
std::map<std::string, std::shared_ptr<TUIWidget>> named_widgets_;
4649

47-
std::shared_ptr<TUIWidget> createWidgetFromJson(const nlohmann::json& widget_json);
48-
void applyProperties(std::shared_ptr<TUIWidget> widget, const nlohmann::json& properties_json);
49-
};
50+
void applyProperties(std::shared_ptr<TUIWidget> widget, const nlohmann::json &properties_json);
51+
};
5052

5153
} // namespace TUIKIT

0 commit comments

Comments
 (0)