7 n__gui_8h
gullradriel edited this page 2026-10-12 07:59:13 +02:00

include/nilorea/n_gui.h

GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows. More...

Structures

Name
struct N_GUI_WINDOW
A pseudo window that contains widgets.
struct N_GUI_WIDGET
A single GUI widget.
struct N_GUI_TREE_STATE
A tree's view state, captured by path so it survives a rebuild that replaces every node (a reloaded model has new pointers and new indices).
struct N_GUI_TREE_NODE
a single node in the tree view
struct N_GUI_TREE
tree view built from an N_GUI listbox
struct N_GUI_THEME
Color theme for a widget.
struct N_GUI_TEXT_DIMS
bounding box dimensions returned by n_gui_get_text_dims()
struct N_GUI_TEXTAREA_DATA
text area specific data
struct N_GUI_TB_BUTTONS
Titlebar button state for a window (minimize, maximize, close).
struct N_GUI_TAB_PANEL
tab panel built from toggle buttons with content window management
struct N_GUI_SYNTAXVIEW_DATA
syntax view specific data: read-only highlighted text
struct N_GUI_STYLE
Global style holding every configurable layout constant.
struct N_GUI_SPLITPANE_DATA
split pane specific data: a draggable divider between two regions
struct N_GUI_SLIDER_DATA
slider specific data
struct N_GUI_SECTIONLIST
a vertical accordion of foldable sections inside one window; folding hides a section's widgets and re-stacks the rest, leaving the window's N_GUI_WIN_AUTO_SCROLLBAR to absorb any overflow
struct N_GUI_SECTION
a single foldable section: a clickable header plus the member widgets it shows/hides
struct N_GUI_SCROLLBAR_DATA
scrollbar specific data
struct N_GUI_SB_TRACK
Where a scrollbar a widget draws inside itself was last painted, and whether the pointer is on it.
struct N_GUI_RADIOLIST_DATA
radio list specific data
struct N_GUI_PROGRESSBAR_DATA
Progress-bar widget data (display only).
struct N_GUI_PENDING_WIDGET
Saved user-adjustable state for a keyed widget that did not exist when the layout was loaded (a lazily-created splitpane/datagrid).
struct N_GUI_PENDING_GEOM
A saved window position retained for a window not yet created at load time (see N_GUI_CTX::pending_layout).
struct N_GUI_MOUSE_EVENT
A mouse event handed to a custom widget, in the widget's own coordinates.
struct N_GUI_MARKDOWNVIEW_DATA
markdownview specific data.
struct N_GUI_LISTITEM
a single item in a list/radio/combo widget
struct N_GUI_LISTBOX_DATA
listbox specific data
struct N_GUI_LABEL_DATA
static text label specific data
struct N_GUI_KV_ROW
a single row in the KV table
struct N_GUI_KVTABLE
key-value table built from textareas, checkboxes, and buttons.
struct N_GUI_IMAGE_DATA
image widget specific data
struct N_GUI_HEXVIEW_DATA
hex viewer specific data: a read-only byte dump
struct N_GUI_DROPMENU_ENTRY
A single entry in a dropdown menu (static or dynamic).
struct N_GUI_DROPMENU_DATA
dropdown menu specific data
struct N_GUI_DATAGRID_DATA
data grid specific data: a sortable column table
struct N_GUI_DATAGRID_COL
one data grid column definition
struct N_GUI_CUSTOM_DATA
Owner-draw custom widget data: a paint callback, an optional mouse callback, and their user pointers.
struct N_GUI_CTX
The top-level GUI context that holds all windows.
struct N_GUI_COMBOBOX_DATA
combo box specific data
struct N_GUI_CHECKBOX_DATA
checkbox specific data
struct N_GUI_BUTTON_DATA
button specific data

Detailed Description

GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.

Author: Castagnier Mickael

Version: 3.0

Date: 02/03/2026

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#ifndef __NILOREA_GUI__
#define __NILOREA_GUI__

#ifdef __cplusplus
extern "C" {
#endif


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>

#include <nilorea/n_allegro5.h>
#include <nilorea/n_common.h>
#include <nilorea/n_str.h>
#include <nilorea/n_log.h>
#include <nilorea/n_gui_md.h>
#include <nilorea/n_list.h>
#include <nilorea/n_hash.h>

#define N_GUI_ID_MAX 128
#define N_GUI_TEXT_MAX 4096

#define N_GUI_SUGGEST_MAX 64

#define N_GUI_SUGGEST_VISIBLE 8

#define N_GUI_SUGGEST_SEP_MAX 8

/* widget type enum */
#define N_GUI_TYPE_BUTTON 1
#define N_GUI_TYPE_SLIDER 2
#define N_GUI_TYPE_TEXTAREA 3
#define N_GUI_TYPE_CHECKBOX 4
#define N_GUI_TYPE_SCROLLBAR 5
#define N_GUI_TYPE_LISTBOX 6
#define N_GUI_TYPE_RADIOLIST 7
#define N_GUI_TYPE_COMBOBOX 8
#define N_GUI_TYPE_IMAGE 9
#define N_GUI_TYPE_LABEL 10
#define N_GUI_TYPE_DROPMENU 11
#define N_GUI_TYPE_SPLITPANE 12
#define N_GUI_TYPE_HEXVIEW 13
#define N_GUI_TYPE_SYNTAXVIEW 14
#define N_GUI_TYPE_DATAGRID 15

#define N_GUI_TYPE_PROGRESSBAR 16

#define N_GUI_TYPE_CUSTOM 17

#define N_GUI_TYPE_MARKDOWNVIEW 18

#define N_GUI_SPLIT_VERTICAL 0
#define N_GUI_SPLIT_HORIZONTAL 1

#define N_GUI_SYNTAX_PLAIN 0
#define N_GUI_SYNTAX_HTTP 1
#define N_GUI_SYNTAX_JSON 2
#define N_GUI_SYNTAX_XML 3
#define N_GUI_SYNTAX_YAML 4
#define N_GUI_SYNTAX_JS 5
#define N_GUI_SYNTAX_DIFF 6

/* shape flags */
#define N_GUI_SHAPE_RECT 0
#define N_GUI_SHAPE_ROUNDED 1
#define N_GUI_SHAPE_BITMAP 2

/* slider mode */
#define N_GUI_SLIDER_VALUE 0
#define N_GUI_SLIDER_PERCENT 1

/* slider orientation */
#define N_GUI_SLIDER_H 0
#define N_GUI_SLIDER_V 1

/* scrollbar orientation */
#define N_GUI_SCROLLBAR_H 0
#define N_GUI_SCROLLBAR_V 1

/* list selection modes */
#define N_GUI_SELECT_NONE 0
#define N_GUI_SELECT_SINGLE 1
#define N_GUI_SELECT_MULTIPLE 2

/* image scale modes */
#define N_GUI_IMAGE_FIT 0
#define N_GUI_IMAGE_STRETCH 1
#define N_GUI_IMAGE_CENTER 2

/* text alignment */
#define N_GUI_ALIGN_LEFT 0
#define N_GUI_ALIGN_CENTER 1
#define N_GUI_ALIGN_RIGHT 2
#define N_GUI_ALIGN_JUSTIFIED 3

#define N_GUI_TRUNCATE_END 0
#define N_GUI_TRUNCATE_START 1
#define N_GUI_TRUNCATE_PATH 2

/* widget state flags */
#define N_GUI_STATE_IDLE 0
#define N_GUI_STATE_HOVER 1
#define N_GUI_STATE_ACTIVE 2
#define N_GUI_STATE_FOCUSED 4
#define N_GUI_STATE_SCROLLBAR_DRAG 8

/* window state flags */
#define N_GUI_WIN_OPEN 1
#define N_GUI_WIN_MINIMISED 2
#define N_GUI_WIN_DRAGGING 4
#define N_GUI_WIN_RESIZING 8
#define N_GUI_WIN_VSCROLL_DRAG 16
#define N_GUI_WIN_HSCROLL_DRAG 32
#define N_GUI_WIN_MAXIMISED 64
#define N_GUI_WIN_VSCROLL_HOVER 128
#define N_GUI_WIN_HSCROLL_HOVER 256

/* window feature flags (set via n_gui_window_set_flags) */
#define N_GUI_GLOBAL_SCROLL_SLACK 0.5f

#define N_GUI_WIN_AUTO_SCROLLBAR 16
#define N_GUI_WIN_RESIZABLE 32
#define N_GUI_WIN_FIXED_POSITION 64
#define N_GUI_WIN_FRAMELESS 128
#define N_GUI_WIN_BTN_MINIMIZE 256
#define N_GUI_WIN_BTN_MAXIMIZE 512
#define N_GUI_WIN_BTN_CLOSE 1024
#define N_GUI_WIN_BTN_ALL (N_GUI_WIN_BTN_MINIMIZE | N_GUI_WIN_BTN_MAXIMIZE | N_GUI_WIN_BTN_CLOSE)
#define N_GUI_WIN_NO_HSCROLL 2048

/* native (detached) window options, passed to n_gui_window_detach */
#define N_GUI_DETACH_NONE 0
#define N_GUI_DETACH_RESIZABLE 1
#define N_GUI_DETACH_SCALE_CONTENT 2
#define N_GUI_DETACH_OWN_CHROME 4

/* titlebar button indices */
#define N_GUI_TB_BTN_NONE 0
#define N_GUI_TB_BTN_MINIMIZE 1
#define N_GUI_TB_BTN_MAXIMIZE 2
#define N_GUI_TB_BTN_CLOSE 3

/* context-level resize modes (set via n_gui_set_resize_mode) */
#define N_GUI_RESIZE_VIRTUAL 0
#define N_GUI_RESIZE_ADAPTIVE 1

/* per-window resize policies (used in ADAPTIVE mode) */
#define N_GUI_WIN_RESIZE_NONE 0
#define N_GUI_WIN_RESIZE_MOVE 1
#define N_GUI_WIN_RESIZE_SCALE 2

/* dialog auto-adjust flags (set via n_gui_window_set_autofit) */
#define N_GUI_AUTOFIT_W 1
#define N_GUI_AUTOFIT_H 2
#define N_GUI_AUTOFIT_WH 3
#define N_GUI_AUTOFIT_EXPAND_LEFT 4
#define N_GUI_AUTOFIT_EXPAND_UP 8
#define N_GUI_AUTOFIT_CENTER 16

/* window z-order modes */
#define N_GUI_ZORDER_NORMAL 0
#define N_GUI_ZORDER_ALWAYS_ON_TOP 1
#define N_GUI_ZORDER_ALWAYS_BEHIND 2
#define N_GUI_ZORDER_FIXED 3
#define N_GUI_ZORDER_POPUP 4

/* text dimension bounding box */

typedef struct N_GUI_TEXT_DIMS {
    int x; 
    int y; 
    int w; 
    int h; 
} N_GUI_TEXT_DIMS;

/* color theme */

typedef struct N_GUI_THEME {
    ALLEGRO_COLOR bg_normal;
    ALLEGRO_COLOR bg_hover;
    ALLEGRO_COLOR bg_active;
    ALLEGRO_COLOR border_normal;
    ALLEGRO_COLOR border_hover;
    ALLEGRO_COLOR border_active;
    ALLEGRO_COLOR text_normal;
    ALLEGRO_COLOR text_hover;
    ALLEGRO_COLOR text_active;
    float border_thickness;
    float corner_rx;
    float corner_ry;
    ALLEGRO_COLOR selection_color;
} N_GUI_THEME;

/* button glyphs: small icons drawn with primitives in the button's text colour,
 * for square buttons where a text label would not fit or would read poorly
 * (a row of "move up / move down / duplicate" actions, a toolbar of arrows).
 * They cost no assets, follow the theme, and stay crisp at any size. */
#define N_GUI_GLYPH_NONE 0
#define N_GUI_GLYPH_ARROW_UP 1
#define N_GUI_GLYPH_ARROW_DOWN 2
#define N_GUI_GLYPH_ARROW_LEFT 3
#define N_GUI_GLYPH_ARROW_RIGHT 4
#define N_GUI_GLYPH_PLUS 5
#define N_GUI_GLYPH_MINUS 6
#define N_GUI_GLYPH_CROSS 7
#define N_GUI_GLYPH_COPY 8
#define N_GUI_GLYPH_COUNT 9

/* button key modifier flags (use with n_gui_button_set_keycode) */
#define N_GUI_KEY_MOD_MASK (ALLEGRO_KEYMOD_SHIFT | ALLEGRO_KEYMOD_CTRL | ALLEGRO_KEYMOD_ALT | ALLEGRO_KEYMOD_ALTGR)

#define N_GUI_KEY_SOURCES_MAX 8

typedef struct N_GUI_SB_TRACK {
    float x;
    float y;
    float w;
    float h;
    int hovered;
} N_GUI_SB_TRACK;

/* per-widget data */

typedef struct N_GUI_BUTTON_DATA {
    char label[N_GUI_ID_MAX];
    ALLEGRO_BITMAP* bitmap;
    ALLEGRO_BITMAP* bitmap_hover;
    ALLEGRO_BITMAP* bitmap_active;
    int shape;
    int glyph;
    int toggle_mode;
    int toggled;
    int keycode;
    int key_modifiers;
    int key_focus_only;
    int key_sources[N_GUI_KEY_SOURCES_MAX];
    double key_press_until;
    void (*on_click)(int widget_id, void* user_data);
    void* user_data;
} N_GUI_BUTTON_DATA;

typedef struct N_GUI_SLIDER_DATA {
    double min_val;
    double max_val;
    double value;
    double step;
    double key_step;
    int mode;
    int orientation;
    ALLEGRO_BITMAP* track_bitmap;
    ALLEGRO_BITMAP* fill_bitmap;
    ALLEGRO_BITMAP* handle_bitmap;
    ALLEGRO_BITMAP* handle_hover_bitmap;
    ALLEGRO_BITMAP* handle_active_bitmap;
    void (*on_change)(int widget_id, double value, void* user_data);
    void* user_data;
    char value_format[32];
    int value_visible;
    int has_default;
    double default_value;
} N_GUI_SLIDER_DATA;

typedef struct N_GUI_LISTITEM {
    char text[N_GUI_ID_MAX];
    int selected;
} N_GUI_LISTITEM;

typedef struct N_GUI_SUGGEST_SINK N_GUI_SUGGEST_SINK;

int n_gui_suggest_add(N_GUI_SUGGEST_SINK* sink, const char* text);

typedef void (*N_GUI_SUGGEST_FN)(const char* prefix, N_GUI_SUGGEST_SINK* sink, void* user_data);

typedef struct N_GUI_TEXTAREA_DATA {
    char* text;
    size_t text_len;
    size_t text_alloc;
    size_t char_limit;
    int multiline;
    size_t cursor_pos;
    size_t sel_start;
    size_t sel_end;
    int scroll_y;
    float scroll_x;
    int scroll_from_wheel;
    double cursor_time;
    ALLEGRO_BITMAP* bg_bitmap;
    char mask_char;
    char* placeholder;
    void (*on_change)(int widget_id, const char* text, void* user_data);
    void* user_data;
    N_GUI_SUGGEST_FN suggest;
    void* suggest_user_data;
    char suggest_separators[N_GUI_SUGGEST_SEP_MAX];
    N_GUI_LISTITEM* suggest_items;
    int suggest_count;
    int suggest_highlight;
    int suggest_scroll;
    int suggest_open;
    char suggest_prefix[N_GUI_ID_MAX];
} N_GUI_TEXTAREA_DATA;

typedef struct N_GUI_CHECKBOX_DATA {
    char label[N_GUI_ID_MAX];
    int checked;
    ALLEGRO_BITMAP* box_bitmap;
    ALLEGRO_BITMAP* box_checked_bitmap;
    ALLEGRO_BITMAP* box_hover_bitmap;
    void (*on_toggle)(int widget_id, int checked, void* user_data);
    void* user_data;
} N_GUI_CHECKBOX_DATA;

typedef struct N_GUI_SCROLLBAR_DATA {
    int orientation;
    double content_size;
    double viewport_size;
    double scroll_pos;
    double wheel_step;
    int shape;
    ALLEGRO_BITMAP* track_bitmap;
    ALLEGRO_BITMAP* thumb_bitmap;
    ALLEGRO_BITMAP* thumb_hover_bitmap;
    ALLEGRO_BITMAP* thumb_active_bitmap;
    void (*on_scroll)(int widget_id, double scroll_pos, void* user_data);
    void* user_data;
} N_GUI_SCROLLBAR_DATA;

typedef struct N_GUI_LISTBOX_DATA {
    N_GUI_LISTITEM* items;
    size_t nb_items;
    size_t items_capacity;
    int selection_mode;
    int scroll_offset;
    float h_scroll;
    int h_scroll_dragging;
    float item_height;
    ALLEGRO_BITMAP* bg_bitmap;
    ALLEGRO_BITMAP* item_bg_bitmap;
    ALLEGRO_BITMAP* item_selected_bitmap;
    void (*on_select)(int widget_id, int index, int selected, void* user_data);
    void* user_data;
    int hover_row;
} N_GUI_LISTBOX_DATA;

typedef struct N_GUI_SPLITPANE_DATA {
    int orientation;
    float ratio;
    float min_ratio;
    float max_ratio;
    float divider;
    void (*on_change)(int widget_id, float ratio, void* user_data);
    void* user_data;
} N_GUI_SPLITPANE_DATA;

typedef struct N_GUI_HEXVIEW_DATA {
    unsigned char* data;
    size_t len;
    size_t cap;
    int bytes_per_row;
    int scroll_offset;
    int editable;
    size_t cursor;
    int nibble;
    void (*on_change)(int widget_id, const unsigned char* data, size_t len, void* user_data);
    void* user_data;
} N_GUI_HEXVIEW_DATA;

typedef struct N_GUI_SYNTAXVIEW_DATA {
    char* text;
    size_t len;
    int mode;
    int scroll_offset;
    float h_scroll;
    int h_scroll_dragging;
    int sel_start;
    int sel_end;
    int sel_dragging;
    int lines_valid;
    int cached_nb_lines;
    int cached_headers_end;
    int width_valid;
    float cached_content_w;
    const ALLEGRO_FONT* metrics_font;
} N_GUI_SYNTAXVIEW_DATA;

typedef struct N_GUI_DATAGRID_COL {
    char title[N_GUI_ID_MAX];
    float width;
    int visible;
} N_GUI_DATAGRID_COL;

typedef struct N_GUI_DATAGRID_DATA {
    N_GUI_DATAGRID_COL* cols;
    size_t nb_cols;
    size_t cols_cap;
    size_t* col_order;
    int col_resize_col;
    float col_resize_x0;
    float col_resize_w0;
    void (*on_columns_changed)(int widget_id, void* user_data);
    char** cells;
    size_t nb_rows;
    size_t rows_cap;
    int sort_col;
    int sort_dir;
    int selected_row;
    int multiselect;
    unsigned char* row_sel;
    int anchor_row;
    ALLEGRO_COLOR* row_color;
    unsigned char* row_has_color;
    int scroll_offset;
    float h_scroll;
    int h_scroll_dragging;
    void (*on_select)(int widget_id, int row, void* user_data);
    void (*on_context)(int widget_id, int row, int x, int y, void* user_data);
    void* user_data;
} N_GUI_DATAGRID_DATA;

typedef struct N_GUI_RADIOLIST_DATA {
    N_GUI_LISTITEM* items;
    size_t nb_items;
    size_t items_capacity;
    int selected_index;
    int scroll_offset;
    float item_height;
    ALLEGRO_BITMAP* bg_bitmap;
    ALLEGRO_BITMAP* item_bg_bitmap;
    ALLEGRO_BITMAP* item_selected_bitmap;
    void (*on_select)(int widget_id, int index, void* user_data);
    void* user_data;
} N_GUI_RADIOLIST_DATA;

/* combobox feature flags */
#define N_GUI_COMBOBOX_AUTO_WIDTH 1
#define N_GUI_COMBOBOX_EXPAND_UP 2

typedef struct N_GUI_COMBOBOX_DATA {
    N_GUI_LISTITEM* items;
    size_t nb_items;
    size_t items_capacity;
    int selected_index;
    int is_open;
    int scroll_offset;
    int highlight_index;
    float item_height;
    int max_visible;
    ALLEGRO_BITMAP* bg_bitmap;
    ALLEGRO_BITMAP* item_bg_bitmap;
    ALLEGRO_BITMAP* item_selected_bitmap;
    void (*on_select)(int widget_id, int index, void* user_data);
    void* user_data;
    int flags;
} N_GUI_COMBOBOX_DATA;

typedef struct N_GUI_IMAGE_DATA {
    ALLEGRO_BITMAP* bitmap;
    int scale_mode;
} N_GUI_IMAGE_DATA;

typedef struct N_GUI_LABEL_DATA {
    char text[N_GUI_TEXT_MAX];
    char link[N_GUI_TEXT_MAX];
    int align;
    int truncate;
    float scroll_y;
    int sel_start;
    int sel_end;
    int sel_dragging;
    ALLEGRO_BITMAP* bg_bitmap;
    void (*on_link_click)(int widget_id, const char* link, void* user_data);
    void* user_data;
} N_GUI_LABEL_DATA;

typedef struct N_GUI_PROGRESSBAR_DATA {
    float value;
    char text[N_GUI_TEXT_MAX];
} N_GUI_PROGRESSBAR_DATA;

typedef struct N_GUI_DROPMENU_ENTRY {
    char text[N_GUI_ID_MAX];
    int is_dynamic;
    int tag;
    void (*on_click)(int widget_id, int entry_index, int tag, void* user_data);
    void* user_data;
} N_GUI_DROPMENU_ENTRY;

/* dropmenu feature flags */
#define N_GUI_DROPMENU_EXPAND_UP 1

typedef struct N_GUI_DROPMENU_DATA {
    char label[N_GUI_ID_MAX];
    N_GUI_DROPMENU_ENTRY* entries;
    size_t nb_entries;
    size_t entries_capacity;
    int is_open;
    int scroll_offset;
    int highlight_index;
    int max_visible;
    float item_height;
    ALLEGRO_BITMAP* panel_bitmap;
    ALLEGRO_BITMAP* item_hover_bitmap;
    void (*on_open)(int widget_id, void* user_data);
    void* on_open_user_data;
    int flags;
} N_GUI_DROPMENU_DATA;

/* widget */

typedef struct N_GUI_WIDGET {
    int id;
    int type;
    float x;
    float y;
    float w;
    float h;
    int state;
    N_GUI_SB_TRACK sb_v;
    N_GUI_SB_TRACK sb_h;
    int visible;
    int enabled;
    N_GUI_THEME theme;
    ALLEGRO_FONT* font;
    void* data;
    char* tooltip;
    char persist_key[N_GUI_ID_MAX];
    float norm_x;
    float norm_y;
    float norm_w;
    float norm_h;
} N_GUI_WIDGET;

typedef struct N_GUI_MARKDOWNVIEW_DATA {
    char* text;
    N_GUI_MD_FONTS fonts;
    N_GUI_MD_STYLE style;
    float scroll_y;
    float content_h;
    float measured_w;
    int dirty;
    int theme_derived;
    void (*on_link)(int widget_id, const char* url, void* user_data);
    void* user_data;
} N_GUI_MARKDOWNVIEW_DATA;

typedef void (*N_GUI_CUSTOM_DRAW)(N_GUI_WIDGET* widget, float x, float y, ALLEGRO_FONT* font, void* user_data);

#define N_GUI_MOUSE_DOWN 1
#define N_GUI_MOUSE_UP 2
#define N_GUI_MOUSE_MOVE 3
#define N_GUI_MOUSE_DRAG 4
#define N_GUI_MOUSE_WHEEL 5

typedef struct N_GUI_MOUSE_EVENT {
    int type;
    int button;
    float x;
    float y;
    int dz;
    int modifiers;
} N_GUI_MOUSE_EVENT;

typedef int (*N_GUI_CUSTOM_MOUSE)(int widget_id, const N_GUI_MOUSE_EVENT* ev, void* user_data);

typedef struct N_GUI_CUSTOM_DATA {
    N_GUI_CUSTOM_DRAW draw;
    void* user_data;
    N_GUI_CUSTOM_MOUSE on_mouse;
    void* mouse_user_data;
    void (*destroy)(void* user_data);
} N_GUI_CUSTOM_DATA;

/* titlebar buttons state */

typedef struct N_GUI_TB_BUTTONS {
    int hovered;
    int pressed;
    float restore_x;
    float restore_y;
    float restore_w;
    float restore_h;
    N_GUI_THEME btn_theme;
    N_GUI_THEME close_theme;
    ALLEGRO_BITMAP* minimize_bitmap;
    ALLEGRO_BITMAP* minimize_hover_bitmap;
    ALLEGRO_BITMAP* minimize_active_bitmap;
    ALLEGRO_BITMAP* maximize_bitmap;
    ALLEGRO_BITMAP* maximize_hover_bitmap;
    ALLEGRO_BITMAP* maximize_active_bitmap;
    ALLEGRO_BITMAP* close_bitmap;
    ALLEGRO_BITMAP* close_hover_bitmap;
    ALLEGRO_BITMAP* close_active_bitmap;
    void (*on_minimize)(int window_id, void* user_data);
    void* on_minimize_user_data;
    void (*on_maximize)(int window_id, void* user_data);
    void* on_maximize_user_data;
    void (*on_close)(int window_id, void* user_data);
    void* on_close_user_data;
} N_GUI_TB_BUTTONS;

/* pseudo window */

typedef struct N_GUI_WINDOW {
    int id;
    char title[N_GUI_ID_MAX];
    char caption[N_GUI_ID_MAX];
    int wheel_scrollbar;
    float x;
    float y;
    float w;
    float h;
    float titlebar_h;
    int state;
    int flags;
    LIST* widgets;
    N_GUI_THEME theme;
    ALLEGRO_FONT* font;
    float drag_ox;
    float drag_oy;
    float min_w;
    float min_h;
    float scroll_y;
    float scroll_x;
    float content_h;
    float content_w;
    int z_order;
    int z_value;
    ALLEGRO_BITMAP* bg_bitmap;
    ALLEGRO_BITMAP* titlebar_bitmap;
    int bg_scale_mode;
    int autofit_flags;
    float autofit_border;
    float autofit_origin_x;
    float autofit_origin_y;
    int resize_policy;
    float norm_x;
    float norm_y;
    float norm_w;
    float norm_h;
    N_GUI_TB_BUTTONS tb_buttons;
    void (*on_content_draw)(int window_id, void* user_data);
    void* on_content_draw_data;
    ALLEGRO_DISPLAY* native;
    int detach_flags;
    int want_native;
    int native_close_pending;
    float saved_x;
    float saved_y;
    float saved_w;
    float saved_h;
    int saved_flags;
    float native_w;
    float native_h;
    int native_pos_x;
    int native_pos_y;
    int native_drag_cx;
    int native_drag_cy;
    int native_drag_wx;
    int native_drag_wy;
    int native_drag_anchored;
    int native_halted;
} N_GUI_WINDOW;

/* global style (all configurable sizes, colors, paddings) */

typedef struct N_GUI_STYLE {
    /* window chrome */
    float titlebar_h;
    float min_win_w;
    float min_win_h;
    float title_padding;
    float title_max_w_reserve;

    /* titlebar buttons */
    float tb_btn_size;
    float tb_btn_spacing;
    float tb_btn_right_margin;
    float tb_btn_glyph_thickness;

    /* window auto-scrollbar */
    float scrollbar_size;
    float scrollbar_thumb_min;
    float scrollbar_thumb_padding;
    float scrollbar_thumb_corner_r;
    ALLEGRO_COLOR scrollbar_track_color;
    ALLEGRO_COLOR scrollbar_thumb_color;

    /* global display scrollbar */
    float global_scrollbar_size;
    float global_scrollbar_thumb_min;
    float global_scrollbar_thumb_padding;
    float global_scrollbar_thumb_corner_r;
    float global_scrollbar_border_thickness;
    ALLEGRO_COLOR global_scrollbar_track_color;
    ALLEGRO_COLOR global_scrollbar_thumb_color;
    ALLEGRO_COLOR global_scrollbar_thumb_border_color;

    /* resize grip */
    float grip_size;
    float grip_line_thickness;
    ALLEGRO_COLOR grip_color;

    /* slider */
    float slider_track_size;
    float slider_track_corner_r;
    float slider_track_border_thickness;
    float slider_handle_min_r;
    float slider_handle_edge_offset;
    float slider_handle_border_thickness;
    float slider_value_label_offset;

    /* text area */
    float tooltip_delay;
    ALLEGRO_COLOR tooltip_bg;
    ALLEGRO_COLOR tooltip_border;
    ALLEGRO_COLOR tooltip_fg;
    float tooltip_max_w;
    float textarea_padding;
    float textarea_cursor_width;
    float textarea_cursor_blink_period;

    /* checkbox */
    float checkbox_max_size;
    float checkbox_mark_margin;
    float checkbox_mark_thickness;
    float checkbox_label_gap;
    float checkbox_label_offset;

    /* radio list */
    float radio_circle_min_r;
    float radio_circle_border_thickness;
    float radio_inner_offset;
    float radio_label_gap;

    /* list items (shared by listbox, radiolist, combobox, dropmenu) */
    float listbox_default_item_height;
    float radiolist_default_item_height;
    int combobox_max_visible;
    int dropmenu_max_visible;
    float item_text_padding;
    float item_selection_inset;
    float item_height_pad;

    /* dropdown arrow (combobox & dropmenu) */
    float dropdown_arrow_reserve;
    float dropdown_arrow_thickness;
    float dropdown_arrow_half_h;
    float dropdown_arrow_half_w;
    float dropdown_border_thickness;

    int shape_mode;

    /* label */
    float label_padding;
    float link_underline_thickness;
    ALLEGRO_COLOR link_color_normal;
    ALLEGRO_COLOR link_color_hover;

    /* scroll step */
    float scroll_step;
    float global_scroll_step;

    /* combobox auto-width cap */
    float combobox_max_dropdown_width;
} N_GUI_STYLE;

/* gui context */

typedef struct N_GUI_PENDING_GEOM {
    char title[N_GUI_ID_MAX]; 
    float x;                  
    float y;                  
    float w;                  
    float h;                  
    int state;                
    unsigned char consumed;   
} N_GUI_PENDING_GEOM;

#define N_GUI_PERSIST_MAX_COLS 64

typedef struct N_GUI_PENDING_WIDGET {
    char title[N_GUI_ID_MAX];            
    char key[N_GUI_ID_MAX];              
    int kind;                            
    float ratio;                         
    int ncols;                           
    int order[N_GUI_PERSIST_MAX_COLS];   
    int visible[N_GUI_PERSIST_MAX_COLS]; 
    float width[N_GUI_PERSIST_MAX_COLS]; 
    unsigned char consumed;              
} N_GUI_PENDING_WIDGET;

typedef struct N_GUI_CTX {
    LIST* windows;
    HASH_TABLE* widgets_by_id;
    int next_widget_id;
    int next_window_id;
    ALLEGRO_FONT* default_font;
    N_GUI_THEME default_theme;
    int focused_widget_id;
    int mouse_x;
    int mouse_y;
    int mouse_b1;
    int mouse_b1_prev;
    int cursor_shape;
    ALLEGRO_MOUSE_CURSOR* cursor_custom;
    int cursor_want_shape;
    ALLEGRO_MOUSE_CURSOR* cursor_want_custom;
    int click_widget_id;
    double click_time;
    int click_x;
    int click_y;
    int double_clicked_widget_id;
    double double_click_time;
    int double_click_radius;
    int tooltip_widget_id;
    double tooltip_armed_at;
    int tooltip_anchor_x;
    int tooltip_anchor_y;
    int open_combobox_id;
    int scrollbar_drag_widget_id;
    int open_dropmenu_id;
    int open_suggest_id;
    float display_w;
    float display_h;
    int display_size_set;
    int display_size_warned;
    float global_scroll_x;
    float global_scroll_y;
    float gui_bounds_w;
    float gui_bounds_h;
    float dpi_scale;
    float virtual_w;
    float virtual_h;
    float gui_scale;
    float gui_offset_x;
    float gui_offset_y;
    int global_vscroll_drag;
    int global_hscroll_drag;
    N_GUI_STYLE style;
    ALLEGRO_DISPLAY* display;
    int selected_label_id;
    int selected_syntaxview_id;
    int resize_mode;
    float ref_display_w;
    float ref_display_h;
    N_GUI_THEME default_tb_btn_theme;
    N_GUI_THEME default_tb_close_theme;
    N_GUI_PENDING_GEOM* pending_layout;
    int pending_layout_count;
    N_GUI_PENDING_WIDGET* pending_widgets;
    int pending_widgets_count;
    ALLEGRO_EVENT_QUEUE* event_queue;
    ALLEGRO_DISPLAY* pass_display;
    ALLEGRO_DISPLAY* active_display;
    int dirty;
    double anim_until;
    int prev_over_win;
    int held_modifier_keys;
} N_GUI_CTX;

/* API */

/* style helpers */

N_GUI_STYLE n_gui_default_style(void);

/* theme helpers */

N_GUI_THEME n_gui_default_theme(void);
N_GUI_THEME n_gui_default_tb_btn_theme(void);
N_GUI_THEME n_gui_default_tb_close_theme(void);
N_GUI_THEME n_gui_make_theme(
    ALLEGRO_COLOR bg,
    ALLEGRO_COLOR bg_hover,
    ALLEGRO_COLOR bg_active,
    ALLEGRO_COLOR border,
    ALLEGRO_COLOR border_hover,
    ALLEGRO_COLOR border_active,
    ALLEGRO_COLOR text,
    ALLEGRO_COLOR text_hover,
    ALLEGRO_COLOR text_active,
    float border_thickness,
    float corner_rx,
    float corner_ry);

void n_gui_set_default_theme(N_GUI_CTX* ctx, N_GUI_THEME theme);

/* text measurement */

N_GUI_TEXT_DIMS n_gui_get_text_dims(ALLEGRO_FONT* font, const char* text);

int n_gui_text_shorten(ALLEGRO_FONT* font, const char* text, float max_w, int mode, char* out, size_t out_len);

/* context */

N_GUI_CTX* n_gui_new_ctx(ALLEGRO_FONT* default_font);
int n_gui_set_default_font(N_GUI_CTX* ctx, ALLEGRO_FONT* font);
void n_gui_destroy_ctx(N_GUI_CTX** ctx);

/* window management */

int n_gui_add_window(N_GUI_CTX* ctx, const char* title, float x, float y, float w, float h);
int n_gui_add_window_auto(N_GUI_CTX* ctx, const char* title, float x, float y);
N_GUI_WINDOW* n_gui_get_window(N_GUI_CTX* ctx, int window_id);
void n_gui_close_window(N_GUI_CTX* ctx, int window_id);
void n_gui_open_window(N_GUI_CTX* ctx, int window_id);
void n_gui_toggle_window(N_GUI_CTX* ctx, int window_id);
int n_gui_window_is_open(N_GUI_CTX* ctx, int window_id);
int n_gui_window_button_rect(N_GUI_CTX* ctx, int window_id, int btn_type, float* x, float* y, float* w, float* h);
int n_gui_menu_is_open(const N_GUI_CTX* ctx);
void n_gui_close_menus(N_GUI_CTX* ctx);
void n_gui_minimize_window(N_GUI_CTX* ctx, int window_id);
void n_gui_restore_window(N_GUI_CTX* ctx, int window_id);
int n_gui_window_is_minimised(N_GUI_CTX* ctx, int window_id);
void n_gui_maximize_window(N_GUI_CTX* ctx, int window_id);
int n_gui_window_is_maximised(N_GUI_CTX* ctx, int window_id);

/* native (detached) windows: promote a pop-up to a real OS window */

void n_gui_set_event_queue(N_GUI_CTX* ctx, ALLEGRO_EVENT_QUEUE* queue);
ALLEGRO_EVENT_QUEUE* n_gui_get_event_queue(N_GUI_CTX* ctx);
int n_gui_window_detach(N_GUI_CTX* ctx, int window_id, int detach_flags);
int n_gui_window_attach(N_GUI_CTX* ctx, int window_id);
int n_gui_window_is_detached(N_GUI_CTX* ctx, int window_id);
ALLEGRO_DISPLAY* n_gui_window_get_display(N_GUI_CTX* ctx, int window_id);
int n_gui_window_from_display(N_GUI_CTX* ctx, ALLEGRO_DISPLAY* display);
int n_gui_count_detached(N_GUI_CTX* ctx);
int n_gui_window_set_native_icons(N_GUI_CTX* ctx, int window_id, ALLEGRO_BITMAP** icons, int num_icons);
int n_gui_window_native_is_hidden(N_GUI_CTX* ctx, int window_id);
void n_gui_draw_detached(N_GUI_CTX* ctx);
void n_gui_window_set_close_callback(N_GUI_CTX* ctx, int window_id, void (*on_close)(int, void*), void* user_data);
void n_gui_window_set_content_draw_callback(N_GUI_CTX* ctx, int window_id, void (*on_content_draw)(int, void*), void* user_data);
void n_gui_window_set_minimize_callback(N_GUI_CTX* ctx, int window_id, void (*on_minimize)(int, void*), void* user_data);
void n_gui_window_set_maximize_callback(N_GUI_CTX* ctx, int window_id, void (*on_maximize)(int, void*), void* user_data);
void n_gui_window_set_tb_btn_theme(N_GUI_CTX* ctx, int window_id, N_GUI_THEME theme);
void n_gui_window_set_tb_close_theme(N_GUI_CTX* ctx, int window_id, N_GUI_THEME theme);
void n_gui_window_set_tb_button_bitmaps(N_GUI_CTX* ctx, int window_id, int btn_type, ALLEGRO_BITMAP* normal, ALLEGRO_BITMAP* hover, ALLEGRO_BITMAP* active);
void n_gui_raise_window(N_GUI_CTX* ctx, int window_id);
void n_gui_lower_window(N_GUI_CTX* ctx, int window_id);
int n_gui_get_topmost_open_window(const N_GUI_CTX* ctx);
void n_gui_focus_window(N_GUI_CTX* ctx, int window_id);
void n_gui_window_set_zorder(N_GUI_CTX* ctx, int window_id, int z_mode, int z_value);
void n_gui_window_set_caption(N_GUI_CTX* ctx, int window_id, const char* caption);
void n_gui_window_set_wheel_scrollbar(N_GUI_CTX* ctx, int window_id, int scrollbar_id);
int n_gui_window_get_zorder(N_GUI_CTX* ctx, int window_id);
int n_gui_window_get_zvalue(N_GUI_CTX* ctx, int window_id);
void n_gui_window_set_flags(N_GUI_CTX* ctx, int window_id, int flags);
int n_gui_window_get_flags(N_GUI_CTX* ctx, int window_id);
void n_gui_window_autosize(N_GUI_CTX* ctx, int window_id);
void n_gui_window_set_autofit(N_GUI_CTX* ctx, int window_id, int autofit_flags, float border);
void n_gui_window_apply_autofit(N_GUI_CTX* ctx, int window_id);

/* resize mode */

void n_gui_set_resize_mode(N_GUI_CTX* ctx, int mode);

int n_gui_get_resize_mode(N_GUI_CTX* ctx);

void n_gui_window_set_resize_policy(N_GUI_CTX* ctx, int window_id, int policy);

int n_gui_window_get_resize_policy(N_GUI_CTX* ctx, int window_id);

void n_gui_window_update_normalized(N_GUI_CTX* ctx, int window_id);

void n_gui_window_set_rect(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h);

void n_gui_window_set_rect_axes(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, int scale_x, int scale_y);

void n_gui_apply_adaptive_resize(N_GUI_CTX* ctx, float new_w, float new_h);

/* widget creation (returns widget id) */

int n_gui_add_button(N_GUI_CTX* ctx, int window_id, const char* label, float x, float y, float w, float h, int shape, void (*on_click)(int, void*), void* user_data);

int n_gui_add_button_bitmap(N_GUI_CTX* ctx, int window_id, const char* label, float x, float y, float w, float h, ALLEGRO_BITMAP* normal, ALLEGRO_BITMAP* hover, ALLEGRO_BITMAP* active, void (*on_click)(int, void*), void* user_data);

int n_gui_add_toggle_button(N_GUI_CTX* ctx, int window_id, const char* label, float x, float y, float w, float h, int shape, int initial_state, void (*on_click)(int, void*), void* user_data);

int n_gui_button_is_toggled(N_GUI_CTX* ctx, int widget_id);

void n_gui_button_set_label(N_GUI_CTX* ctx, int widget_id, const char* label);
const char* n_gui_button_get_label(N_GUI_CTX* ctx, int widget_id);
int n_gui_window_find_button(N_GUI_CTX* ctx, int window_id, const char* label);
void n_gui_button_set_glyph(N_GUI_CTX* ctx, int widget_id, int glyph);
int n_gui_button_get_glyph(N_GUI_CTX* ctx, int widget_id);
void n_gui_button_set_state_bitmaps(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* normal, ALLEGRO_BITMAP* hover, ALLEGRO_BITMAP* active);
void n_gui_set_widget_tooltip(N_GUI_CTX* ctx, int widget_id, const char* text);
int n_gui_tooltip_pending(N_GUI_CTX* ctx);
void n_gui_button_set_toggled(N_GUI_CTX* ctx, int widget_id, int toggled);

void n_gui_set_cursor(N_GUI_CTX* ctx, int system_shape, ALLEGRO_MOUSE_CURSOR* custom);

int n_gui_get_cursor(N_GUI_CTX* ctx, ALLEGRO_MOUSE_CURSOR** custom);
void n_gui_button_set_toggle_mode(N_GUI_CTX* ctx, int widget_id, int toggle_mode);

/* button keyboard binding */
void n_gui_button_set_keycode(N_GUI_CTX* ctx, int widget_id, int keycode, int modifiers);

void n_gui_button_set_keycode_focused(N_GUI_CTX* ctx, int widget_id, int keycode, int modifiers, const int* sources, int source_count);

int n_gui_add_slider(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, double min_val, double max_val, double initial, int mode, void (*on_change)(int, double, void*), void* user_data);

int n_gui_add_vslider(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, double min_val, double max_val, double initial, int mode, void (*on_change)(int, double, void*), void* user_data);

int n_gui_add_textarea(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, int multiline, size_t char_limit, void (*on_change)(int, const char*, void*), void* user_data);

int n_gui_add_checkbox(N_GUI_CTX* ctx, int window_id, const char* label, float x, float y, float w, float h, int initial_checked, void (*on_toggle)(int, int, void*), void* user_data);

int n_gui_add_scrollbar(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, int orientation, int shape, double content_size, double viewport_size, void (*on_scroll)(int, double, void*), void* user_data);

/* widget access */

N_GUI_WIDGET* n_gui_get_widget(N_GUI_CTX* ctx, int widget_id);
int n_gui_hovered_widget(const N_GUI_CTX* ctx);
int n_gui_double_clicked_widget(const N_GUI_CTX* ctx);
int n_gui_was_double_click(const N_GUI_CTX* ctx, int widget_id);
void n_gui_set_double_click_time(N_GUI_CTX* ctx, double seconds);
void n_gui_set_double_click_radius(N_GUI_CTX* ctx, int pixels);
void n_gui_set_widget_theme(N_GUI_CTX* ctx, int widget_id, N_GUI_THEME theme);
void n_gui_reset_all_widget_themes(N_GUI_CTX* ctx);
void n_gui_set_widget_visible(N_GUI_CTX* ctx, int widget_id, int visible);
int n_gui_widget_is_visible(N_GUI_CTX* ctx, int widget_id);
void n_gui_widget_set_rect(N_GUI_CTX* ctx, int widget_id, float x, float y, float w, float h);
int n_gui_widget_screen_rect(N_GUI_CTX* ctx, int widget_id, float* x, float* y, float* w, float* h);
void n_gui_set_widget_enabled(N_GUI_CTX* ctx, int widget_id, int enabled);
int n_gui_is_widget_enabled(N_GUI_CTX* ctx, int widget_id);
void n_gui_set_focus(N_GUI_CTX* ctx, int widget_id);
int n_gui_get_focus(const N_GUI_CTX* ctx);
int n_gui_focus_takes_key(const N_GUI_CTX* ctx, int keycode);

/* slider helpers */
double n_gui_slider_get_value(N_GUI_CTX* ctx, int widget_id);
void n_gui_slider_set_value(N_GUI_CTX* ctx, int widget_id, double value);
void n_gui_slider_set_range(N_GUI_CTX* ctx, int widget_id, double min_val, double max_val);
void n_gui_slider_set_step(N_GUI_CTX* ctx, int widget_id, double step);
void n_gui_slider_set_key_step(N_GUI_CTX* ctx, int widget_id, double step);
double n_gui_slider_get_key_step(N_GUI_CTX* ctx, int widget_id);
void n_gui_slider_set_default(N_GUI_CTX* ctx, int widget_id, double value);
int n_gui_slider_get_default(N_GUI_CTX* ctx, int widget_id, double* value);
int n_gui_slider_handle_pos(N_GUI_CTX* ctx, int widget_id, float* pos, float* radius);
void n_gui_slider_set_value_format(N_GUI_CTX* ctx, int widget_id, const char* fmt);
void n_gui_slider_set_value_visible(N_GUI_CTX* ctx, int widget_id, int visible);

float n_gui_slider_value_width(N_GUI_CTX* ctx, int widget_id);

/* textarea helpers */
const char* n_gui_textarea_get_text(N_GUI_CTX* ctx, int widget_id);
void n_gui_textarea_set_text(N_GUI_CTX* ctx, int widget_id, const char* text);
void n_gui_textarea_scroll_to_bottom(N_GUI_CTX* ctx, int widget_id);
void n_gui_textarea_set_selection(N_GUI_CTX* ctx, int widget_id, size_t start, size_t end);
void n_gui_textarea_scroll_to_offset(N_GUI_CTX* ctx, int widget_id, size_t byte_offset);
size_t n_gui_textarea_get_text_length(N_GUI_CTX* ctx, int widget_id);
int n_gui_textarea_has_selection(N_GUI_CTX* ctx, int widget_id);

/* checkbox helpers */
int n_gui_checkbox_is_checked(N_GUI_CTX* ctx, int widget_id);
void n_gui_checkbox_set_checked(N_GUI_CTX* ctx, int widget_id, int checked);
void n_gui_checkbox_set_label(N_GUI_CTX* ctx, int widget_id, const char* label);
const char* n_gui_checkbox_get_label(N_GUI_CTX* ctx, int widget_id);

/* scrollbar helpers */
double n_gui_scrollbar_get_pos(N_GUI_CTX* ctx, int widget_id);
void n_gui_scrollbar_set_pos(N_GUI_CTX* ctx, int widget_id, double pos);
void n_gui_scrollbar_set_sizes(N_GUI_CTX* ctx, int widget_id, double content_size, double viewport_size);
void n_gui_scrollbar_set_wheel_step(N_GUI_CTX* ctx, int widget_id, double step);

/* listbox */

int n_gui_add_listbox(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, int selection_mode, void (*on_select)(int, int, int, void*), void* user_data);
int n_gui_listbox_add_item(N_GUI_CTX* ctx, int widget_id, const char* text);
int n_gui_listbox_remove_item(N_GUI_CTX* ctx, int widget_id, int index);
void n_gui_listbox_clear(N_GUI_CTX* ctx, int widget_id);
int n_gui_listbox_get_count(N_GUI_CTX* ctx, int widget_id);
const char* n_gui_listbox_get_item_text(N_GUI_CTX* ctx, int widget_id, int index);
int n_gui_listbox_get_selected(N_GUI_CTX* ctx, int widget_id);
int n_gui_listbox_is_selected(N_GUI_CTX* ctx, int widget_id, int index);
void n_gui_listbox_set_selected(N_GUI_CTX* ctx, int widget_id, int index, int selected);
int n_gui_listbox_get_scroll_offset(N_GUI_CTX* ctx, int widget_id);
void n_gui_listbox_set_scroll_offset(N_GUI_CTX* ctx, int widget_id, int offset);
float n_gui_listbox_content_width(N_GUI_CTX* ctx, int widget_id);
float n_gui_listbox_get_h_scroll(N_GUI_CTX* ctx, int widget_id);
void n_gui_listbox_set_h_scroll(N_GUI_CTX* ctx, int widget_id, float px);

/* split pane */

int n_gui_add_splitpane(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, int orientation, float ratio, void (*on_change)(int, float, void*), void* user_data);
float n_gui_splitpane_get_ratio(N_GUI_CTX* ctx, int widget_id);
void n_gui_splitpane_set_ratio(N_GUI_CTX* ctx, int widget_id, float ratio);
void n_gui_splitpane_set_limits(N_GUI_CTX* ctx, int widget_id, float min_ratio, float max_ratio);

int n_gui_splitpane_at(N_GUI_CTX* ctx, float x, float y);
float n_gui_splitpane_band(N_GUI_CTX* ctx, int widget_id);

/* hex view */

int n_gui_add_hexview(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h);
void n_gui_hexview_set_data(N_GUI_CTX* ctx, int widget_id, const unsigned char* data, size_t len);
size_t n_gui_hexview_get_length(N_GUI_CTX* ctx, int widget_id);
void n_gui_hexview_set_editable(N_GUI_CTX* ctx, int widget_id, int editable);
void n_gui_hexview_set_on_change(N_GUI_CTX* ctx, int widget_id, void (*on_change)(int, const unsigned char*, size_t, void*), void* user_data);
const unsigned char* n_gui_hexview_get_data(N_GUI_CTX* ctx, int widget_id, size_t* len);
void n_gui_hexview_set_cursor(N_GUI_CTX* ctx, int widget_id, size_t offset);
size_t n_gui_hexview_get_cursor(N_GUI_CTX* ctx, int widget_id);

/* syntax view */

int n_gui_add_syntaxview(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, int mode);
void n_gui_syntaxview_set_text(N_GUI_CTX* ctx, int widget_id, const char* text);
void n_gui_syntaxview_set_mode(N_GUI_CTX* ctx, int widget_id, int mode);
int n_gui_syntaxview_get_line_count(N_GUI_CTX* ctx, int widget_id);
const char* n_gui_syntaxview_get_text(N_GUI_CTX* ctx, int widget_id);
char* n_gui_syntaxview_get_selected_text(N_GUI_CTX* ctx, int widget_id);
void n_gui_syntaxview_set_selection(N_GUI_CTX* ctx, int widget_id, int start, int end);
void n_gui_syntaxview_select_all(N_GUI_CTX* ctx, int widget_id);
void n_gui_syntaxview_scroll_to_offset(N_GUI_CTX* ctx, int widget_id, int byte_offset);
float n_gui_syntaxview_content_width(N_GUI_CTX* ctx, int widget_id);
float n_gui_syntaxview_get_h_scroll(N_GUI_CTX* ctx, int widget_id);
void n_gui_syntaxview_set_h_scroll(N_GUI_CTX* ctx, int widget_id, float px);

/* data grid */

int n_gui_add_datagrid(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, void (*on_select)(int, int, void*), void* user_data);
int n_gui_datagrid_add_column(N_GUI_CTX* ctx, int widget_id, const char* title, float width);
int n_gui_datagrid_add_row(N_GUI_CTX* ctx, int widget_id, const char** values);
void n_gui_datagrid_clear_rows(N_GUI_CTX* ctx, int widget_id);
int n_gui_datagrid_get_row_count(N_GUI_CTX* ctx, int widget_id);
const char* n_gui_datagrid_get_cell(N_GUI_CTX* ctx, int widget_id, int row, int col);
int n_gui_datagrid_get_selected(N_GUI_CTX* ctx, int widget_id);
void n_gui_datagrid_set_selected(N_GUI_CTX* ctx, int widget_id, int row);
void n_gui_datagrid_set_on_context(N_GUI_CTX* ctx, int widget_id, void (*on_context)(int, int, int, int, void*));
void n_gui_datagrid_sort(N_GUI_CTX* ctx, int widget_id, int col, int dir);
int n_gui_datagrid_get_sort(N_GUI_CTX* ctx, int widget_id, int* col, int* dir);
void n_gui_datagrid_set_multiselect(N_GUI_CTX* ctx, int widget_id, int enabled);
int n_gui_datagrid_is_row_selected(N_GUI_CTX* ctx, int widget_id, int row);
size_t n_gui_datagrid_get_selected_rows(N_GUI_CTX* ctx, int widget_id, int* out, size_t max);
void n_gui_datagrid_clear_selection(N_GUI_CTX* ctx, int widget_id);
void n_gui_datagrid_select_row(N_GUI_CTX* ctx, int widget_id, int row, int selected);

void n_gui_datagrid_set_row_color(N_GUI_CTX* ctx, int widget_id, int row, ALLEGRO_COLOR color);
void n_gui_datagrid_clear_row_color(N_GUI_CTX* ctx, int widget_id, int row);
int n_gui_datagrid_get_row_color(N_GUI_CTX* ctx, int widget_id, int row, ALLEGRO_COLOR* out);
void n_gui_datagrid_clear_row_colors(N_GUI_CTX* ctx, int widget_id);
int n_gui_datagrid_get_scroll_offset(N_GUI_CTX* ctx, int widget_id);
void n_gui_datagrid_set_scroll_offset(N_GUI_CTX* ctx, int widget_id, int offset);
float n_gui_datagrid_columns_width(N_GUI_CTX* ctx, int widget_id);

float n_gui_datagrid_get_h_scroll(N_GUI_CTX* ctx, int widget_id);
void n_gui_datagrid_set_h_scroll(N_GUI_CTX* ctx, int widget_id, float px);

size_t n_gui_datagrid_get_column_count(N_GUI_CTX* ctx, int widget_id);
const char* n_gui_datagrid_get_column_title(N_GUI_CTX* ctx, int widget_id, int col);
void n_gui_datagrid_set_column_visible(N_GUI_CTX* ctx, int widget_id, int col, int visible);
int n_gui_datagrid_get_column_visible(N_GUI_CTX* ctx, int widget_id, int col);
void n_gui_datagrid_set_column_width(N_GUI_CTX* ctx, int widget_id, int col, float width);
float n_gui_datagrid_get_column_width(N_GUI_CTX* ctx, int widget_id, int col);
int n_gui_datagrid_display_to_physical(N_GUI_CTX* ctx, int widget_id, int display_pos);
void n_gui_datagrid_move_column(N_GUI_CTX* ctx, int widget_id, int from, int to);
void n_gui_datagrid_set_on_columns_changed(N_GUI_CTX* ctx, int widget_id, void (*cb)(int, void*));

/* progress bar */

int n_gui_add_progressbar(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h);
void n_gui_progressbar_set_value(N_GUI_CTX* ctx, int widget_id, float value);
float n_gui_progressbar_get_value(N_GUI_CTX* ctx, int widget_id);
void n_gui_progressbar_set_text(N_GUI_CTX* ctx, int widget_id, const char* text);

/* custom (owner-draw) widget */

int n_gui_add_custom(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, N_GUI_CUSTOM_DRAW draw, void* user_data);

void n_gui_custom_set_mouse(N_GUI_CTX* ctx, int widget_id, N_GUI_CUSTOM_MOUSE on_mouse, void* user_data);

int n_gui_get_key_modifiers(const N_GUI_CTX* ctx);

void n_gui_custom_set_destroy(N_GUI_CTX* ctx, int widget_id, void (*destroy)(void* user_data));

/* markdownview */

int n_gui_add_markdownview(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, const N_GUI_MD_FONTS* fonts);
void n_gui_markdownview_set_text(N_GUI_CTX* ctx, int widget_id, const char* text);
const char* n_gui_markdownview_get_text(N_GUI_CTX* ctx, int widget_id);
void n_gui_markdownview_set_fonts(N_GUI_CTX* ctx, int widget_id, const N_GUI_MD_FONTS* fonts);
N_GUI_MD_STYLE* n_gui_markdownview_style(N_GUI_CTX* ctx, int widget_id);
void n_gui_markdownview_scroll_to(N_GUI_CTX* ctx, int widget_id, float y);
float n_gui_markdownview_get_scroll(N_GUI_CTX* ctx, int widget_id);
float n_gui_markdownview_content_height(N_GUI_CTX* ctx, int widget_id);
void n_gui_markdownview_set_on_link(N_GUI_CTX* ctx, int widget_id, void (*on_link)(int widget_id, const char* url, void* user_data), void* user_data);

/* radiolist */

int n_gui_add_radiolist(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, void (*on_select)(int, int, void*), void* user_data);
int n_gui_radiolist_add_item(N_GUI_CTX* ctx, int widget_id, const char* text);
void n_gui_radiolist_clear(N_GUI_CTX* ctx, int widget_id);
int n_gui_radiolist_get_selected(N_GUI_CTX* ctx, int widget_id);
void n_gui_radiolist_set_selected(N_GUI_CTX* ctx, int widget_id, int index);

/* combobox */

int n_gui_add_combobox(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, void (*on_select)(int, int, void*), void* user_data);
int n_gui_combobox_add_item(N_GUI_CTX* ctx, int widget_id, const char* text);
void n_gui_combobox_clear(N_GUI_CTX* ctx, int widget_id);
int n_gui_combobox_get_selected(N_GUI_CTX* ctx, int widget_id);
const char* n_gui_combobox_get_item_text(N_GUI_CTX* ctx, int widget_id, int index);
int n_gui_combobox_get_count(N_GUI_CTX* ctx, int widget_id);
int n_gui_combobox_remove_item(N_GUI_CTX* ctx, int widget_id, int index);
void n_gui_combobox_set_selected(N_GUI_CTX* ctx, int widget_id, int index);

void n_gui_combobox_set_flags(N_GUI_CTX* ctx, int widget_id, int flags);

/* image */

int n_gui_add_image(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, ALLEGRO_BITMAP* bitmap, int scale_mode);
void n_gui_image_set_bitmap(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* bitmap);

/* label (static text, optional hyperlink) */

int n_gui_add_label(N_GUI_CTX* ctx, int window_id, const char* text, float x, float y, float w, float h, int align);
int n_gui_add_label_link(N_GUI_CTX* ctx, int window_id, const char* text, const char* link, float x, float y, float w, float h, int align, void (*on_link_click)(int, const char*, void*), void* user_data);
void n_gui_label_set_text(N_GUI_CTX* ctx, int widget_id, const char* text);
const char* n_gui_label_get_text(N_GUI_CTX* ctx, int widget_id);
void n_gui_label_set_link(N_GUI_CTX* ctx, int widget_id, const char* link);
void n_gui_label_set_truncate(N_GUI_CTX* ctx, int widget_id, int mode);
int n_gui_label_get_truncate(N_GUI_CTX* ctx, int widget_id);
int n_gui_label_fits(N_GUI_CTX* ctx, int widget_id, float* needed_w, float* needed_h);

/* dropdown menu */

int n_gui_add_dropmenu(N_GUI_CTX* ctx, int window_id, const char* label, float x, float y, float w, float h, void (*on_open)(int, void*), void* on_open_user_data);
int n_gui_dropmenu_add_entry(N_GUI_CTX* ctx, int widget_id, const char* text, int tag, void (*on_click)(int, int, int, void*), void* user_data);
int n_gui_dropmenu_add_dynamic_entry(N_GUI_CTX* ctx, int widget_id, const char* text, int tag, void (*on_click)(int, int, int, void*), void* user_data);
void n_gui_dropmenu_clear_dynamic(N_GUI_CTX* ctx, int widget_id);
void n_gui_dropmenu_clear(N_GUI_CTX* ctx, int widget_id);
void n_gui_dropmenu_set_entry_text(N_GUI_CTX* ctx, int widget_id, int index, const char* text);
int n_gui_dropmenu_get_count(N_GUI_CTX* ctx, int widget_id);
int n_gui_dropmenu_set_open(N_GUI_CTX* ctx, int widget_id, int open);
int n_gui_dropmenu_is_open(N_GUI_CTX* ctx, int widget_id);
float n_gui_dropmenu_panel_width(N_GUI_CTX* ctx, int widget_id);
void n_gui_dropmenu_set_flags(N_GUI_CTX* ctx, int widget_id, int flags);
void n_gui_dropmenu_set_label(N_GUI_CTX* ctx, int widget_id, const char* label);

/* bitmap skinning */

void n_gui_window_set_bitmaps(N_GUI_CTX* ctx, int window_id, ALLEGRO_BITMAP* bg, ALLEGRO_BITMAP* titlebar, int bg_scale_mode);

void n_gui_slider_set_bitmaps(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* track, ALLEGRO_BITMAP* fill, ALLEGRO_BITMAP* handle, ALLEGRO_BITMAP* handle_hover, ALLEGRO_BITMAP* handle_active);

void n_gui_scrollbar_set_bitmaps(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* track, ALLEGRO_BITMAP* thumb, ALLEGRO_BITMAP* thumb_hover, ALLEGRO_BITMAP* thumb_active);

void n_gui_checkbox_set_bitmaps(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* box, ALLEGRO_BITMAP* box_checked, ALLEGRO_BITMAP* box_hover);

void n_gui_textarea_set_bitmap(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* bg);

void n_gui_textarea_set_mask_char(N_GUI_CTX* ctx, int widget_id, char mask);

void n_gui_textarea_set_placeholder(N_GUI_CTX* ctx, int widget_id, const char* text);

void n_gui_textarea_set_suggest(N_GUI_CTX* ctx, int widget_id, N_GUI_SUGGEST_FN fn, const char* separators, void* user_data);

int n_gui_textarea_suggest_is_open(N_GUI_CTX* ctx, int widget_id);

void n_gui_textarea_suggest_close(N_GUI_CTX* ctx, int widget_id);

void n_gui_listbox_set_bitmaps(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* bg, ALLEGRO_BITMAP* item_bg, ALLEGRO_BITMAP* item_selected);

void n_gui_radiolist_set_bitmaps(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* bg, ALLEGRO_BITMAP* item_bg, ALLEGRO_BITMAP* item_selected);

void n_gui_combobox_set_bitmaps(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* bg, ALLEGRO_BITMAP* item_bg, ALLEGRO_BITMAP* item_selected);

void n_gui_dropmenu_set_bitmaps(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* panel, ALLEGRO_BITMAP* item_hover);

void n_gui_label_set_bitmap(N_GUI_CTX* ctx, int widget_id, ALLEGRO_BITMAP* bg);

/* JSON theme I/O */

int n_gui_save_theme_json(N_GUI_CTX* ctx, const char* filepath);

int n_gui_load_theme_json(N_GUI_CTX* ctx, const char* filepath);

/* JSON window layout I/O */

int n_gui_save_layout_json(N_GUI_CTX* ctx, const char* filepath);

int n_gui_load_layout_json(N_GUI_CTX* ctx, const char* filepath);

void n_gui_widget_set_persist_key(N_GUI_CTX* ctx, int widget_id, const char* key);

const char* n_gui_widget_get_persist_key(N_GUI_CTX* ctx, int widget_id);

/* virtual canvas / display / DPI */

void n_gui_set_virtual_size(N_GUI_CTX* ctx, float w, float h);

void n_gui_update_transform(N_GUI_CTX* ctx);

void n_gui_screen_to_virtual(const N_GUI_CTX* ctx, float sx, float sy, float* vx, float* vy);

void n_gui_set_display_size(N_GUI_CTX* ctx, float w, float h);
void n_gui_set_display(N_GUI_CTX* ctx, ALLEGRO_DISPLAY* display);

void n_gui_set_shape_mode(N_GUI_CTX* ctx, int shape_mode);

int n_gui_get_shape_mode(N_GUI_CTX* ctx);

void n_gui_set_dpi_scale(N_GUI_CTX* ctx, float scale);

float n_gui_get_dpi_scale(const N_GUI_CTX* ctx);

float n_gui_detect_dpi_scale(N_GUI_CTX* ctx, ALLEGRO_DISPLAY* display);

/* event processing & drawing */

int n_gui_process_event(N_GUI_CTX* ctx, ALLEGRO_EVENT event);
void n_gui_draw(N_GUI_CTX* ctx);
int n_gui_wants_mouse(N_GUI_CTX* ctx);

void n_gui_mark_dirty(N_GUI_CTX* ctx);

int n_gui_needs_redraw(N_GUI_CTX* ctx);

#define N_GUI_TAB_MAX 16

typedef struct N_GUI_TAB_PANEL {
    N_GUI_CTX* ctx;                        
    int parent_window_id;                  
    int button_ids[N_GUI_TAB_MAX];         
    int content_window_ids[N_GUI_TAB_MAX]; 
    int nb_tabs;                           
    int active_tab;                        
    float x;                               
    float y;                               
    float button_w;                        
    float button_h;                        
    void (*on_tab_change)(int, void*);     
    void* user_data;                       
} N_GUI_TAB_PANEL;

N_GUI_TAB_PANEL* n_gui_tab_create(N_GUI_CTX* ctx, int window_id, float x, float y, float button_w, float button_h, void (*on_tab_change)(int, void*), void* user_data);
int n_gui_tab_add(N_GUI_TAB_PANEL* panel, const char* label);
void n_gui_tab_set_content_window(N_GUI_TAB_PANEL* panel, int tab_index, int window_id);
void n_gui_tab_set_active(N_GUI_TAB_PANEL* panel, int index);
int n_gui_tab_get_active(const N_GUI_TAB_PANEL* panel);
void n_gui_tab_relayout(N_GUI_TAB_PANEL* panel, float x, float y, float button_w, float button_h);
void n_gui_tab_free(N_GUI_TAB_PANEL** panel);

#define N_GUI_SECTIONLIST_MAX 16

#define N_GUI_SECTION_WIDGETS_MAX 96

typedef struct N_GUI_SECTION {
    char title[64];                               
    int header_id;                                
    int widget_ids[N_GUI_SECTION_WIDGETS_MAX];    
    float widget_dy[N_GUI_SECTION_WIDGETS_MAX];   
    int widget_hidden[N_GUI_SECTION_WIDGETS_MAX]; 
    int nb_widgets;                               
    int folded;                                   
    float natural_h;                              
} N_GUI_SECTION;

typedef struct N_GUI_SECTIONLIST {
    N_GUI_CTX* ctx;                                
    int window_id;                                 
    float x;                                       
    float y0;                                      
    float width;                                   
    float header_h;                                
    float gap;                                     
    N_GUI_SECTION sections[N_GUI_SECTIONLIST_MAX]; 
    int nb_sections;                               
    float first_inset;                             
    int order[N_GUI_SECTIONLIST_MAX];              
    int section_hidden[N_GUI_SECTIONLIST_MAX];     
    void (*on_toggle)(int, int, void*);            
    void* user_data;                               
} N_GUI_SECTIONLIST;

N_GUI_SECTIONLIST* n_gui_sectionlist_create(N_GUI_CTX* ctx, int window_id, float x, float y0, float width, float header_h, float gap, void (*on_toggle)(int, int, void*), void* user_data);
int n_gui_sectionlist_add_section(N_GUI_SECTIONLIST* sl, const char* title, float header_y, int folded);
int n_gui_sectionlist_add_widget(N_GUI_SECTIONLIST* sl, int section_index, int widget_id);
void n_gui_sectionlist_set_widget_offset(N_GUI_SECTIONLIST* sl, int section_index, int widget_id, float dy);
void n_gui_sectionlist_set_widget_hidden(N_GUI_SECTIONLIST* sl, int section_index, int widget_id, int hidden);
void n_gui_sectionlist_set_geometry(N_GUI_SECTIONLIST* sl, float x, float y0, float width, float header_h, float gap);
void n_gui_sectionlist_set_first_inset(N_GUI_SECTIONLIST* sl, float inset);
void n_gui_sectionlist_relayout(N_GUI_SECTIONLIST* sl);
void n_gui_sectionlist_set_folded(N_GUI_SECTIONLIST* sl, int section_index, int folded);
int n_gui_sectionlist_is_folded(const N_GUI_SECTIONLIST* sl, int section_index);
float n_gui_sectionlist_content_height(const N_GUI_SECTIONLIST* sl);
int n_gui_sectionlist_get_count(const N_GUI_SECTIONLIST* sl);
void n_gui_sectionlist_set_order(N_GUI_SECTIONLIST* sl, const int* order, int count);
int n_gui_sectionlist_get_order(const N_GUI_SECTIONLIST* sl, int position);
void n_gui_sectionlist_set_section_hidden(N_GUI_SECTIONLIST* sl, int section_index, int hidden);
int n_gui_sectionlist_is_section_hidden(const N_GUI_SECTIONLIST* sl, int section_index);
void n_gui_sectionlist_free(N_GUI_SECTIONLIST** sl);

#define N_GUI_TREE_MAX 2048

typedef struct N_GUI_TREE_NODE {
    char label[128];  
    int parent_index; 
    int expanded;     
    int depth;        
    int has_children; 
    void* user_data;  
    int expandable;   
    int filled;       
} N_GUI_TREE_NODE;

#define N_GUI_DROP_BEFORE 0
#define N_GUI_DROP_INTO 1
#define N_GUI_DROP_AFTER 2

typedef int (*n_gui_tree_on_drop_t)(int src_node, int dst_node, int position, void* user_data);

typedef struct N_GUI_TREE {
    N_GUI_CTX* ctx;                        
    int listbox_id;                        
    int window_id;                         
    N_GUI_TREE_NODE nodes[N_GUI_TREE_MAX]; 
    int nb_nodes;                          
    int visible_map[N_GUI_TREE_MAX];       
    int nb_visible;                        
    void (*on_select)(int, void*);         
    void* user_data;                       
    void (*on_expand)(int, void*);         
    void* expand_user_data;                

    n_gui_tree_on_drop_t on_drop; 
    void* drop_user_data;         
    int drag_source_node;         
    int drag_armed_node;          
    int drag_hover_node;          
    int drop_position;            
    int drag_active;              
    float drag_press_x;           
    float drag_press_y;           
    int mouse_b1_prev;            
} N_GUI_TREE;

#define N_GUI_TREE_STATE_MAX 512
#define N_GUI_TREE_PATH_MAX 1024
#define N_GUI_TREE_PATH_SEP '\x1f'

typedef struct N_GUI_TREE_STATE {
    char* expanded[N_GUI_TREE_STATE_MAX]; 
    int nb_expanded;                      
    char* selected;                       
    int scroll_offset;                    
    float h_scroll;                       
} N_GUI_TREE_STATE;

N_GUI_TREE* n_gui_tree_create(N_GUI_CTX* ctx, int window_id, float x, float y, float w, float h, void (*on_select)(int, void*), void* user_data);
int n_gui_tree_add_node(N_GUI_TREE* tree, const char* label, int parent_index, void* user_data);
void n_gui_tree_clear(N_GUI_TREE* tree);
void n_gui_tree_remove_node(N_GUI_TREE* tree, int node_index);
void n_gui_tree_set_label(N_GUI_TREE* tree, int node_index, const char* new_label);
int n_gui_tree_get_selection(N_GUI_TREE* tree);
void n_gui_tree_set_selection(N_GUI_TREE* tree, int node_index);
int n_gui_tree_find_by_user_data(const N_GUI_TREE* tree, const void* ptr);
void n_gui_tree_toggle_expand(N_GUI_TREE* tree, int node_index);
void n_gui_tree_expand(N_GUI_TREE* tree, int node_index);
void n_gui_tree_set_expand_callback(N_GUI_TREE* tree, void (*on_expand)(int node_index, void* user_data), void* user_data);
void n_gui_tree_set_expandable(N_GUI_TREE* tree, int node_index, int expandable);
void n_gui_tree_rebuild(N_GUI_TREE* tree);
void n_gui_tree_free(N_GUI_TREE** tree);

int n_gui_tree_node_path(const N_GUI_TREE* tree, int node_index, char* buf, size_t bufsz);
int n_gui_tree_find_by_path(const N_GUI_TREE* tree, const char* path);
int n_gui_tree_state_save(const N_GUI_TREE* tree, N_GUI_TREE_STATE* out);
void n_gui_tree_state_apply(N_GUI_TREE* tree, const N_GUI_TREE_STATE* state);
void n_gui_tree_state_free(N_GUI_TREE_STATE* state);

void n_gui_tree_set_on_drop(N_GUI_TREE* tree,
                            n_gui_tree_on_drop_t on_drop,
                            void* user_data);

void n_gui_tree_tick(N_GUI_TREE* tree);

void n_gui_tree_draw_overlay(N_GUI_TREE* tree);

#define N_GUI_KV_MAX 128

typedef struct N_GUI_KV_ROW {
    int key_id;     
    int value_id;   
    int desc_id;    
    int enabled_id; 
    int remove_id;  
    int up_id;      
    int down_id;    
    int dup_id;     
    int active;     
} N_GUI_KV_ROW;

typedef struct N_GUI_KVTABLE {
    N_GUI_CTX* ctx;                  
    int window_id;                   
    N_GUI_KV_ROW rows[N_GUI_KV_MAX]; 
    int nb_rows;                     
    int nb_active;                   
    float row_height;                
    float padding;                   
    float header_height;             
    float init_win_w;                
    float init_win_h;                
    int lbl_key;                     
    int lbl_value;                   
    int lbl_desc;                    
    int lbl_enabled;                 
    int btn_add;                     
    void (*on_remove)(int, void*);   
    void* user_data;                 
    int show_value;                  
    int show_desc;                   
    int show_enabled;                
    int show_actions;                
    char* key_placeholder;           
    char* value_placeholder;         
    float top_offset;                
    float col_frac[3];               
    int frac_cols;                   
    int split_a;                     
    int split_b;                     
} N_GUI_KVTABLE;

N_GUI_KVTABLE* n_gui_kvtable_create(N_GUI_CTX* ctx, int window_id, float row_height, float padding, void (*on_remove)(int, void*), void* user_data);
int n_gui_kvtable_add_row(N_GUI_KVTABLE* table,
                          const char* key,
                          const char* value,
                          const char* description,
                          int enabled);
void n_gui_kvtable_remove_row(N_GUI_KVTABLE* table, int row_index);
void n_gui_kvtable_clear(N_GUI_KVTABLE* table);
void n_gui_kvtable_set_columns(N_GUI_KVTABLE* table, int show_value, int show_desc, int show_enabled);
void n_gui_kvtable_set_row_actions(N_GUI_KVTABLE* table, int enabled);
int n_gui_kvtable_move_row(N_GUI_KVTABLE* table, int row_index, int direction);
int n_gui_kvtable_duplicate_row(N_GUI_KVTABLE* table, int row_index);
void n_gui_kvtable_set_placeholders(N_GUI_KVTABLE* table, const char* key_hint, const char* value_hint);
void n_gui_kvtable_set_top_offset(N_GUI_KVTABLE* table, float top_offset);
int n_gui_kvtable_get_count(const N_GUI_KVTABLE* table);
void n_gui_kvtable_relayout(N_GUI_KVTABLE* table);
void n_gui_kvtable_free(N_GUI_KVTABLE** table);


#ifdef __cplusplus
}
#endif

#endif /* __NILOREA_GUI__ */