Class Nuklear


  • public class Nuklear
    extends java.lang.Object
    This is a minimal state immediate mode graphical user interface single header toolkit written in ANSI C and licensed under public domain. It was designed as a simple embeddable user interface for application and does not have any dependencies, a default renderbackend or OS window and input handling but instead provides a very modular library approach by using simple input state for input and draw commands describing primitive shapes as output. So instead of providing a layered library that tries to abstract over a number of platform and render backends it only focuses on the actual UI.

    VALUES

    • Immediate mode graphical user interface toolkit
    • Single header library
    • Written in C89 (ANSI C)
    • Small codebase (~15kLOC)
    • Focus on portability, efficiency and simplicity
    • No dependencies (not even the standard library if not wanted)
    • Fully skinnable and customizable
    • Low memory footprint with total memory control if needed or wanted
    • UTF-8 support
    • No global or hidden state
    • Customizable library modules (you can compile and use only what you need)
    • Optional font baker and vertex buffer output

    FEATURES

    • Absolutely no platform dependent code
    • Memory management control ranging from/to
    • Ease of use by allocating everything from the standard library
    • Control every byte of memory inside the library
    • Font handling control ranging from/to
    • Use your own font implementation for everything
    • Use this libraries internal font baking and handling API
    • Drawing output control ranging from/to
    • Simple shapes for more high level APIs which already having drawing capabilities
    • Hardware accessible anti-aliased vertex buffer output
    • Customizable colors and properties ranging from/to
    • Simple changes to color by filling a simple color table
    • Complete control with ability to use skinning to decorate widgets
    • Bendable UI library with widget ranging from/to
    • Basic widgets like buttons, checkboxes, slider, ...
    • Advanced widget like abstract comboboxes, contextual menus,...
    • Compile time configuration to only compile what you need
    • Subset which can be used if you do not want to link or use the standard library
    • Can be easily modified to only update on user input instead of frame updates

    FONT

    Font handling in this library was designed to be quite customizable and lets you decide what you want to use and what you want to provide. There are three different ways to use the font atlas. The first two will use your font handling scheme and only requires essential data to run nuklear. The next slightly more advanced features is font handling with vertex buffer output.

    1. Using your own implementation without vertex buffer output
      So first of the easiest way to do font handling is by just providing a NkUserFont struct which only requires the height in pixel of the used font and a callback to calculate the width of a string. This way of handling fonts is best fitted for using the normal draw shape command API where you do all the text drawing yourself and the library does not require any kind of deeper knowledge about which font handling mechanism you use. IMPORTANT: the NkUserFont pointer provided to nuklear has to persist over the complete life time! I know this sucks but it is currently the only way to switch between fonts.
      
       float your_text_width_calculation(nk_handle handle, float height, const char *text, int len)
       {
           your_font_type *type = handle.ptr;
           float text_width = ...;
           return text_width;
       }
       
       struct nk_user_font font;
       font.userdata.ptr = &your_font_class_or_struct;
       font.height = your_font_height;
       font.width = your_text_width_calculation;
       
       struct nk_context ctx;
       nk_init_default(&ctx, &font);
    2. Using your own implementation with vertex buffer output
      While the first approach works fine if you don't want to use the optional vertex buffer output it is not enough if you do. To get font handling working for these cases you have to provide two additional parameters inside the `nk_user_font`. First a texture atlas handle used to draw text as subimages of a bigger font atlas texture and a callback to query a character's glyph information (offset, size, ...). So it is still possible to provide your own font and use the vertex buffer output.
      
       float your_text_width_calculation(nk_handle handle, float height, const char *text, int len)
       {
           your_font_type *type = handle.ptr;
           float text_width = ...;
           return text_width;
       }
       void query_your_font_glyph(nk_handle handle, float font_height, struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint)
       {
           your_font_type *type = handle.ptr;
           glyph.width = ...;
           glyph.height = ...;
           glyph.xadvance = ...;
           glyph.uv[0].x = ...;
           glyph.uv[0].y = ...;
           glyph.uv[1].x = ...;
           glyph.uv[1].y = ...;
           glyph.offset.x = ...;
           glyph.offset.y = ...;
       }
       
       struct nk_user_font font;
       font.userdata.ptr = &your_font_class_or_struct;
       font.height = your_font_height;
       font.width = your_text_width_calculation;
       font.query = query_your_font_glyph;
       font.texture.id = your_font_texture;
       
       struct nk_context ctx;
       nk_init_default(&ctx, &font);

    MEMORY BUFFER

    A basic (double)-buffer with linear allocation and resetting as only freeing policy. The buffer's main purpose is to control all memory management inside the GUI toolkit and still leave memory control as much as possible in the hand of the user while also making sure the library is easy to use if not as much control is needed. In general all memory inside this library can be provided from the user in three different ways.

    The first way and the one providing most control is by just passing a fixed size memory block. In this case all control lies in the hand of the user since he can exactly control where the memory comes from and how much memory the library should consume. Of course using the fixed size API removes the ability to automatically resize a buffer if not enough memory is provided so you have to take over the resizing. While being a fixed sized buffer sounds quite limiting, it is very effective in this library since the actual memory consumption is quite stable and has a fixed upper bound for a lot of cases.

    If you don't want to think about how much memory the library should allocate at all time or have a very dynamic UI with unpredictable memory consumption habits but still want control over memory allocation you can use the dynamic allocator based API. The allocator consists of two callbacks for allocating and freeing memory and optional userdata so you can plugin your own allocator.

    TEXT EDITOR

    Editing text in this library is handled by either edit_string or edit_buffer. But like almost everything in this library there are multiple ways of doing it and a balance between control and ease of use with memory as well as functionality controlled by flags.

    This library generally allows three different levels of memory control: First of is the most basic way of just providing a simple char array with string length. This method is probably the easiest way of handling simple user text input. Main upside is complete control over memory while the biggest downside in comparsion with the other two approaches is missing undo/redo.

    For UIs that require undo/redo the second way was created. It is based on a fixed size NkTextEdit struct, which has an internal undo/redo stack. This is mainly useful if you want something more like a text editor but don't want to have a dynamically growing buffer.

    The final way is using a dynamically growing nk_text_edit struct, which has both a default version if you don't care where memory comes from and an allocator version if you do. While the text editor is quite powerful for its complexity I would not recommend editing gigabytes of data with it. It is rather designed for uses cases which make sense for a GUI library not for an full blown text editor.

    DRAWING

    This library was designed to be render backend agnostic so it does not draw anything to screen. Instead all drawn shapes, widgets are made of, are buffered into memory and make up a command queue. Each frame therefore fills the command buffer with draw commands that then need to be executed by the user and his own render backend. After that the command buffer needs to be cleared and a new frame can be started. It is probably important to note that the command buffer is the main drawing API and the optional vertex buffer API only takes this format and converts it into a hardware accessible format.

    To use the command queue to draw your own widgets you can access the command buffer of each window by calling window_get_canvas after previously having called begin:

    
     void draw_red_rectangle_widget(struct nk_context *ctx)
     {
         struct nk_command_buffer *canvas;
         struct nk_input *input = &ctx->input;
         canvas = nk_window_get_canvas(ctx);
     
         struct nk_rect space;
         enum nk_widget_layout_states state;
         state = nk_widget(&space, ctx);
         if (!state) return;
     
         if (state != NK_WIDGET_ROM)
             update_your_widget_by_user_input(...);
         nk_fill_rect(canvas, space, 0, nk_rgb(255,0,0));
     }
     
     if (nk_begin(...)) {
         nk_layout_row_dynamic(ctx, 25, 1);
         draw_red_rectangle_widget(ctx);
     }
     nk_end(..)

    Important to know if you want to create your own widgets is the widget call. It allocates space on the panel reserved for this widget to be used, but also returns the state of the widget space. If your widget is not seen and does not have to be updated it is '0' and you can just return. If it only has to be drawn the state will be WIDGET_ROM otherwise you can do both update and draw your widget. The reason for separating is to only draw and update what is actually neccessary which is crucial for performance.

    STACK

    The style modifier stack can be used to temporarily change a property inside `nk_style`. For example if you want a special red button you can temporarily push the old button color onto a stack draw the button with a red color and then you just pop the old color back from the stack:

    
     nk_style_push_style_item(ctx, &ctx->style.button.normal, nk_style_item_color(nk_rgb(255,0,0)));
     nk_style_push_style_item(ctx, &ctx->style.button.hover, nk_style_item_color(nk_rgb(255,0,0)));
     nk_style_push_style_item(ctx, &ctx->style.button.active, nk_style_item_color(nk_rgb(255,0,0)));
     nk_style_push_vec2(ctx, &cx->style.button.padding, nk_vec2(2,2));
      
     nk_button(...);
      
     nk_style_pop_style_item(ctx);
     nk_style_pop_style_item(ctx);
     nk_style_pop_style_item(ctx);
     nk_style_pop_vec2(ctx);

    Nuklear has a stack for style_items, float properties, vector properties, flags, colors, fonts and for button_behavior. Each has it's own fixed size stack which can be changed in compile time.