Create new application
This page has been automatically translated using the Google Translate API services. We are working on improving texts. Thank you for your understanding and patience.
I consider myself a technical person who chose a great project and an excellent way to carry it out. Linus Torvalds.
In Build NAppGUI we have seen how to compile and package the SDK. Also, in Hello World!, we learned the basic structure of a NAppGUI-based application. The time has come to create our own applications, taking advantage of the CMake modules included in the /prj
folder of the installation.
This chapter is focused on the use of CMake. If you use another build system in your projects, you will have to adapt the dependency management yourself.
1. Use of find_package()
NAppGUI supports the CMake find_package()
command, so managing dependencies is extremely simple. To create a new desktop application, start in a new folder with two files: CMakeLists.txt
and main.c
:
1 2 3 4 |
12/15/23 04:20 PM <DIR> . 12/15/23 04:19 PM <DIR> .. 12/15/23 04:11 PM 292 CMakeLists.txt 12/15/23 03:57 PM 2,315 main.c |
1 2 3 4 5 6 |
cmake_minimum_required(VERSION 3.0) project(NAppHello) find_package(nappgui REQUIRED) # Remove WIN32 in Linux and macOS add_executable(naphello WIN32 main.c) target_link_libraries(naphello ${NAPPGUI_LIBRARIES}) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
/* NAppGUI Hello World */ #include <nappgui.h> typedef struct _app_t App; struct _app_t { Window *window; TextView *text; uint32_t clicks; }; /*---------------------------------------------------------------------------*/ static void i_OnButton(App *app, Event *e) { String *msg = str_printf("Button click (%d)\n", app->clicks); textview_writef(app->text, tc(msg)); str_destroy(&msg); app->clicks += 1; unref(e); } /*---------------------------------------------------------------------------*/ static Panel *i_panel(App *app) { Panel *panel = panel_create(); Layout *layout = layout_create(1, 3); Label *label = label_create(); Button *button = button_push(); TextView *text = textview_create(); app->text = text; label_text(label, "Hello!, I'm a label"); button_text(button, "Click Me!"); button_OnClick(button, listener(app, i_OnButton, App)); layout_label(layout, label, 0, 0); layout_button(layout, button, 0, 1); layout_textview(layout, text, 0, 2); layout_hsize(layout, 0, 250); layout_vsize(layout, 2, 100); layout_margin(layout, 5); layout_vmargin(layout, 0, 5); layout_vmargin(layout, 1, 5); panel_layout(panel, layout); return panel; } /*---------------------------------------------------------------------------*/ static void i_OnClose(App *app, Event *e) { osapp_finish(); unref(app); unref(e); } /*---------------------------------------------------------------------------*/ static App *i_create(void) { App *app = heap_new0(App); Panel *panel = i_panel(app); app->window = window_create(ekWINDOW_STD); window_panel(app->window, panel); window_title(app->window, "Hello, World!"); window_origin(app->window, v2df(500, 200)); window_OnClose(app->window, listener(app, i_OnClose, App)); window_show(app->window); return app; } /*---------------------------------------------------------------------------*/ static void i_destroy(App **app) { window_destroy(&(*app)->window); heap_delete(app, App); } /*---------------------------------------------------------------------------*/ #include "osmain.h" osmain(i_create, i_destroy, "", App) |
You can now generate and compile the solution, using CMake in the usual way. If you installed NAppGUI cmake --install
in a specific location (parameter --prefix
) you must indicate the same path using -DCMAKE_INSTALL_PREFIX
.
|
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=C:/nappgui cmake --build build |
The find_package()
command knows how to locate a package within the usual system directories, depending on each platform. We will need to specify the prefix only when the package is installed in any alternate directory.
-DCMAKE_INSTALL_PREFIX does not imply priority in the search. find_package()
might first find an installation in the system folders.
In the /build/Debug
directory you will have the napphello
executable (Figure 1).
2. NAppProject.cmake
While you can manage your project's CMakeLists.txt
yourself, setting up a cross-platform desktop application can be a bit tedious (even for CMake). NAppGUI provides a number of modules within the /prj
directory of the installation, which can simplify this task. To test it, create a new folder and add this single file CMakeLists.txt
:
1 2 3 4 5 |
cmake_minimum_required(VERSION 3.0) project(NAppHello) find_package(nappgui REQUIRED) include("${NAPPGUI_ROOT_PATH}/prj/NAppProject.cmake") nap_project_desktop_app(napphello hello) |
We execute CMake in the same way as in the previous case:
|
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=C:/nappgui cmake --build build |
In this case we have used the function nap_project_desktop_app()
of the module NAppProject.cmake
, which has created a new folder /hello
. We open the Visual Studio solution that has been generated in /build
(Figure 2).
|
nap_project_desktop_app(appName path) |
appName
: The name of the application.path
: Relative path toCMakeLists.txt
where the project will be located (in this case./hello
). Any route depth is supported. For example:games/myapp
,demo/games/myapp
, etc.
The first time this function is executed, several things are done:
- A new directory
hello
has been created with a default desktop applicationnapphello.c
and aCMakeLists.txt
. - A folder
hello/res
has been created with an image, and it has been used as an icon for the application (Figure 3). In Resources we will continue going deeper. - The newly created
/hello/CMakeLists.txt
has automatically linked to the NAppGUI binaries.
Successive calls to CMake will not overwrite the project files, so we can edit them without fear of losing the changes. Once the project is created, nap_project_desktop_app()
will simply call add_subdirectory()
. The nap_desktop_app()
command in /hello/CMakeLists.txt
knows how to handle cross-platform quirks. For example, in the case of macOS it will create a bundle instead of an isolated executable.
We don't have to limit ourselves to a single application. Our solution will support numerous targets. For example add this line to CMakeLists.txt
and rerun cmake -S . -B build
.
1 2 3 4 5 6 |
cmake_minimum_required(VERSION 3.0) project(NAppHello) find_package(nappgui REQUIRED) include("${NAPPGUI_ROOT_PATH}/prj/NAppProject.cmake") nap_project_desktop_app(napphello hello) nap_project_desktop_app(nappbye bye) |
If the solution was already open, it is possible that the IDE will notify you that there have been changes (Figure 4). After pressing [Reload]
, you will see that the new project (Figure 5) has appeared.
3. Add files
Going back to the napphello
project, we see that by default only one source code file (napphello.c
) is created that contains the entire application. You will most likely want to split the code between different files. Create a pair of new files /hello/myfunc.c
and /hello/myfunc.h
from the IDE or directly from the browser. Open them and add these lines:
1 2 3 4 5 |
1 2 3 4 5 6 7 8 |
Abre /hello/napphello.c
y edita la función i_OnButton
.
1 2 3 4 5 6 7 8 9 |
... static void i_OnButton(App *app, Event *e) { real32_t res = myadd_func(56.4f, 23.3f); textview_printf(app->text, "Button click (%d-%.2f)\n", app->clicks, res); app->clicks += 1; unref(e); } ... |
Re-generate the solution with cmake -S . -B build
. The IDE, Visual Studio in this case, informs us again that there have been changes in the napphello
project. Simply press [Reload All]
as we did in the previous case.
Recompile and run napphello
to see the changes you just made. You can create as many files and subfolders within the /hello
directory as you need to better organize your code. Always remember to run cmake -S . -B build
every time you add or remove files from the project. The nap_desktop_app()
command will update the solution by "cloning" the directory structure within the project (napphello
in this case).
At this point we recommend that you spend some time researching, compiling and testing the examples in the demo
folder within the NAppGUI repository.
4. Command line applications
Similar to the desktop applications seen above, it is possible to create console applications. Add this new line to the CMakeLists.txt
of the solution.
1 2 3 4 5 6 7 |
cmake_minimum_required(VERSION 3.0) project(NAppHello) find_package(nappgui REQUIRED) include("${NAPPGUI_ROOT_PATH}/prj/NAppProject.cmake") nap_project_desktop_app(napphello hello) nap_project_desktop_app(nappbye bye) nap_project_command_app(myutil utils/myutil) |
When regenerating the solution with cmake -S . -B build
, Visual Studio will alert you again that you need to reload the solution. A new project will have been created in ./utils/myutil
(Figure 6), but this time if you compile and run it no window will appear. You will only see a message in the Visual Studio console:
1 |
Hello world! |
If you open myutil.c
you will find the code that generated the previous output:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* NAppGUI Console Application */ #include <core/coreall.h> int main(int argc, char *argv[]) { unref(argc); unref(argv); core_start(); bstd_printf("Hello world!\n"); core_finish(); return 0; } |
Which is the typical template of a C program, to which the support of the core library has been included. From here, we can modify the code and compile. nap_command_app()
already set everything up for us.
|
nap_project_command_app(appName path) |
appName
: The name of the application.path
: Relative path to.
where the project will be located (in this case./utils/myutil
).
It goes without saying that the behavior of nap_project_command_app()
is identical to that of nap_project_desktop_app()
. It will not overwrite the project files once created and will integrate all new files that we add in the future.
5. Use of libraries
Let's imagine that our three applications needed to share certain functionalities. The smartest thing would be to encapsulate these functions in a library and for all three to have access to them. We will achieve this by inserting a new line in our CMakeLists.txt
:
1 2 3 4 5 6 7 8 |
cmake_minimum_required(VERSION 3.0) project(NAppHello) find_package(nappgui REQUIRED) include("${NAPPGUI_ROOT_PATH}/prj/NAppProject.cmake") nap_project_library(common common) nap_project_desktop_app(napphello hello) nap_project_desktop_app(nappbye bye) nap_project_command_app(myutil utils/myutil) |
Notice that the command we have inserted nap_project_library()
precedes the applications. This is because CMake needs to process dependencies before the projects that use them.
|
nap_project_library(libName path) |
libName
: The name of the library.path
: Relative path toCMakeLists.txt
where the project will be located (in this case./common
).
As with application projects, the first time nap_project_library()
runs, a series of default files are created. Later they can be edited, deleted or added more as we have just seen in the case of applications:
common.def
: File that defines the_common_api
macro necessary for the export of symbols. More information in Symbols and visibility.common.hxx
: Here we will include the definitions of public types, such asenum
,struct
. At the momentcommon
does not contain public types.common.h
: Header file. Here we will write the general function declaration of the library. By default, CMake creates two:common_start()
andcommon_finish()
, where we would implement global start and end code for the library, if necessary.common.c
: Implementation of general functions.CMakeLists.txt
: Where thenap_library()
command is used, analogous tonap_desktop_app()
which will handle the particularities of each platform.
Open common.h
and common.c
, adding a new function:
Edit the command nap_desktop_app()
in /hello/CMakeLists.txt
, and include the dependency with common
:
1 |
nap_desktop_app(napphello "common" NRC_NONE) |
Run cmake -S -B build
again for all changes to take effect. You can now use the new common_add
function within napphello.c
:
1 2 3 4 5 6 7 |
static void i_OnButton(App *app, Event *e) { uint32_t r = common_add(100, 200); textview_printf(app->text, "Button click (%d-%d)\n", app->clicks, r); app->clicks += 1; unref(e); } |
You can create as many libraries as your project needs. The only thing you should keep in mind is to include the name of the new dependency in the commands nap_desktop_app()
, nap_command_app()
or nap_library()
of each target. At Create new library we will continue to delve deeper into the use of libraries.
6. C/C++ Standard
Compilers generally allow you to check that code conforms to certain C/C++ standards, issuing warnings or errors when it does not. For the sake of portability, all projects generated by nap_desktop_app()
and nap_command_app()
set the older standards (C90
and C++98
respectively). You may want to use more modern standards in your projects. Open /hello/CMakeLists.txt
and add these two lines:
1 2 3 4 |
nap_desktop_app(napphello "" NRC_NONE) nap_target_c_standard(napphello 11) nap_target_cxx_standard(napphello 14) target_link_libraries(napphello ${NAPPGUI_LIBRARIES}) |
The command nap_target_c_standard()
has set the standard C11
for napphello
. Same form, nap_target_cxx_standard()
has selected C++14
.
- C Standard:
90
,99
,11
,17
y23
. - C++ Standard:
98
,11
,14
,17
,20
,23
y26
.
If CMake or the compiler does not support the indicated standard, the highest allowed will be established. It is the responsibility of the programmer to use the appropriate compilers for the chosen standard.
7. NAppCompilers.cmake
De igual forma que el módulo NAppProject.cmake
nos ayuda a crear y configurar nuestros propios proyectos, NAppCompilers.cmake
que hace lo propio con los compiladores. Para utilizarlo en tu proyecto, tan solo deberás añadir estas dos líneas a tu CMakeLists.txt
principal.
1 2 3 4 5 6 7 |
cmake_minimum_required(VERSION 3.0) project(NAppHello) find_package(nappgui REQUIRED) include("${NAPPGUI_ROOT_PATH}/prj/NAppCompilers.cmake") nap_config_compiler() include("${NAPPGUI_ROOT_PATH}/prj/NAppProject.cmake") nap_project_desktop_app(napphello hello) |
The function nap_config_compiler()
detects the compiler among all those supported by NAppGUI: MSVC
, GCC
, Clang
and AppleClang
, setting the typical options for each configuration and platform. It also adds support for the different CMake configuration options that NAppGUI uses in its own libraries and example applications. More information at win_macos_linux.