]> uap-core.de Git - note.git/commitdiff
use ui_splitview_window instead of manually creating a splitview
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 19 Oct 2025 19:10:51 +0000 (21:10 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 19 Oct 2025 19:10:51 +0000 (21:10 +0200)
161 files changed:
application/application.h
application/menu.c
application/window.c
ucx/Makefile
ucx/allocator.c
ucx/array_list.c
ucx/compare.c
ucx/cx/allocator.h
ucx/cx/array_list.h
ucx/cx/buffer.h
ucx/cx/collection.h
ucx/cx/common.h
ucx/cx/compare.h
ucx/cx/hash_key.h
ucx/cx/hash_map.h
ucx/cx/iterator.h
ucx/cx/json.h
ucx/cx/kv_list.h [new file with mode: 0644]
ucx/cx/linked_list.h
ucx/cx/list.h
ucx/cx/map.h
ucx/cx/printf.h
ucx/cx/properties.h
ucx/cx/streams.h
ucx/cx/string.h
ucx/cx/test.h
ucx/cx/tree.h
ucx/hash_key.c
ucx/hash_map.c
ucx/json.c
ucx/kv_list.c [new file with mode: 0644]
ucx/linked_list.c
ucx/list.c
ucx/map.c
ucx/mempool.c
ucx/properties.c
ucx/string.c
ui/cocoa/BoxContainer.h [new file with mode: 0644]
ui/cocoa/BoxContainer.m [new file with mode: 0644]
ui/cocoa/EventData.m
ui/cocoa/GridLayout.h
ui/cocoa/GridLayout.m
ui/cocoa/MainWindow.h
ui/cocoa/MainWindow.m
ui/cocoa/TabView.h [new file with mode: 0644]
ui/cocoa/TabView.m [new file with mode: 0644]
ui/cocoa/Toolbar.m
ui/cocoa/button.h
ui/cocoa/button.m
ui/cocoa/container.h
ui/cocoa/container.m
ui/cocoa/entry.h [new file with mode: 0644]
ui/cocoa/entry.m [new file with mode: 0644]
ui/cocoa/label.m
ui/cocoa/list.h
ui/cocoa/list.m
ui/cocoa/objs.mk
ui/cocoa/text.m
ui/cocoa/toolkit.h
ui/cocoa/toolkit.m
ui/cocoa/widget.h [new file with mode: 0644]
ui/cocoa/widget.m [new file with mode: 0644]
ui/cocoa/window.h
ui/cocoa/window.m
ui/common/args.c
ui/common/args.h
ui/common/container.c [new file with mode: 0644]
ui/common/container.h [new file with mode: 0644]
ui/common/context.c
ui/common/context.h
ui/common/document.c
ui/common/document.h
ui/common/menu.c
ui/common/menu.h
ui/common/object.c
ui/common/object.h
ui/common/objs.mk
ui/common/properties.c
ui/common/toolbar.c
ui/common/types.c
ui/common/wrapper.c
ui/common/wrapper.h
ui/gtk/button.c
ui/gtk/button.h
ui/gtk/container.c
ui/gtk/container.h
ui/gtk/display.c
ui/gtk/draw_cairo.c
ui/gtk/draw_cairo.h
ui/gtk/entry.c
ui/gtk/graphics.c
ui/gtk/graphics.h
ui/gtk/headerbar.c
ui/gtk/headerbar.h
ui/gtk/image.c
ui/gtk/list.c
ui/gtk/list.h
ui/gtk/menu.c
ui/gtk/menu.h
ui/gtk/range.c
ui/gtk/text.c
ui/gtk/toolbar.c
ui/gtk/toolkit.c
ui/gtk/toolkit.h
ui/gtk/webview.c
ui/gtk/widget.c
ui/gtk/window.c
ui/motif/Grid.c
ui/motif/Grid.h
ui/motif/button.c
ui/motif/container.c
ui/motif/container.h
ui/motif/label.c
ui/motif/list.c
ui/motif/menu.c
ui/motif/text.c
ui/motif/toolkit.c
ui/motif/widget.c
ui/qt/Makefile
ui/qt/button.cpp
ui/qt/container.cpp
ui/qt/container.h
ui/qt/entry.cpp
ui/qt/label.cpp
ui/qt/list.cpp
ui/qt/text.cpp
ui/qt/toolkit.cpp
ui/qt/widget.cpp
ui/qt/window.cpp
ui/ui/button.h
ui/ui/container.h
ui/ui/display.h
ui/ui/entry.h
ui/ui/graphics.h
ui/ui/image.h
ui/ui/menu.h
ui/ui/properties.h
ui/ui/text.h
ui/ui/toolbar.h
ui/ui/toolkit.h
ui/ui/tree.h
ui/ui/webview.h
ui/ui/widget.h
ui/ui/win32.h [new file with mode: 0644]
ui/ui/window.h
ui/win32/button.c
ui/win32/button.h
ui/win32/container.c
ui/win32/container.h
ui/win32/grid.c
ui/win32/grid.h
ui/win32/objs.mk
ui/win32/toolkit.c
ui/win32/toolkit.h
ui/win32/widget.c [new file with mode: 0644]
ui/win32/widget.h [new file with mode: 0644]
ui/win32/win32.c [new file with mode: 0644]
ui/win32/win32.h [new file with mode: 0644]
ui/win32/window.c
ui/win32/window.h
ui/winui/toolkit.cpp

index 9104afb167b6df4e454f9acc868156683946ba8e..1fdd9f9a1e921ce88b7150166f65ae06d565de44 100644 (file)
@@ -59,7 +59,6 @@ typedef struct MainWindow {
     CxList *navstack;
     size_t navstack_pos;
     
-    UIWIDGET splitpane;
     UIWIDGET document_tabview;
     UIWIDGET textview;
     UIWIDGET attachments;
index d2d1ae0cb4f219733ce0f4a35abc9a32187a0ac3..3bbd536796c03cfec38cd7de8d33210638671ee6 100644 (file)
@@ -38,7 +38,7 @@ void menu_init() {
 }
 
 void menu_cleanup() {
-    ui_menubuilder_free(notelist_context_menu);
+    ui_menubuilder_unref(notelist_context_menu);
 }
 
 void toolbar_init() {
index 0cff93e640b8ca3131e04d973169b6782fb072ee..982246b3da0b91de97cf806cacff335350c01b8b 100644 (file)
@@ -41,7 +41,7 @@
 
 
 void window_create() {
-    UiObject *obj = ui_sidebar_window("note", NULL);
+    UiObject *obj = ui_splitview_window("note", TRUE);
     ui_window_size(obj, 1600, 1200);
     MainWindow *wdata = window_init_data(obj);
     
@@ -60,7 +60,7 @@ void window_create() {
         }
     }
     
-    ui_hsplitpane_w(obj, wdata->splitpane, .initial_position = 500, .fill = TRUE) {
+    ui_left_panel0(obj) {
         // splitpane left: table
         UiModel* model = ui_model(obj->ctx, UI_STRING, "Name", UI_STRING_FREE, "Last Modified", -1);
         model->columnsize[0] = -1;
@@ -75,8 +75,10 @@ void window_create() {
                 .onactivate = action_note_activated,
                 .fill = TRUE);
         
+    }
+    ui_right_panel0(obj) {
         // splitpane right: content
-        ui_tabview_w(obj, wdata->document_tabview, .tabview = UI_TABVIEW_INVISIBLE, .varname = "note_type") {
+        ui_tabview_w(obj, wdata->document_tabview, .tabview = UI_TABVIEW_INVISIBLE, .varname = "note_type", .fill = TRUE) {
             ui_tab(obj, "empty") {
                 
             }
@@ -139,7 +141,8 @@ MainWindow* window_init_data(UiObject *obj) {
 }
 
 void window_notelist_setvisible(MainWindow *window, UiBool visible) {
-    ui_splitpane_set_visible(window->splitpane, 0, visible);
+    // TODO: re-activate, use new splitview window API
+    //ui_splitpane_set_visible(window->splitpane, 0, visible);
     window->notelist_isvisible = visible;
 }
 
index 1005259ccf35597f830ac529b9091915b48e1c7b..4b61a5678a845dea72a44685915833544494c4e0 100644 (file)
@@ -38,6 +38,7 @@ SRC += hash_key.c
 SRC += hash_map.c
 SRC += iterator.c
 SRC += linked_list.c
+SRC += kv_list.c
 SRC += list.c
 SRC += map.c
 SRC += printf.c
index b10096c05efc84b4dc272e2d8e244aee2a7fe6d5..323b14b59e04df862440b946562d186333f61efe 100644 (file)
@@ -73,7 +73,7 @@ struct cx_allocator_s cx_stdlib_allocator = {
         NULL
 };
 const CxAllocator * const cxStdlibAllocator = &cx_stdlib_allocator;
-const CxAllocator * cxDefaultAllocator = cxStdlibAllocator;
+const CxAllocator * cxDefaultAllocator = &cx_stdlib_allocator;
 
 int cx_reallocate_(
         void **mem,
index a66db5110b3ce421f024f424dc9a886ebcb98355..7dc28d149a9c5ef78a61176b5bdded61f06521ca 100644 (file)
@@ -291,7 +291,7 @@ int cx_array_copy(
                 *target, oldcap, newcap, elem_size, reallocator
         );
         if (newmem == NULL) {
-            return 1;
+            return 1; // LCOV_EXCL_LINE
         }
 
         // repair src pointer, if necessary
@@ -335,7 +335,7 @@ int cx_array_copy(
     return 0;
 }
 
-int cx_array_insert_sorted(
+static int cx_array_insert_sorted_impl(
         void **target,
         size_t *size,
         size_t *capacity,
@@ -343,7 +343,8 @@ int cx_array_insert_sorted(
         const void *sorted_data,
         size_t elem_size,
         size_t elem_count,
-        CxArrayReallocator *reallocator
+        CxArrayReallocator *reallocator,
+        bool allow_duplicates
 ) {
     // assert pointers
     assert(target != NULL);
@@ -369,6 +370,7 @@ int cx_array_insert_sorted(
     // store some counts
     size_t old_size = *size;
     size_t old_capacity = *capacity;
+    // the necessary capacity is the worst case assumption, including duplicates
     size_t needed_capacity = old_size + elem_count;
 
     // if we need more than we have, try a reallocation
@@ -418,13 +420,60 @@ int cx_array_insert_sorted(
                 bptr,
                 cmp_func
         );
+        // binary search gives us the smallest index;
+        // we also want to include equal elements here
+        while (si + copy_len < elem_count
+                && cmp_func(bptr, src+copy_len*elem_size) == 0) {
+            copy_len++;
+        }
 
         // copy the source elements
-        bytes_copied = copy_len * elem_size;
-        memcpy(dest, src, bytes_copied);
-        dest += bytes_copied;
-        src += bytes_copied;
-        si += copy_len;
+        if (copy_len > 0) {
+            if (allow_duplicates) {
+                // we can copy the entire chunk
+                bytes_copied = copy_len * elem_size;
+                memcpy(dest, src, bytes_copied);
+                dest += bytes_copied;
+                src += bytes_copied;
+                si += copy_len;
+                di += copy_len;
+            } else {
+                // first, check the end of the source chunk
+                // for being a duplicate of the bptr
+                const char *end_of_src = src + (copy_len - 1) * elem_size;
+                size_t skip_len = 0;
+                while (copy_len > 0 && cmp_func(bptr, end_of_src) == 0) {
+                    end_of_src -= elem_size;
+                    skip_len++;
+                    copy_len--;
+                }
+                char *last = dest == *target ? NULL : dest - elem_size;
+                // then iterate through the source chunk
+                // and skip all duplicates with the last element in the array
+                size_t more_skipped = 0;
+                for (unsigned j = 0; j < copy_len; j++) {
+                    if (last != NULL && cmp_func(last, src) == 0) {
+                        // duplicate - skip
+                        src += elem_size;
+                        si++;
+                        more_skipped++;
+                    } else {
+                        memcpy(dest, src, elem_size);
+                        src += elem_size;
+                        last = dest;
+                        dest += elem_size;
+                        si++;
+                        di++;
+                    }
+                }
+                // skip the previously identified elements as well
+                src += skip_len * elem_size;
+                si += skip_len;
+                skip_len += more_skipped;
+                // reduce the actual size by the number of skipped elements
+                *size -= skip_len;
+            }
+        }
 
         // when all source elements are in place, we are done
         if (si >= elem_count) break;
@@ -443,20 +492,103 @@ int cx_array_insert_sorted(
         memmove(dest, bptr, bytes_copied);
         dest += bytes_copied;
         bptr += bytes_copied;
+        di += copy_len;
         bi += copy_len;
     }
 
-    // still source elements left? simply append them
+    // still source elements left?
     if (si < elem_count) {
-        memcpy(dest, src, elem_size * (elem_count - si));
+        if (allow_duplicates) {
+            // duplicates allowed or nothing inserted yet: simply copy everything
+            memcpy(dest, src, elem_size * (elem_count - si));
+        } else {
+            if (dest != *target) {
+                // skip all source elements that equal the last element
+                char *last = dest - elem_size;
+                while (si < elem_count) {
+                    if (last != NULL && cmp_func(last, src) == 0) {
+                        src += elem_size;
+                        si++;
+                        (*size)--;
+                    } else {
+                        break;
+                    }
+                }
+            }
+            // we must check the elements in the chunk one by one
+            while (si < elem_count) {
+                // find a chain of elements that can be copied
+                size_t copy_len = 1, skip_len = 0;
+                {
+                    const char *left_src = src;
+                    while (si + copy_len < elem_count) {
+                        const char *right_src = left_src + elem_size;
+                        int d = cmp_func(left_src,  right_src);
+                        if (d < 0) {
+                            if (skip_len > 0) {
+                                // new larger element found;
+                                // handle it in the next cycle
+                                break;
+                            }
+                            left_src += elem_size;
+                            copy_len++;
+                        } else if (d == 0) {
+                            left_src += elem_size;
+                            skip_len++;
+                        } else {
+                            break;
+                        }
+                    }
+                }
+                size_t bytes_copied = copy_len * elem_size;
+                memcpy(dest, src, bytes_copied);
+                dest += bytes_copied;
+                src += bytes_copied + skip_len * elem_size;
+                si += copy_len + skip_len;
+                di += copy_len;
+                *size -= skip_len;
+            }
+        }
     }
 
-    // still buffer elements left?
-    // don't worry, we already moved them to the correct place
+    // buffered elements need to be moved when we skipped duplicates
+    size_t total_skipped = new_size - *size;
+    if (bi < new_size && total_skipped > 0) {
+        // move the remaining buffer to the end of the array
+        memmove(dest, bptr, elem_size * (new_size - bi));
+    }
 
     return 0;
 }
 
+int cx_array_insert_sorted(
+        void **target,
+        size_t *size,
+        size_t *capacity,
+        cx_compare_func cmp_func,
+        const void *sorted_data,
+        size_t elem_size,
+        size_t elem_count,
+        CxArrayReallocator *reallocator
+) {
+    return cx_array_insert_sorted_impl(target, size, capacity,
+        cmp_func, sorted_data, elem_size, elem_count, reallocator, true);
+}
+
+int cx_array_insert_unique(
+        void **target,
+        size_t *size,
+        size_t *capacity,
+        cx_compare_func cmp_func,
+        const void *sorted_data,
+        size_t elem_size,
+        size_t elem_count,
+        CxArrayReallocator *reallocator
+) {
+    return cx_array_insert_sorted_impl(target, size, capacity,
+        cmp_func, sorted_data, elem_size, elem_count, reallocator, false);
+}
+
 size_t cx_array_binary_search_inf(
         const void *arr,
         size_t size,
@@ -502,6 +634,13 @@ size_t cx_array_binary_search_inf(
         result = cmp_func(elem, arr_elem);
         if (result == 0) {
             // found it!
+            // check previous elements;
+            // when they are equal, report the smallest index
+            arr_elem -= elem_size;
+            while (pivot_index > 0 && cmp_func(elem, arr_elem) == 0) {
+                pivot_index--;
+                arr_elem -= elem_size;
+            }
             return pivot_index;
         } else if (result < 0) {
             // element is smaller than pivot, continue search left
@@ -700,6 +839,31 @@ static size_t cx_arl_insert_sorted(
     }
 }
 
+static size_t cx_arl_insert_unique(
+        struct cx_list_s *list,
+        const void *sorted_data,
+        size_t n
+) {
+    // get a correctly typed pointer to the list
+    cx_array_list *arl = (cx_array_list *) list;
+
+    if (cx_array_insert_unique(
+            &arl->data,
+            &list->collection.size,
+            &arl->capacity,
+            list->collection.cmpfunc,
+            sorted_data,
+            list->collection.elem_size,
+            n,
+            &arl->reallocator
+    )) {
+        // array list implementation is "all or nothing"
+        return 0;
+    } else {
+        return n;
+    }
+}
+
 static void *cx_arl_insert_element(
         struct cx_list_s *list,
         size_t index,
@@ -942,6 +1106,7 @@ static void cx_arl_iter_next(void *it) {
     if (iter->base.remove) {
         iter->base.remove = false;
         cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL);
+        iter->elem_count--;
     } else {
         iter->index++;
         iter->elem_handle =
@@ -956,6 +1121,7 @@ static void cx_arl_iter_prev(void *it) {
     if (iter->base.remove) {
         iter->base.remove = false;
         cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL);
+        iter->elem_count--;
     }
     iter->index--;
     if (iter->index < list->base.collection.size) {
@@ -991,6 +1157,7 @@ static cx_list_class cx_array_list_class = {
         cx_arl_insert_element,
         cx_arl_insert_array,
         cx_arl_insert_sorted,
+        cx_arl_insert_unique,
         cx_arl_insert_iter,
         cx_arl_remove,
         cx_arl_clear,
index b260aff424c28275b2679411e6f44c678b749f0f..1fc036d7029b8545425f53010a05dc89e18e49b3 100644 (file)
@@ -198,6 +198,20 @@ int cx_cmp_uint64(const void *i1, const void *i2) {
     return cx_vcmp_uint64(a, b);
 }
 
+int cx_vcmp_size(size_t a, size_t b) {
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_size(const void *i1, const void *i2) {
+    size_t a = *((const size_t *) i1);
+    size_t b = *((const size_t *) i2);
+    return cx_vcmp_size(a, b);
+}
+
 int cx_vcmp_float(float a, float b) {
     if (fabsf(a - b) < 1e-6f) {
         return 0;
index c169a928d470ae963eadad85df7b27b6c9451080..2514389aeeb916ddb9f67b5a9de503f9cbd5be94 100644 (file)
@@ -271,7 +271,7 @@ void *cxMalloc(
 /**
  * Reallocate the previously allocated block in @p mem, making the new block
  * @p n bytes long.
- * This function may return the same pointer that was passed to it, if moving
+ * This function may return the same pointer passed to it if moving
  * the memory was not necessary.
  *
  * @note Re-allocating a block allocated by a different allocator is undefined.
@@ -295,11 +295,11 @@ void *cxRealloc(
 /**
  * Reallocate the previously allocated block in @p mem, making the new block
  * @p n bytes long.
- * This function may return the same pointer that was passed to it, if moving
+ * This function may return the same pointer passed to it if moving
  * the memory was not necessary.
  *
  * The size is calculated by multiplying @p nemb and @p size.
- * If that multiplication overflows, this function returns @c NULL and @c errno
+ * If that multiplication overflows, this function returns @c NULL, and @c errno
  * will be set.
  *
  * @note Re-allocating a block allocated by a different allocator is undefined.
@@ -330,7 +330,7 @@ void *cxReallocArray(
  * @note Re-allocating a block allocated by a different allocator is undefined.
  *
  * @par Error handling
- * @c errno will be set, if the underlying realloc function does so.
+ * @c errno will be set if the underlying realloc function does so.
  *
  * @param allocator the allocator
  * @param mem pointer to the pointer to allocated block
@@ -355,7 +355,7 @@ int cxReallocate_(
  * @note Re-allocating a block allocated by a different allocator is undefined.
  *
  * @par Error handling
- * @c errno will be set, if the underlying realloc function does so.
+ * @c errno will be set if the underlying realloc function does so.
  *
  * @param allocator (@c CxAllocator*) the allocator
  * @param mem (@c void**) pointer to the pointer to allocated block
index b38710d778a508844bb03578b0760003fe8a3faa..15c686f4d39350abd2c55d43fd1cd714c3a8cd3e 100644 (file)
@@ -44,8 +44,8 @@ extern "C" {
 #endif
 
 /**
- * The maximum item size in an array list that fits into stack buffer
- * when swapped.
+ * The maximum item size in an array list that fits into
+ * a stack buffer when swapped.
  */
 cx_attr_export
 extern const unsigned cx_array_swap_sbo_size;
@@ -84,7 +84,7 @@ extern const unsigned cx_array_swap_sbo_size;
 /**
  * Declares variables for an array that can be used with the convenience macros.
  *
- * The size and capacity variables will have @c size_t type.
+ * The size and capacity variables will have type @c size_t.
  * Use #CX_ARRAY_DECLARE_SIZED() to specify a different type.
  *
  * @par Examples
@@ -147,7 +147,7 @@ extern const unsigned cx_array_swap_sbo_size;
  * const CxAllocator *al = // ...
  * cx_array_initialize_a(al, myarray, 128);
  * // ...
- * cxFree(al, myarray); // don't forget to free with same allocator
+ * cxFree(al, myarray); // remember to free with the same allocator
  * @endcode
  *
  * @param allocator (@c CxAllocator*) the allocator
@@ -230,16 +230,16 @@ extern CxArrayReallocator *cx_array_default_reallocator;
  * When @p allocator is @c NULL, the cxDefaultAllocator will be used.
  *
  * When @p stackmem is not @c NULL, the reallocator is supposed to be used
- * @em only for the specific array that is initially located at @p stackmem.
- * When reallocation is needed, the reallocator checks, if the array is
+ * @em only for the specific array initially located at @p stackmem.
+ * When reallocation is needed, the reallocator checks if the array is
  * still located at @p stackmem and copies the contents to the heap.
  *
- * @note Invoking this function with both arguments @c NULL will return a
+ * @note Invoking this function with both arguments being @c NULL will return a
  * reallocator that behaves like #cx_array_default_reallocator.
  *
  * @param allocator the allocator this reallocator shall be based on
  * @param stackmem the address of the array when the array is initially located
- * on the stack or shall not reallocated in place
+ * on the stack or shall not reallocate in place
  * @return an array reallocator
  */
 cx_attr_export
@@ -263,7 +263,7 @@ CxArrayReallocator cx_array_reallocator(
  *
  * The @p width in bytes refers to the size and capacity.
  * Both must have the same width.
- * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64 bit
+ * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit
  * architecture. If set to zero, the native word width is used.
  *
  * @param array a pointer to the target array
@@ -296,7 +296,7 @@ int cx_array_reserve(
  * The elements are copied to the @p target array at the specified @p index,
  * overwriting possible elements. The @p index does not need to be in range of
  * the current array @p size. If the new index plus the number of elements added
- * would extend the array's size, the remaining @p capacity is used.
+ * extends the array's size, the remaining @p capacity is used.
  *
  * If the @p capacity is also insufficient to hold the new data, a reallocation
  * attempt is made with the specified @p reallocator.
@@ -305,7 +305,7 @@ int cx_array_reserve(
  *
  * The @p width in bytes refers to the size and capacity.
  * Both must have the same width.
- * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64 bit
+ * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit
  * architecture. If set to zero, the native word width is used.
  *
  * @param target a pointer to the target array
@@ -586,6 +586,135 @@ int cx_array_insert_sorted(
 #define cx_array_simple_insert_sorted(array, src, n, cmp_func) \
     cx_array_simple_insert_sorted_a(NULL, array, src, n, cmp_func)
 
+
+/**
+ * Inserts a sorted array into another sorted array, avoiding duplicates.
+ *
+ * If either the target or the source array is not already sorted with respect
+ * to the specified @p cmp_func, the behavior is undefined.
+ *
+ * If the capacity is insufficient to hold the new data, a reallocation
+ * attempt is made.
+ * You can create your own reallocator by hand, use #cx_array_default_reallocator,
+ * or use the convenience function cx_array_reallocator() to create a custom reallocator.
+ *
+ * @param target a pointer to the target array
+ * @param size a pointer to the size of the target array
+ * @param capacity a pointer to the capacity of the target array
+ * @param cmp_func the compare function for the elements
+ * @param src the source array
+ * @param elem_size the size of one element
+ * @param elem_count the number of elements to insert
+ * @param reallocator the array reallocator to use
+ * (@c NULL defaults to #cx_array_default_reallocator)
+ * @retval zero success
+ * @retval non-zero failure
+ */
+cx_attr_nonnull_arg(1, 2, 3, 5)
+cx_attr_export
+int cx_array_insert_unique(
+        void **target,
+        size_t *size,
+        size_t *capacity,
+        cx_compare_func cmp_func,
+        const void *src,
+        size_t elem_size,
+        size_t elem_count,
+        CxArrayReallocator *reallocator
+);
+
+/**
+ * Inserts an element into a sorted array if it does not exist.
+ *
+ * If the target array is not already sorted with respect
+ * to the specified @p cmp_func, the behavior is undefined.
+ *
+ * If the capacity is insufficient to hold the new data, a reallocation
+ * attempt is made.
+ *
+ * The \@ SIZE_TYPE is flexible and can be any unsigned integer type.
+ * It is important, however, that @p size and @p capacity are pointers to
+ * variables of the same type.
+ *
+ * @param target (@c void**) a pointer to the target array
+ * @param size (@c SIZE_TYPE*) a pointer to the size of the target array
+ * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array
+ * @param elem_size (@c size_t) the size of one element
+ * @param elem (@c void*) a pointer to the element to add
+ * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
+ * @retval zero success (also when the element was already present)
+ * @retval non-zero failure
+ */
+#define cx_array_add_unique(target, size, capacity, elem_size, elem, cmp_func, reallocator) \
+    cx_array_insert_unique((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator)
+
+/**
+ * Convenience macro for cx_array_add_unique() with a default
+ * layout and the specified reallocator.
+ *
+ * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
+ * @param array the name of the array (NOT a pointer or alias to the array)
+ * @param elem the element to add (NOT a pointer, address is automatically taken)
+ * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @retval zero success
+ * @retval non-zero failure
+ * @see CX_ARRAY_DECLARE()
+ * @see cx_array_simple_add_unique()
+ */
+#define cx_array_simple_add_unique_a(reallocator, array, elem, cmp_func) \
+    cx_array_add_unique(&array, &(array##_size), &(array##_capacity), \
+        sizeof((array)[0]), &(elem), cmp_func, reallocator)
+
+/**
+ * Convenience macro for cx_array_add_unique() with a default
+ * layout and the default reallocator.
+ *
+ * @param array the name of the array (NOT a pointer or alias to the array)
+ * @param elem the element to add (NOT a pointer, address is automatically taken)
+ * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @retval zero success
+ * @retval non-zero failure
+ * @see CX_ARRAY_DECLARE()
+ * @see cx_array_simple_add_unique_a()
+ */
+#define cx_array_simple_add_unique(array, elem, cmp_func) \
+    cx_array_simple_add_unique_a(NULL, array, elem, cmp_func)
+
+/**
+ * Convenience macro for cx_array_insert_unique() with a default
+ * layout and the specified reallocator.
+ *
+ * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
+ * @param array the name of the array (NOT a pointer or alias to the array)
+ * @param src (@c void*) pointer to the source array
+ * @param n (@c size_t) number of elements in the source array
+ * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @retval zero success
+ * @retval non-zero failure
+ * @see CX_ARRAY_DECLARE()
+ * @see cx_array_simple_insert_unique()
+ */
+#define cx_array_simple_insert_unique_a(reallocator, array, src, n, cmp_func) \
+    cx_array_insert_unique((void**)(&array), &(array##_size), &(array##_capacity), \
+        cmp_func, src, sizeof((array)[0]), n, reallocator)
+
+/**
+ * Convenience macro for cx_array_insert_unique() with a default
+ * layout and the default reallocator.
+ *
+ * @param array the name of the array (NOT a pointer or alias to the array)
+ * @param src (@c void*) pointer to the source array
+ * @param n (@c size_t) number of elements in the source array
+ * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @retval zero success
+ * @retval non-zero failure
+ * @see CX_ARRAY_DECLARE()
+ * @see cx_array_simple_insert_unique_a()
+ */
+#define cx_array_simple_insert_unique(array, src, n, cmp_func) \
+    cx_array_simple_insert_unique_a(NULL, array, src, n, cmp_func)
+
 /**
  * Searches the largest lower bound in a sorted array.
  *
@@ -681,8 +810,8 @@ size_t cx_array_binary_search_sup(
  *
  * @param arr the array
  * @param elem_size the element size
- * @param idx1 index of first element
- * @param idx2 index of second element
+ * @param idx1 index of the first element
+ * @param idx2 index of the second element
  */
 cx_attr_nonnull
 cx_attr_export
@@ -697,7 +826,7 @@ void cx_array_swap(
  * Allocates an array list for storing elements with @p elem_size bytes each.
  *
  * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
- * copies of the added elements and the compare function will be automatically set
+ * copies of the added elements, and the compare function will be automatically set
  * to cx_cmp_ptr(), if none is given.
  *
  * @param allocator the allocator for allocating the list memory
index 867fb3084f207816a62d053700982cd56123b5f5..e9d98cbb4d1560f0f96e3a68713e7f8ca063921c 100644 (file)
@@ -62,7 +62,7 @@ extern "C" {
  * If this flag is enabled, the buffer will automatically free its contents when destroyed.
  *
  * Do NOT set this flag together with #CX_BUFFER_COPY_ON_WRITE. It will be automatically
- * set when the copy-on-write operations is performed.
+ * set when the copy-on-write operation is performed.
  */
 #define CX_BUFFER_FREE_CONTENTS 0x01
 
@@ -74,7 +74,7 @@ extern "C" {
 /**
  * If this flag is enabled, the buffer will allocate new memory when written to.
  *
- * The current contents of the buffer will be copied to the new memory and the flag
+ * The current contents of the buffer will be copied to the new memory, and the flag
  * will be cleared while the #CX_BUFFER_FREE_CONTENTS flag will be set automatically.
  */
 #define CX_BUFFER_COPY_ON_WRITE 0x04
@@ -127,7 +127,7 @@ struct cx_buffer_flush_config_s {
     size_t blkmax;
 
     /**
-     * The target for write function.
+     * The target for the write function.
      */
     void *target;
 
@@ -202,7 +202,7 @@ typedef struct cx_buffer_s CxBuffer;
  * you will need to cast the pointer, and you should set the
  * #CX_BUFFER_COPY_ON_WRITE flag.
  *
- * You need to set the size manually after initialization, if
+ * You need to set the size manually after initialization if
  * you provide @p space which already contains data.
  *
  * When you specify stack memory as @p space and decide to use
@@ -210,7 +210,7 @@ typedef struct cx_buffer_s CxBuffer;
  * #CX_BUFFER_COPY_ON_EXTEND flag, instead of the
  * #CX_BUFFER_AUTO_EXTEND flag.
  *
- * @note You may provide @c NULL as argument for @p space.
+ * @note You may provide @c NULL as the argument for @p space.
  * Then this function will allocate the space and enforce
  * the #CX_BUFFER_FREE_CONTENTS flag. In that case, specifying
  * copy-on-write should be avoided, because the allocated
@@ -276,9 +276,6 @@ void cxBufferDestroy(CxBuffer *buffer);
  * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys
  * the contents. If you @em only want to destroy the contents, use cxBufferDestroy().
  *
- * @remark As with all free() functions, this accepts @c NULL arguments in which
- * case it does nothing.
- *
  * @param buffer the buffer to deallocate
  * @see cxBufferCreate()
  */
@@ -296,7 +293,7 @@ void cxBufferFree(CxBuffer *buffer);
  * #CX_BUFFER_COPY_ON_EXTEND flag, instead of the
  * #CX_BUFFER_AUTO_EXTEND flag.
  *
- * @note You may provide @c NULL as argument for @p space.
+ * @note You may provide @c NULL as the argument for @p space.
  * Then this function will allocate the space and enforce
  * the #CX_BUFFER_FREE_CONTENTS flag.
  *
@@ -327,8 +324,8 @@ CxBuffer *cxBufferCreate(
  * If auto extension is enabled, the buffer grows, if necessary.
  * In case the auto extension fails, this function returns a non-zero value and
  * no contents are changed.
- * If auto extension is disabled, the contents that do not fit into the buffer
- * are discarded.
+ * When the auto extension is disabled, the contents that do not fit into the
+ * buffer are discarded.
  *
  * If the offset is negative, the contents are shifted to the left where the
  * first @p shift bytes are discarded.
@@ -336,15 +333,15 @@ CxBuffer *cxBufferCreate(
  * If this value is larger than the buffer size, the buffer is emptied (but
  * not cleared, see the security note below).
  *
- * The buffer position gets shifted alongside with the content but is kept
+ * The buffer position gets shifted alongside the content but is kept
  * within the boundaries of the buffer.
  *
  * @note For situations where @c off_t is not large enough, there are specialized cxBufferShiftLeft() and
- * cxBufferShiftRight() functions using a @c size_t as parameter type.
+ * cxBufferShiftRight() functions using a @c size_t as the parameter type.
  *
  * @attention
  * Security Note: The shifting operation does @em not erase the previously occupied memory cells.
- * But you can easily do that manually, e.g. by calling
+ * But you can do that manually by calling
  * <code>memset(buffer->bytes, 0, shift)</code> for a right shift or
  * <code>memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size)</code>
  * for a left shift.
@@ -517,7 +514,7 @@ void cxBufferShrink(
  * Writes data to a CxBuffer.
  *
  * If automatic flushing is not enabled, the data is simply written into the
- * buffer at the current position and the position of the buffer is increased
+ * buffer at the current position, and the position of the buffer is increased
  * by the number of bytes written.
  *
  * If flushing is enabled and the buffer needs to flush, the data is flushed to
@@ -526,7 +523,7 @@ void cxBufferShrink(
  * data in this buffer is shifted to the beginning of this buffer so that the
  * newly available space can be used to append as much data as possible.
  *
- * This function only stops writing more elements, when the flush target and this
+ * This function only stops writing more elements when the flush target and this
  * buffer are both incapable of taking more data or all data has been written.
  *
  * If, after flushing, the number of items that shall be written still exceeds
@@ -534,14 +531,14 @@ void cxBufferShrink(
  * to the flush target, if possible.
  *
  * The number returned by this function is the number of elements from
- * @c ptr that could be written to either the flush target or the buffer
- * (so it does not include the number of items that had been already in the buffer
- * in were flushed during the process).
+ * @c ptr that could be written to either the flush target or the buffer.
+ * That means it does @em not include the number of items that were already in
+ * the buffer and were also flushed during the process.
  *
  * @attention
  * When @p size is larger than one and the contents of the buffer are not aligned
  * with @p size, flushing stops after all complete items have been flushed, leaving
- * the mis-aligned part in the buffer.
+ * the misaligned part in the buffer.
  * Afterward, this function only writes as many items as possible to the buffer.
  *
  * @note The signature is compatible with the fwrite() family of functions.
@@ -614,19 +611,19 @@ size_t cxBufferAppend(
  * at position 200. The flush configuration is
  * @c blkmax=4 and @c blksize=64 .
  * Assume that the entire flush operation is successful.
- * All 200 bytes on the left hand-side from the current
+ * All 200 bytes on the left-hand-side from the current
  * position are written.
- * That means, the size of the buffer is now 140 and the
+ * That means the size of the buffer is now 140 and the
  * position is zero.
  *
  * @par Example 2
  * Same as Example 1, but now the @c blkmax is 1.
- * The size of the buffer is now 276 and the position is 136.
+ * The size of the buffer is now 276, and the position is 136.
  *
  * @par Example 3
  * Same as Example 1, but now assume the flush target
  * only accepts 100 bytes before returning zero.
- * That means, the flush operations manages to flush
+ * That means the flush operation manages to flush
  * one complete block and one partial block, ending
  * up with a buffer with size 240 and position 100.
  *
@@ -636,8 +633,8 @@ size_t cxBufferAppend(
  * @remark When the buffer uses copy-on-write, the memory
  * is copied first, before attempting any flush.
  * This is, however, considered an erroneous use of the
- * buffer, because it does not make much sense to put
- * readonly data into an UCX buffer for flushing, instead
+ * buffer because it makes little sense to put
+ * readonly data into an UCX buffer for flushing instead
  * of writing it directly to the target.
  *
  * @param buffer the buffer
@@ -678,9 +675,9 @@ size_t cxBufferRead(
  * The least significant byte of the argument is written to the buffer. If the
  * end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled,
  * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature
- * is disabled or buffer extension fails, @c EOF is returned.
+ * is disabled or the buffer extension fails, @c EOF is returned.
  *
- * On successful write, the position of the buffer is increased.
+ * On successful writing, the position of the buffer is increased.
  *
  * If you just want to write a null-terminator at the current position, you
  * should use cxBufferTerminate() instead.
@@ -688,7 +685,7 @@ size_t cxBufferRead(
  * @param buffer the buffer to write to
  * @param c the character to write
  * @return the byte that has been written or @c EOF when the end of the stream is
- * reached and automatic extension is not enabled or not possible
+ * reached, and automatic extension is not enabled or not possible
  * @see cxBufferTerminate()
  */
 cx_attr_nonnull
index ba329dcb1fcc1a766dfd22fdd6868be9a5ac4325..60dcd0c94e69bfc0ee68c192a225892818d30bcb 100644 (file)
@@ -142,8 +142,10 @@ struct cx_collection_s {
 /**
  * Indicates whether the collection can guarantee that the stored elements are currently sorted.
  *
- * This may return false even when the elements are sorted.
- * It is totally up to the implementation of the collection whether it keeps track of the order of its elements.
+ * This may return @c false even when the elements are sorted.
+ * It is totally up to the implementation of the collection when to check if the elements are sorted.
+ * It is usually a good practice to establish this property as an invariant that does not need
+ * to be re-checked on certain operations.
  *
  * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
  * @retval true if the elements are currently sorted wrt. the collection's compare function
index 0fc9e47dec846c76e6cf210972a7f791dfed63d9..ce4225fb5a617279fb02e9eb83f03cb22d3b17e7 100644 (file)
@@ -49,7 +49,7 @@
  * <a href="https://uap-core.de/hg/ucx">https://uap-core.de/hg/ucx</a>
  * </p>
  *
- * <h2>LICENCE</h2>
+ * <h2>LICENSE</h2>
  *
  * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
  *
 #define __attribute__(x)
 #endif
 
+/**
+ * Inform the compiler that falling through a switch case is intentional.
+ */
+#define cx_attr_fallthrough __attribute__((__fallthrough__))
+
 /**
  * All pointer arguments must be non-NULL.
  */
  */
 #define cx_attr_cstr_arg(idx)
 /**
- * No support for access attribute in clang.
+ * No support for the access attribute in clang.
  */
 #define cx_attr_access(mode, ...)
 #else
index cb4ddf17045181c9fd12071b0a5247b0f9be86d3..efcb28b62834429f60a2577dd3ee87971900c4f1 100644 (file)
@@ -47,7 +47,7 @@ extern "C" {
  *
  * All functions from compare.h with the cx_cmp prefix are
  * compatible with this signature and can be used as
- * compare function for collections, or other implementations
+ * compare function for collections or other implementations
  * that need to be type-agnostic.
  *
  * For simple comparisons the cx_vcmp family of functions
@@ -422,6 +422,36 @@ cx_attr_nodiscard
 cx_attr_export
 int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
 
+/**
+ * Compares two integers of type size_t.
+ *
+ * @note the parameters deliberately have type @c void* to be
+ * compatible with #cx_compare_func without the need of a cast.
+ *
+ * @param i1 pointer to size_t one
+ * @param i2 pointer to size_t two
+ * @retval -1 if the left argument is less than the right argument
+ * @retval 0 if both arguments are equal
+ * @retval 1 if the left argument is greater than the right argument
+ */
+cx_attr_nonnull
+cx_attr_nodiscard
+cx_attr_export
+int cx_cmp_size(const void *i1, const void *i2);
+
+/**
+ * Compares two integers of type size_t.
+ *
+ * @param i1 size_t one
+ * @param i2 size_t two
+ * @retval -1 if the left argument is less than the right argument
+ * @retval 0 if both arguments are equal
+ * @retval 1 if the left argument is greater than the right argument
+ */
+cx_attr_nodiscard
+cx_attr_export
+int cx_vcmp_size(size_t i1, size_t i2);
+
 /**
  * Compares two real numbers of type float with precision 1e-6f.
  *
index cdf8864057e41032ddddc890945be0c409c7c58d..f4bec904ce73f99ad26713d831f4265398edf8b5 100644 (file)
@@ -46,14 +46,17 @@ extern "C" {
 
 /** Internal structure for a key within a hash map. */
 struct cx_hash_key_s {
-    /** The key data. */
+    /**
+     * The key data.
+     * May be NULL when the hash is collision-free.
+     */
     const void *data;
     /**
      * The key data length.
      */
     size_t len;
     /** The hash value of the key data. */
-    unsigned hash;
+    uint64_t hash;
 };
 
 /**
@@ -79,6 +82,48 @@ cx_attr_nonnull
 cx_attr_export
 void cx_hash_murmur(CxHashKey *key);
 
+/**
+ * Mixes up a 32-bit integer to be used as a hash.
+ *
+ * This function produces no collisions and has a good statistical distribution.
+ *
+ * @param x the integer
+ * @return the hash
+ */
+cx_attr_export
+uint32_t cx_hash_u32(uint32_t x);
+
+/**
+ * Mixes up a 64-bit integer to be used as a hash.
+ *
+ * This function produces no collisions and has a good statistical distribution.
+ *
+ * @param x the integer
+ * @return the hash
+ */
+cx_attr_export
+uint64_t cx_hash_u64(uint64_t x);
+
+/**
+ * Computes a hash key from a 32-bit integer.
+ *
+ * @param x the integer
+ * @return the hash key
+ */
+cx_attr_nodiscard
+cx_attr_export
+CxHashKey cx_hash_key_u32(uint32_t x);
+
+/**
+ * Computes a hash key from a 64-bit integer.
+ *
+ * @param x the integer
+ * @return the hash key
+ */
+cx_attr_nodiscard
+cx_attr_export
+CxHashKey cx_hash_key_u64(uint64_t x);
+
 /**
  * Computes a hash key from a string.
  *
@@ -92,6 +137,24 @@ cx_attr_cstr_arg(1)
 cx_attr_export
 CxHashKey cx_hash_key_str(const char *str);
 
+/**
+ * Computes a hash key from a string.
+ *
+ * Use this function when the string is represented
+ * as an unsigned char array.
+ *
+ * The string needs to be zero-terminated.
+ *
+ * @param str the string
+ * @return the hash key
+ */
+cx_attr_nodiscard
+cx_attr_cstr_arg(1)
+cx_attr_export
+static inline CxHashKey cx_hash_key_ustr(const unsigned char *str) {
+    return cx_hash_key_str((const char*)str);
+}
+
 /**
  * Computes a hash key from a byte array.
  *
@@ -115,7 +178,7 @@ CxHashKey cx_hash_key_bytes(
  * used for data exchange with different machines.
  *
  * @param obj a pointer to an arbitrary object
- * @param len the length of object in memory
+ * @param len the length of the object in memory
  * @return the hash key
  */
 cx_attr_nodiscard
@@ -140,13 +203,106 @@ static inline CxHashKey cx_hash_key_cxstr(cxstring str) {
 /**
  * Computes a hash key from a UCX string.
  *
+ * @param str the string
+ * @return the hash key
+ */
+cx_attr_nodiscard
+static inline CxHashKey cx_hash_key_mutstr(cxmutstr str) {
+    return cx_hash_key(str.ptr, str.length);
+}
+
+/**
+ * The identity function for the CX_HASH_KEY() macro.
+ * You should never need to use this manually.
+ *
+ * @param key the key
+ * @return a copy of the key
+ */
+cx_attr_nodiscard
+static inline CxHashKey cx_hash_key_identity(CxHashKey key) {
+    return key;
+}
+
+#ifndef __cplusplus
+/**
+ * Creates a hash key from any of the supported types with implicit length.
+ *
+ * Does nothing when passing a CxHashkey.
+ *
+ * Supported types are UCX strings, zero-terminated C strings,
+ * and 32-bit or 64-bit unsigned integers.
+ *
+ * @param key the key data
+ * @returns the @c CxHashKey
+ */
+#define CX_HASH_KEY(key) _Generic((key), \
+        CxHashKey: cx_hash_key_identity, \
+        cxstring: cx_hash_key_cxstr, \
+        cxmutstr: cx_hash_key_mutstr, \
+        char*: cx_hash_key_str, \
+        const char*: cx_hash_key_str, \
+        unsigned char*: cx_hash_key_ustr, \
+        const unsigned char*: cx_hash_key_ustr, \
+        uint32_t: cx_hash_key_u32, \
+        uint64_t: cx_hash_key_u64) \
+        (key)
+#endif // __cplusplus
+
+/**
+ * Computes a hash key from a UCX string.
+ * Convenience macro that accepts both cxstring and cxmutstr.
+ * @deprecated use the CX_HASH_KEY() macro instead
  * @param str (@c cxstring or @c cxmutstr) the string
  * @return (@c CxHashKey) the hash key
  */
 #define cx_hash_key_cxstr(str) cx_hash_key_cxstr(cx_strcast(str))
 
+/**
+ * Compare function for hash keys.
+ *
+ * @param left the first key
+ * @param right the second key
+ * @return zero when the keys equal, non-zero when they differ
+ */
+cx_attr_nodiscard
+cx_attr_nonnull
+cx_attr_export
+int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right);
+
 #ifdef __cplusplus
 } // extern "C"
+
+// ----------------------------------------------------------
+// Overloads of CX_HASH_KEY (the C++ version of a _Generic)
+// ----------------------------------------------------------
+
+static inline CxHashKey CX_HASH_KEY(CxHashKey key) {
+    return key;
+}
+
+static inline CxHashKey CX_HASH_KEY(cxstring str) {
+    return cx_hash_key_cxstr(str);
+}
+
+static inline CxHashKey CX_HASH_KEY(cxmutstr str) {
+    return cx_hash_key_mutstr(str);
+}
+
+static inline CxHashKey CX_HASH_KEY(const char *str) {
+    return cx_hash_key_str(str);
+}
+
+static inline CxHashKey CX_HASH_KEY(const unsigned char *str) {
+    return cx_hash_key_ustr(str);
+}
+
+static inline CxHashKey CX_HASH_KEY(uint32_t key) {
+    return cx_hash_key_u32(key);
+}
+
+static inline CxHashKey CX_HASH_KEY(uint64_t key) {
+    return cx_hash_key_u64(key);
+}
 #endif
 
 #endif // UCX_HASH_KEY_H
index 6b11e2c1c61cbad1184ff48275ee2287244d9530..3b1a4bc3c9320334fac072c4e9659599f1a6ba3c 100644 (file)
@@ -73,7 +73,8 @@ struct cx_hash_map_s {
  * copies of the added elements.
  *
  * @note Iterators provided by this hash map implementation provide the remove operation.
- * The index value of an iterator is incremented when the iterator advanced without removal.
+ * The index value of an iterator is incremented when the iterator advanced without
+ * removing an entry.
  * In other words, when the iterator is finished, @c index==size .
  *
  * @param allocator the allocator to use
@@ -99,7 +100,8 @@ CxMap *cxHashMapCreate(
  * copies of the added elements.
  *
  * @note Iterators provided by this hash map implementation provide the remove operation.
- * The index value of an iterator is incremented when the iterator advanced without removal.
+ * The index value of an iterator is incremented when the iterator advanced without
+ * removing an entry.
  * In other words, when the iterator is finished, @c index==size .
  *
  * @param itemsize (@c size_t) the size of one element
@@ -111,10 +113,10 @@ CxMap *cxHashMapCreate(
  * Increases the number of buckets, if necessary.
  *
  * The load threshold is @c 0.75*buckets. If the element count exceeds the load
- * threshold, the map will be rehashed. Otherwise, no action is performed and
+ * threshold, the map will be rehashed. Otherwise, no action is performed, and
  * this function simply returns 0.
  *
- * The rehashing process ensures, that the number of buckets is at least
+ * The rehashing process ensures that the number of buckets is at least
  * 2.5 times the element count. So there is enough room for additional
  * elements without the need of another soon rehashing.
  *
index 204c4df29790c5f48b86e8f01c00ba9671170f89..d267d9ff12a856f0933721b06aafa5f6fae9d24e 100644 (file)
@@ -69,6 +69,10 @@ struct cx_iterator_base_s {
      * When valid returns false, the behavior of this function is undefined.
      */
     void (*next)(void *);
+    /**
+     * Original implementation in case the function needs to be wrapped.
+     */
+    void (*next_impl)(void *);
     /**
      * Indicates whether this iterator may remove elements.
      */
@@ -141,7 +145,7 @@ struct cx_iterator_s {
  * Iterator type.
  *
  * An iterator points to a certain element in a (possibly unbounded) chain of elements.
- * Iterators that are based on collections (which have a defined "first" element), are supposed
+ * Iterators that are based on collections (which have a defined "first" element) are supposed
  * to be "position-aware", which means that they keep track of the current index within the collection.
  *
  * @note Objects that are pointed to by an iterator are always mutable through that iterator. However,
@@ -178,7 +182,7 @@ typedef struct cx_iterator_s CxIterator;
 #define cxIteratorNext(iter) (iter).base.next(&iter)
 
 /**
- * Flags the current element for removal, if this iterator is mutating.
+ * Flags the current element for removal if this iterator is mutating.
  *
  * Does nothing for non-mutating iterators.
  *
@@ -210,7 +214,7 @@ for (type elem; cxIteratorValid(iter) && (elem = (type)cxIteratorCurrent(iter))
 /**
  * Creates an iterator for the specified plain array.
  *
- * The @p array can be @c NULL in which case the iterator will be immediately
+ * The @p array can be @c NULL, in which case the iterator will be immediately
  * initialized such that #cxIteratorValid() returns @c false.
  *
  * This iterator yields the addresses of the array elements.
@@ -244,7 +248,7 @@ CxIterator cxIterator(
  * moving all subsequent elements by one. Usually, when the order of elements is
  * not important, this parameter should be set to @c false.
  *
- * The @p array can be @c NULL in which case the iterator will be immediately
+ * The @p array can be @c NULL, in which case the iterator will be immediately
  * initialized such that #cxIteratorValid() returns @c false.
  *
  *
@@ -268,9 +272,9 @@ CxIterator cxMutIterator(
  * Creates an iterator for the specified plain pointer array.
  *
  * This iterator assumes that every element in the array is a pointer
- * and yields exactly those pointers during iteration (while in contrast
- * an iterator created with cxIterator() would return the addresses
- * of those pointers within the array).
+ * and yields exactly those pointers during iteration (on the other
+ * hand, an iterator created with cxIterator() would return the
+ * addresses of those pointers within the array).
  *
  * @param array a pointer to the array (can be @c NULL)
  * @param elem_count the number of elements in the array
index ee09ec1d72cdeb8884334bc03a2fdcdd53635407..597941db76a05ce06462b5c05a69163f82409759 100644 (file)
@@ -90,11 +90,11 @@ enum cx_json_token_type {
      */
     CX_JSON_TOKEN_STRING,
     /**
-     * A number token that can be represented as integer.
+     * A number token that can be represented as an integer.
      */
     CX_JSON_TOKEN_INTEGER,
     /**
-     * A number token that cannot be represented as integer.
+     * A number token that cannot be represented as an integer.
      */
     CX_JSON_TOKEN_NUMBER,
     /**
@@ -196,11 +196,11 @@ typedef struct cx_json_object_s CxJsonObject;
  */
 typedef struct cx_mutstr_s CxJsonString;
 /**
- * Type alias for a number that can be represented as 64-bit signed integer.
+ * Type alias for a number that can be represented as 64-bit signed integer.
  */
 typedef int64_t CxJsonInteger;
 /**
- * Type alias for number that is not an integer.
+ * Type alias for number that is not an integer.
  */
 typedef double CxJsonNumber;
 /**
@@ -272,27 +272,27 @@ struct cx_json_value_s {
      */
     union {
         /**
-         * The array data if type is #CX_JSON_ARRAY.
+         * The array data if the type is #CX_JSON_ARRAY.
          */
         CxJsonArray array;
         /**
-         * The object data if type is #CX_JSON_OBJECT.
+         * The object data if the type is #CX_JSON_OBJECT.
          */
         CxJsonObject object;
         /**
-         * The string data if type is #CX_JSON_STRING.
+         * The string data if the type is #CX_JSON_STRING.
          */
         CxJsonString string;
         /**
-         * The integer if type is #CX_JSON_INTEGER.
+         * The integer if the type is #CX_JSON_INTEGER.
          */
         CxJsonInteger integer;
         /**
-         * The number if type is #CX_JSON_NUMBER.
+         * The number if the type is #CX_JSON_NUMBER.
          */
         CxJsonNumber number;
         /**
-         * The literal type if type is #CX_JSON_LITERAL.
+         * The literal type if the type is #CX_JSON_LITERAL.
          */
         CxJsonLiteral literal;
     } value;
@@ -377,7 +377,7 @@ struct cx_json_s {
 };
 
 /**
- * Status codes for the json interface.
+ * Status codes for the JSON interface.
  */
 enum cx_json_status {
     /**
@@ -391,7 +391,7 @@ enum cx_json_status {
     /**
      * The input ends unexpectedly.
      *
-     * Refill the buffer with cxJsonFill() to complete the json data.
+     * Refill the buffer with cxJsonFill() to complete the JSON data.
      */
     CX_JSON_INCOMPLETE_DATA,
     /**
@@ -400,7 +400,7 @@ enum cx_json_status {
      * You can use this enumerator to check for all "good" status results
      * by checking if the status is less than @c CX_JSON_OK.
      *
-     * A "good" status means, that you can refill data and continue parsing.
+     * A "good" status means that you can refill data and continue parsing.
      */
     CX_JSON_OK,
     /**
@@ -412,7 +412,7 @@ enum cx_json_status {
      */
     CX_JSON_BUFFER_ALLOC_FAILED,
     /**
-     * Allocating memory for a json value failed.
+     * Allocating memory for a JSON value failed.
      */
     CX_JSON_VALUE_ALLOC_FAILED,
     /**
@@ -426,7 +426,7 @@ enum cx_json_status {
 };
 
 /**
- * Typedef for the json status enum.
+ * Typedef for the JSON status enum.
  */
 typedef enum cx_json_status CxJsonStatus;
 
@@ -445,7 +445,7 @@ struct cx_json_writer_s {
     /**
      * The maximum number of fractional digits in a number value.
      * The default value is 6 and values larger than 15 are reduced to 15.
-     * Note, that the actual number of digits may be lower, depending on the concrete number.
+     * Note that the actual number of digits may be lower, depending on the concrete number.
      */
     uint8_t frac_max_digits;
     /**
@@ -465,7 +465,7 @@ struct cx_json_writer_s {
 };
 
 /**
- * Typedef for the json writer.
+ * Typedef for the JSON writer.
  */
 typedef struct cx_json_writer_s CxJsonWriter;
 
@@ -496,9 +496,8 @@ CxJsonWriter cxJsonWriterPretty(bool use_spaces);
  * that the data is only partially written when an error occurs with no
  * way to indicate how much data was written.
  * To avoid this problem, you can use a CxBuffer as @p target which is
- * unlikely to fail a write operation and either use the buffer's flush
- * feature to relay the data or use the data in the buffer manually to
- * write it to the actual target.
+ * unlikely to fail a write operation. You can, for example, use the buffer's flush
+ * feature to relay the data.
  *
  * @param target the buffer or stream where to write to
  * @param value the value that shall be written
@@ -517,9 +516,9 @@ int cxJsonWrite(
 );
 
 /**
- * Initializes the json interface.
+ * Initializes the JSON interface.
  *
- * @param json the json interface
+ * @param json the JSON interface
  * @param allocator the allocator that shall be used for the produced values
  * @see cxJsonDestroy()
  */
@@ -528,9 +527,9 @@ cx_attr_export
 void cxJsonInit(CxJson *json, const CxAllocator *allocator);
 
 /**
- * Destroys the json interface.
+ * Destroys the JSON interface.
  *
- * @param json the json interface
+ * @param json the JSON interface
  * @see cxJsonInit()
  */
 cx_attr_nonnull
@@ -538,12 +537,12 @@ cx_attr_export
 void cxJsonDestroy(CxJson *json);
 
 /**
- * Destroys and re-initializes the json interface.
+ * Destroys and re-initializes the JSON interface.
  *
- * You might want to use this, to reset the parser after
+ * You might want to use this to reset the parser after
  * encountering a syntax error.
  *
- * @param json the json interface
+ * @param json the JSON interface
  */
 cx_attr_nonnull
 static inline void cxJsonReset(CxJson *json) {
@@ -563,7 +562,7 @@ static inline void cxJsonReset(CxJson *json) {
  * the additional data is appended - inevitably leading to
  * an allocation of a new buffer and copying the previous contents.
  *
- * @param json the json interface
+ * @param json the JSON interface
  * @param buf the source buffer
  * @param len the length of the source buffer
  * @retval zero success
@@ -575,36 +574,20 @@ cx_attr_access_r(2, 3)
 cx_attr_export
 int cxJsonFilln(CxJson *json, const char *buf, size_t len);
 
-#ifdef __cplusplus
-} // extern "C"
-
-cx_attr_nonnull
-static inline int cxJsonFill(
-        CxJson *json,
-        cxstring str
-) {
-    return cxJsonFilln(json, str.ptr, str.length);
-}
 
+/**
+ * Internal function, do not use.
+ *
+ * @param json the JSON interface
+ * @param str the string
+ * @retval zero success
+ * @retval non-zero internal allocation error
+ */
 cx_attr_nonnull
-static inline int cxJsonFill(
-        CxJson *json,
-        cxmutstr str
-) {
+static inline int cx_json_fill(CxJson *json, cxstring str) {
     return cxJsonFilln(json, str.ptr, str.length);
 }
 
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cxJsonFill(
-        CxJson *json,
-        const char *str
-) {
-    return cxJsonFilln(json, str, strlen(str));
-}
-
-extern "C" {
-#else // __cplusplus
 /**
  * Fills the input buffer.
  *
@@ -616,53 +599,13 @@ extern "C" {
  * the additional data is appended - inevitably leading to
  * an allocation of a new buffer and copying the previous contents.
  *
- * @param json the json interface
+ * @param json the JSON interface
  * @param str the source string
  * @retval zero success
  * @retval non-zero internal allocation error
  * @see cxJsonFilln()
  */
-#define cxJsonFill(json, str) _Generic((str), \
-    cxstring: cx_json_fill_cxstr,             \
-    cxmutstr: cx_json_fill_mutstr,            \
-    char*: cx_json_fill_str,                  \
-    const char*: cx_json_fill_str)            \
-    (json, str)
-
-/**
- * @copydoc cxJsonFill()
- */
-cx_attr_nonnull
-static inline int cx_json_fill_cxstr(
-        CxJson *json,
-        cxstring str
-) {
-    return cxJsonFilln(json, str.ptr, str.length);
-}
-
-/**
- * @copydoc cxJsonFill()
- */
-cx_attr_nonnull
-static inline int cx_json_fill_mutstr(
-        CxJson *json,
-        cxmutstr str
-) {
-    return cxJsonFilln(json, str.ptr, str.length);
-}
-
-/**
- * @copydoc cxJsonFill()
- */
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cx_json_fill_str(
-        CxJson *json,
-        const char *str
-) {
-    return cxJsonFilln(json, str, strlen(str));
-}
-#endif
+#define cxJsonFill(json, str) cx_json_fill(json, cx_strcast(str))
 
 /**
  * Creates a new (empty) JSON object.
@@ -973,8 +916,8 @@ CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral
  * Recursively deallocates the memory of a JSON value.
  *
  * @remark The type of each deallocated value will be changed
- * to #CX_JSON_NOTHING and values of such type will be skipped
- * by the de-allocation. That means, this function protects
+ * to #CX_JSON_NOTHING, and values of such a type will be skipped
+ * by the deallocation. That means this function protects
  * you from double-frees when you are accidentally freeing
  * a nested value and then the parent value (or vice versa).
  *
@@ -993,7 +936,7 @@ void cxJsonValueFree(CxJsonValue *value);
  * add the missing data with another invocation of cxJsonFill()
  * and then repeat the call to cxJsonNext().
  *
- * @param json the json interface
+ * @param json the JSON interface
  * @param value a pointer where the next value shall be stored
  * @retval CX_JSON_NO_ERROR successfully retrieve the @p value
  * @retval CX_JSON_NO_DATA there is no (more) data in the buffer to read from
@@ -1049,7 +992,7 @@ static inline bool cxJsonIsString(const CxJsonValue *value) {
 /**
  * Checks if the specified value is a JSON number.
  *
- * This function will return true for both floating point and
+ * This function will return true for both floating-point and
  * integer numbers.
  *
  * @param value a pointer to the value
@@ -1109,7 +1052,7 @@ static inline bool cxJsonIsBool(const CxJsonValue *value) {
 /**
  * Checks if the specified value is @c true.
  *
- * @remark Be advised, that this is not the same as
+ * @remark Be advised that this is different from
  * testing @c !cxJsonIsFalse(v).
  *
  * @param value a pointer to the value
@@ -1126,7 +1069,7 @@ static inline bool cxJsonIsTrue(const CxJsonValue *value) {
 /**
  * Checks if the specified value is @c false.
  *
- * @remark Be advised, that this is not the same as
+ * @remark Be advised that this is different from
  * testing @c !cxJsonIsTrue(v).
  *
  * @param value a pointer to the value
@@ -1197,7 +1140,7 @@ static inline cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) {
 }
 
 /**
- * Obtains a double-precision floating point value from the given JSON value.
+ * Obtains a double-precision floating-point value from the given JSON value.
  *
  * If the @p value is not a JSON number, the behavior is undefined.
  *
@@ -1283,6 +1226,23 @@ cx_attr_returns_nonnull
 cx_attr_export
 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
 
+/**
+ * Removes an element from a JSON array.
+ *
+ * If the @p value is not a JSON array, the behavior is undefined.
+ *
+ * This function, in contrast to cxJsonArrayGet(), returns @c NULL
+ * when the index is out of bounds.
+ *
+ * @param value the JSON value
+ * @param index the index in the array
+ * @return the removed value from the specified index or @c NULL when the index was out of bounds
+ * @see cxJsonIsArray()
+ */
+cx_attr_nonnull
+cx_attr_export
+CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index);
+
 /**
  * Returns an iterator over the JSON array elements.
  *
@@ -1317,30 +1277,16 @@ cx_attr_export
 CxIterator cxJsonObjIter(const CxJsonValue *value);
 
 /**
- * @copydoc cxJsonObjGet()
+ * Internal function, do not use.
+ * @param value the JSON object
+ * @param name the key to look up
+ * @return the value corresponding to the key
  */
 cx_attr_nonnull
 cx_attr_returns_nonnull
 cx_attr_export
-CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name);
-
-#ifdef __cplusplus
-} // extern "C"
-
-static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) {
-    return cx_json_obj_get_cxstr(value, name);
-}
-
-static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) {
-    return cx_json_obj_get_cxstr(value, cx_strcast(name));
-}
-
-static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) {
-    return cx_json_obj_get_cxstr(value, cx_str(name));
-}
+CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name);
 
-extern "C" {
-#else
 /**
  * Returns a value corresponding to a key in a JSON object.
  *
@@ -1355,32 +1301,32 @@ extern "C" {
  * @return the value corresponding to the key
  * @see cxJsonIsObject()
  */
-#define cxJsonObjGet(value, name) _Generic((name), \
-        cxstring: cx_json_obj_get_cxstr,           \
-        cxmutstr: cx_json_obj_get_mutstr,          \
-        char*: cx_json_obj_get_str,                \
-        const char*: cx_json_obj_get_str)          \
-        (value, name)
+#define cxJsonObjGet(value, name) cx_json_obj_get(value, cx_strcast(name))
 
 /**
- * @copydoc cxJsonObjGet()
+ * Internal function, do not use.
+ * @param value the JSON object
+ * @param name the key to look up
+ * @return the value corresponding to the key or @c NULL when the key is not part of the object
  */
 cx_attr_nonnull
-cx_attr_returns_nonnull
-static inline CxJsonValue *cx_json_obj_get_mutstr(const CxJsonValue *value, cxmutstr name) {
-    return cx_json_obj_get_cxstr(value, cx_strcast(name));
-}
+cx_attr_export
+CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name);
 
 /**
- * @copydoc cxJsonObjGet()
+ * Removes and returns a value corresponding to a key in a JSON object.
+ *
+ * If the @p value is not a JSON object, the behavior is undefined.
+ *
+ * This function, in contrast to cxJsonObjGet() returns @c NULL when the
+ * object does not contain @p name.
+ *
+ * @param value the JSON object
+ * @param name the key to look up
+ * @return the value corresponding to the key or @c NULL when the key is not part of the object
+ * @see cxJsonIsObject()
  */
-cx_attr_nonnull
-cx_attr_returns_nonnull
-cx_attr_cstr_arg(2)
-static inline CxJsonValue *cx_json_obj_get_str(const CxJsonValue *value, const char *name) {
-    return cx_json_obj_get_cxstr(value, cx_str(name));
-}
-#endif
+#define cxJsonObjRemove(value, name) cx_json_obj_remove(value, cx_strcast(name))
 
 #ifdef __cplusplus
 }
diff --git a/ucx/cx/kv_list.h b/ucx/cx/kv_list.h
new file mode 100644 (file)
index 0000000..ebcb218
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @file kv_list.h
+ * @brief Linked list implementation with key/value-lookup.
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ * @copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_KV_LIST_H
+#define UCX_KV_LIST_H
+
+#include "common.h"
+#include "list.h"
+#include "map.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
+ *
+ * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
+ * copies of the added elements, and the compare function will be automatically set
+ * to cx_cmp_ptr() if none is given.
+ *
+ * After creating the list, it can also be used as a map after converting the pointer
+ * to a CxMap pointer with cxKvListAsMap().
+ * When you want to use the list interface again, you can also convert the map pointer back
+ * with cxKvListAsList().
+ *
+ * @param allocator the allocator for allocating the list nodes
+ * (if @c NULL, the cxDefaultAllocator will be used)
+ * @param comparator the comparator for the elements
+ * (if @c NULL, and the list is not storing pointers, sort and find
+ * functions will not work)
+ * @param elem_size the size of each element in bytes
+ * @return the created list
+ * @see cxKvListAsMap()
+ * @see cxKvListAsList()
+ */
+cx_attr_nodiscard
+cx_attr_malloc
+cx_attr_dealloc(cxListFree, 1)
+cx_attr_export
+CxList *cxKvListCreate(
+        const CxAllocator *allocator,
+        cx_compare_func comparator,
+        size_t elem_size
+);
+
+/**
+ * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
+ *
+ * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
+ * copies of the added elements, and the compare function will be automatically set
+ * to cx_cmp_ptr() if none is given.
+ *
+ * This function creates the list with cxKvListCreate() and immediately applies
+ * cxKvListAsMap(). If you want to use the returned object as a list, you can call
+ * cxKvListAsList() later.
+ *
+ * @param allocator the allocator for allocating the list nodes
+ * (if @c NULL, the cxDefaultAllocator will be used)
+ * @param comparator the comparator for the elements
+ * (if @c NULL, and the list is not storing pointers, sort and find
+ * functions will not work)
+ * @param elem_size the size of each element in bytes
+ * @return the created list wrapped into the CxMap interface
+ * @see cxKvListAsMap()
+ * @see cxKvListAsList()
+ */
+cx_attr_nodiscard
+cx_attr_malloc
+cx_attr_dealloc(cxMapFree, 1)
+cx_attr_export
+CxMap *cxKvListCreateAsMap(
+        const CxAllocator *allocator,
+        cx_compare_func comparator,
+        size_t elem_size
+);
+
+/**
+ * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
+ *
+ * The list will use cxDefaultAllocator and no comparator function. If you want
+ * to call functions that need a comparator, you must either set one immediately
+ * after list creation or use cxKvListCreate().
+ *
+ * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
+ * copies of the added elements, and the compare function will be automatically set
+ * to cx_cmp_ptr().
+ *
+ * After creating the list, it can also be used as a map after converting the pointer
+ * to a CxMap pointer with cxKvListAsMap().
+ * When you want to use the list interface again, you can also convert the map pointer back
+ * with cxKvListAsList().
+ *
+ * @param elem_size (@c size_t) the size of each element in bytes
+ * @return (@c CxList*) the created list
+ * @see cxKvListAsMap()
+ * @see cxKvListAsList()
+ */
+#define cxKvListCreateSimple(elem_size) cxKvListCreate(NULL, NULL, elem_size)
+
+/**
+ * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
+ *
+ * The list will use cxDefaultAllocator and no comparator function. If you want
+ * to call functions that need a comparator, you must either set one immediately
+ * after list creation or use cxKvListCreate().
+ *
+ * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
+ * copies of the added elements, and the compare function will be automatically set
+ * to cx_cmp_ptr().
+ *
+ * This macro behaves as if the list was created with cxKvListCreateSimple() and
+ * immediately followed up by cxKvListAsMap().
+ * If you want to use the returned object as a list, you can call cxKvListAsList() later.
+ *
+ * @param elem_size (@c size_t) the size of each element in bytes
+ * @return (@c CxMap*) the created list wrapped into the CxMap interface
+ * @see cxKvListAsMap()
+ * @see cxKvListAsList()
+ */
+#define cxKvListCreateAsMapSimple(elem_size) cxKvListCreateAsMap(NULL, NULL, elem_size)
+
+/**
+ * Converts a map pointer belonging to a key-value-List back to the original list pointer.
+ *
+ * @param map a map pointer that was returned by a call to cxKvListAsMap()
+ * @return the original list pointer
+ */
+cx_attr_nodiscard
+cx_attr_nonnull
+cx_attr_returns_nonnull
+cx_attr_export
+CxList *cxKvListAsList(CxMap *map);
+
+/**
+ * Converts a map pointer belonging to a key-value-List back to the original list pointer.
+ *
+ * @param list a list created by cxKvListCreate() or cxKvListCreateSimple()
+ * @return a map pointer that lets you use the list as if it was a map
+ */
+cx_attr_nodiscard
+cx_attr_nonnull
+cx_attr_returns_nonnull
+cx_attr_export
+CxMap *cxKvListAsMap(CxList *list);
+
+/**
+ * Sets or updates the key of a list item.
+ *
+ * This is, for example, useful when you have inserted an element using the CxList interface,
+ * and now you want to associate this element with a key.
+ *
+ * @param list the list
+ * @param index the index of the element in the list
+ * @param key the key
+ * @retval zero success
+ * @retval non-zero memory allocation failure or the index is out of bounds
+ * @see cxKvListSetKey()
+ */
+cx_attr_nonnull
+cx_attr_export
+int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key);
+
+/**
+ * Inserts an item into the list at the specified index and associates it with the specified key.
+ *
+ * @param list the list
+ * @param index the index the inserted element shall have
+ * @param key the key
+ * @param value the value
+ * @retval zero success
+ * @retval non-zero memory allocation failure or the index is out of bounds
+ * @see cxKvListInsert()
+ */
+cx_attr_nonnull
+cx_attr_export
+int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value);
+
+/**
+ * Sets or updates the key of a list item.
+ *
+ * This is, for example, useful when you have inserted an element using the CxList interface,
+ * and now you want to associate this element with a key.
+ *
+ * @param list (@c CxList*) the list
+ * @param index (@c size_t) the index of the element in the list
+ * @param key (any supported key type) the key
+ * @retval zero success
+ * @retval non-zero memory allocation failure or the index is out of bounds
+ * @see CX_HASH_KEY()
+ */
+#define cxKvListSetKey(list, index, key) cx_kv_list_set_key(list, index, CX_HASH_KEY(key))
+
+/**
+ * Inserts an item into the list at the specified index and associates it with the specified key.
+ *
+ * @param list (@c CxList*) the list
+ * @param index (@c size_t) the index the inserted element shall have
+ * @param key (any supported key type) the key
+ * @param value (@c void*) the value
+ * @retval zero success
+ * @retval non-zero memory allocation failure or the index is out of bounds
+ * @see CX_HASH_KEY()
+ */
+#define cxKvListInsert(list, index, key, value) cx_kv_list_insert(list, index, CX_HASH_KEY(key), value)
+
+
+/**
+ * Removes the key of a list item.
+ *
+ * This can be useful if you want to explicitly remove an item from the lookup map.
+ *
+ * If no key is associated with the item, nothing happens, and this function returns zero.
+ *
+ * @param list the list
+ * @param index the index of the element in the list
+ * @retval zero success
+ * @retval non-zero the index is out of bounds
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxKvListRemoveKey(CxList *list, size_t index);
+
+/**
+ * Returns the key of a list item.
+ *
+ * @param list the list
+ * @param index the index of the element in the list
+ * @return a pointer to the key or @c NULL when the index is out of bounds or the item does not have a key
+ */
+cx_attr_nonnull
+cx_attr_export
+const CxHashKey *cxKvListGetKey(CxList *list, size_t index);
+
+/**
+ * Adds an item into the list and associates it with the specified key.
+ *
+ * @param list (@c CxList*) the list
+ * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @param value (@c void*) the value
+ * @retval zero success
+ * @retval non-zero memory allocation failure
+ */
+#define cxKvListAdd(list, key, value) cxKvListInsert(list, (list)->collection.size, key, value)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_KV_LIST_H
index 3da2cae45b759b1b81b62ce3bf91bb2637ff4c50..8efec3abbb7af64f95560e734814cf673581ba6f 100644 (file)
 extern "C" {
 #endif
 
+/**
+ * Metadata for a linked list.
+ */
+typedef struct cx_linked_list_s {
+    /** Base members. */
+    struct cx_list_s base;
+    /**
+     * Location of the prev pointer (mandatory).
+     */
+    off_t loc_prev;
+    /**
+     * Location of the next pointer (mandatory).
+     */
+    off_t loc_next;
+    /**
+     * Location of the payload (mandatory).
+     */
+    off_t loc_data;
+    /**
+     * Additional bytes to allocate @em behind the payload (e.g. for metadata).
+     */
+    size_t extra_data_len;
+    /**
+     * Pointer to the first node.
+     */
+    void *begin;
+    /**
+     * Pointer to the last node.
+     */
+    void *end;
+} cx_linked_list;
+
 /**
  * Allocates a linked list for storing elements with @p elem_size bytes each.
  *
  * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
- * copies of the added elements and the compare function will be automatically set
- * to cx_cmp_ptr(), if none is given.
+ * copies of the added elements, and the compare function will be automatically set
+ * to cx_cmp_ptr() if none is given.
  *
  * @param allocator the allocator for allocating the list nodes
  * (if @c NULL, the cxDefaultAllocator will be used)
@@ -76,7 +108,7 @@ CxList *cxLinkedListCreate(
  * after list creation or use cxLinkedListCreate().
  *
  * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
- * copies of the added elements and the compare function will be automatically set
+ * copies of the added elements, and the compare function will be automatically set
  * to cx_cmp_ptr().
  *
  * @param elem_size (@c size_t) the size of each element in bytes
@@ -89,12 +121,12 @@ CxList *cxLinkedListCreate(
  * Finds the node at a certain index.
  *
  * This function can be used to start at an arbitrary position within the list.
- * If the search index is large than the start index, @p loc_advance must denote
- * the location of some sort of @c next pointer (i.e. a pointer to the next node).
+ * If the search index is larger than the start index, @p loc_advance must denote
+ * the location of a @c next pointer (i.e., a pointer to the next node).
  * But it is also possible that the search index is smaller than the start index
- * (e.g. in cases where traversing a list backwards is faster) in which case
- * @p loc_advance must denote the location of some sort of @c prev pointer
- * (i.e. a pointer to the previous node).
+ * (e.g., in cases where traversing a list backwards is faster).
+ * In that case @p loc_advance must denote the location of a @c prev pointer
+ * (i.e., a pointer to the previous node).
  *
  * @param start a pointer to the start node
  * @param start_index the start index
@@ -122,7 +154,7 @@ void *cx_linked_list_at(
  * @param elem a pointer to the element to find
  * @param found_index an optional pointer where the index of the found node
  * (given that @p start has index 0) is stored
- * @return the index of the element, if found - unspecified if not found
+ * @return a pointer to the found node or @c NULL if no matching node was found
  */
 cx_attr_nonnull_arg(1, 4, 5)
 cx_attr_export
@@ -193,7 +225,7 @@ void *cx_linked_list_prev(
 
 /**
  * Adds a new node to a linked list.
- * The node must not be part of any list already.
+ * The node must not be part of any list yet.
  *
  * @remark One of the pointers @p begin or @p end may be @c NULL, but not both.
  *
@@ -215,7 +247,7 @@ void cx_linked_list_add(
 
 /**
  * Prepends a new node to a linked list.
- * The node must not be part of any list already.
+ * The node must not be part of any list yet.
  *
  * @remark One of the pointers @p begin or @p end may be @c NULL, but not both.
  *
@@ -273,7 +305,7 @@ void cx_linked_list_unlink(
 
 /**
  * Inserts a new node after a given node of a linked list.
- * The new node must not be part of any list already.
+ * The new node must not be part of any list yet.
  *
  * @note If you specify @c NULL as the @p node to insert after, this function needs either the @p begin or
  * the @p end pointer to determine the start of the list. Then the new node will be prepended to the list.
@@ -298,7 +330,7 @@ void cx_linked_list_insert(
 
 /**
  * Inserts a chain of nodes after a given node of a linked list.
- * The chain must not be part of any list already.
+ * The chain must not be part of any list yet.
  *
  * If you do not explicitly specify the end of the chain, it will be determined by traversing
  * the @c next pointer.
@@ -330,7 +362,7 @@ void cx_linked_list_insert_chain(
 
 /**
  * Inserts a node into a sorted linked list.
- * The new node must not be part of any list already.
+ * The new node must not be part of any list yet.
  *
  * If the list starting with the node pointed to by @p begin is not sorted
  * already, the behavior is undefined.
@@ -355,14 +387,14 @@ void cx_linked_list_insert_sorted(
 
 /**
  * Inserts a chain of nodes into a sorted linked list.
- * The chain must not be part of any list already.
+ * The chain must not be part of any list yet.
  *
  * If either the list starting with the node pointed to by @p begin or the list
  * starting with @p insert_begin is not sorted, the behavior is undefined.
  *
  * @attention In contrast to cx_linked_list_insert_chain(), the source chain
  * will be broken and inserted into the target list so that the resulting list
- * will be sorted according to @p cmp_func. That means, each node in the source
+ * will be sorted according to @p cmp_func. That means each node in the source
  * chain may be re-linked with nodes from the target list.
  *
  * @param begin a pointer to the beginning node pointer (required)
@@ -383,10 +415,67 @@ void cx_linked_list_insert_sorted_chain(
         cx_compare_func cmp_func
 );
 
+/**
+ * Inserts a node into a sorted linked list if no other node with the same value already exists.
+ * The new node must not be part of any list yet.
+ *
+ * If the list starting with the node pointed to by @p begin is not sorted
+ * already, the behavior is undefined.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param new_node a pointer to the node that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @retval zero when the node was inserted
+ * @retval non-zero when a node with the same value already exists
+ */
+cx_attr_nonnull_arg(1, 5, 6)
+cx_attr_export
+int cx_linked_list_insert_unique(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *new_node,
+        cx_compare_func cmp_func
+);
+
+/**
+ * Inserts a chain of nodes into a sorted linked list, avoiding duplicates.
+ * The chain must not be part of any list yet.
+ *
+ * If either the list starting with the node pointed to by @p begin or the list
+ * starting with @p insert_begin is not sorted, the behavior is undefined.
+ *
+ * @attention In contrast to cx_linked_list_insert_sorted(), not all nodes of the
+ * chain might be added. This function returns a new chain consisting of all the duplicates.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param insert_begin a pointer to the first node of the chain that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @return a pointer to a new chain with all duplicates that were not inserted (or @c NULL when there were no duplicates)
+ */
+cx_attr_nonnull_arg(1, 5, 6)
+cx_attr_nodiscard
+cx_attr_export
+void *cx_linked_list_insert_unique_chain(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *insert_begin,
+        cx_compare_func cmp_func
+);
+
 /**
  * Removes a chain of nodes from the linked list.
  *
- * If one of the nodes to remove is the beginning (resp. end) node of the list and if @p begin (resp. @p end)
+ * If one of the nodes to remove is the beginning (resp. end) node of the list, and if @p begin (resp. @p end)
  * addresses are provided, the pointers are adjusted accordingly.
  *
  * The following combinations of arguments are valid (more arguments are optional):
@@ -418,7 +507,7 @@ size_t cx_linked_list_remove_chain(
 /**
  * Removes a node from the linked list.
  *
- * If the node to remove is the beginning (resp. end) node of the list and if @p begin (resp. @p end)
+ * If the node to remove is the beginning (resp. end) node of the list, and if @p begin (resp. @p end)
  * addresses are provided, the pointers are adjusted accordingly.
  *
  * The following combinations of arguments are valid (more arguments are optional):
@@ -496,7 +585,7 @@ void cx_linked_list_sort(
 /**
  * Compares two lists element wise.
  *
- * @attention Both list must have the same structure.
+ * @attention Both lists must have the same structure.
  *
  * @param begin_left the beginning of the left list (@c NULL denotes an empty list)
  * @param begin_right the beginning of the right list  (@c NULL denotes an empty list)
index 14ddc043c2f86a95a8b4b0cb3c8a1e1b09a3c2d2..682d5b6f821f890d68292a70f2c5558485ec275a 100644 (file)
@@ -39,6 +39,8 @@
 #include "common.h"
 #include "collection.h"
 
+#include <assert.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -80,8 +82,8 @@ struct cx_list_class_s {
 
     /**
      * Member function for inserting a single element.
-     * The data pointer may be @c NULL in which case the function shall only allocate memory.
-     * Returns a pointer to the data of the inserted element.
+     * The data pointer may be @c NULL, in which case the function shall only allocate memory.
+     * Returns a pointer to the allocated memory or @c NULL if allocation fails.
      */
     void *(*insert_element)(
             struct cx_list_s *list,
@@ -112,6 +114,17 @@ struct cx_list_class_s {
             size_t n
     );
 
+    /**
+     * Member function for inserting multiple elements if they do not exist.
+     *
+     * @see cx_list_default_insert_unique()
+     */
+    size_t (*insert_unique)(
+            struct cx_list_s *list,
+            const void *sorted_data,
+            size_t n
+    );
+
     /**
      * Member function for inserting an element relative to an iterator position.
      */
@@ -181,9 +194,8 @@ struct cx_list_class_s {
     /**
      * Optional member function for comparing this list
      * to another list of the same type.
-     * If set to @c NULL, comparison won't be optimized.
+     * If set to @c NULL, the comparison won't be optimized.
      */
-    cx_attr_nonnull
     int (*compare)(
             const struct cx_list_s *list,
             const struct cx_list_s *other
@@ -214,7 +226,7 @@ typedef struct cx_list_s CxList;
  *
  * Writing to that list is not allowed.
  *
- * You can use this is a placeholder for initializing CxList pointers
+ * You can use this as a placeholder for initializing CxList pointers
  * for which you do not want to reserve memory right from the beginning.
  */
 cx_attr_export
@@ -267,6 +279,30 @@ size_t cx_list_default_insert_sorted(
         size_t n
 );
 
+/**
+ * Default implementation of an array insert where only elements are inserted when they don't exist in the list.
+ *
+ * This function is similar to cx_list_default_insert_sorted(), except it skips elements that are already in the list.
+ *
+ * @note The return value of this function denotes the number of elements from the @p sorted_data that are definitely
+ * contained in the list after completing the call. It is @em not the number of elements that were newly inserted.
+ * That means, when no error occurred, the return value should be @p n.
+ *
+ * Use this in your own list class if you do not want to implement an optimized version for your list.
+ *
+ * @param list the list
+ * @param sorted_data a pointer to the array of pre-sorted data to insert
+ * @param n the number of elements to insert
+ * @return the number of elements from the @p sorted_data that are definitely present in the list after this call
+ */
+cx_attr_nonnull
+cx_attr_export
+size_t cx_list_default_insert_unique(
+        struct cx_list_s *list,
+        const void *sorted_data,
+        size_t n
+);
+
 /**
  * Default unoptimized sort implementation.
  *
@@ -304,12 +340,12 @@ int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
  *
  * Only use this function if you are creating your own list implementation.
  * The purpose of this function is to be called in the initialization code
- * of your list, to set certain members correctly.
+ * of your list to set certain members correctly.
  *
  * This is particularly important when you want your list to support
  * #CX_STORE_POINTERS as @p elem_size. This function will wrap the list
  * class accordingly and make sure that you can implement your list as if
- * it was only storing objects and the wrapper will automatically enable
+ * it was only storing objects, and the wrapper will automatically enable
  * the feature of storing pointers.
  *
  * @par Example
@@ -391,7 +427,7 @@ static inline int cxListAdd(
  * If there is not enough memory to add all elements, the returned value is
  * less than @p n.
  *
- * If this list is storing pointers instead of objects @p array is expected to
+ * If this list is storing pointers instead of objects, @p array is expected to
  * be an array of pointers.
  *
  * @param list the list
@@ -412,7 +448,7 @@ static inline size_t cxListAddArray(
 /**
  * Inserts an item at the specified index.
  *
- * If @p index equals the list @c size, this is effectively cxListAdd().
+ * If the @p index equals the list @c size, this is effectively cxListAdd().
  *
  * @param list the list
  * @param index the index the element shall have
@@ -482,14 +518,36 @@ static inline int cxListInsertSorted(
         CxList *list,
         const void *elem
 ) {
-    list->collection.sorted = true; // guaranteed by definition
+    assert(list->collection.sorted || list->collection.size == 0);
+    list->collection.sorted = true;
     const void *data = list->collection.store_pointer ? &elem : elem;
     return list->cl->insert_sorted(list, data, 1) == 0;
 }
 
+/**
+ * Inserts an item into a sorted list if it does not exist.
+ *
+ * If the list is not sorted already, the behavior is undefined.
+ *
+ * @param list the list
+ * @param elem a pointer to the element to add
+ * @retval zero success (also when the element was already in the list)
+ * @retval non-zero memory allocation failure
+ */
+cx_attr_nonnull
+static inline int cxListInsertUnique(
+        CxList *list,
+        const void *elem
+) {
+    assert(list->collection.sorted || list->collection.size == 0);
+    list->collection.sorted = true;
+    const void *data = list->collection.store_pointer ? &elem : elem;
+    return list->cl->insert_unique(list, data, 1) == 0;
+}
+
 /**
  * Inserts multiple items to the list at the specified index.
- * If @p index equals the list size, this is effectively cxListAddArray().
+ * If the @p index equals the list size, this is effectively cxListAddArray().
  *
  * This method is usually more efficient than invoking cxListInsert()
  * multiple times.
@@ -497,7 +555,7 @@ static inline int cxListInsertSorted(
  * If there is not enough memory to add all elements, the returned value is
  * less than @p n.
  *
- * If this list is storing pointers instead of objects @p array is expected to
+ * If this list is storing pointers instead of objects, @p array is expected to
  * be an array of pointers.
  *
  * @param list the list
@@ -520,13 +578,13 @@ static inline size_t cxListInsertArray(
 /**
  * Inserts a sorted array into a sorted list.
  *
- * This method is usually more efficient than inserting each element separately,
+ * This method is usually more efficient than inserting each element separately
  * because consecutive chunks of sorted data are inserted in one pass.
  *
  * If there is not enough memory to add all elements, the returned value is
  * less than @p n.
  *
- * If this list is storing pointers instead of objects @p array is expected to
+ * If this list is storing pointers instead of objects, @p array is expected to
  * be an array of pointers.
  *
  * If the list is not sorted already, the behavior is undefined.
@@ -542,10 +600,50 @@ static inline size_t cxListInsertSortedArray(
         const void *array,
         size_t n
 ) {
-    list->collection.sorted = true; // guaranteed by definition
+    assert(list->collection.sorted || list->collection.size == 0);
+    list->collection.sorted = true;
     return list->cl->insert_sorted(list, array, n);
 }
 
+/**
+ * Inserts a sorted array into a sorted list, skipping duplicates.
+ *
+ * This method is usually more efficient than inserting each element separately
+ * because consecutive chunks of sorted data are inserted in one pass.
+ *
+ * If there is not enough memory to add all elements, the returned value is
+ * less than @p n.
+ *
+ * @note The return value of this function denotes the number of elements
+ * from the @p sorted_data that are definitely contained in the list after
+ * completing the call. It is @em not the number of elements that were newly
+ * inserted. That means, when no error occurred, the return value should
+ * be @p n.
+ *
+ * If this list is storing pointers instead of objects @p array is expected to
+ * be an array of pointers.
+ *
+ * If the list is not sorted already, the behavior is undefined.
+ * This is also the case when the @p array is not sorted.
+ *
+ * @param list the list
+ * @param array a pointer to the elements to add
+ * @param n the number of elements to add
+ * @return the number of added elements
+ *
+ * @return the number of elements from the @p sorted_data that are definitely present in the list after this call
+ */
+cx_attr_nonnull
+static inline size_t cxListInsertUniqueArray(
+        CxList *list,
+        const void *array,
+        size_t n
+) {
+    assert(list->collection.sorted || list->collection.size == 0);
+    list->collection.sorted = true;
+    return list->cl->insert_unique(list, array, n);
+}
+
 /**
  * Inserts an element after the current location of the specified iterator.
  *
@@ -650,7 +748,7 @@ static inline int cxListRemoveAndGet(
  * @param list the list
  * @param targetbuf a buffer where to copy the element
  * @retval zero success
- * @retval non-zero list is empty
+ * @retval non-zero the list is empty
  * @see cxListPopFront()
  * @see cxListRemoveAndGetLast()
  */
@@ -675,7 +773,7 @@ static inline int cxListRemoveAndGetFirst(
  * @param list (@c CxList*) the list
  * @param targetbuf (@c void*) a buffer where to copy the element
  * @retval zero success
- * @retval non-zero list is empty
+ * @retval non-zero the list is empty
  * @see cxListRemoveAndGetFirst()
  * @see cxListPop()
  */
@@ -692,7 +790,7 @@ static inline int cxListRemoveAndGetFirst(
  * @param list the list
  * @param targetbuf a buffer where to copy the element
  * @retval zero success
- * @retval non-zero list is empty
+ * @retval non-zero the list is empty
  */
 cx_attr_nonnull
 cx_attr_access_w(2)
@@ -716,7 +814,7 @@ static inline int cxListRemoveAndGetLast(
  * @param list (@c CxList*) the list
  * @param targetbuf (@c void*) a buffer where to copy the element
  * @retval zero success
- * @retval non-zero list is empty
+ * @retval non-zero the list is empty
  * @see cxListRemoveAndGetLast()
  * @see cxListPopFront()
  */
@@ -780,8 +878,8 @@ static inline size_t cxListRemoveArrayAndGet(
  */
 cx_attr_nonnull
 static inline void cxListClear(CxList *list) {
-    list->collection.sorted = true; // empty lists are always sorted
     list->cl->clear(list);
+    list->collection.sorted = true; // empty lists are always sorted
 }
 
 /**
@@ -872,18 +970,18 @@ int cxListSet(
  *
  * The returned iterator is position-aware.
  *
- * If the index is out of range, a past-the-end iterator will be returned.
+ * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
  *
  * @param list the list
  * @param index the index where the iterator shall point at
  * @return a new iterator
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 static inline CxIterator cxListIteratorAt(
         const CxList *list,
         size_t index
 ) {
+    if (list == NULL) list = cxEmptyList;
     return list->cl->iterator(list, index, false);
 }
 
@@ -892,18 +990,18 @@ static inline CxIterator cxListIteratorAt(
  *
  * The returned iterator is position-aware.
  *
- * If the index is out of range, a past-the-end iterator will be returned.
+ * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
  *
  * @param list the list
  * @param index the index where the iterator shall point at
  * @return a new iterator
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 static inline CxIterator cxListBackwardsIteratorAt(
         const CxList *list,
         size_t index
 ) {
+    if (list == NULL) list = cxEmptyList;
     return list->cl->iterator(list, index, true);
 }
 
@@ -912,13 +1010,12 @@ static inline CxIterator cxListBackwardsIteratorAt(
  *
  * The returned iterator is position-aware.
  *
- * If the index is out of range, a past-the-end iterator will be returned.
+ * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
  *
  * @param list the list
  * @param index the index where the iterator shall point at
  * @return a new iterator
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_export
 CxIterator cxListMutIteratorAt(
@@ -932,13 +1029,12 @@ CxIterator cxListMutIteratorAt(
  *
  * The returned iterator is position-aware.
  *
- * If the index is out of range, a past-the-end iterator will be returned.
+ * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
  *
  * @param list the list
  * @param index the index where the iterator shall point at
  * @return a new iterator
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_export
 CxIterator cxListMutBackwardsIteratorAt(
@@ -1032,7 +1128,7 @@ static inline size_t cxListFind(
 }
 
 /**
- * Checks, if the list contains the specified element.
+ * Checks if the list contains the specified element.
  *
  * The elements are compared with the list's comparator function.
  *
@@ -1137,7 +1233,7 @@ int cxListCompare(
  *
  * Also calls the content destructor functions for each element, if specified.
  *
- * @param list the list which shall be freed
+ * @param list the list that shall be freed
  */
 cx_attr_export
 void cxListFree(CxList *list);
index 36067d55e364f2a1bf975ead176cababb67a91ab..100ab8477753c8800abbf290d404408017a385a2 100644 (file)
@@ -185,8 +185,11 @@ struct cx_map_class_s {
 
     /**
      * Add or overwrite an element.
+     * If the @p value is @c NULL, the implementation
+     * shall only allocate memory instead of adding an existing value to the map.
+     * Returns a pointer to the allocated memory or @c NULL if allocation fails.
      */
-    int (*put)(
+    void *(*put)(
             CxMap *map,
             CxHashKey key,
             void *value
@@ -227,7 +230,7 @@ struct cx_map_class_s {
  *
  * Writing to that map is not allowed.
  *
- * You can use this is a placeholder for initializing CxMap pointers
+ * You can use this as a placeholder for initializing CxMap pointers
  * for which you do not want to reserve memory right from the beginning.
  */
 cx_attr_export
@@ -277,50 +280,50 @@ static inline size_t cxMapSize(const CxMap *map) {
  * @note An iterator iterates over all elements successively. Therefore, the order
  * highly depends on the map implementation and may change arbitrarily when the contents change.
  *
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
  * @return an iterator for the currently stored values
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 static inline CxMapIterator cxMapIteratorValues(const CxMap *map) {
+    if (map == NULL) map = cxEmptyMap;
     return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
 }
 
 /**
  * Creates a key iterator for a map.
  *
- * The elements of the iterator are keys of type CxHashKey and the pointer returned
+ * The elements of the iterator are keys of type CxHashKey, and the pointer returned
  * during iterator shall be treated as @c const @c CxHashKey* .
  *
  * @note An iterator iterates over all elements successively. Therefore, the order
  * highly depends on the map implementation and may change arbitrarily when the contents change.
  *
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
  * @return an iterator for the currently stored keys
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 static inline CxMapIterator cxMapIteratorKeys(const CxMap *map) {
+    if (map == NULL) map = cxEmptyMap;
     return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
 }
 
 /**
  * Creates an iterator for a map.
  *
- * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned
+ * The elements of the iterator are key/value pairs of type CxMapEntry, and the pointer returned
  * during iterator shall be treated as @c const @c CxMapEntry* .
  *
  * @note An iterator iterates over all elements successively. Therefore, the order
  * highly depends on the map implementation and may change arbitrarily when the contents change.
  *
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
  * @return an iterator for the currently stored entries
  * @see cxMapIteratorKeys()
  * @see cxMapIteratorValues()
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 static inline CxMapIterator cxMapIterator(const CxMap *map) {
+    if (map == NULL) map = cxEmptyMap;
     return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
 }
 
@@ -335,10 +338,9 @@ static inline CxMapIterator cxMapIterator(const CxMap *map) {
  * @note An iterator iterates over all elements successively. Therefore, the order
  * highly depends on the map implementation and may change arbitrarily when the contents change.
  *
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
  * @return an iterator for the currently stored values
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_export
 CxMapIterator cxMapMutIteratorValues(CxMap *map);
@@ -346,16 +348,15 @@ CxMapIterator cxMapMutIteratorValues(CxMap *map);
 /**
  * Creates a mutating iterator over the keys of a map.
  *
- * The elements of the iterator are keys of type CxHashKey and the pointer returned
+ * The elements of the iterator are keys of type CxHashKey, and the pointer returned
  * during iterator shall be treated as @c const @c CxHashKey* .
  *
  * @note An iterator iterates over all elements successively. Therefore, the order
  * highly depends on the map implementation and may change arbitrarily when the contents change.
  *
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
  * @return an iterator for the currently stored keys
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_export
 CxMapIterator cxMapMutIteratorKeys(CxMap *map);
@@ -363,176 +364,40 @@ CxMapIterator cxMapMutIteratorKeys(CxMap *map);
 /**
  * Creates a mutating iterator for a map.
  *
- * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned
+ * The elements of the iterator are key/value pairs of type CxMapEntry, and the pointer returned
  * during iterator shall be treated as @c const @c CxMapEntry* .
  *
  * @note An iterator iterates over all elements successively. Therefore, the order
  * highly depends on the map implementation and may change arbitrarily when the contents change.
  *
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
  * @return an iterator for the currently stored entries
  * @see cxMapMutIteratorKeys()
  * @see cxMapMutIteratorValues()
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_export
 CxMapIterator cxMapMutIterator(CxMap *map);
 
-#ifdef __cplusplus
-} // end the extern "C" block here, because we want to start overloading
-cx_attr_nonnull
-static inline int cxMapPut(
-        CxMap *map,
-        CxHashKey const &key,
-        void *value
-) {
-    return map->cl->put(map, key, value);
-}
-
-cx_attr_nonnull
-static inline int cxMapPut(
-        CxMap *map,
-        cxstring const &key,
-        void *value
-) {
-    return map->cl->put(map, cx_hash_key_cxstr(key), value);
-}
-
-cx_attr_nonnull
-static inline int cxMapPut(
-        CxMap *map,
-        cxmutstr const &key,
-        void *value
-) {
-    return map->cl->put(map, cx_hash_key_cxstr(key), value);
-}
-
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cxMapPut(
-        CxMap *map,
-        const char *key,
-        void *value
-) {
-    return map->cl->put(map, cx_hash_key_str(key), value);
-}
-
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxMapGet(
-        const CxMap *map,
-        CxHashKey const &key
-) {
-    return map->cl->get(map, key);
-}
-
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxMapGet(
-        const CxMap *map,
-        cxstring const &key
-) {
-    return map->cl->get(map, cx_hash_key_cxstr(key));
-}
-
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxMapGet(
-        const CxMap *map,
-        cxmutstr const &key
-) {
-    return map->cl->get(map, cx_hash_key_cxstr(key));
-}
-
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_cstr_arg(2)
-static inline void *cxMapGet(
-        const CxMap *map,
-        const char *key
-) {
-    return map->cl->get(map, cx_hash_key_str(key));
-}
-
-cx_attr_nonnull
-static inline int cxMapRemove(
-        CxMap *map,
-        CxHashKey const &key
-) {
-    return map->cl->remove(map, key, nullptr);
-}
-
-cx_attr_nonnull
-static inline int cxMapRemove(
-        CxMap *map,
-        cxstring const &key
-) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr);
-}
-
-cx_attr_nonnull
-static inline int cxMapRemove(
-        CxMap *map,
-        cxmutstr const &key
-) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr);
-}
-
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cxMapRemove(
-        CxMap *map,
-        const char *key
-) {
-    return map->cl->remove(map, cx_hash_key_str(key), nullptr);
-}
-
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cxMapRemoveAndGet(
-        CxMap *map,
-        CxHashKey key,
-        void *targetbuf
-) {
-    return map->cl->remove(map, key, targetbuf);
-}
-
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cxMapRemoveAndGet(
-        CxMap *map,
-        cxstring key,
-        void *targetbuf
-) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
-}
-
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cxMapRemoveAndGet(
-        CxMap *map,
-        cxmutstr key,
-        void *targetbuf
-) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
-}
-
-cx_attr_nonnull
-cx_attr_access_w(3)
-cx_attr_cstr_arg(2)
-static inline int cxMapRemoveAndGet(
-        CxMap *map,
-        const char *key,
-        void *targetbuf
-) {
-    return map->cl->remove(map, cx_hash_key_str(key), targetbuf);
-}
-
-#else // __cplusplus
-
 /**
- * @copydoc cxMapPut()
+ * Puts a key/value-pair into the map.
+ *
+ * A possible existing value will be overwritten.
+ * If destructor functions are specified, they are called for
+ * the overwritten element.
+ *
+ * If this map is storing pointers, the @p value pointer is written
+ * to the map. Otherwise, the memory is copied from @p value with
+ * memcpy().
+ *
+ * The @p key is always copied.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @retval zero success
+ * @retval non-zero value on memory allocation failure
+ * @see cxMapPut()
  */
 cx_attr_nonnull
 static inline int cx_map_put(
@@ -540,44 +405,7 @@ static inline int cx_map_put(
         CxHashKey key,
         void *value
 ) {
-    return map->cl->put(map, key, value);
-}
-
-/**
- * @copydoc cxMapPut()
- */
-cx_attr_nonnull
-static inline int cx_map_put_cxstr(
-        CxMap *map,
-        cxstring key,
-        void *value
-) {
-    return map->cl->put(map, cx_hash_key_cxstr(key), value);
-}
-
-/**
- * @copydoc cxMapPut()
- */
-cx_attr_nonnull
-static inline int cx_map_put_mustr(
-        CxMap *map,
-        cxmutstr key,
-        void *value
-) {
-    return map->cl->put(map, cx_hash_key_cxstr(key), value);
-}
-
-/**
- * @copydoc cxMapPut()
- */
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cx_map_put_str(
-        CxMap *map,
-        const char *key,
-        void *value
-) {
-    return map->cl->put(map, cx_hash_key_str(key), value);
+    return map->cl->put(map, key, value) == NULL;
 }
 
 /**
@@ -594,66 +422,81 @@ static inline int cx_map_put_str(
  * The @p key is always copied.
  *
  * @param map (@c CxMap*) the map
- * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @param key (any supported key type) the key
  * @param value (@c void*) the value
  * @retval zero success
  * @retval non-zero value on memory allocation failure
+ * @see CX_HASH_KEY()
  */
-#define cxMapPut(map, key, value) _Generic((key), \
-    CxHashKey: cx_map_put,                        \
-    cxstring: cx_map_put_cxstr,                   \
-    cxmutstr: cx_map_put_mustr,                   \
-    char*: cx_map_put_str,                        \
-    const char*: cx_map_put_str)                  \
-    (map, key, value)
+#define cxMapPut(map, key, value) cx_map_put(map, CX_HASH_KEY(key), value)
 
 /**
- * @copydoc cxMapGet()
+ * Allocates memory for a value in the map associated with the specified key.
+ *
+ * A possible existing value will be overwritten.
+ * If destructor functions are specified, they are called for
+ * the overwritten element.
+ *
+ * If the map is storing pointers, this function returns a @c void** pointer,
+ * meaning a pointer to that pointer.
+ *
+ * The @p key is always copied.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the pointer to the allocated memory or @c NULL if allocation fails
+ * @retval zero success
+ * @retval non-zero value on memory allocation failure
+ * @see cxMapEmplace()
  */
 cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cx_map_get(
-        const CxMap *map,
+static inline void *cx_map_emplace(
+        CxMap *map,
         CxHashKey key
 ) {
-    return map->cl->get(map, key);
-}
-
-/**
- * @copydoc cxMapGet()
- */
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cx_map_get_cxstr(
-        const CxMap *map,
-        cxstring key
-) {
-    return map->cl->get(map, cx_hash_key_cxstr(key));
+    return map->cl->put(map, key, NULL);
 }
 
 /**
- * @copydoc cxMapGet()
+ * Allocates memory for a value in the map associated with the specified key.
+ *
+ * A possible existing value will be overwritten.
+ * If destructor functions are specified, they are called for
+ * the overwritten element.
+ *
+ * If the map is storing pointers, this function returns a @c void** pointer,
+ * meaning a pointer to that pointer.
+ *
+ * The @p key is always copied.
+ *
+ * @param map (@c CxMap*) the map
+ * @param key (any supported key type) the key
+ * @return the pointer to the allocated memory or @c NULL if allocation fails
+ * @retval zero success
+ * @retval non-zero value on memory allocation failure
+ * @see CX_HASH_KEY()
  */
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cx_map_get_mustr(
-        const CxMap *map,
-        cxmutstr key
-) {
-    return map->cl->get(map, cx_hash_key_cxstr(key));
-}
+#define cxMapEmplace(map, key) cx_map_emplace(map, CX_HASH_KEY(key))
 
 /**
- * @copydoc cxMapGet()
+ * Retrieves a value by using a key.
+ *
+ * If this map is storing pointers, the stored pointer is returned.
+ * Otherwise, a pointer to the element within the map's memory
+ * is returned (which is valid as long as the element stays in the map).
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ * @see cxMapGet()
  */
 cx_attr_nonnull
 cx_attr_nodiscard
-cx_attr_cstr_arg(2)
-static inline void *cx_map_get_str(
+static inline void *cx_map_get(
         const CxMap *map,
-        const char *key
+        CxHashKey key
 ) {
-    return map->cl->get(map, cx_hash_key_str(key));
+    return map->cl->get(map, key);
 }
 
 /**
@@ -664,88 +507,29 @@ static inline void *cx_map_get_str(
  * is returned (which is valid as long as the element stays in the map).
  *
  * @param map (@c CxMap*) the map
- * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @param key (any supported key type) the key
  * @return (@c void*) the value
+ * @see CX_HASH_KEY()
  */
-#define cxMapGet(map, key) _Generic((key), \
-    CxHashKey: cx_map_get,                 \
-    cxstring: cx_map_get_cxstr,            \
-    cxmutstr: cx_map_get_mustr,            \
-    char*: cx_map_get_str,                 \
-    const char*: cx_map_get_str)           \
-    (map, key)
-
-/**
- * @copydoc cxMapRemove()
- */
-cx_attr_nonnull
-static inline int cx_map_remove(
-        CxMap *map,
-        CxHashKey key
-) {
-    return map->cl->remove(map, key, NULL);
-}
-
-/**
- * @copydoc cxMapRemove()
- */
-cx_attr_nonnull
-static inline int cx_map_remove_cxstr(
-        CxMap *map,
-        cxstring key
-) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), NULL);
-}
-
-/**
- * @copydoc cxMapRemove()
- */
-cx_attr_nonnull
-static inline int cx_map_remove_mustr(
-        CxMap *map,
-        cxmutstr key
-) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), NULL);
-}
-
-/**
- * @copydoc cxMapRemove()
- */
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cx_map_remove_str(
-        CxMap *map,
-        const char *key
-) {
-    return map->cl->remove(map, cx_hash_key_str(key), NULL);
-}
+#define cxMapGet(map, key) cx_map_get(map, CX_HASH_KEY(key))
 
 /**
  * Removes a key/value-pair from the map by using the key.
  *
- * Always invokes the destructors functions, if any, on the removed element.
+ * Invokes the destructor functions, if any, on the removed element if and only if the
+ * @p targetbuf is @c NULL.
  *
- * @param map (@c CxMap*) the map
- * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @param map the map
+ * @param key the key
+ * @param targetbuf the optional buffer where the removed element shall be copied to
  * @retval zero success
  * @retval non-zero the key was not found
- * 
+ *
+ * @see cxMapRemove()
  * @see cxMapRemoveAndGet()
  */
-#define cxMapRemove(map, key) _Generic((key), \
-    CxHashKey: cx_map_remove,                 \
-    cxstring: cx_map_remove_cxstr,            \
-    cxmutstr: cx_map_remove_mustr,            \
-    char*: cx_map_remove_str,                 \
-    const char*: cx_map_remove_str)           \
-    (map, key)
-
-/**
- * @copydoc cxMapRemoveAndGet()
- */
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cx_map_remove_and_get(
+cx_attr_nonnull_arg(1)
+static inline int cx_map_remove(
         CxMap *map,
         CxHashKey key,
         void *targetbuf
@@ -754,44 +538,19 @@ static inline int cx_map_remove_and_get(
 }
 
 /**
- * @copydoc cxMapRemoveAndGet()
- */
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cx_map_remove_and_get_cxstr(
-        CxMap *map,
-        cxstring key,
-        void *targetbuf
-) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
-}
-
-/**
- * @copydoc cxMapRemoveAndGet()
- */
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cx_map_remove_and_get_mustr(
-        CxMap *map,
-        cxmutstr key,
-        void *targetbuf
-) {
-    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
-}
-
-/**
- * @copydoc cxMapRemoveAndGet()
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor functions, if any, on the removed element.
+ *
+ * @param map (@c CxMap*) the map
+ * @param key (any supported key type) the key
+ * @retval zero success
+ * @retval non-zero the key was not found
+ * 
+ * @see cxMapRemoveAndGet()
+ * @see CX_HASH_KEY()
  */
-cx_attr_nonnull
-cx_attr_access_w(3)
-cx_attr_cstr_arg(2)
-static inline int cx_map_remove_and_get_str(
-        CxMap *map,
-        const char *key,
-        void *targetbuf
-) {
-    return map->cl->remove(map, cx_hash_key_str(key), targetbuf);
-}
+#define cxMapRemove(map, key) cx_map_remove(map, CX_HASH_KEY(key), NULL)
 
 /**
  * Removes a key/value-pair from the map by using the key.
@@ -805,21 +564,18 @@ static inline int cx_map_remove_and_get_str(
  * and not the object it points to.
  *
  * @param map (@c CxMap*) the map
- * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @param key (any supported key type) the key
  * @param targetbuf (@c void*) the buffer where the element shall be copied to
  * @retval zero success
  * @retval non-zero the key was not found
  *
  * @see cxMapRemove()
+ * @see CX_HASH_KEY()
  */
-#define cxMapRemoveAndGet(map, key, targetbuf) _Generic((key), \
-    CxHashKey: cx_map_remove_and_get,               \
-    cxstring: cx_map_remove_and_get_cxstr,          \
-    cxmutstr: cx_map_remove_and_get_mustr,          \
-    char*: cx_map_remove_and_get_str,               \
-    const char*: cx_map_remove_and_get_str)         \
-    (map, key, targetbuf)
-
-#endif // __cplusplus
+#define cxMapRemoveAndGet(map, key, targetbuf) cx_map_remove(map, CX_HASH_KEY(key), targetbuf)
+
+#ifdef    __cplusplus
+} // extern "C"
+#endif
 
 #endif // UCX_MAP_H
index 4ffd05f3381887dadaa76b78aa41e16b5b2b9baa..141a44cbe4bef66ec1662bb3f84b360edce820e7 100644 (file)
@@ -27,7 +27,7 @@
  */
 /**
  * @file printf.h
- * @brief Wrapper for write functions with a printf-like interface.
+ * @brief Wrapper for write-functions with a printf-like interface.
  * @author Mike Becker
  * @author Olaf Wintermann
  * @copyright 2-Clause BSD License
@@ -102,7 +102,7 @@ int cx_vfprintf(
 );
 
 /**
- * A @c asprintf like function which allocates space for a string
+ * An @c asprintf like function which allocates space for a string
  * the result is written to.
  *
  * @note The resulting string is guaranteed to be zero-terminated,
@@ -126,7 +126,7 @@ cxmutstr cx_asprintf_a(
 );
 
 /**
- * A @c asprintf like function which allocates space for a string
+ * An @c asprintf like function which allocates space for a string
  * the result is written to.
  *
  * @note The resulting string is guaranteed to be zero-terminated,
@@ -169,7 +169,7 @@ cxmutstr cx_vasprintf_a(
  * the result is written to.
  *
  * @note The resulting string is guaranteed to be zero-terminated,
- * unless there was in error, in which case the string's pointer
+ * unless there was an error, in which case the string's pointer
  * will be @c NULL.
  *
  * @param fmt (@c char*) format string
@@ -204,7 +204,7 @@ cxmutstr cx_vasprintf_a(
  * @param len (@c size_t*) a pointer to the length of the buffer
  * @param fmt (@c char*) the format string
  * @param ... additional arguments
- * @return (@c int) the length of produced string or an error code from stdlib printf implementation
+ * @return (@c int) the length of the produced string or an error code from stdlib printf implementation
  */
 #define cx_sprintf(str, len, fmt, ...) cx_sprintf_a(cxDefaultAllocator, str, len, fmt, __VA_ARGS__)
 
@@ -222,7 +222,7 @@ cxmutstr cx_vasprintf_a(
  * @param len a pointer to the length of the buffer
  * @param fmt the format string
  * @param ... additional arguments
- * @return the length of produced string or an error code from stdlib printf implementation
+ * @return the length of the produced string or an error code from stdlib printf implementation
  */
 cx_attr_nonnull_arg(1, 2, 3, 4)
 cx_attr_printf(4, 5)
@@ -248,7 +248,7 @@ int cx_sprintf_a(
  * @param len (@c size_t*) a pointer to the length of the buffer
  * @param fmt (@c char*) the format string
  * @param ap (@c va_list) argument list
- * @return (@c int) the length of produced string or an error code from stdlib printf implementation
+ * @return (@c int) the length of the produced string or an error code from stdlib printf implementation
  */
 #define cx_vsprintf(str, len, fmt, ap) cx_vsprintf_a(cxDefaultAllocator, str, len, fmt, ap)
 
@@ -266,7 +266,7 @@ int cx_sprintf_a(
  * @param len a pointer to the length of the buffer
  * @param fmt the format string
  * @param ap argument list
- * @return the length of produced string or an error code from stdlib printf implementation
+ * @return the length of the produced string or an error code from stdlib printf implementation
  */
 cx_attr_nonnull
 cx_attr_cstr_arg(4)
@@ -300,7 +300,7 @@ int cx_vsprintf_a(
  * @param str (@c char**) a pointer where the location of the result shall be stored
  * @param fmt (@c char*) the format string
  * @param ... additional arguments
- * @return (@c int) the length of produced string or an error code from stdlib printf implementation
+ * @return (@c int) the length of the produced string or an error code from stdlib printf implementation
  */
 #define cx_sprintf_s(buf, len, str, fmt, ...) cx_sprintf_sa(cxDefaultAllocator, buf, len, str, fmt, __VA_ARGS__)
 
@@ -323,7 +323,7 @@ int cx_vsprintf_a(
  * @param str a pointer where the location of the result shall be stored
  * @param fmt the format string
  * @param ... additional arguments
- * @return the length of produced string or an error code from stdlib printf implementation
+ * @return the length of the produced string or an error code from stdlib printf implementation
  */
 cx_attr_nonnull_arg(1, 2, 4, 5)
 cx_attr_printf(5, 6)
@@ -359,7 +359,7 @@ int cx_sprintf_sa(
  * @param str (@c char**) a pointer where the location of the result shall be stored
  * @param fmt (@c char*) the format string
  * @param ap (@c va_list) argument list
- * @return (@c int) the length of produced string or an error code from stdlib printf implementation
+ * @return (@c int) the length of the produced string or an error code from stdlib printf implementation
  */
 #define cx_vsprintf_s(buf, len, str, fmt, ap) cx_vsprintf_sa(cxDefaultAllocator, buf, len, str, fmt, ap)
 
@@ -382,7 +382,7 @@ int cx_sprintf_sa(
  * @param str a pointer where the location of the result shall be stored
  * @param fmt the format string
  * @param ap argument list
- * @return the length of produced string or an error code from stdlib printf implementation
+ * @return the length of the produced string or an error code from stdlib printf implementation
  */
 cx_attr_nonnull
 cx_attr_cstr_arg(5)
index d6df8f5fb2104cd10da4a14b216f58dfec38e673..2dc6bef5c2b6af37f0e96a1fe476f6959c1dd66c 100644 (file)
@@ -122,7 +122,7 @@ enum cx_properties_status {
      * You can use this enumerator to check for all "good" status results
      * by checking if the status is less than @c CX_PROPERTIES_OK.
      *
-     * A "good" status means, that you can refill data and continue parsing.
+     * A "good" status means that you can refill data and continue parsing.
      */
     CX_PROPERTIES_OK,
     /**
@@ -130,11 +130,11 @@ enum cx_properties_status {
      */
     CX_PROPERTIES_NULL_INPUT,
     /**
-     * The line contains a delimiter, but no key.
+     * The line contains a delimiter but no key.
      */
     CX_PROPERTIES_INVALID_EMPTY_KEY,
     /**
-     * The line contains data, but no delimiter.
+     * The line contains data but no delimiter.
      */
     CX_PROPERTIES_INVALID_MISSING_DELIMITER,
     /**
@@ -200,7 +200,7 @@ typedef struct cx_properties_sink_s CxPropertiesSink;
 /**
  * A function that consumes a k/v-pair in a sink.
  *
- * The sink could be e.g. a map and the sink function would be calling
+ * The sink could be a map, and the sink function would be calling
  * a map function to store the k/v-pair.
  *
  * @param prop the properties interface that wants to sink a k/v-pair
@@ -297,7 +297,7 @@ struct cx_properties_source_s {
     /**
      * The source object.
      *
-     * For example a file stream or a string.
+     * For example, a file stream or a string.
      */
     void *src;
     /**
@@ -339,9 +339,9 @@ void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
  *
  * @note Even when you are certain that you did not use the interface in a
  * way that caused a memory allocation, you should call this function anyway.
- * Future versions of the library might add features that need additional memory
- * and you really don't want to search the entire code where you might need
- * add call to this function.
+ * Future versions of the library might add features that need additional memory,
+ * and you really don't want to search the entire code where you might need to
+ * add call to this function.
  *
  * @param prop the properties interface
  */
@@ -352,7 +352,7 @@ void cxPropertiesDestroy(CxProperties *prop);
 /**
  * Destroys and re-initializes the properties interface.
  *
- * You might want to use this, to reset the parser after
+ * You might want to use this to reset the parser after
  * encountering a syntax error.
  *
  * @param prop the properties interface
@@ -403,35 +403,22 @@ int cxPropertiesFilln(
         size_t len
 );
 
-#ifdef __cplusplus
-} // extern "C"
+/**
+ * Internal function, do not use.
+ *
+ * @param prop the properties interface
+ * @param str the text to fill in
+ * @retval zero success
+ * @retval non-zero a memory allocation was necessary but failed
+ */
 cx_attr_nonnull
-static inline int cxPropertiesFill(
+static inline int cx_properties_fill(
         CxProperties *prop,
         cxstring str
 ) {
     return cxPropertiesFilln(prop, str.ptr, str.length);
 }
 
-cx_attr_nonnull
-static inline int cxPropertiesFill(
-        CxProperties *prop,
-        cxmutstr str
-) {
-    return cxPropertiesFilln(prop, str.ptr, str.length);
-}
-
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cxPropertiesFill(
-        CxProperties *prop,
-        const char *str
-) {
-    return cxPropertiesFilln(prop, str, strlen(str));
-}
-
-extern "C" {
-#else // __cplusplus
 /**
  * Fills the input buffer with data.
  *
@@ -452,50 +439,10 @@ extern "C" {
  * @retval non-zero a memory allocation was necessary but failed
  * @see cxPropertiesFilln()
  */
-#define cxPropertiesFill(prop, str) _Generic((str), \
-    cxstring: cx_properties_fill_cxstr,             \
-    cxmutstr: cx_properties_fill_mutstr,            \
-    char*: cx_properties_fill_str,                  \
-    const char*: cx_properties_fill_str)            \
-    (prop, str)
-
-/**
- * @copydoc cxPropertiesFill()
- */
-cx_attr_nonnull
-static inline int cx_properties_fill_cxstr(
-        CxProperties *prop,
-        cxstring str
-) {
-    return cxPropertiesFilln(prop, str.ptr, str.length);
-}
-
-/**
- * @copydoc cxPropertiesFill()
- */
-cx_attr_nonnull
-static inline int cx_properties_fill_mutstr(
-        CxProperties *prop,
-        cxmutstr str
-) {
-    return cxPropertiesFilln(prop, str.ptr, str.length);
-}
-
-/**
- * @copydoc cxPropertiesFill()
- */
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cx_properties_fill_str(
-        CxProperties *prop,
-        const char *str
-) {
-    return cxPropertiesFilln(prop, str, strlen(str));
-}
-#endif
+#define cxPropertiesFill(prop, str) cx_properties_fill(prop, cx_strcast(str))
 
 /**
- * Specifies stack memory that shall be used as internal buffer.
+ * Specifies stack memory that shall be used as an internal buffer.
  *
  * @param prop the properties interface
  * @param buf a pointer to stack memory
index 3cc35faa17ce68360a22dec9abf351d9bdd7e4f6..8f87a7cee607ab1ffaebdd3b85868ed4d59205b5 100644 (file)
@@ -54,7 +54,7 @@ extern "C" {
  * @param wfnc the write function
  * @param buf a pointer to the copy buffer or @c NULL if a buffer
  * shall be implicitly created on the heap
- * @param bufsize the size of the copy buffer - if @p buf is @c NULL you can
+ * @param bufsize the size of the copy buffer - if @p buf is @c NULL, you can
  * set this to zero to let the implementation decide
  * @param n the maximum number of bytes that shall be copied.
  * If this is larger than @p bufsize, the content is copied over multiple
@@ -86,7 +86,7 @@ size_t cx_stream_bncopy(
  * @param buf (@c char*) a pointer to the copy buffer or @c NULL if a buffer
  * shall be implicitly created on the heap
  * @param bufsize (@c size_t) the size of the copy buffer - if @p buf is
- * @c NULL you can set this to zero to let the implementation decide
+ * @c NULL, you can set this to zero to let the implementation decide
  * @return total number of bytes copied
  */
 #define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \
@@ -95,7 +95,7 @@ size_t cx_stream_bncopy(
 /**
  * Reads data from a stream and writes it to another stream.
  *
- * The data is temporarily stored in a stack allocated buffer.
+ * The data is temporarily stored in a stack-allocated buffer.
  *
  * @param src the source stream
  * @param dest the destination stream
@@ -119,7 +119,7 @@ size_t cx_stream_ncopy(
 /**
  * Reads data from a stream and writes it to another stream.
  *
- * The data is temporarily stored in a stack allocated buffer.
+ * The data is temporarily stored in a stack-allocated buffer.
  *
  * @param src (@c void*) the source stream
  * @param dest (@c void*) the destination stream
index 02c82c85935423ffb85445468f638197e24f8bdf..618092588accb1f61d20d673f18939cce17f2879 100644 (file)
@@ -112,10 +112,10 @@ struct cx_strtok_ctx_s {
      */
     size_t pos;
     /**
-     * Position of next delimiter in the source string.
+     * Position of the next delimiter in the source string.
      *
      * If the tokenizer has not yet returned a token, the content of this field
-     * is undefined. If the tokenizer reached the end of the string, this field
+     * is undefined. If the tokenizer reaches the end of the string, this field
      * contains the length of the source string.
      */
     size_t delim_pos;
@@ -167,17 +167,18 @@ extern "C" {
  *
  * The length is implicitly inferred by using a call to @c strlen().
  *
+ * When @c NULL is passed, the length will be set to zero.
+ *
  * @note the wrapped string will share the specified pointer to the string.
  * If you do want a copy, use cx_strdup() on the return value of this function.
  *
  * If you need to wrap a constant string, use cx_str().
  *
- * @param cstring the string to wrap, must be zero-terminated
+ * @param cstring the string to wrap (must be zero-terminated)
  * @return the wrapped string
  *
  * @see cx_mutstrn()
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_cstr_arg(1)
 cx_attr_export
@@ -212,17 +213,18 @@ cxmutstr cx_mutstrn(
  *
  * The length is implicitly inferred by using a call to @c strlen().
  *
+ * When @c NULL is passed, the length will be set to zero.
+ *
  * @note the wrapped string will share the specified pointer to the string.
  * If you do want a copy, use cx_strdup() on the return value of this function.
  *
  * If you need to wrap a non-constant string, use cx_mutstr().
  *
- * @param cstring the string to wrap, must be zero-terminated
+ * @param cstring the string to wrap (must be zero-terminated)
  * @return the wrapped string
  *
  * @see cx_strn()
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_cstr_arg(1)
 cx_attr_export
@@ -263,6 +265,10 @@ cx_attr_nodiscard
 static inline cxstring cx_strcast(cxstring str) {
     return str;
 }
+cx_attr_nodiscard
+static inline cxstring cx_strcast(const char *str) {
+    return cx_str(str);
+}
 extern "C" {
 #else
 /**
@@ -287,32 +293,40 @@ static inline cxstring cx_strcast_c(cxstring str) {
 }
 
 /**
-* Casts a mutable string to an immutable string.
-*
-* Does nothing for already immutable strings.
-*
-* @note This is not seriously a cast. Instead, you get a copy
-* of the struct with the desired pointer type. Both structs still
-* point to the same location, though!
-*
-* @param str (@c cxstring or @c cxmutstr) the string to cast
-* @return (@c cxstring) an immutable copy of the string pointer
-*/
+ * Internal function, do not use.
+ * @param str
+ * @return
+ * @see cx_strcast()
+ */
+cx_attr_nodiscard
+static inline cxstring cx_strcast_z(const char *str) {
+    return cx_str(str);
+}
+
+/**
+ * Wraps any string into an UCX string.
+ *
+ * @param str (any supported string type) the string to cast
+ * @return (@c cxstring) the string wrapped as UCX string
+ */
 #define cx_strcast(str) _Generic((str), \
         cxmutstr: cx_strcast_m, \
-        cxstring: cx_strcast_c) \
-        (str)
+        cxstring: cx_strcast_c, \
+        const unsigned char*: cx_strcast_z, \
+        unsigned char *: cx_strcast_z, \
+        const char*: cx_strcast_z, \
+        char *: cx_strcast_z) (str)
 #endif
 
 /**
  * Passes the pointer in this string to the cxDefaultAllocator's @c free() function.
  *
- * The pointer in the struct is set to @c NULL and the length is set to zero
+ * The pointer in the struct is set to @c NULL, and the length is set to zero,
  * which means that this function protects you against double-free.
  *
  * @note There is no implementation for cxstring, because it is unlikely that
  * you ever have a <code>const char*</code> you are really supposed to free.
- * If you encounter such situation, you should double-check your code.
+ * If you encounter such situation, you should double-check your code.
  *
  * @param str the string to free
  */
@@ -320,14 +334,14 @@ cx_attr_export
 void cx_strfree(cxmutstr *str);
 
 /**
- * Passes the pointer in this string to the allocators free function.
+ * Passes the pointer in this string to the allocator's free function.
  *
- * The pointer in the struct is set to @c NULL and the length is set to zero
+ * The pointer in the struct is set to @c NULL, and the length is set to zero,
  * which means that this function protects you against double-free.
  *
  * @note There is no implementation for cxstring, because it is unlikely that
  * you ever have a <code>const char*</code> you are really supposed to free.
- * If you encounter such situation, you should double-check your code.
+ * If you encounter such situation, you should double-check your code.
  *
  * @param alloc the allocator
  * @param str the string to free
@@ -969,7 +983,7 @@ cx_attr_export
 cxmutstr cx_strtrim_m(cxmutstr string);
 
 /**
- * Checks, if a string has a specific prefix.
+ * Checks if a string has a specific prefix.
  *
  * @param string the string to check
  * @param prefix the prefix the string should have
@@ -984,7 +998,7 @@ bool cx_strprefix(
 );
 
 /**
- * Checks, if a string has a specific suffix.
+ * Checks if a string has a specific suffix.
  *
  * @param string the string to check
  * @param suffix the suffix the string should have
@@ -999,7 +1013,7 @@ bool cx_strsuffix(
 );
 
 /**
- * Checks, if a string has a specific prefix, ignoring the case.
+ * Checks if a string has a specific prefix, ignoring the case.
  *
  * @param string the string to check
  * @param prefix the prefix the string should have
@@ -1031,7 +1045,7 @@ bool cx_strcasesuffix(
 /**
  * Replaces a string with another string.
  *
- * Replaces at most @p replmax occurrences.
+ * The function replaces at most @p replmax occurrences.
  *
  * The returned string will be allocated by @p allocator and is guaranteed
  * to be zero-terminated.
@@ -1060,7 +1074,7 @@ cxmutstr cx_strreplacen_a(
 /**
  * Replaces a string with another string.
  *
- * Replaces at most @p replmax occurrences.
+ * The function replaces at most @p replmax occurrences.
  *
  * The returned string will be allocated by the cxDefaultAllocator and is guaranteed
  * to be zero-terminated.
@@ -1211,7 +1225,7 @@ void cx_strtok_delim(
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1228,7 +1242,7 @@ int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1245,7 +1259,7 @@ int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1262,7 +1276,7 @@ int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1279,7 +1293,7 @@ int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groups
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1296,7 +1310,7 @@ int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep)
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1313,7 +1327,7 @@ int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1330,7 +1344,7 @@ int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1347,7 +1361,7 @@ int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1364,7 +1378,7 @@ int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *g
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1381,7 +1395,7 @@ int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *grou
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1398,7 +1412,7 @@ int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *gr
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1415,7 +1429,7 @@ int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const ch
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1432,7 +1446,7 @@ int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1449,7 +1463,7 @@ int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groups
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1466,7 +1480,7 @@ int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groups
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1483,7 +1497,7 @@ int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groups
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1491,7 +1505,7 @@ cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
 
 /**
- * Converts a string to a single precision floating point number.
+ * Converts a string to a single precision floating-point number.
  *
  * The function returns non-zero when conversion is not possible.
  * In that case the function sets errno to EINVAL when the reason is an invalid character.
@@ -1500,7 +1514,7 @@ int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
  * @param str the string to convert
  * @param output a pointer to the float variable where the result shall be stored
  * @param decsep the decimal separator
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1508,7 +1522,7 @@ cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
 
 /**
- * Converts a string to a double precision floating point number.
+ * Converts a string to a double precision floating-point number.
  *
  * The function returns non-zero when conversion is not possible.
  * In that case the function sets errno to EINVAL when the reason is an invalid character.
@@ -1517,7 +1531,7 @@ int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep
  * @param str the string to convert
  * @param output a pointer to the float variable where the result shall be stored
  * @param decsep the decimal separator
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1534,7 +1548,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1550,7 +1564,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1566,7 +1580,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1582,7 +1596,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1598,7 +1612,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1614,7 +1628,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1630,7 +1644,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1646,7 +1660,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1662,7 +1676,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1678,7 +1692,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1694,7 +1708,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1710,7 +1724,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1726,7 +1740,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1742,7 +1756,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1758,7 +1772,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1774,7 +1788,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1790,7 +1804,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the integer variable where the result shall be stored
  * @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
@@ -1803,7 +1817,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -1821,7 +1835,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -1839,7 +1853,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -1857,7 +1871,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -1875,7 +1889,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -1893,7 +1907,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -1911,7 +1925,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -1929,7 +1943,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -1947,7 +1961,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -1965,7 +1979,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -1983,7 +1997,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -2001,7 +2015,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -2019,7 +2033,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -2037,7 +2051,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -2055,7 +2069,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -2073,7 +2087,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -2091,7 +2105,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
  * It sets errno to ERANGE when the target datatype is too small.
  *
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
  *
  * @param str the string to convert
@@ -2103,7 +2117,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
 #define cx_strtou64(str, output, base) cx_strtou64_lc_(cx_strcast(str), output, base, ",")
 
 /**
- * Converts a string to a single precision floating point number.
+ * Converts a string to a single precision floating-point number.
  *
  * The function returns non-zero when conversion is not possible.
  * In that case the function sets errno to EINVAL when the reason is an invalid character.
@@ -2112,14 +2126,14 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the float variable where the result shall be stored
  * @param decsep the decimal separator
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
 #define cx_strtof_lc(str, output, decsep, groupsep) cx_strtof_lc_(cx_strcast(str), output, decsep, groupsep)
 
 /**
- * Converts a string to a double precision floating point number.
+ * Converts a string to a double precision floating-point number.
  *
  * The function returns non-zero when conversion is not possible.
  * In that case the function sets errno to EINVAL when the reason is an invalid character.
@@ -2127,21 +2141,21 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
  * @param str the string to convert
  * @param output a pointer to the double variable where the result shall be stored
  * @param decsep the decimal separator
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
 #define cx_strtod_lc(str, output, decsep, groupsep) cx_strtod_lc_(cx_strcast(str), output, decsep, groupsep)
 
 /**
- * Converts a string to a single precision floating point number.
+ * Converts a string to a single precision floating-point number.
  *
  * The function returns non-zero when conversion is not possible.
  * In that case the function sets errno to EINVAL when the reason is an invalid character.
  * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h.
  *
  * The decimal separator is assumed to be a dot character.
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose a different format, use cx_strtof_lc().
  *
  * @param str the string to convert
@@ -2152,13 +2166,13 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
 #define cx_strtof(str, output) cx_strtof_lc_(cx_strcast(str), output, '.', ",")
 
 /**
- * Converts a string to a double precision floating point number.
+ * Converts a string to a double precision floating-point number.
  *
  * The function returns non-zero when conversion is not possible.
  * In that case the function sets errno to EINVAL when the reason is an invalid character.
  *
  * The decimal separator is assumed to be a dot character.
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as group separator and ignored during parsing.
  * If you want to choose a different format, use cx_strtof_lc().
  *
  * @param str the string to convert
index fdd9269dc28bd96433844da6999951acd40dcbf8..cc6639929611cdbbc803bb09d69c1eda4b5f3b76 100644 (file)
@@ -56,7 +56,7 @@
  * }
  * </code>
  * 
- * @attention Do not call own functions within a test, that use
+ * @attention Do not call own functions within a test that use
  * CX_TEST_ASSERT() macros and are not defined by using CX_TEST_SUBROUTINE().
  *
  * @author Mike Becker
@@ -171,7 +171,7 @@ static inline void cx_test_suite_free(CxTestSuite* suite) {
 /**
  * Registers a test function with the specified test suite.
  * 
- * @param suite the suite, the test function shall be added to
+ * @param suite the suite the test function shall be added to
  * @param test the test function to register
  * @retval zero success
  * @retval non-zero failure
@@ -263,7 +263,7 @@ static inline void cx_test_run(CxTestSuite *suite,
  * }
  * @endcode
  *
- * @attention Any CX_TEST_ASSERT() calls must be performed in scope of
+ * @attention Any CX_TEST_ASSERT() calls must be performed in the scope of
  * #CX_TEST_DO.
  */
 #define CX_TEST_DO _writefnc_("Running ", 1, 8, _output_);\
index 14832dbeff20cb06fea7f008b02cc18ec6f6e33e..bb24345588675304ed5bec05eab0aceda2b4a3da 100644 (file)
@@ -49,12 +49,12 @@ extern "C" {
  *
  * This iterator is not position-aware in a strict sense, as it does not assume
  * a particular order of elements in the tree. However, the iterator keeps track
- * of the number of nodes it has passed in a counter variable.
+ * of the number of nodes it has passed in a counter-variable.
  * Each node, regardless of the number of passes, is counted only once.
  *
  * @note Objects that are pointed to by an iterator are mutable through that
  * iterator. However, if the
- * underlying data structure is mutated by other means than this iterator (e.g.
+ * underlying data structure is mutated by other means than this iterator (e.g.,
  * elements added or removed), the iterator becomes invalid (regardless of what
  * cxIteratorValid() returns).
  *
@@ -71,7 +71,7 @@ typedef struct cx_tree_iterator_s {
     bool skip;
     /**
      * Set to true, when the iterator shall visit a node again
-     * when all it's children have been processed.
+     * when all its children have been processed.
      */
     bool visit_on_exit;
     /**
@@ -97,7 +97,7 @@ typedef struct cx_tree_iterator_s {
      */
     void *node;
     /**
-     * Stores a copy of the next pointer of the visited node.
+     * Stores a copy of the pointer to the successor of the visited node.
      * Allows freeing a node on exit without corrupting the iteration.
      */
     void *node_next;
@@ -155,12 +155,12 @@ struct cx_tree_visitor_queue_s {
  *
  * This iterator is not position-aware in a strict sense, as it does not assume
  * a particular order of elements in the tree. However, the iterator keeps track
- * of the number of nodes it has passed in a counter variable.
+ * of the number of nodes it has passed in a counter-variable.
  * Each node, regardless of the number of passes, is counted only once.
  *
  * @note Objects that are pointed to by an iterator are mutable through that
  * iterator. However, if the
- * underlying data structure is mutated by other means than this iterator (e.g.
+ * underlying data structure is mutated by other means than this iterator (e.g.,
  * elements added or removed), the iterator becomes invalid (regardless of what
  * cxIteratorValid() returns).
  *
@@ -250,7 +250,7 @@ static inline void cxTreeVisitorDispose(CxTreeVisitor *visitor) {
 /**
  * Links a node to a (new) parent.
  *
- * If the node has already a parent, it is unlinked, first.
+ * If the node already has a parent, it is unlinked, first.
  * If the parent has children already, the node is @em appended to the list
  * of all currently existing children.
  *
@@ -318,8 +318,8 @@ void cx_tree_unlink(
  * Zero means exact match and a positive number is an implementation defined
  * measure for the distance to an exact match.
  *
- * For example if a tree stores file path information, a node that is
- * describing a parent directory of a filename that is searched, shall
+ * For example, consider a tree that stores file path information.
+ * A node which is describing a parent directory of a searched file shall
  * return a positive number to indicate that a child node might contain the
  * searched item. On the other hand, if the node denotes a path that is not a
  * prefix of the searched filename, the function would return -1 to indicate
@@ -330,7 +330,7 @@ void cx_tree_unlink(
  *
  * @return 0 if the node contains the data,
  * positive if one of the children might contain the data,
- * negative if neither the node, nor the children contains the data
+ * negative if neither the node nor the children contains the data
  */
 cx_attr_nonnull
 typedef int (*cx_tree_search_data_func)(const void *node, const void *data);
@@ -348,8 +348,8 @@ typedef int (*cx_tree_search_data_func)(const void *node, const void *data);
  * Zero means exact match and a positive number is an implementation defined
  * measure for the distance to an exact match.
  *
- * For example if a tree stores file path information, a node that is
- * describing a parent directory of a filename that is searched, shall
+ * For example, consider a tree that stores file path information.
+ * A node which is describing a parent directory of a searched file shall
  * return a positive number to indicate that a child node might contain the
  * searched item. On the other hand, if the node denotes a path that is not a
  * prefix of the searched filename, the function would return -1 to indicate
@@ -360,7 +360,7 @@ typedef int (*cx_tree_search_data_func)(const void *node, const void *data);
  *
  * @return 0 if @p node contains the same data as @p new_node,
  * positive if one of the children might contain the data,
- * negative if neither the node, nor the children contains the data
+ * negative if neither the node nor the children contains the data
  */
 cx_attr_nonnull
 typedef int (*cx_tree_search_func)(const void *node, const void *new_node);
@@ -368,11 +368,11 @@ typedef int (*cx_tree_search_func)(const void *node, const void *new_node);
 /**
  * Searches for data in a tree.
  *
- * When the data cannot be found exactly, the search function might return a
- * closest result which might be a good starting point for adding a new node
+ * When the data cannot be found exactly, the search function might return the
+ * closest result, which might be a good starting point for adding a new node
  * to the tree (see also #cx_tree_add()).
  *
- * Depending on the tree structure it is not necessarily guaranteed that the
+ * Depending on the tree structure, it is not necessarily guaranteed that the
  * "closest" match is uniquely defined. This function will search for a node
  * with the best match according to the @p sfunc (meaning: the return value of
  * @p sfunc which is closest to zero). If that is also ambiguous, an arbitrary
@@ -406,10 +406,10 @@ int cx_tree_search_data(
  * Searches for a node in a tree.
  *
  * When no node with the same data can be found, the search function might
- * return a closest result which might be a good starting point for adding the
+ * return the closest result, which might be a good starting point for adding the
  * new node to the tree (see also #cx_tree_add()).
  *
- * Depending on the tree structure it is not necessarily guaranteed that the
+ * Depending on the tree structure, it is not necessarily guaranteed that the
  * "closest" match is uniquely defined. This function will search for a node
  * with the best match according to the @p sfunc (meaning: the return value of
  * @p sfunc which is closest to zero). If that is also ambiguous, an arbitrary
@@ -496,8 +496,8 @@ CxTreeVisitor cx_tree_visitor(
 
 /**
  * Describes a function that creates a tree node from the specified data.
- * The first argument points to the data the node shall contain and
- * the second argument may be used for additional data (e.g. an allocator).
+ * The first argument points to the data the node shall contain, and
+ * the second argument may be used for additional data (e.g., an allocator).
  * Functions of this type shall either return a new pointer to a newly
  * created node or @c NULL when allocation fails.
  *
@@ -637,17 +637,17 @@ size_t cx_tree_add_array(
  * When a location is found, the @p cfunc will be invoked with @p cdata.
  *
  * The node returned by @p cfunc will be linked into the tree.
- * When @p sfunc returned a positive integer, the new node will be linked as a
+ * When @p sfunc returns a positive integer, the new node will be linked as a
  * child. The other children (now siblings of the new node) are then checked
  * with @p sfunc, whether they could be children of the new node and re-linked
  * accordingly.
  *
- * When @p sfunc returned zero and the found node has a parent, the new
- * node will be added as sibling - otherwise, the new node will be added
+ * When @p sfunc returns zero and the found node has a parent, the new
+ * node will be added as sibling - otherwise, the new node will be added
  * as a child.
  *
- * When @p sfunc returned a negative value, the new node will not be added to
- * the tree and this function returns a non-zero value.
+ * When @p sfunc returns a negative value, the new node will not be added to
+ * the tree, and this function returns a non-zero value.
  * The caller should check if @p cnode contains a node pointer and deal with the
  * node that could not be added.
  *
@@ -747,9 +747,9 @@ struct cx_tree_s {
      * A function to create new nodes.
      *
      * Invocations to this function will receive a pointer to this tree
-     * structure as second argument.
+     * structure as the second argument.
      *
-     * Nodes MAY use #cx_tree_node_base_s as base layout, but do not need to.
+     * Nodes MAY use #cx_tree_node_base_s as the base layout, but do not need to.
      */
     cx_tree_node_create_func node_create;
 
@@ -814,7 +814,7 @@ struct cx_tree_s {
  * Macro to roll out the #cx_tree_node_base_s structure with a custom
  * node type.
  *
- * Must be used as first member in your custom tree struct.
+ * Must be used as the first member in your custom tree struct.
  *
  * @param type the data type for the nodes
  */
@@ -858,7 +858,7 @@ struct cx_tree_class_s {
     /**
      * Member function for inserting multiple elements.
      *
-     * Implementations SHALL avoid to perform a full search in the tree for
+     * Implementations SHALL avoid performing a full search in the tree for
      * every element even though the source data MAY be unsorted.
      */
     size_t (*insert_many)(
@@ -885,7 +885,7 @@ typedef struct cx_tree_s CxTree;
 
 
 /**
- * Destroys a node and it's subtree.
+ * Destroys a node and its subtree.
  *
  * It is guaranteed that the simple destructor is invoked before
  * the advanced destructor, starting with the leaf nodes of the subtree.
@@ -921,7 +921,7 @@ void cxTreeDestroySubtree(CxTree *tree, void *node);
  *
  * @attention Be careful when calling this function when no destructor function
  * is registered that actually frees the memory of nodes. In that case you will
- * need a reference to the (former) root node of the tree somewhere or
+ * need a reference to the (former) root node of the tree somewhere, or
  * otherwise you will be leaking memory.
  *
  * @param tree the tree
@@ -992,9 +992,9 @@ CxTree *cxTreeCreate(
 /**
  * Creates a new tree structure based on a default layout.
  *
- * Nodes created by @p create_func MUST contain #cx_tree_node_base_s as first
+ * Nodes created by @p create_func MUST contain #cx_tree_node_base_s as the first
  * member (or at least respect the default offsets specified in the tree
- * struct) and they MUST be allocated with the specified allocator.
+ * struct), and they MUST be allocated with the specified allocator.
  *
  * @note This function will also register an advanced destructor which
  * will free the nodes with the allocator's free() method.
@@ -1019,7 +1019,7 @@ cx_tree_node_base_layout)
  * @attention This function will create an incompletely defined tree structure
  * where neither the create function, the search function, nor a destructor
  * will be set. If you wish to use any of this functionality for the wrapped
- * tree, you need to specify those functions afterwards.
+ * tree, you need to specify those functions afterward.
  *
  * @param allocator the allocator that was used for nodes of the wrapped tree
  * (if @c NULL, the cxDefaultAllocator is assumed)
@@ -1289,7 +1289,7 @@ static inline CxTreeVisitor cxTreeVisit(CxTree *tree) {
 /**
  * Sets the (new) parent of the specified child.
  *
- * If the @p child is not already member of the tree, this function behaves
+ * If the @p child is not already member of the tree, this function behaves
  * as #cxTreeAddChildNode().
  *
  * @param tree the tree
@@ -1308,11 +1308,11 @@ void cxTreeSetParent(
 /**
  * Adds a new node to the tree.
  *
- * If the @p child is already member of the tree, the behavior is undefined.
+ * If the @p child is already member of the tree, the behavior is undefined.
  * Use #cxTreeSetParent() if you want to move a subtree to another location.
  *
  * @attention The node may be externally created, but MUST obey the same rules
- * as if it was created by the tree itself with #cxTreeAddChild() (e.g. use
+ * as if it was created by the tree itself with #cxTreeAddChild() (e.g., use
  * the same allocator).
  *
  * @param tree the tree
@@ -1336,7 +1336,7 @@ void cxTreeAddChildNode(
  * leaving this task to the tree by using #cxTreeInsert().
  *
  * Be aware that adding nodes at arbitrary locations in the tree might cause
- * wrong or undesired results when subsequently invoking #cxTreeInsert() and
+ * wrong or undesired results when subsequently invoking #cxTreeInsert(), and
  * the invariant imposed by the search function does not hold any longer.
  *
  * @param tree the tree
@@ -1395,7 +1395,7 @@ int cxTreeRemoveNode(
 );
 
 /**
- * Removes a node and it's subtree from the tree.
+ * Removes a node and its subtree from the tree.
  *
  * If the node is not part of the tree, the behavior is undefined.
  *
index fac29bb87f3acd35f13038e2f9647ac1753a5038..8a894d6b82d828f6ff6f1d11db3665560e5e669f 100644 (file)
@@ -27,6 +27,7 @@
  */
 
 #include "cx/hash_key.h"
+#include "cx/compare.h"
 #include <string.h>
 
 void cx_hash_murmur(CxHashKey *key) {
@@ -62,14 +63,14 @@ void cx_hash_murmur(CxHashKey *key) {
     switch (len) {
         case 3:
             h ^= (data[i + 2] & 0xFF) << 16;
-                    __attribute__((__fallthrough__));
+            cx_attr_fallthrough;
         case 2:
             h ^= (data[i + 1] & 0xFF) << 8;
-                    __attribute__((__fallthrough__));
+            cx_attr_fallthrough;
         case 1:
             h ^= (data[i + 0] & 0xFF);
             h *= m;
-                    __attribute__((__fallthrough__));
+            cx_attr_fallthrough;
         default: // do nothing
             ;
     }
@@ -81,6 +82,21 @@ void cx_hash_murmur(CxHashKey *key) {
     key->hash = h;
 }
 
+
+uint32_t cx_hash_u32(uint32_t x) {
+    x = ((x >> 16) ^ x) * 0x45d9f3bu;
+    x = ((x >> 16) ^ x) * 0x45d9f3bu;
+    x = (x >> 16) ^ x;
+    return x;
+}
+
+uint64_t cx_hash_u64(uint64_t x) {
+    x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
+    x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
+    x = x ^ (x >> 31);
+    return x;
+}
+
 CxHashKey cx_hash_key_str(const char *str) {
     CxHashKey key;
     key.data = str;
@@ -110,3 +126,29 @@ CxHashKey cx_hash_key(
     cx_hash_murmur(&key);
     return key;
 }
+
+CxHashKey cx_hash_key_u32(uint32_t x) {
+    CxHashKey key;
+    key.data = NULL;
+    key.len = 0;
+    key.hash = cx_hash_u32(x);
+    return key;
+}
+
+CxHashKey cx_hash_key_u64(uint64_t x) {
+    CxHashKey key;
+    key.data = NULL;
+    key.len = 0;
+    key.hash = cx_hash_u64(x);
+    return key;
+}
+
+int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right) {
+    int d;
+    d = cx_vcmp_uint64(left->hash, right->hash);
+    if (d != 0) return d;
+    d = cx_vcmp_size(left->len, right->len);
+    if (d != 0) return d;
+    if (left->len == 0) return 0;
+    return memcmp(left->data, right->data, left->len);
+}
index 1721e8c9575989158b389b87a3e8fe5bd4ac6ff4..a7de4e37fce64ca1363ab9768b0221dd84b404e5 100644 (file)
@@ -78,7 +78,7 @@ static void cx_hash_map_destructor(struct cx_map_s *map) {
     cxFree(map->collection.allocator, map);
 }
 
-static int cx_hash_map_put(
+static void *cx_hash_map_put(
         CxMap *map,
         CxHashKey key,
         void *value
@@ -101,11 +101,12 @@ static int cx_hash_map_put(
         elm = elm->next;
     }
 
-    if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len &&
-        memcmp(elm->key.data, key.data, key.len) == 0) {
+    if (elm != NULL && cx_hash_key_cmp(&elm->key, &key) == 0) {
         // overwrite existing element, but call destructors first
         cx_invoke_destructor(map, elm->data);
-        if (map->collection.store_pointer) {
+        if (value == NULL) {
+            memset(elm->data, 0, map->collection.elem_size);
+        } else if (map->collection.store_pointer) {
             memcpy(elm->data, &value, sizeof(void *));
         } else {
             memcpy(elm->data, value, map->collection.elem_size);
@@ -116,10 +117,12 @@ static int cx_hash_map_put(
                 allocator,
                 sizeof(struct cx_hash_map_element_s) + map->collection.elem_size
         );
-        if (e == NULL) return -1;
+        if (e == NULL) return NULL;
 
         // write the value
-        if (map->collection.store_pointer) {
+        if (value == NULL) {
+            memset(e->data, 0, map->collection.elem_size);
+        } else if (map->collection.store_pointer) {
             memcpy(e->data, &value, sizeof(void *));
         } else {
             memcpy(e->data, value, map->collection.elem_size);
@@ -127,7 +130,10 @@ static int cx_hash_map_put(
 
         // copy the key
         void *kd = cxMalloc(allocator, key.len);
-        if (kd == NULL) return -1;
+        if (kd == NULL) {
+            cxFree(allocator, e);
+            return NULL;
+        }
         memcpy(kd, key.data, key.len);
         e->key.data = kd;
         e->key.len = key.len;
@@ -140,12 +146,14 @@ static int cx_hash_map_put(
             prev->next = e;
         }
         e->next = elm;
+        elm = e;
 
         // increase the size
         map->collection.size++;
     }
 
-    return 0;
+    // return pointer to the element
+    return elm->data;
 }
 
 static void cx_hash_map_unlink(
@@ -205,27 +213,25 @@ static int cx_hash_map_get_remove(
     struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
     struct cx_hash_map_element_s *prev = NULL;
     while (elm && elm->key.hash <= hash) {
-        if (elm->key.hash == hash && elm->key.len == key.len) {
-            if (memcmp(elm->key.data, key.data, key.len) == 0) {
-                if (remove) {
-                    if (targetbuf == NULL) {
-                        cx_invoke_destructor(map, elm->data);
-                    } else {
-                        memcpy(targetbuf, elm->data, map->collection.elem_size);
-                    }
-                    cx_hash_map_unlink(hash_map, slot, prev, elm);
+        if (cx_hash_key_cmp(&elm->key, &key) == 0) {
+            if (remove) {
+                if (targetbuf == NULL) {
+                    cx_invoke_destructor(map, elm->data);
+                } else {
+                    memcpy(targetbuf, elm->data, map->collection.elem_size);
+                }
+                cx_hash_map_unlink(hash_map, slot, prev, elm);
+            } else {
+                assert(targetbuf != NULL);
+                void *data = NULL;
+                if (map->collection.store_pointer) {
+                    data = *(void **) elm->data;
                 } else {
-                    assert(targetbuf != NULL);
-                    void *data = NULL;
-                    if (map->collection.store_pointer) {
-                        data = *(void **) elm->data;
-                    } else {
-                        data = elm->data;
-                    }
-                    memcpy(targetbuf, &data, sizeof(void *));
+                    data = elm->data;
                 }
-                return 0;
+                memcpy(targetbuf, &data, sizeof(void *));
             }
+            return 0;
         }
         prev = elm;
         elm = prev->next;
@@ -260,19 +266,12 @@ static void *cx_hash_map_iter_current_entry(const void *it) {
 
 static void *cx_hash_map_iter_current_key(const void *it) {
     const CxMapIterator *iter = it;
-    struct cx_hash_map_element_s *elm = iter->elem;
-    return &elm->key;
+    return (void*) iter->entry.key;
 }
 
 static void *cx_hash_map_iter_current_value(const void *it) {
     const CxMapIterator *iter = it;
-    const CxMap *map = iter->map.c;
-    struct cx_hash_map_element_s *elm = iter->elem;
-    if (map->collection.store_pointer) {
-        return *(void **) elm->data;
-    } else {
-        return elm->data;
-    }
+    return iter->entry.value;
 }
 
 static bool cx_hash_map_iter_valid(const void *it) {
@@ -309,6 +308,7 @@ static void cx_hash_map_iter_next(void *it) {
 
         // unlink
         cx_hash_map_unlink(hmap, iter->slot, prev, elm);
+        iter->elem_count--;
 
         // advance
         elm = next;
index 484c7d374c7f21de0893c38b63650de0fb59242a..b09c49d0f3e372cd1628f08fc2ec98515c0c7be3 100644 (file)
@@ -32,6 +32,7 @@
 #include <assert.h>
 #include <stdio.h>
 #include <inttypes.h>
+#include <ctype.h>
 
 /*
  * RFC 8259
@@ -46,22 +47,17 @@ static int json_cmp_objvalue(const void *l, const void *r) {
     return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name));
 }
 
-static CxJsonObjValue *json_find_objvalue(const CxJsonValue *obj, cxstring name) {
+static size_t json_find_objvalue(const CxJsonValue *obj, cxstring name) {
     assert(obj->type == CX_JSON_OBJECT);
     CxJsonObjValue kv_dummy;
     kv_dummy.name = cx_mutstrn((char*) name.ptr, name.length);
-    size_t index = cx_array_binary_search(
+    return cx_array_binary_search(
             obj->value.object.values,
             obj->value.object.values_size,
             sizeof(CxJsonObjValue),
             &kv_dummy,
             json_cmp_objvalue
     );
-    if (index == obj->value.object.values_size) {
-        return NULL;
-    } else {
-        return &obj->value.object.values[index];
-    }
 }
 
 static int json_add_objvalue(CxJsonValue *objv, CxJsonObjValue member) {
@@ -132,16 +128,6 @@ static void token_destroy(CxJsonToken *token) {
     }
 }
 
-static bool json_isdigit(char c) {
-    // TODO: remove once UCX has public API for this
-    return c >= '0' && c <= '9';
-}
-
-static bool json_isspace(char c) {
-    // TODO: remove once UCX has public API for this
-    return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f';
-}
-
 static int num_isexp(const char *content, size_t length, size_t pos) {
     if (pos >= length) {
         return 0;
@@ -150,7 +136,7 @@ static int num_isexp(const char *content, size_t length, size_t pos) {
     int ok = 0;
     for (size_t i = pos; i < length; i++) {
         char c = content[i];
-        if (json_isdigit(c)) {
+        if (isdigit((unsigned char)c)) {
             ok = 1;
         } else if (i == pos) {
             if (!(c == '+' || c == '-')) {
@@ -167,7 +153,7 @@ static int num_isexp(const char *content, size_t length, size_t pos) {
 static CxJsonTokenType token_numbertype(const char *content, size_t length) {
     if (length == 0) return CX_JSON_TOKEN_ERROR;
 
-    if (content[0] != '-' && !json_isdigit(content[0])) {
+    if (content[0] != '-' && !isdigit((unsigned char)content[0])) {
         return CX_JSON_TOKEN_ERROR;
     }
 
@@ -180,7 +166,7 @@ static CxJsonTokenType token_numbertype(const char *content, size_t length) {
             type = CX_JSON_TOKEN_NUMBER;
         } else if (content[i] == 'e' || content[i] == 'E') {
             return num_isexp(content, length, i + 1) ? CX_JSON_TOKEN_NUMBER : CX_JSON_TOKEN_ERROR;
-        } else if (!json_isdigit(content[i])) {
+        } else if (!isdigit((unsigned char)content[i])) {
             return CX_JSON_TOKEN_ERROR; // char is not a digit, decimal separator or exponent sep
         }
     }
@@ -244,7 +230,7 @@ static CxJsonTokenType char2ttype(char c) {
             return CX_JSON_TOKEN_STRING;
         }
         default: {
-            if (json_isspace(c)) {
+            if (isspace((unsigned char)c)) {
                 return CX_JSON_TOKEN_SPACE;
             }
         }
@@ -1126,6 +1112,20 @@ CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) {
     return value->value.array.array[index];
 }
 
+CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) {
+    if (index >= value->value.array.array_size) {
+        return NULL;
+    }
+    CxJsonValue *ret = value->value.array.array[index];
+    // TODO: replace with a low level cx_array_remove()
+    size_t count = value->value.array.array_size - index - 1;
+    if (count > 0) {
+        memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*));
+    }
+    value->value.array.array_size--;
+    return ret;
+}
+
 CxIterator cxJsonArrIter(const CxJsonValue *value) {
     return cxIteratorPtr(
         value->value.array.array,
@@ -1141,12 +1141,26 @@ CxIterator cxJsonObjIter(const CxJsonValue *value) {
     );
 }
 
-CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name) {
-    CxJsonObjValue *member = json_find_objvalue(value, name);
-    if (member == NULL) {
+CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) {
+    size_t index = json_find_objvalue(value, name);
+    if (index >= value->value.object.values_size) {
         return &cx_json_value_nothing;
     } else {
-        return member->value;
+        return value->value.object.values[index].value;
+    }
+}
+
+CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) {
+    size_t index = json_find_objvalue(value, name);
+    if (index >= value->value.object.values_size) {
+        return NULL;
+    } else {
+        CxJsonObjValue kv = value->value.object.values[index];
+        cx_strfree_a(value->allocator, &kv.name);
+        // TODO: replace with cx_array_remove()
+        value->value.object.values_size--;
+        memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue));
+        return kv.value;
     }
 }
 
diff --git a/ucx/kv_list.c b/ucx/kv_list.c
new file mode 100644 (file)
index 0000000..7126952
--- /dev/null
@@ -0,0 +1,703 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/kv_list.h"
+#include "cx/hash_map.h"
+#include "cx/linked_list.h"
+
+#include <string.h>
+#include <assert.h>
+
+typedef struct cx_kv_list_s cx_kv_list;
+
+struct cx_kv_list_map_s {
+    struct cx_hash_map_s map_base;
+    /** Back-reference to the list. */
+    cx_kv_list *list;
+};
+
+struct cx_kv_list_s {
+    struct cx_linked_list_s list;
+    /** The lookup map - stores pointers to the nodes. */
+    struct cx_kv_list_map_s *map;
+    const cx_list_class *list_methods;
+    const cx_map_class *map_methods;
+    cx_destructor_func list_destr;
+    cx_destructor_func2 list_destr2;
+    void *list_destr_data;
+    cx_destructor_func map_destr;
+    cx_destructor_func2 map_destr2;
+    void *map_destr_data;
+};
+
+static void cx_kv_list_destructor_wrapper(void *list_ptr, void *elem) {
+    const cx_kv_list *kv_list = list_ptr;
+    // list destructor is already called with proper deref of the elem
+    if (kv_list->list_destr) {
+        kv_list->list_destr(elem);
+    }
+    if (kv_list->list_destr2) {
+        kv_list->list_destr2(kv_list->list_destr_data, elem);
+    }
+    if (kv_list->map_destr) {
+        kv_list->map_destr(elem);
+    }
+    if (kv_list->map_destr2) {
+        kv_list->map_destr2(kv_list->map_destr_data, elem);
+    }
+}
+
+static void cx_kv_list_update_destructors(cx_kv_list *list) {
+    // we copy the destructors to our custom fields and register
+    // an own destructor function which invokes all these
+    if (list->list.base.collection.simple_destructor != NULL) {
+        list->list_destr = list->list.base.collection.simple_destructor;
+        list->list.base.collection.simple_destructor = NULL;
+    }
+    if (list->list.base.collection.advanced_destructor != cx_kv_list_destructor_wrapper) {
+        list->list_destr2 = list->list.base.collection.advanced_destructor;
+        list->list_destr_data = list->list.base.collection.destructor_data;
+        list->list.base.collection.advanced_destructor = cx_kv_list_destructor_wrapper;
+        list->list.base.collection.destructor_data = list;
+    }
+    if (list->map->map_base.base.collection.simple_destructor != NULL) {
+        list->map_destr = list->map->map_base.base.collection.simple_destructor;
+        list->map->map_base.base.collection.simple_destructor = NULL;
+    }
+    if (list->map->map_base.base.collection.advanced_destructor != NULL) {
+        list->map_destr2 = list->map->map_base.base.collection.advanced_destructor;
+        list->map_destr_data = list->map->map_base.base.collection.destructor_data;
+        list->map->map_base.base.collection.advanced_destructor = NULL;
+        list->map->map_base.base.collection.destructor_data = NULL;
+    }
+}
+
+static CxHashKey *cx_kv_list_loc_key(cx_kv_list *list, void *node_data) {
+    return (CxHashKey*)((char*)node_data + list->list.base.collection.elem_size);
+}
+
+static void cx_kvl_deallocate(struct cx_list_s *list) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    // patch the destructors
+    cx_kv_list_update_destructors(kv_list);
+    kv_list->map_methods->deallocate(&kv_list->map->map_base.base);
+    // then free the list, now the destructors may be called
+    kv_list->list_methods->deallocate(list);
+}
+
+static void *cx_kvl_insert_element(
+        struct cx_list_s *list,
+        size_t index,
+        const void *data
+) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    return kv_list->list_methods->insert_element(list, index, data);
+}
+
+static size_t cx_kvl_insert_array(
+        struct cx_list_s *list,
+        size_t index,
+        const void *data,
+        size_t n
+) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    return kv_list->list_methods->insert_array(list, index, data, n);
+}
+
+static size_t cx_kvl_insert_sorted(
+        struct cx_list_s *list,
+        const void *sorted_data,
+        size_t n
+) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    return kv_list->list_methods->insert_sorted(list, sorted_data, n);
+}
+
+static size_t cx_kvl_insert_unique(
+        struct cx_list_s *list,
+        const void *sorted_data,
+        size_t n
+) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    return kv_list->list_methods->insert_unique(list, sorted_data, n);
+}
+
+static int cx_kvl_insert_iter(
+        struct cx_iterator_s *iter,
+        const void *elem,
+        int prepend
+) {
+    cx_kv_list *kv_list = iter->src_handle.m;
+    return kv_list->list_methods->insert_iter(iter, elem, prepend);
+}
+
+static size_t cx_kvl_remove(
+        struct cx_list_s *list,
+        size_t index,
+        size_t num,
+        void *targetbuf
+) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    // patch the destructors
+    // we also have to do that when targetbuf is NULL,
+    // because we do not want wrong destructors to be called when we remove keys from the map
+    cx_kv_list_update_destructors(kv_list);
+    // iterate through the elements first and remove their keys from the map
+    CxIterator iter = kv_list->list_methods->iterator(list, index, false);
+    for (size_t i = 0; i < num && cxIteratorValid(iter); i++) {
+        void *node_data = cxIteratorCurrent(iter);
+        CxHashKey *key = cx_kv_list_loc_key(kv_list, node_data);
+        // when the hash is zero, there is no key assigned to that element
+        if (key->hash != 0) {
+            kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL);
+        }
+        cxIteratorNext(iter);
+    }
+    return kv_list->list_methods->remove(list, index, num, targetbuf);
+}
+
+static void cx_kvl_clear(struct cx_list_s *list) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    // patch the destructors
+    cx_kv_list_update_destructors(kv_list);
+    // clear the list
+    kv_list->list_methods->clear(list);
+    // then clear the map
+    kv_list->map_methods->clear(&kv_list->map->map_base.base);
+}
+
+static int cx_kvl_swap(
+        struct cx_list_s *list,
+        size_t i,
+        size_t j
+) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    return kv_list->list_methods->swap(list, i, j);
+}
+
+static void *cx_kvl_at(
+        const struct cx_list_s *list,
+        size_t index
+) {
+    const cx_kv_list *kv_list = (const cx_kv_list*)list;
+    return kv_list->list_methods->at(list, index);
+}
+
+static size_t cx_kvl_find_remove(
+        struct cx_list_s *list,
+        const void *elem,
+        bool remove
+) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    // we do not use the original list methods,
+    // because that would need two passes for removal
+    // (the first to find the index, the second to get a pointer)
+    if (list->collection.size == 0) return 0;
+
+    size_t index;
+    cx_linked_list *ll = &kv_list->list;
+    char *node = cx_linked_list_find(
+            ll->begin,
+            ll->loc_next, ll->loc_data,
+            list->collection.cmpfunc, elem,
+            &index
+    );
+    if (node == NULL) {
+        return list->collection.size;
+    }
+    if (remove) {
+        cx_kv_list_update_destructors(kv_list);
+        cx_invoke_advanced_destructor(list, node + ll->loc_data);
+        cx_linked_list_remove(&ll->begin, &ll->end,
+                              ll->loc_prev, ll->loc_next, node);
+        CxHashKey *key = cx_kv_list_loc_key(kv_list, node + ll->loc_data);
+        if (key->hash != 0) {
+            kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL);
+        }
+        list->collection.size--;
+        cxFree(list->collection.allocator, node);
+    }
+    return index;
+}
+
+static void cx_kvl_sort(struct cx_list_s *list) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    kv_list->list_methods->sort(list);
+}
+
+static void cx_kvl_reverse(struct cx_list_s *list) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    kv_list->list_methods->reverse(list);
+}
+
+static void cx_kvl_list_iter_next(void *it) {
+    struct cx_iterator_s *iter = it;
+    if (iter->base.remove) {
+        // remove the assigned key from the map before calling the actual function
+        cx_kv_list *kv_list = iter->src_handle.m;
+        cx_kv_list_update_destructors(kv_list);
+        char *node = iter->elem_handle;
+        CxHashKey *key = cx_kv_list_loc_key(kv_list, node + kv_list->list.loc_data);
+        if (key->hash != 0) {
+            kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL);
+        }
+    }
+    iter->base.next_impl(it);
+}
+
+static struct cx_iterator_s cx_kvl_iterator(
+        const struct cx_list_s *list,
+        size_t index,
+        bool backward
+) {
+    const cx_kv_list *kv_list = (const cx_kv_list*)list;
+    struct cx_iterator_s iter = kv_list->list_methods->iterator(list, index, backward);
+    iter.base.next_impl = iter.base.next;
+    iter.base.next = cx_kvl_list_iter_next;
+    return iter;
+}
+
+static void cx_kvl_map_deallocate(struct cx_map_s *map) {
+    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+    kv_list->map_methods->deallocate(map);
+    kv_list->list_methods->deallocate(&kv_list->list.base);
+}
+
+static void cx_kvl_map_clear(struct cx_map_s *map) {
+    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+    cx_kv_list_update_destructors(kv_list);
+    kv_list->list_methods->clear(&kv_list->list.base);
+    kv_list->map_methods->clear(map);
+}
+
+static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
+    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+    // if the hash has not yet been computed, do it now
+    if (key.hash == 0) {
+        cx_hash_murmur(&key);
+    }
+
+    // reserve memory in the map first
+    void **map_data = kv_list->map_methods->put(map, key, NULL);
+    if (map_data == NULL) return NULL; // LCOV_EXCL_LINE
+
+    // insert the data into the list (which most likely destroys the sorted property)
+    kv_list->list.base.collection.sorted = false;
+    void *node_data = kv_list->list_methods->insert_element(
+        &kv_list->list.base, kv_list->list.base.collection.size,
+        kv_list->list.base.collection.store_pointer ? &value : value);
+    if (node_data == NULL) { // LCOV_EXCL_START
+        // non-destructively remove the key again
+        kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
+        return NULL;
+    } // LCOV_EXCL_STOP
+
+    // write the node pointer to the map entry
+    *map_data = node_data;
+
+    // copy the key to the node data
+    CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
+    *key_ptr = key;
+
+    // we must return node_data here and not map_data,
+    // because the node_data is the actual element of this collection
+    return node_data;
+}
+
+void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
+    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+    void *node_data = kv_list->map_methods->get(map, key);
+    if (node_data == NULL) return NULL; // LCOV_EXCL_LINE
+    // return the node data
+    return kv_list->list.base.collection.store_pointer ? *(void**)node_data : node_data;
+}
+
+int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
+    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+
+    void *node_data;
+    if (kv_list->map_methods->remove(map, key, &node_data)) {
+        return 1;
+    }
+    // we cannot just call a list method (because we don't have the index)
+    // and tbh. we also don't want to (because it's not performant when we
+    // can have the node ptr directly instead)
+    // therefore, we re-implement the logic ourselves
+
+    // check if the outside caller want's us to return or to destroy the element
+    if (targetbuf == NULL) {
+        // patch the destructors and invoke them through the wrapper
+        cx_kv_list_update_destructors(kv_list);
+        cx_invoke_advanced_destructor(&kv_list->list.base, node_data);
+    } else {
+        // copy the element to the target buffer
+        memcpy(targetbuf, node_data, kv_list->list.base.collection.elem_size);
+    }
+
+    // calculate the address of the node
+    void *node_ptr = (char*)node_data - kv_list->list.loc_data;
+
+    // unlink the node
+    cx_linked_list_remove(
+        &kv_list->list.begin,
+        &kv_list->list.end,
+        kv_list->list.loc_prev,
+        kv_list->list.loc_next,
+        node_ptr
+    );
+
+    // decrement the list's size
+    kv_list->list.base.collection.size--;
+
+    // deallocate the node
+    cxFree(kv_list->list.base.collection.allocator, node_ptr);
+
+    return 0;
+}
+
+static void *cx_kvl_iter_current_entry(const void *it) {
+    const CxMapIterator *iter = it;
+    return (void*)&iter->entry;
+}
+
+static void *cx_kvl_iter_current_key(const void *it) {
+    const CxMapEntry *entry = cx_kvl_iter_current_entry(it);
+    return (void*)entry->key;
+}
+
+static void *cx_kvl_iter_current_value(const void *it) {
+    const CxMapEntry *entry = cx_kvl_iter_current_entry(it);
+    return entry->value;
+}
+
+static void cx_kvl_iter_next(void *it) {
+    CxMapIterator *iter = it;
+    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)iter->map.m)->list;
+
+    // find the next list entry that has a key assigned
+    CxHashKey *key = NULL;
+    char *next = iter->elem;
+    while (true) {
+        next = *(char**)(next + kv_list->list.loc_next);
+        if (next == NULL) break;
+        key = cx_kv_list_loc_key(kv_list, next + kv_list->list.loc_data);
+        if (key->hash != 0) break;
+    }
+
+    // remove previous element if requested
+    if (iter->base.remove) {
+        iter->base.remove = false;
+        cx_kv_list_update_destructors(kv_list);
+        char *elem = iter->elem;
+        char *elem_data = elem + kv_list->list.loc_data;
+        CxHashKey *elem_key = cx_kv_list_loc_key(kv_list, elem_data);
+        // key is guaranteed to exist because iterator only iterates over elems with a key
+        kv_list->map_methods->remove(&kv_list->map->map_base.base, *elem_key, NULL);
+        cx_invoke_advanced_destructor(&kv_list->list.base, elem_data);
+        cx_linked_list_remove(
+            &kv_list->list.begin,
+            &kv_list->list.end,
+            kv_list->list.loc_prev,
+            kv_list->list.loc_next,
+            elem
+        );
+        cxFree(kv_list->list.base.collection.allocator, elem);
+        kv_list->list.base.collection.size--;
+        iter->index--;
+        iter->elem_count--;
+    }
+
+    // advance to the next element, if any
+    if (next == NULL) {
+        iter->index = kv_list->list.base.collection.size;
+        iter->elem = NULL;
+        iter->entry = (CxMapEntry){NULL, NULL};
+        return;
+    }
+    iter->index++;
+    iter->elem = next;
+    iter->entry.key = key;
+    if (kv_list->list.base.collection.store_pointer) {
+        iter->entry.value = *(void**)(next + kv_list->list.loc_data);
+    } else {
+        iter->entry.value = (void*)(next + kv_list->list.loc_data);
+    }
+}
+
+static bool cx_kvl_iter_valid(const void *it) {
+    const CxMapIterator *iter = it;
+    return iter->elem != NULL;
+}
+
+CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) {
+    CxMapIterator iter = {0};
+
+    iter.type = type;
+    iter.map.c = map;
+    // although we iterate over the list, we only report that many elements that have a key in the map
+    iter.elem_count = map->collection.size;
+
+    switch (type) {
+        case CX_MAP_ITERATOR_PAIRS:
+            iter.elem_size = sizeof(CxMapEntry);
+            iter.base.current = cx_kvl_iter_current_entry;
+            break;
+        case CX_MAP_ITERATOR_KEYS:
+            iter.elem_size = sizeof(CxHashKey);
+            iter.base.current = cx_kvl_iter_current_key;
+            break;
+        case CX_MAP_ITERATOR_VALUES:
+            iter.elem_size = map->collection.elem_size;
+            iter.base.current = cx_kvl_iter_current_value;
+            break;
+        default:
+            assert(false); // LCOV_EXCL_LINE
+    }
+
+    iter.base.next = cx_kvl_iter_next;
+    iter.base.valid = cx_kvl_iter_valid;
+
+    // find the first list entry that has a key assigned
+    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+    CxHashKey *key = NULL;
+    char *next = kv_list->list.begin;
+    while (next != NULL) {
+        key = cx_kv_list_loc_key(kv_list, next + kv_list->list.loc_data);
+        if (key->hash != 0) break;
+        next = *(char**)(next + kv_list->list.loc_next);
+    }
+    if (next == NULL) {
+        iter.elem = NULL;
+        iter.entry = (CxMapEntry){NULL, NULL};
+    } else {
+        iter.elem = next;
+        iter.entry.key = key;
+        if (kv_list->list.base.collection.store_pointer) {
+            iter.entry.value = *(void**)(next + kv_list->list.loc_data);
+        } else {
+            iter.entry.value = (void*)(next + kv_list->list.loc_data);
+        }
+    }
+
+    return iter;
+}
+
+static cx_list_class cx_kv_list_class = {
+    cx_kvl_deallocate,
+    cx_kvl_insert_element,
+    cx_kvl_insert_array,
+    cx_kvl_insert_sorted,
+    cx_kvl_insert_unique,
+    cx_kvl_insert_iter,
+    cx_kvl_remove,
+    cx_kvl_clear,
+    cx_kvl_swap,
+    cx_kvl_at,
+    cx_kvl_find_remove,
+    cx_kvl_sort,
+    NULL,
+    cx_kvl_reverse,
+    cx_kvl_iterator,
+};
+
+static cx_map_class cx_kv_map_class = {
+    cx_kvl_map_deallocate,
+    cx_kvl_map_clear,
+    cx_kvl_map_put,
+    cx_kvl_map_get,
+    cx_kvl_map_remove,
+    cx_kvl_map_iterator,
+};
+
+CxList *cxKvListCreate(
+        const CxAllocator *allocator,
+        cx_compare_func comparator,
+        size_t elem_size
+) {
+    if (allocator == NULL) {
+        allocator = cxDefaultAllocator;
+    }
+
+    // create a normal linked list and a normal hash map, first
+    CxList *list = cxLinkedListCreate(allocator, comparator, elem_size);
+    if (list == NULL) return NULL; // LCOV_EXCL_LINE
+    cx_linked_list *ll = (cx_linked_list*)list;
+    ll->extra_data_len = sizeof(CxHashKey);
+    CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
+    if (map == NULL) { // LCOV_EXCL_START
+        cxListFree(list);
+        return NULL;
+    } // LCOV_EXCL_STOP
+
+    // patch the kv-list class with the compare function of the linked list
+    // this allows cxListCompare() to optimize comparisons between linked lists and kv-list
+    cx_kv_list_class.compare = list->cl->compare;
+
+    // reallocate the map to add memory for the list back-reference
+    struct cx_kv_list_map_s *kv_map = cxRealloc(allocator, map, sizeof(struct cx_kv_list_map_s));
+    
+    // reallocate the list to add memory for storing the metadata
+    cx_kv_list *kv_list = cxRealloc(allocator, list, sizeof(struct cx_kv_list_s));
+
+    // if any of the reallocations failed, we bail out
+    if (kv_map != NULL && kv_list != NULL) {
+        map = (CxMap*) kv_map;
+        list = (CxList*) kv_list;
+    } else { // LCOV_EXCL_START
+        cxListFree(list);
+        cxMapFree(map);
+        return NULL;
+    } // LCOV_EXCL_STOP
+
+    // zero the custom destructor information
+    memset((char*)kv_list + offsetof(cx_kv_list, list_destr), 0, sizeof(void*)*6);
+
+    // combine the list and the map aspect
+    kv_list->map = kv_map;
+    kv_map->list = kv_list;
+
+    // remember the base methods and override them
+    kv_list->map_methods = map->cl;
+    map->cl = &cx_kv_map_class;
+    if (list->climpl == NULL) {
+        kv_list->list_methods = list->cl;
+        list->cl = &cx_kv_list_class;
+    } else {
+        kv_list->list_methods = list->climpl;
+        list->climpl = &cx_kv_list_class;
+    }
+
+    return list;
+}
+
+CxMap *cxKvListCreateAsMap(
+        const CxAllocator *allocator,
+        cx_compare_func comparator,
+        size_t elem_size
+) {
+    CxList *list = cxKvListCreate(allocator, comparator, elem_size);
+    return list == NULL ? NULL : cxKvListAsMap(list);
+}
+
+CxList *cxKvListAsList(CxMap *map) {
+    return &((struct cx_kv_list_map_s*)map)->list->list.base;
+}
+
+CxMap *cxKvListAsMap(CxList *list) {
+    return &((cx_kv_list*)list)->map->map_base.base;
+}
+
+int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    void *node_data = kv_list->list_methods->at(list, index);
+    if (node_data == NULL) {
+        return 1;
+    }
+    // if the hash has not yet been computed, do it now
+    if (key.hash == 0) {
+        cx_hash_murmur(&key);
+    }
+
+    // check if the key is already assigned
+    void *existing = kv_list->map_methods->get(&kv_list->map->map_base.base, key);
+    if (existing == node_data) {
+        return 0; // nothing to do
+    }
+    if (existing != NULL) {
+        // the key is already assigned to another node, we disallow re-assignment
+        return 1;
+    }
+
+    // add the key to the map;
+    if (NULL == kv_list->map_methods->put(&kv_list->map->map_base.base, key, node_data)) {
+        return 1; // LCOV_EXCL_LINE
+    }
+
+    // write the key to the list's node
+    CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
+    *loc_key = key;
+
+    return 0;
+}
+
+int cxKvListRemoveKey(CxList *list, size_t index) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    void *node_data = kv_list->list_methods->at(list, index);
+    if (node_data == NULL) {
+        return 1;
+    }
+    CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
+    if (loc_key->hash == 0) {
+        return 0;
+    }
+    kv_list->map_methods->remove(&kv_list->map->map_base.base, *loc_key, NULL);
+    // also zero the memory in the list node,
+    // but don't free the key data (that was done by the map remove)
+    memset(loc_key, 0, sizeof(CxHashKey));
+    return 0;
+}
+
+const CxHashKey *cxKvListGetKey(CxList *list, size_t index) {
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    void *node_data = kv_list->list_methods->at(list, index);
+    if (node_data == NULL) {
+        return NULL;
+    }
+    CxHashKey *key = cx_kv_list_loc_key(kv_list, node_data);
+    if (key->hash == 0) {
+        return NULL;
+    }
+    return key;
+}
+
+int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value) {
+    // assume we are losing the sorted property
+    list->collection.sorted = false;
+
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+
+    // reserve memory in the map
+    void **map_data = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL);
+    if (map_data == NULL) return 1; // LCOV_EXCL_LINE
+
+    // insert the node
+    void *node_data = kv_list->list_methods->insert_element(&kv_list->list.base, index,
+        kv_list->list.base.collection.store_pointer ? &value : value);
+    if (node_data == NULL) { // LCOV_EXCL_START
+        // non-destructively remove the key again
+        kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
+        return 1;
+    } // LCOV_EXCL_STOP
+    *map_data = node_data;
+
+    // write the key to the node
+    CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
+    *loc_key = key;
+
+    return 0;
+}
index 7cd2ff2e49a0608d01a2294425746a294ce08534..e9d9332adf0d130a2cb343c3fd84558e2a099a56 100644 (file)
@@ -30,6 +30,7 @@
 #include "cx/compare.h"
 #include <string.h>
 #include <assert.h>
+#include <unistd.h>
 
 // LOW LEVEL LINKED LIST FUNCTIONS
 
@@ -244,80 +245,184 @@ void cx_linked_list_insert_sorted(
             begin, end, loc_prev, loc_next, new_node, cmp_func);
 }
 
-void cx_linked_list_insert_sorted_chain(
+static void *cx_linked_list_insert_sorted_chain_impl(
         void **begin,
         void **end,
         ptrdiff_t loc_prev,
         ptrdiff_t loc_next,
         void *insert_begin,
-        cx_compare_func cmp_func
+        cx_compare_func cmp_func,
+        bool allow_duplicates
 ) {
     assert(begin != NULL);
     assert(loc_next >= 0);
     assert(insert_begin != NULL);
 
-    // track currently observed nodes
-    void *dest_prev = NULL;
-    void *dest = *begin;
-    void *src = insert_begin;
-
-    // special case: list is empty
-    if (dest == NULL) {
-        *begin = src;
-        if (end != NULL) {
-            *end = cx_linked_list_last(src, loc_next);
+    // strategy: build completely new chains from scratch
+    void *source_original = *begin;
+    void *source_argument = insert_begin;
+    void *new_begin = NULL;
+    void *new_end = NULL;
+    void *dup_begin = NULL;
+    void *dup_end = NULL;
+
+    // determine the new start
+    {
+        int d = source_original ==  NULL ? 1 : cmp_func(source_original, source_argument);
+        if (d <= 0) {
+            // the new chain starts with the original chain
+            new_begin = new_end = source_original;
+            source_original = ll_next(source_original);
+            if (d == 0) {
+                if (allow_duplicates) {
+                    // duplicate allowed, append it to the chain
+                    cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
+                    new_end = source_argument;
+                } else {
+                    // duplicate is not allowed, start a duplicate chain with the argument
+                    dup_begin = dup_end = source_argument;
+                }
+                source_argument = ll_next(source_argument);
+            }
+        } else {
+            // input is smaller, or there is no original chain;
+            // start the new chain with the source argument
+            new_begin = new_end = source_argument;
+            source_argument = ll_next(source_argument);
         }
-        return;
     }
 
-    // search the list for insertion points
-    while (dest != NULL && src != NULL) {
-        // compare current list node with source node
-        // if less or equal, skip
-        if (cmp_func(dest, src) <= 0) {
-            dest_prev = dest;
-            dest = ll_next(dest);
-            continue;
-        }
-
-        // determine chain of elements that can be inserted
-        void *end_of_chain = src;
-        void *next_in_chain = ll_next(src);
-        while (next_in_chain != NULL) {
-            // once we become larger than the list elem, break
-            if (cmp_func(dest, next_in_chain) <= 0) {
-                break;
+    // now successively compare the elements and add them to the correct chains
+    while (source_original != NULL && source_argument != NULL) {
+        int d = cmp_func(source_original, source_argument);
+        if (d <= 0) {
+            // the original is not larger, add it to the chain
+            cx_linked_list_link(new_end, source_original, loc_prev, loc_next);
+            new_end = source_original;
+            source_original = ll_next(source_original);
+            if (d == 0) {
+                if (allow_duplicates) {
+                    // duplicate allowed, append it to the chain
+                    cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
+                    new_end = source_argument;
+                } else {
+                    // duplicate is not allowed, append it to the duplicate chain
+                    if (dup_end == NULL) {
+                        dup_begin = dup_end = source_argument;
+                    } else {
+                        cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
+                        dup_end = source_argument;
+                    }
+                }
+                source_argument = ll_next(source_argument);
             }
-            // otherwise, we can insert one more
-            end_of_chain = next_in_chain;
-            next_in_chain = ll_next(next_in_chain);
+        } else {
+            // the original is larger, append the source argument to the chain
+            // check if we must discard the source argument as duplicate
+            if (!allow_duplicates && cmp_func(new_end, source_argument) == 0) {
+                if (dup_end == NULL) {
+                    dup_begin = dup_end = source_argument;
+                } else {
+                    cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
+                    dup_end = source_argument;
+                }
+            } else {
+                // no duplicate or duplicates allowed
+                cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
+                new_end = source_argument;
+            }
+            source_argument = ll_next(source_argument);
         }
+    }
 
-        // insert the elements
-        if (dest_prev == NULL) {
-            // new begin
-            *begin = src;
+    if (source_original != NULL) {
+        // something is left from the original chain, append it
+        cx_linked_list_link(new_end, source_original, loc_prev, loc_next);
+        new_end = cx_linked_list_last(source_original, loc_next);
+    } else if (source_argument != NULL) {
+        // something is left from the input chain;
+        // when we allow duplicates, append it
+        if (allow_duplicates) {
+            cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
+            new_end = cx_linked_list_last(source_argument, loc_next);
         } else {
-            cx_linked_list_link(dest_prev, src, loc_prev, loc_next);
+            // otherwise we must check one-by-one
+            while (source_argument != NULL) {
+                if (cmp_func(new_end, source_argument) == 0) {
+                    if (dup_end == NULL) {
+                        dup_begin = dup_end = source_argument;
+                    } else {
+                        cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
+                        dup_end = source_argument;
+                    }
+                } else {
+                    cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
+                    new_end = source_argument;
+                }
+                source_argument = ll_next(source_argument);
+            }
         }
-        cx_linked_list_link(end_of_chain, dest, loc_prev, loc_next);
+    }
 
-        // continue with next
-        src = next_in_chain;
-        dest_prev = dest;
-        dest = ll_next(dest);
+    // null the next pointers at the end of the chain
+    ll_next(new_end) = NULL;
+    if (dup_end != NULL) {
+        ll_next(dup_end) = NULL;
     }
 
-    // insert remaining items
-    if (src != NULL) {
-        cx_linked_list_link(dest_prev, src, loc_prev, loc_next);
+    // null the optional prev pointers
+    if (loc_prev >= 0) {
+        ll_prev(new_begin) = NULL;
+        if (dup_begin != NULL) {
+            ll_prev(dup_begin) = NULL;
+        }
     }
 
-    // determine new end of list, if requested
+    // output
+    *begin = new_begin;
     if (end != NULL) {
-        *end = cx_linked_list_last(
-                dest != NULL ? dest : dest_prev, loc_next);
+        *end = new_end;
     }
+    return dup_begin;
+}
+
+void cx_linked_list_insert_sorted_chain(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *insert_begin,
+        cx_compare_func cmp_func
+) {
+    cx_linked_list_insert_sorted_chain_impl(
+            begin, end, loc_prev, loc_next,
+            insert_begin, cmp_func, true);
+}
+
+int cx_linked_list_insert_unique(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *new_node,
+        cx_compare_func cmp_func
+) {
+    assert(ll_next(new_node) == NULL);
+    return NULL != cx_linked_list_insert_unique_chain(
+            begin, end, loc_prev, loc_next, new_node, cmp_func);
+}
+
+void *cx_linked_list_insert_unique_chain(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *insert_begin,
+        cx_compare_func cmp_func
+) {
+    return cx_linked_list_insert_sorted_chain_impl(
+            begin, end, loc_prev, loc_next,
+            insert_begin, cmp_func, false);
 }
 
 size_t cx_linked_list_remove_chain(
@@ -564,64 +669,46 @@ void cx_linked_list_reverse(
 
 // HIGH LEVEL LINKED LIST IMPLEMENTATION
 
-typedef struct cx_linked_list_node cx_linked_list_node;
-struct cx_linked_list_node {
-    cx_linked_list_node *prev;
-    cx_linked_list_node *next;
-    char payload[];
-};
-
-#define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev)
-#define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next)
-#define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload)
-
-typedef struct {
-    struct cx_list_s base;
-    cx_linked_list_node *begin;
-    cx_linked_list_node *end;
-} cx_linked_list;
-
-static cx_linked_list_node *cx_ll_node_at(
+static void *cx_ll_node_at(
         const cx_linked_list *list,
         size_t index
 ) {
     if (index >= list->base.collection.size) {
         return NULL;
     } else if (index > list->base.collection.size / 2) {
-        return cx_linked_list_at(list->end, list->base.collection.size - 1, CX_LL_LOC_PREV, index);
+        return cx_linked_list_at(list->end, list->base.collection.size - 1, list->loc_prev, index);
     } else {
-        return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index);
+        return cx_linked_list_at(list->begin, 0, list->loc_next, index);
     }
 }
 
-static cx_linked_list_node *cx_ll_malloc_node(const struct cx_list_s *list) {
-    return cxMalloc(list->collection.allocator,
-                    sizeof(cx_linked_list_node) + list->collection.elem_size);
+static void *cx_ll_malloc_node(const cx_linked_list *list) {
+    return cxZalloc(list->base.collection.allocator,
+                    list->loc_data + list->base.collection.elem_size + list->extra_data_len);
 }
 
 static int cx_ll_insert_at(
         struct cx_list_s *list,
-        cx_linked_list_node *node,
+        void *node,
         const void *elem
 ) {
+    cx_linked_list *ll = (cx_linked_list *) list;
 
     // create the new new_node
-    cx_linked_list_node *new_node = cx_ll_malloc_node(list);
+    void *new_node = cx_ll_malloc_node(ll);
 
     // sortir if failed
     if (new_node == NULL) return 1;
 
-    // initialize new new_node
-    new_node->prev = new_node->next = NULL;
+    // copy the data
     if (elem != NULL) {
-        memcpy(new_node->payload, elem, list->collection.elem_size);
+        memcpy((char*)new_node + ll->loc_data, elem, list->collection.elem_size);
     }
 
     // insert
-    cx_linked_list *ll = (cx_linked_list *) list;
     cx_linked_list_insert_chain(
-            (void **) &ll->begin, (void **) &ll->end,
-            CX_LL_LOC_PREV, CX_LL_LOC_NEXT,
+            &ll->begin, &ll->end,
+            ll->loc_prev, ll->loc_next,
             node, new_node, new_node
     );
 
@@ -640,7 +727,7 @@ static size_t cx_ll_insert_array(
     if (index > list->collection.size || n == 0) return 0;
 
     // find position efficiently
-    cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
+    void *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
 
     // perform first insert
     if (0 != cx_ll_insert_at(list, node, array)) return 1;
@@ -649,14 +736,15 @@ static size_t cx_ll_insert_array(
     if (n == 1) return 1;
 
     // we now know exactly where we are
-    node = node == NULL ? ((cx_linked_list *) list)->begin : node->next;
+    cx_linked_list *ll = (cx_linked_list *) list;
+    node = node == NULL ? ((cx_linked_list *) list)->begin : CX_LL_PTR(node, ll->loc_next);
 
     // we can add the remaining nodes and immediately advance to the inserted node
     const char *source = array;
     for (size_t i = 1; i < n; i++) {
         source += list->collection.elem_size;
         if (0 != cx_ll_insert_at(list, node, source)) return i;
-        node = node->next;
+        node = CX_LL_PTR(node, ll->loc_next);
     }
     return n;
 }
@@ -670,76 +758,113 @@ static void *cx_ll_insert_element(
     if (index > list->collection.size) return NULL;
 
     // find position efficiently
-    cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
+    void *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
 
     // perform first insert
     if (cx_ll_insert_at(list, node, element)) return NULL;
 
     // return a pointer to the data of the inserted node
+    cx_linked_list *ll = (cx_linked_list *) list;
     if (node == NULL) {
-        return ((cx_linked_list *) list)->begin->payload;
+        return (char*)(ll->begin) + ll->loc_data;
     } else {
-        return node->next->payload;
+        char *next = CX_LL_PTR(node, ll->loc_next);
+        return next + ll->loc_data;
     }
 }
 
 static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func;
+static _Thread_local off_t cx_ll_insert_sorted_loc_data;
 
 static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) {
-    const cx_linked_list_node *left = l;
-    const cx_linked_list_node *right = r;
-    return cx_ll_insert_sorted_cmp_func(left->payload, right->payload);
+    const char *left = (const char*)l + cx_ll_insert_sorted_loc_data;
+    const char *right = (const char*)r + cx_ll_insert_sorted_loc_data;
+    return cx_ll_insert_sorted_cmp_func(left, right);
 }
 
-static size_t cx_ll_insert_sorted(
+static size_t cx_ll_insert_sorted_impl(
         struct cx_list_s *list,
         const void *array,
-        size_t n
+        size_t n,
+        bool allow_duplicates
 ) {
+    cx_linked_list *ll = (cx_linked_list *) list;
+
     // special case
     if (n == 0) return 0;
 
     // create a new chain of nodes
-    cx_linked_list_node *chain = cx_ll_malloc_node(list);
+    void *chain = cx_ll_malloc_node(ll);
     if (chain == NULL) return 0;
 
-    memcpy(chain->payload, array, list->collection.elem_size);
-    chain->prev = NULL;
-    chain->next = NULL;
+    memcpy((char*)chain + ll->loc_data, array, list->collection.elem_size);
 
     // add all elements from the array to that chain
-    cx_linked_list_node *prev = chain;
+    void *prev = chain;
     const char *src = array;
     size_t inserted = 1;
     for (; inserted < n; inserted++) {
-        cx_linked_list_node *next = cx_ll_malloc_node(list);
+        void *next = cx_ll_malloc_node(ll);
         if (next == NULL) break;
         src += list->collection.elem_size;
-        memcpy(next->payload, src, list->collection.elem_size);
-        prev->next = next;
-        next->prev = prev;
+        memcpy((char*)next + ll->loc_data, src, list->collection.elem_size);
+        CX_LL_PTR(prev, ll->loc_next) = next;
+        CX_LL_PTR(next, ll->loc_prev) = prev;
         prev = next;
     }
-    prev->next = NULL;
+    CX_LL_PTR(prev, ll->loc_next) = NULL;
 
     // invoke the low level function
-    cx_linked_list *ll = (cx_linked_list *) list;
     cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc;
-    cx_linked_list_insert_sorted_chain(
-            (void **) &ll->begin,
-            (void **) &ll->end,
-            CX_LL_LOC_PREV,
-            CX_LL_LOC_NEXT,
-            chain,
-            cx_ll_insert_sorted_cmp_helper
-    );
-
-    // adjust the list metadata
-    list->collection.size += inserted;
+    cx_ll_insert_sorted_loc_data = ll->loc_data;
+    if (allow_duplicates) {
+        cx_linked_list_insert_sorted_chain(
+                &ll->begin,
+                &ll->end,
+                ll->loc_prev,
+                ll->loc_next,
+                chain,
+                cx_ll_insert_sorted_cmp_helper
+        );
+        list->collection.size += inserted;
+    } else {
+        void *duplicates = cx_linked_list_insert_unique_chain(
+                &ll->begin,
+                &ll->end,
+                ll->loc_prev,
+                ll->loc_next,
+                chain,
+                cx_ll_insert_sorted_cmp_helper
+        );
+        list->collection.size += inserted;
+        // free the nodes that did not make it into the list
+        while (duplicates != NULL) {
+            void *next = CX_LL_PTR(duplicates, ll->loc_next);
+            cxFree(list->collection.allocator, duplicates);
+            duplicates = next;
+            list->collection.size--;
+        }
+    }
 
     return inserted;
 }
 
+static size_t cx_ll_insert_sorted(
+        struct cx_list_s *list,
+        const void *array,
+        size_t n
+) {
+    return cx_ll_insert_sorted_impl(list, array, n, true);
+}
+
+static size_t cx_ll_insert_unique(
+        struct cx_list_s *list,
+        const void *array,
+        size_t n
+) {
+    return cx_ll_insert_sorted_impl(list, array, n, false);
+}
+
 static size_t cx_ll_remove(
         struct cx_list_s *list,
         size_t index,
@@ -747,7 +872,7 @@ static size_t cx_ll_remove(
         void *targetbuf
 ) {
     cx_linked_list *ll = (cx_linked_list *) list;
-    cx_linked_list_node *node = cx_ll_node_at(ll, index);
+    void *node = cx_ll_node_at(ll, index);
 
     // out-of-bounds check
     if (node == NULL) return 0;
@@ -756,8 +881,8 @@ static size_t cx_ll_remove(
     size_t removed = cx_linked_list_remove_chain(
             (void **) &ll->begin,
             (void **) &ll->end,
-            CX_LL_LOC_PREV,
-            CX_LL_LOC_NEXT,
+            ll->loc_prev,
+            ll->loc_next,
             node,
             num
     );
@@ -767,28 +892,28 @@ static size_t cx_ll_remove(
 
     // copy or destroy the removed chain
     if (targetbuf == NULL) {
-        cx_linked_list_node *n = node;
+        char *n = node;
         for (size_t i = 0; i < removed; i++) {
             // element destruction
-            cx_invoke_destructor(list, n->payload);
+            cx_invoke_destructor(list, n + ll->loc_data);
 
             // free the node and advance
-            void *next = n->next;
+            void *next = CX_LL_PTR(n, ll->loc_next);
             cxFree(list->collection.allocator, n);
             n = next;
         }
     } else {
         char *dest = targetbuf;
-        cx_linked_list_node *n = node;
+        char *n = node;
         for (size_t i = 0; i < removed; i++) {
             // copy payload
-            memcpy(dest, n->payload, list->collection.elem_size);
+            memcpy(dest, n + ll->loc_data, list->collection.elem_size);
 
             // advance target buffer
             dest += list->collection.elem_size;
 
             // free the node and advance
-            void *next = n->next;
+            void *next = CX_LL_PTR(n, ll->loc_next);
             cxFree(list->collection.allocator, n);
             n = next;
         }
@@ -801,10 +926,10 @@ static void cx_ll_clear(struct cx_list_s *list) {
     if (list->collection.size == 0) return;
 
     cx_linked_list *ll = (cx_linked_list *) list;
-    cx_linked_list_node *node = ll->begin;
+    char *node = ll->begin;
     while (node != NULL) {
-        cx_invoke_destructor(list, node->payload);
-        cx_linked_list_node *next = node->next;
+        cx_invoke_destructor(list, node + ll->loc_data);
+        void *next = CX_LL_PTR(node, ll->loc_next);
         cxFree(list->collection.allocator, node);
         node = next;
     }
@@ -831,14 +956,14 @@ static int cx_ll_swap(
         left = j;
         right = i;
     }
-    cx_linked_list_node *nleft = NULL, *nright = NULL;
+    void *nleft = NULL, *nright = NULL;
     if (left < mid && right < mid) {
         // case 1: both items left from mid
         nleft = cx_ll_node_at(ll, left);
         assert(nleft != NULL);
         nright = nleft;
         for (size_t c = left; c < right; c++) {
-            nright = nright->next;
+            nright = CX_LL_PTR(nright, ll->loc_next);
         }
     } else if (left >= mid && right >= mid) {
         // case 2: both items right from mid
@@ -846,7 +971,7 @@ static int cx_ll_swap(
         assert(nright != NULL);
         nleft = nright;
         for (size_t c = right; c > left; c--) {
-            nleft = nleft->prev;
+            nleft = CX_LL_PTR(nleft, ll->loc_prev);
         }
     } else {
         // case 3: one item left, one item right
@@ -872,12 +997,12 @@ static int cx_ll_swap(
             if (closest == left) {
                 nright = nleft;
                 for (size_t c = left; c < right; c++) {
-                    nright = nright->next;
+                    nright = CX_LL_PTR(nright, ll->loc_next);
                 }
             } else {
                 nleft = nright;
                 for (size_t c = right; c > left; c--) {
-                    nleft = nleft->prev;
+                    nleft = CX_LL_PTR(nleft, ll->loc_prev);
                 }
             }
         } else {
@@ -890,33 +1015,33 @@ static int cx_ll_swap(
         }
     }
 
-    cx_linked_list_node *prev = nleft->prev;
-    cx_linked_list_node *next = nright->next;
-    cx_linked_list_node *midstart = nleft->next;
-    cx_linked_list_node *midend = nright->prev;
+    void *prev = CX_LL_PTR(nleft, ll->loc_prev);
+    void *next = CX_LL_PTR(nright, ll->loc_next);
+    void *midstart = CX_LL_PTR(nleft, ll->loc_next);
+    void *midend = CX_LL_PTR(nright, ll->loc_prev);
 
     if (prev == NULL) {
         ll->begin = nright;
     } else {
-        prev->next = nright;
+        CX_LL_PTR(prev, ll->loc_next) = nright;
     }
-    nright->prev = prev;
+    CX_LL_PTR(nright, ll->loc_prev) = prev;
     if (midstart == nright) {
         // special case: both nodes are adjacent
-        nright->next = nleft;
-        nleft->prev = nright;
+        CX_LL_PTR(nright, ll->loc_next) = nleft;
+        CX_LL_PTR(nleft, ll->loc_prev) = nright;
     } else {
         // likely case: a chain is between the two nodes
-        nright->next = midstart;
-        midstart->prev = nright;
-        midend->next = nleft;
-        nleft->prev = midend;
+        CX_LL_PTR(nright, ll->loc_next) = midstart;
+        CX_LL_PTR(midstart, ll->loc_prev) = nright;
+        CX_LL_PTR(midend, ll->loc_next) = nleft;
+        CX_LL_PTR(nleft, ll->loc_prev) = midend;
     }
-    nleft->next = next;
+    CX_LL_PTR(nleft, ll->loc_next) = next;
     if (next == NULL) {
         ll->end = nleft;
     } else {
-        next->prev = nleft;
+        CX_LL_PTR(next, ll->loc_prev) = nleft;
     }
 
     return 0;
@@ -927,8 +1052,8 @@ static void *cx_ll_at(
         size_t index
 ) {
     cx_linked_list *ll = (cx_linked_list *) list;
-    cx_linked_list_node *node = cx_ll_node_at(ll, index);
-    return node == NULL ? NULL : node->payload;
+    char *node = cx_ll_node_at(ll, index);
+    return node == NULL ? NULL : node + ll->loc_data;
 }
 
 static size_t cx_ll_find_remove(
@@ -939,10 +1064,10 @@ static size_t cx_ll_find_remove(
     if (list->collection.size == 0) return 0;
 
     size_t index;
-    cx_linked_list *ll = ((cx_linked_list *) list);
-    cx_linked_list_node *node = cx_linked_list_find(
+    cx_linked_list *ll = (cx_linked_list *) list;
+    char *node = cx_linked_list_find(
             ll->begin,
-            CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+            ll->loc_next, ll->loc_data,
             list->collection.cmpfunc, elem,
             &index
     );
@@ -950,9 +1075,9 @@ static size_t cx_ll_find_remove(
         return list->collection.size;
     }
     if (remove) {
-        cx_invoke_destructor(list, node->payload);
-        cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
-                              CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+        cx_invoke_destructor(list, node + ll->loc_data);
+        cx_linked_list_remove(&ll->begin, &ll->end,
+                              ll->loc_prev, ll->loc_next, node);
         list->collection.size--;
         cxFree(list->collection.allocator, node);
     }
@@ -961,14 +1086,14 @@ static size_t cx_ll_find_remove(
 
 static void cx_ll_sort(struct cx_list_s *list) {
     cx_linked_list *ll = (cx_linked_list *) list;
-    cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end,
-                        CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+    cx_linked_list_sort(&ll->begin, &ll->end,
+                        ll->loc_prev, ll->loc_next, ll->loc_data,
                         list->collection.cmpfunc);
 }
 
 static void cx_ll_reverse(struct cx_list_s *list) {
     cx_linked_list *ll = (cx_linked_list *) list;
-    cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT);
+    cx_linked_list_reverse(&ll->begin, &ll->end, ll->loc_prev, ll->loc_next);
 }
 
 static int cx_ll_compare(
@@ -977,8 +1102,10 @@ static int cx_ll_compare(
 ) {
     cx_linked_list *left = (cx_linked_list *) list;
     cx_linked_list *right = (cx_linked_list *) other;
+    assert(left->loc_next == right->loc_next);
+    assert(left->loc_data == right->loc_data);
     return cx_linked_list_compare(left->begin, right->begin,
-                                  CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+                                  left->loc_next, left->loc_data,
                                   list->collection.cmpfunc);
 }
 
@@ -993,17 +1120,19 @@ static void cx_ll_iter_next(void *it) {
         iter->base.remove = false;
         struct cx_list_s *list = iter->src_handle.m;
         cx_linked_list *ll = iter->src_handle.m;
-        cx_linked_list_node *node = iter->elem_handle;
-        iter->elem_handle = node->next;
-        cx_invoke_destructor(list, node->payload);
-        cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
-                              CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+        char *node = iter->elem_handle;
+        iter->elem_handle = CX_LL_PTR(node, ll->loc_next);
+        cx_invoke_destructor(list, node + ll->loc_data);
+        cx_linked_list_remove(&ll->begin, &ll->end,
+                              ll->loc_prev, ll->loc_next, node);
         list->collection.size--;
+        iter->elem_count--;
         cxFree(list->collection.allocator, node);
     } else {
+        const cx_linked_list *ll = iter->src_handle.c;
         iter->index++;
-        cx_linked_list_node *node = iter->elem_handle;
-        iter->elem_handle = node->next;
+        void *node = iter->elem_handle;
+        iter->elem_handle = CX_LL_PTR(node, ll->loc_next);
     }
 }
 
@@ -1013,25 +1142,28 @@ static void cx_ll_iter_prev(void *it) {
         iter->base.remove = false;
         struct cx_list_s *list = iter->src_handle.m;
         cx_linked_list *ll = iter->src_handle.m;
-        cx_linked_list_node *node = iter->elem_handle;
-        iter->elem_handle = node->prev;
+        char *node = iter->elem_handle;
+        iter->elem_handle = CX_LL_PTR(node, ll->loc_prev);
         iter->index--;
-        cx_invoke_destructor(list, node->payload);
-        cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
-                              CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+        cx_invoke_destructor(list, node + ll->loc_data);
+        cx_linked_list_remove(&ll->begin, &ll->end,
+                              ll->loc_prev, ll->loc_next, node);
         list->collection.size--;
+        iter->elem_count--;
         cxFree(list->collection.allocator, node);
     } else {
+        const cx_linked_list *ll = iter->src_handle.c;
         iter->index--;
-        cx_linked_list_node *node = iter->elem_handle;
-        iter->elem_handle = node->prev;
+        char *node = iter->elem_handle;
+        iter->elem_handle = CX_LL_PTR(node, ll->loc_prev);
     }
 }
 
 static void *cx_ll_iter_current(const void *it) {
     const struct cx_iterator_s *iter = it;
-    cx_linked_list_node *node = iter->elem_handle;
-    return node->payload;
+    const cx_linked_list *ll = iter->src_handle.c;
+    char *node = iter->elem_handle;
+    return node + ll->loc_data;
 }
 
 static CxIterator cx_ll_iterator(
@@ -1059,10 +1191,11 @@ static int cx_ll_insert_iter(
         int prepend
 ) {
     struct cx_list_s *list = iter->src_handle.m;
-    cx_linked_list_node *node = iter->elem_handle;
+    cx_linked_list *ll = iter->src_handle.m;
+    void *node = iter->elem_handle;
     if (node != NULL) {
         assert(prepend >= 0 && prepend <= 1);
-        cx_linked_list_node *choice[2] = {node, node->prev};
+        void *choice[2] = {node, CX_LL_PTR(node, ll->loc_prev)};
         int result = cx_ll_insert_at(list, choice[prepend], elem);
         if (result == 0) {
             iter->elem_count++;
@@ -1084,10 +1217,10 @@ static int cx_ll_insert_iter(
 static void cx_ll_destructor(CxList *list) {
     cx_linked_list *ll = (cx_linked_list *) list;
 
-    cx_linked_list_node *node = ll->begin;
+    char *node = ll->begin;
     while (node) {
-        cx_invoke_destructor(list, node->payload);
-        void *next = node->next;
+        cx_invoke_destructor(list, node + ll->loc_data);
+        void *next = CX_LL_PTR(node, ll->loc_next);
         cxFree(list->collection.allocator, node);
         node = next;
     }
@@ -1100,6 +1233,7 @@ static cx_list_class cx_linked_list_class = {
         cx_ll_insert_element,
         cx_ll_insert_array,
         cx_ll_insert_sorted,
+        cx_ll_insert_unique,
         cx_ll_insert_iter,
         cx_ll_remove,
         cx_ll_clear,
@@ -1123,6 +1257,10 @@ CxList *cxLinkedListCreate(
 
     cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
     if (list == NULL) return NULL;
+    list->extra_data_len = 0;
+    list->loc_prev = 0;
+    list->loc_next = sizeof(void*);
+    list->loc_data = sizeof(void*)*2;
     cx_list_init((CxList*)list, &cx_linked_list_class,
             allocator, comparator, elem_size);
 
index 7fbbb8b76de9a6ac806d81afe2b5ab1ad226ef13..598be7a6ccc137bf47af9d1bafcd3d0ae8163782 100644 (file)
@@ -38,10 +38,18 @@ static int cx_pl_cmpfunc(
         const void *l,
         const void *r
 ) {
+    // l and r are guaranteed to be non-NULL pointing to the list's memory
     void *const *lptr = l;
     void *const *rptr = r;
-    const void *left = lptr == NULL ? NULL : *lptr;
-    const void *right = rptr == NULL ? NULL : *rptr;
+    const void *left = *lptr;
+    const void *right = *rptr;
+    if (left == NULL) {
+        // NULL is smaller than any value except NULL
+        return right == NULL ? 0 : -1;
+    } else if (right == NULL) {
+        // any value is larger than NULL
+        return 1;
+    }
     return cx_pl_cmpfunc_impl(left, right);
 }
 
@@ -90,6 +98,17 @@ static size_t cx_pl_insert_sorted(
     return result;
 }
 
+static size_t cx_pl_insert_unique(
+        struct cx_list_s *list,
+        const void *array,
+        size_t n
+) {
+    cx_pl_hack_cmpfunc(list);
+    size_t result = list->climpl->insert_unique(list, array, n);
+    cx_pl_unhack_cmpfunc(list);
+    return result;
+}
+
 static int cx_pl_insert_iter(
         struct cx_iterator_s *iter,
         const void *elem,
@@ -181,6 +200,7 @@ static cx_list_class cx_pointer_list_class = {
         cx_pl_insert_element,
         cx_pl_insert_array,
         cx_pl_insert_sorted,
+        cx_pl_insert_unique,
         cx_pl_insert_iter,
         cx_pl_remove,
         cx_pl_clear,
@@ -238,6 +258,7 @@ static cx_list_class cx_empty_list_class = {
         NULL,
         NULL,
         NULL,
+        NULL,
         cx_emptyl_noop,
         NULL,
         cx_emptyl_at,
@@ -284,15 +305,19 @@ size_t cx_list_default_insert_array(
     for (; i < n; i++) {
         if (NULL == invoke_list_func(
             insert_element, list, index + i,
-            src + (i * elem_size))) return i;
+            src + i * elem_size)
+        ) {
+            return i; // LCOV_EXCL_LINE
+        }
     }
     return i;
 }
 
-size_t cx_list_default_insert_sorted(
+static size_t cx_list_default_insert_sorted_impl(
         struct cx_list_s *list,
         const void *sorted_data,
-        size_t n
+        size_t n,
+        bool allow_duplicates
 ) {
     // corner case
     if (n == 0) return 0;
@@ -302,22 +327,54 @@ size_t cx_list_default_insert_sorted(
     const char *src = sorted_data;
 
     // track indices and number of inserted items
-    size_t di = 0, si = 0, inserted = 0;
+    size_t di = 0, si = 0, processed = 0;
 
     // search the list for insertion points
-    for (; di < list->collection.size; di++) {
+    while (di < list->collection.size) {
         const void *list_elm = invoke_list_func(at, list, di);
 
-        // compare current list element with first source element
-        // if less or equal, skip
-        if (cmp(list_elm, src) <= 0) {
-            continue;
+        // compare the current list element with the first source element
+        // if less, skip the list elements
+        // if equal, skip the list elements and optionally the source elements
+        {
+            int d = cmp(list_elm, src);
+            if (d <= 0) {
+                if (!allow_duplicates && d == 0) {
+                    src += elem_size;
+                    si++;
+                    processed++; // we also count duplicates for the return value
+                    while (si < n && cmp(list_elm, src) == 0) {
+                        src += elem_size;
+                        si++;
+                        processed++;
+                    }
+                    if (processed == n) {
+                        return processed;
+                    }
+                }
+                di++;
+                continue;
+            }
         }
 
-        // determine number of consecutive elements that can be inserted
-        size_t ins = 1;
+        // determine the number of consecutive elements that can be inserted
+        size_t ins = 1, skip = 0;
         const char *next = src;
         while (++si < n) {
+            if (!allow_duplicates) {
+                // skip duplicates within the source
+                if (cmp(next, next + elem_size) == 0) {
+                    next += elem_size;
+                    skip++;
+                    continue;
+                } else {
+                    if (skip > 0) {
+                        // if we had to skip something, we must wait for the next run
+                        next += elem_size;
+                        break;
+                    }
+                }
+            }
             next += elem_size;
             // once we become larger than the list elem, break
             if (cmp(list_elm, next) <= 0) {
@@ -329,33 +386,70 @@ size_t cx_list_default_insert_sorted(
 
         // insert the elements at location si
         if (ins == 1) {
-            if (NULL == invoke_list_func(
-                insert_element, list, di, src)) return inserted;
+            if (NULL == invoke_list_func(insert_element, list, di, src)) {
+                return processed; // LCOV_EXCL_LINE
+            }
         } else {
             size_t r = invoke_list_func(insert_array, list, di, src, ins);
-            if (r < ins) return inserted + r;
+            if (r < ins) {
+                return processed + r;  // LCOV_EXCL_LINE
+            }
         }
-        inserted += ins;
+        processed += ins + skip;
         di += ins;
 
         // everything inserted?
-        if (inserted == n) return inserted;
+        if (processed == n) {
+            return processed;
+        }
         src = next;
     }
 
     // insert remaining items
     if (si < n) {
-        inserted += invoke_list_func(insert_array, list, di, src, n - si);
+        if (allow_duplicates) {
+            processed += invoke_list_func(insert_array, list, di, src, n - si);
+        } else {
+            const void *last = di == 0 ? NULL : invoke_list_func(at, list, di - 1);
+            for (; si < n; si++) {
+                // skip duplicates within the source
+                if (last == NULL || cmp(last, src) != 0) {
+                    if (NULL == invoke_list_func(insert_element, list, di, src)) {
+                        return processed; // LCOV_EXCL_LINE
+                    }
+                    last = src;
+                    di++;
+                }
+                processed++;
+                src += elem_size;
+            }
+        }
     }
 
-    return inserted;
+    return processed;
+}
+
+size_t cx_list_default_insert_sorted(
+        struct cx_list_s *list,
+        const void *sorted_data,
+        size_t n
+) {
+    return cx_list_default_insert_sorted_impl(list, sorted_data, n, true);
+}
+
+size_t cx_list_default_insert_unique(
+        struct cx_list_s *list,
+        const void *sorted_data,
+        size_t n
+) {
+    return cx_list_default_insert_sorted_impl(list, sorted_data, n, false);
 }
 
 void cx_list_default_sort(struct cx_list_s *list) {
     size_t elem_size = list->collection.elem_size;
     size_t list_size = list->collection.size;
     void *tmp = cxMallocDefault(elem_size * list_size);
-    if (tmp == NULL) abort();
+    if (tmp == NULL) abort(); // LCOV_EXCL_LINE
 
     // copy elements from source array
     char *loc = tmp;
@@ -388,7 +482,7 @@ int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) {
     size_t elem_size = list->collection.elem_size;
 
     void *tmp = cxMallocDefault(elem_size);
-    if (tmp == NULL) return 1;
+    if (tmp == NULL) return 1; // LCOV_EXCL_LINE
 
     void *ip = invoke_list_func(at, list, i);
     void *jp = invoke_list_func(at, list, j);
@@ -476,6 +570,7 @@ CxIterator cxListMutIteratorAt(
         CxList *list,
         size_t index
 ) {
+    if (list == NULL) list = cxEmptyList;
     CxIterator it = list->cl->iterator(list, index, false);
     it.base.mutating = true;
     return it;
@@ -485,6 +580,7 @@ CxIterator cxListMutBackwardsIteratorAt(
         CxList *list,
         size_t index
 ) {
+    if (list == NULL) list = cxEmptyList;
     CxIterator it = list->cl->iterator(list, index, true);
     it.base.mutating = true;
     return it;
index 49223f5533ea54d4fa0405d4e6ea88ec9638902b..aab38828df1f261f93ff930c0525ace8d9bf79a2 100644 (file)
--- a/ucx/map.c
+++ b/ucx/map.c
@@ -85,18 +85,21 @@ CxMap *const cxEmptyMap = &cx_empty_map;
 // </editor-fold>\r
 \r
 CxMapIterator cxMapMutIteratorValues(CxMap *map) {\r
+    if (map == NULL) map = cxEmptyMap;\r
     CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);\r
     it.base.mutating = true;\r
     return it;\r
 }\r
 \r
 CxMapIterator cxMapMutIteratorKeys(CxMap *map) {\r
+    if (map == NULL) map = cxEmptyMap;\r
     CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);\r
     it.base.mutating = true;\r
     return it;\r
 }\r
 \r
 CxMapIterator cxMapMutIterator(CxMap *map) {\r
+    if (map == NULL) map = cxEmptyMap;\r
     CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);\r
     it.base.mutating = true;\r
     return it;\r
index b7b2e81ddf1d26cf637ceceffb15e7fc457806d9..fab6427edd150a37c232be90acd2a7cb3a526269 100644 (file)
@@ -633,13 +633,19 @@ int cxMempoolTransfer(
     new_source_allocator->data = source;
 
     // transfer all the data
-    memcpy(&dest->data[dest->size], source->data, sizeof(void*)*source->size);
-    dest->size += source->size;
+    if (source->size > 0) {
+        memcpy(&dest->data[dest->size], source->data,
+            sizeof(void*)*source->size);
+        dest->size += source->size;
+    }
 
     // transfer all registered memory
-    memcpy(&dest->registered[dest->registered_size], source->registered,
-           sizeof(struct cx_mempool_foreign_memory_s) * source->registered_size);
-    dest->registered_size += source->registered_size;
+    if (source->registered_size > 0) {
+        memcpy(&dest->registered[dest->registered_size], source->registered,
+               sizeof(struct cx_mempool_foreign_memory_s)
+               * source->registered_size);
+        dest->registered_size += source->registered_size;
+    }
 
     // register the old allocator with the new pool
     // we have to remove const-ness for this, but that's okay here
index 4593602c84a89162cdfedac781f412636550a192..5a361ae2773bd8331cd83e1ffbd919684d8155a9 100644 (file)
@@ -244,7 +244,7 @@ static int cx_properties_sink_map(
     CxMap *map = sink->sink;
     CxAllocator *alloc = sink->data;
     cxmutstr v = cx_strdup_a(alloc, value);
-    int r = cx_map_put_cxstr(map, key, v.ptr);
+    int r = cxMapPut(map, key, v.ptr);
     if (r != 0) cx_strfree_a(alloc, &v);
     return r;
 }
index 61d88f35acd65dd9afe38320a519ca1a63c9ec03..793f5ebd983038cbfc613be93a1e459cd6f619ef 100644 (file)
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */
+#ifdef MEMRCHR_NEED_GNU
+#define _GNU_SOURCE
+#endif
+
 #include "cx/string.h"
 
 #include <string.h>
@@ -33,6 +37,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <float.h>
+#include <ctype.h>
 
 #ifdef _WIN32
 #define cx_strcasecmp_impl _strnicmp
@@ -42,7 +47,7 @@
 #endif
 
 cxmutstr cx_mutstr(char *cstring) {
-    return (cxmutstr) {cstring, strlen(cstring)};
+    return (cxmutstr) {cstring, cstring == NULL ? 0 : strlen(cstring)};
 }
 
 cxmutstr cx_mutstrn(
@@ -53,7 +58,7 @@ cxmutstr cx_mutstrn(
 }
 
 cxstring cx_str(const char *cstring) {
-    return (cxstring) {cstring, strlen(cstring)};
+    return (cxstring) {cstring, cstring == NULL ? 0 : strlen(cstring)};
 }
 
 cxstring cx_strn(
@@ -231,19 +236,24 @@ cxmutstr cx_strchr_m(
 }
 
 cxstring cx_strrchr(
-        cxstring string,
-        int chr
+    cxstring string,
+    int chr
 ) {
+#ifdef WITH_MEMRCHR
+    char *ret = memrchr(string.ptr, 0xFF & chr, string.length);
+    if (ret == NULL) return (cxstring) {NULL, 0};
+    return (cxstring) {ret, string.length - (ret - string.ptr)};
+#else
     chr = 0xFF & chr;
     size_t i = string.length;
     while (i > 0) {
         i--;
-        // TODO: improve by comparing multiple bytes at once
         if (string.ptr[i] == chr) {
             return cx_strsubs(string, i);
         }
     }
     return (cxstring) {NULL, 0};
+#endif
 }
 
 cxmutstr cx_strrchr_m(
@@ -520,19 +530,13 @@ cxmutstr cx_strdup_a_(
     return result;
 }
 
-static bool str_isspace(char c) {
-    // TODO: remove once UCX has public API for this
-    return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f';
-}
-
 cxstring cx_strtrim(cxstring string) {
     cxstring result = string;
-    // TODO: optimize by comparing multiple bytes at once
-    while (result.length > 0 && str_isspace(*result.ptr)) {
+    while (result.length > 0 && isspace((unsigned char)(result.ptr[0]))) {
         result.ptr++;
         result.length--;
     }
-    while (result.length > 0 && str_isspace(result.ptr[result.length - 1])) {
+    while (result.length > 0 && isspace((unsigned char)result.ptr[result.length - 1])) {
         result.length--;
     }
     return result;
@@ -957,11 +961,6 @@ int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep
     return 0;
 }
 
-static bool str_isdigit(char c) {
-    // TODO: remove once UCX has public API for this
-    return c >= '0' && c <= '9';
-}
-
 int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) {
     // TODO: overflow check
     // TODO: increase precision
@@ -994,7 +993,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
     // parse all digits until we find the decsep
     size_t pos = 0;
     do {
-        if (str_isdigit(str.ptr[pos])) {
+        if (isdigit((unsigned char)str.ptr[pos])) {
             result = result * 10 + (str.ptr[pos] - '0');
         } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
             break;
@@ -1023,7 +1022,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
         // parse everything until exponent or end
         double factor = 1.;
         do {
-            if (str_isdigit(str.ptr[pos])) {
+            if (isdigit((unsigned char)str.ptr[pos])) {
                 factor *= 0.1;
                 result = result + factor * (str.ptr[pos] - '0');
             } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
@@ -1064,7 +1063,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse
     // parse the exponent
     unsigned int exp = 0;
     do {
-        if (str_isdigit(str.ptr[pos])) {
+        if (isdigit((unsigned char)str.ptr[pos])) {
             exp = 10 * exp + (str.ptr[pos] - '0');
         } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
             errno = EINVAL;
diff --git a/ui/cocoa/BoxContainer.h b/ui/cocoa/BoxContainer.h
new file mode 100644 (file)
index 0000000..a80ae7d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "Container.h"
+#import "GridLayout.h"
+
+@interface BoxContainer : GridLayout
+
+@property NSUserInterfaceLayoutOrientation orientation;
+
+- (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing;
+
+@end
+
diff --git a/ui/cocoa/BoxContainer.m b/ui/cocoa/BoxContainer.m
new file mode 100644 (file)
index 0000000..e4b8f7f
--- /dev/null
@@ -0,0 +1,30 @@
+
+
+#import "BoxContainer.h"
+
+@implementation BoxContainer
+
+- (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing {
+    self = [super init];
+    _orientation = orientation;
+    self.columnspacing = spacing;
+    self.rowspacing = spacing;
+    
+    return self;
+}
+
+- (void) addView:(NSView*)view  layout:(UiLayout*)layout {
+    if(_orientation == NSUserInterfaceLayoutOrientationVertical) {
+        layout->hexpand = TRUE;
+        layout->hfill = TRUE;
+    } else {
+        layout->vexpand = TRUE;
+        layout->vfill = TRUE;
+    }
+    [super addView:view layout:layout];
+    if(_orientation == NSUserInterfaceLayoutOrientationVertical) {
+        self.container->newline = TRUE;
+    }
+}
+
+@end
index f8513438535714d0e6963a993599e6078fbc21ba..1df6d9b4f5123c414ae1e129e96daf7c1d0908d3 100644 (file)
@@ -47,6 +47,7 @@
         event.document = event.obj->ctx->document;
         event.eventdata = self.data;
         event.intval = self.value;
+        event.set = ui_get_setop();
         self.callback(&event, self.userdata);
     }
 }
@@ -58,6 +59,7 @@
     event.document = event.obj->ctx->document;
     event.eventdata = NULL;
     event.intval = 0;
+    event.set = ui_get_setop();
     if(_get_eventdata) {
         _get_eventdata(sender, _var, &event.eventdata, &event.intval);
     }
index 0d32c110c7b425281a987844979d18ff5155bc11..362c9b281551a4299ed98696e1836080f1bf3b28 100644 (file)
@@ -34,7 +34,7 @@
 
 typedef struct GridElm {
     NSView *view;
-    int margin;
+    NSEdgeInsets margin;
     int x;
     int y;
     int colspan;
@@ -56,13 +56,13 @@ typedef struct GridDef {
 
 @interface GridLayout : NSView<Container>
 
+@property UiContainerX *container;
+
 @property int columnspacing;
 @property int rowspacing;
 @property CxList *children;
 @property NSSize preferredSize;
 
-@property NSButton *test;
-
 @property int x;
 @property int y;
 @property int cols;
index 3e97cf45d241b6e8b8f8c9de2bae865986dca3ac..c27ba1dfec4e791209e9560d3adbfc6d6a1e832f 100644 (file)
 
 @implementation GridLayout
 
-@synthesize label=_label;
-@synthesize uilayout=_uilayout;
-@synthesize newline=_newline;
+@synthesize container = _container;
 
 - (GridLayout*)init {
     self = [super init];
     _columnspacing = 0;
     _rowspacing = 0;
     _children = cxArrayListCreateSimple(sizeof(GridElm), 32);
+    _preferredSize.width = -1;
+    _preferredSize.height = -1;
     
     return self;
 }
@@ -56,6 +56,9 @@
 }
  */
 
+- (BOOL)isFlipped {
+    return YES;
+}
 
 - (void) layout {
     int ncols = _cols+1;
@@ -64,7 +67,8 @@
     GridDef *cols = calloc(ncols, sizeof(GridDef));
     GridDef *rows = calloc(nrows, sizeof(GridDef));
     
-    NSRect viewFrame = self.frame;
+    //NSRect viewFrame = self.frame;
+    NSRect viewFrame = self.bounds;
     
     int colspacing = _columnspacing;
     int rowspacing = _rowspacing;
             
             NSSize size = elm->view.intrinsicContentSize;
             NSSize size2 = elm->view.fittingSize;
-            NSEdgeInsets alignment = elm->view.alignmentRectInsets;
-            // TODO: remove alignment
-            alignment.left = 0;
-            alignment.right = 0;
-            alignment.top = 0;
-            alignment.bottom = 0;
             if(size.width == NSViewNoIntrinsicMetric) {
                 size.width = size2.width;
             }
                 size.height = size2.height;
             }
             if(size.width != NSViewNoIntrinsicMetric) {
-                CGFloat width = size.width + alignment.left + alignment.right;
+                CGFloat width = size.width + elm->margin.left + elm->margin.right;
                 if(width > cols[elm->x].preferred_size && elm->colspan <= 1 && span_max == 1) {
                     cols[elm->x].preferred_size = width;
                 }
                 elm->preferred_width = width;
             }
             if(size.height != NSViewNoIntrinsicMetric) {
-                CGFloat height = size.height + alignment.top + alignment.right;
-                //CGFloat height = size.height;
+                CGFloat height = size.height + elm->margin.top + elm->margin.bottom;
                 if(height > rows[elm->y].preferred_size && elm->rowspan <= 1 && span_max == 1) {
                     rows[elm->y].preferred_size = height;
                 }
                 elm->preferred_height = height;
             }
             
+            
             if(elm->rowspan > span_max || elm->colspan > span_max) {
                 continue;
             }
     int preferred_width = 0;
     int preferred_height = 0;
     for(int j=0;j<ncols;j++) {
-        preferred_width += cols[j].preferred_size + colspacing;
+        preferred_width += cols[j].preferred_size;
         if(cols[j].expand) {
             col_ext++;
         }
     }
     for(int j=0;j<nrows;j++) {
-        preferred_height += rows[j].preferred_size + rowspacing;
+        preferred_height += rows[j].preferred_size;
         if(rows[j].expand) {
             row_ext++;
         }
     }
+    if(ncols > 0) {
+        preferred_width += (ncols-1) * colspacing;
+    }
+    if(nrows > 0) {
+        preferred_height += (nrows-1) * rowspacing;
+    }
     
     _preferredSize.width = preferred_width;
     _preferredSize.height = preferred_height;
         GridDef *col = &cols[elm->x];
         GridDef *row = &rows[elm->y];
         
-        NSEdgeInsets alignment = elm->view.alignmentRectInsets;
         NSRect frame;
         if(elm->hfill) {
             if(elm->colspan > 1) {
                 if(end_col > ncols) {
                     end_col = ncols;
                 }
+                int real_span = 0;
                 for(int c=elm->x;c<end_col;c++) {
                     cwidth += cols[c].size;
+                    real_span++;
+                }
+                if(real_span > 0) {
+                    cwidth += (real_span-1) * colspacing;
                 }
-                frame.size.width = cwidth + + alignment.left + alignment.right;
+                frame.size.width = cwidth;
             } else {
-                frame.size.width = col->size + alignment.left + alignment.right;
+                frame.size.width = col->size;
             }
         } else {
-            frame.size.width = elm->preferred_width + alignment.left + alignment.right;
+            frame.size.width = elm->preferred_width;
         }
+        frame.size.width -= elm->margin.left + elm->margin.right;
         if(elm->vfill) {
             if(elm->rowspan > 1) {
                 int rheight = 0;
                 if(end_row > nrows) {
                     end_row = nrows;
                 }
+                int real_span = 0;
                 for(int r=elm->y;r<end_row;r++) {
                     rheight += rows[r].size;
+                    real_span++;
+                }
+                if(real_span > 0) {
+                    rheight += (real_span-1) * rowspacing;
                 }
                 frame.size.height = rheight;
             }
         } else {
             frame.size.height = elm->preferred_height;
         }
-        frame.origin.x = col->pos - (alignment.left+alignment.right)/2;
-        //frame.origin.y = viewFrame.size.height - row->pos - frame.size.height + ((alignment.top+alignment.right)/2);
-        frame.origin.y = viewFrame.size.height - row->pos - frame.size.height;
-        elm->view.frame = frame;
+        frame.size.height -= elm->margin.top + elm->margin.bottom;
+        
+        frame.origin.x = col->pos + elm->margin.left;
+        frame.origin.y = row->pos + elm->margin.top;
+        NSRect viewFrame = [elm->view frameForAlignmentRect:frame];
+        elm->view.frame = viewFrame;
     }
     
     free(cols);
  
 
 - (NSSize)intrinsicContentSize {
+    if(_preferredSize.width == -1) {
+        [self layout];
+    }
     return self.preferredSize;
 }
 
-- (void) addView:(NSView*)view {
-    if(_newline) {
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
+    _preferredSize.width = -1;
+    _preferredSize.height = -1;
+    
+    if(self.container != nil && self.container->newline) {
         _y++;
         _x = 0;
-        _newline = FALSE;
+        self.container->newline = FALSE;
     }
     
     GridElm elm;
     elm.x = _x;
     elm.y = _y;
-    elm.margin = 0;
-    elm.colspan = _uilayout.colspan;
-    elm.rowspan = _uilayout.rowspan;
-    if(_uilayout.fill) {
+    elm.margin = NSEdgeInsetsMake(layout->margin_top, layout->margin_left, layout->margin_bottom, layout->margin_right);
+    elm.colspan = layout->colspan;
+    elm.rowspan = layout->rowspan;
+    if(layout->fill) {
         elm.hfill = TRUE;
         elm.vfill = TRUE;
         elm.hexpand = TRUE;
         elm.vexpand = TRUE;
     } else {
-        elm.hfill = _uilayout.hfill;
-        elm.vfill = _uilayout.vfill;
-        elm.hexpand = _uilayout.hexpand;
-        elm.vexpand = _uilayout.vexpand;
+        elm.hfill = layout->hfill;
+        elm.vfill = layout->vfill;
+        elm.hexpand = layout->hexpand;
+        elm.vexpand = layout->vexpand;
     }
     elm.view = view;
     cxListAdd(_children, &elm);
index aab8a700e412ce1352792f7cf843e4ab44b57474..8cf15e78d1a6f8f901f727f84762363fa6e4c13f 100644 (file)
 #import "toolkit.h"
 #import "../ui/window.h"
 
-@interface MainWindow : NSWindow
+@interface MainWindow : NSWindow<UiToplevelObject>
 
-- (MainWindow*)init:(UiObject*)obj;
+@property (strong) NSView *sidebar;
+@property (strong) NSView *leftPanel;
+@property (strong) NSView *rightPanel;
+@property int topOffset;
+
+- (MainWindow*)init:(UiObject*)obj withSidebar:(BOOL)hasSidebar withSplitview:(BOOL)hasSplitview;
 
 @end
 
index 45c8e3076f0d70ad013a71a1e5fa5418ca410315..4793365561e3fb53f9d6885965296974e3f17510 100644 (file)
@@ -29,7 +29,9 @@
 #import "MainWindow.h"
 #import "Container.h"
 #import "GridLayout.h"
+#import "BoxContainer.h"
 #import "../common/object.h"
+#import "../ui/properties.h"
 #import <objc/runtime.h>
 
 #import "EventData.h"
@@ -38,7 +40,7 @@
 
 @implementation MainWindow
 
-- (MainWindow*)init:(UiObject*)obj {
+- (MainWindow*)init:(UiObject*)obj withSidebar:(BOOL)hasSidebar withSplitview:(BOOL)hasSplitview{
     NSRect frame = NSMakeRect(300, 200, 600, 500);
     
     self = [self initWithContentRect:frame
                              backing:NSBackingStoreBuffered
                                defer:false];
     
+    
     if(uic_toolbar_isenabled()) {
         UiToolbar *toolbar = [[UiToolbar alloc]initWithObject:obj];
         [self setToolbar:toolbar];
     }
     
+    int top = 4;
+    NSView *content = self.contentView;
     
-    // create a vertical stackview as default container
-    BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0];
-    //GridLayout *vbox = [[GridLayout alloc] init];
-    vbox.translatesAutoresizingMaskIntoConstraints = false;
-    [self.contentView addSubview:vbox];
-    [NSLayoutConstraint activateConstraints:@[
-        [vbox.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:4],
-        [vbox.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor],
-        [vbox.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor],
-        [vbox.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor]
-    ]];
+    // A sidebar or splitview window need a NSSplitView
+    NSSplitView *splitview;
+    if(hasSidebar || hasSplitview) {
+        self.styleMask |= NSWindowStyleMaskFullSizeContentView;
+        self.titleVisibility = NSWindowTitleHidden;
+        self.titlebarAppearsTransparent = YES;
+        
+        splitview = [[NSSplitView alloc]init];
+        splitview.vertical = YES;
+        splitview.dividerStyle = NSSplitViewDividerStyleThin;
+        splitview.translatesAutoresizingMaskIntoConstraints = false;
+        [self.contentView addSubview:splitview];
+        
+        [NSLayoutConstraint activateConstraints:@[
+            [splitview.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:0],
+            [splitview.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor],
+            [splitview.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor],
+            [splitview.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor]
+        ]];
+        
+        top = 34;
+    }
+    
+    if(hasSidebar) {
+        // add the sidebar
+        const char *sidebarMaterialProperty = ui_get_property("ui.cocoa.sidebar.usematerial");
+        BOOL useMaterial = YES;
+        if(sidebarMaterialProperty && (sidebarMaterialProperty[0] == 'f' || sidebarMaterialProperty[0] == 'F')) {
+            useMaterial = NO;
+        }
+        
+        if(useMaterial) {
+            NSVisualEffectView *v = [[NSVisualEffectView alloc] initWithFrame:NSMakeRect(0,0,0,0)];
+            v.material = NSVisualEffectMaterialSidebar;
+            v.blendingMode = NSVisualEffectBlendingModeBehindWindow;
+            v.state = NSVisualEffectStateActive;
+            _sidebar = v;
+        } else {
+            _sidebar = [[NSView alloc]initWithFrame:NSMakeRect(0,0,0,0)];
+        }
+        _sidebar.translatesAutoresizingMaskIntoConstraints = NO;
+        [splitview addArrangedSubview:_sidebar];
+        [_sidebar.widthAnchor constraintGreaterThanOrEqualToConstant:250].active = YES;
+    }
+    if(hasSplitview) {
+        // add the splitview window left/right panels
+        _leftPanel = [[NSView alloc]initWithFrame:NSMakeRect(0,0,100,100)];
+        [splitview addArrangedSubview:_leftPanel];
+        _rightPanel = [[NSView alloc]initWithFrame:NSMakeRect(0,0,100,100)];
+        [splitview addArrangedSubview:_rightPanel];
+    } else if(hasSidebar) {
+        // sidebar only window: add content view
+        content = [[NSView alloc]initWithFrame:NSMakeRect(0,0,100,100)];
+        [splitview addArrangedSubview:content];
+    }
     
-    uic_object_push_container(obj, ui_create_container(obj, vbox));
+    // normal or sidebar-only windows get a container
+    if(!hasSplitview) {
+        // create a vertical stackview as default container
+        BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0];
+        //GridLayout *vbox = [[GridLayout alloc] init];
+        vbox.translatesAutoresizingMaskIntoConstraints = false;
+        [content addSubview:vbox];
+        [NSLayoutConstraint activateConstraints:@[
+            [vbox.topAnchor constraintEqualToAnchor:content.topAnchor constant:top],
+            [vbox.leadingAnchor constraintEqualToAnchor:content.leadingAnchor],
+            [vbox.trailingAnchor constraintEqualToAnchor:content.trailingAnchor],
+            [vbox.bottomAnchor constraintEqualToAnchor:content.bottomAnchor],
+        ]];
+        UiContainerX *container = ui_create_container(obj, vbox);
+        vbox.container = container;
+        uic_object_push_container(obj, container);
+    }
+    _topOffset = top;
     
     return self;
 }
 
-@end
+- (BOOL) getIsVisible {
+    return [self isVisible];
+}
+
+- (void) setVisible:(BOOL)visible {
+    if(visible) {
+        [self makeKeyAndOrderFront:nil];
+    } else {
+        [self close];
+    }
+}
+
 
+@end
 
 
 @implementation MainWindowController
@@ -273,3 +351,62 @@ void ui_menu_radio_item_set(UiInteger *i, int64_t value) {
         index++;
     }
 }
+
+
+UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
+    MainWindow *window = (__bridge MainWindow*)obj->wobj;
+    if(window.sidebar == nil) {
+        return NULL;
+    }
+    NSView *sidebar = window.sidebar;
+    
+    // create a vertical stackview as default container
+    BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:args->spacing];
+    vbox.container = ui_create_container(obj, vbox);
+    //GridLayout *vbox = [[GridLayout alloc] init];
+    vbox.translatesAutoresizingMaskIntoConstraints = false;
+    [sidebar addSubview:vbox];
+    [NSLayoutConstraint activateConstraints:@[
+        [vbox.topAnchor constraintEqualToAnchor:sidebar.topAnchor constant:34],
+        [vbox.leadingAnchor constraintEqualToAnchor:sidebar.leadingAnchor],
+        [vbox.trailingAnchor constraintEqualToAnchor:sidebar.trailingAnchor],
+        [vbox.bottomAnchor constraintEqualToAnchor:sidebar.bottomAnchor]
+    ]];
+    uic_object_push_container(obj, vbox.container);
+    
+    return NULL;
+}
+
+static UIWIDGET splitview_window_add_panel(UiObject *obj, NSView *panel, UiSidebarArgs *args) {
+    MainWindow *window = (__bridge MainWindow*)obj->wobj;
+    BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0];
+    //GridLayout *vbox = [[GridLayout alloc] init];
+    vbox.container = ui_create_container(obj, vbox);
+    vbox.translatesAutoresizingMaskIntoConstraints = false;
+    [panel addSubview:vbox];
+    [NSLayoutConstraint activateConstraints:@[
+        [vbox.topAnchor constraintEqualToAnchor:panel.topAnchor constant:window.topOffset],
+        [vbox.leadingAnchor constraintEqualToAnchor:panel.leadingAnchor],
+        [vbox.trailingAnchor constraintEqualToAnchor:panel.trailingAnchor],
+        [vbox.bottomAnchor constraintEqualToAnchor:panel.bottomAnchor],
+    ]];
+    uic_object_push_container(obj, vbox.container);
+    return (__bridge void*)vbox;
+}
+
+UIWIDGET ui_left_panel_create(UiObject *obj, UiSidebarArgs *args) {
+    MainWindow *window = (__bridge MainWindow*)obj->wobj;
+    if(window.leftPanel == nil) {
+        return NULL;
+    }
+    return splitview_window_add_panel(obj, window.leftPanel, args);
+}
+
+UIWIDGET ui_right_panel_create(UiObject *obj, UiSidebarArgs *args) {
+    MainWindow *window = (__bridge MainWindow*)obj->wobj;
+    if(window.rightPanel == nil) {
+        return NULL;
+    }
+    return splitview_window_add_panel(obj, window.rightPanel, args);
+}
+
diff --git a/ui/cocoa/TabView.h b/ui/cocoa/TabView.h
new file mode 100644 (file)
index 0000000..d6e4b0d
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "Container.h"
+
+@protocol TabView
+
+- (NSView<Container>*) createTab:(int)index title:(NSString*)title;
+- (void) selectTab:(int)index;
+- (void) removeTab:(int)index;
+- (UiObject*) addTab:(int)index title:(NSString*)title;
+
+@end
+
+@interface UiTopTabView : NSTabView<TabView, Container>
+
+@property UiObject *obj;
+@property UiSubContainerType subcontainer;
+@property int padding;
+@property int spacing;
+@property int columnspacing;
+@property int rowspacing;
+@property ui_callback onchange;
+@property void *onchangedata;
+@property UiVar *var;
+
+- (id)init:(UiObject*)obj args:(UiTabViewArgs*)args;
+
+@end
+
+int64_t ui_nstabview_get(UiInteger *i);
+void ui_nstabview_set(UiInteger *i, int64_t value);
diff --git a/ui/cocoa/TabView.m b/ui/cocoa/TabView.m
new file mode 100644 (file)
index 0000000..5b50048
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "TabView.h"
+#import "BoxContainer.h"
+#import "GridLayout.h"
+
+@implementation UiTopTabView
+
+@synthesize container = _container;
+
+- (id)init:(UiObject*)obj args:(UiTabViewArgs*)args {
+    self = [super init];
+    _obj = obj;
+    _subcontainer = args->subcontainer;
+    _padding = args->padding;
+    _spacing = args->spacing;
+    _columnspacing = args->columnspacing;
+    _rowspacing = args->rowspacing;
+    _onchange = args->onchange;
+    _onchangedata = args->onchangedata;
+    _var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+    
+    if(args->tabview == UI_TABVIEW_INVISIBLE || args->tabview == UI_TABVIEW_NAVIGATION_SIDE) {
+        self.tabViewType = NSNoTabsNoBorder;
+    }
+    
+    if(_var) {
+        UiInteger *i = _var->value;
+        i->obj = (__bridge void*)self;
+        i->get = ui_nstabview_get;
+        i->set = ui_nstabview_set;
+    }
+    
+    return self;
+}
+
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
+    // noop
+}
+
+- (NSView<Container>*) createTab:(int)index title:(NSString*)title {
+    NSTabViewItem *item = [[NSTabViewItem alloc]initWithIdentifier:nil];
+    [item setLabel:title];
+    if(index < 0) {
+        [self addTabViewItem:item];
+    } else {
+        [self insertTabViewItem:item atIndex:index];
+    }
+    
+    BoxContainer *content = [[BoxContainer alloc]init];
+    item.view = content;
+    
+    GridLayout *sub;
+    switch(_subcontainer) {
+        default: sub = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:_spacing]; break;
+        case UI_CONTAINER_HBOX: sub = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationHorizontal spacing:_spacing]; break;
+        case UI_CONTAINER_GRID: {
+            sub = [[GridLayout alloc] init];
+            sub.columnspacing = _columnspacing;
+            sub.rowspacing = _rowspacing;
+            break;
+        }
+    }
+    UiLayout layout = {
+        .margin = _padding,
+        .margin_left = _padding, .margin_right = _padding, .margin_top = _padding, .margin_bottom = _padding,
+        .fill = TRUE };
+    [content addView:sub layout:&layout];
+    
+    return sub;
+}
+
+- (void) selectTab:(int)index {
+    [self selectTabViewItemAtIndex:index];
+}
+
+- (void) removeTab:(int)index {
+    NSTabViewItem *item = [self tabViewItemAtIndex:index];
+    if(item != nil) {
+        [self removeTabViewItem:item];
+    }
+}
+
+- (UiObject*) addTab:(int)index title:(NSString*)title {
+    NSView<Container> *sub = [self createTab:index title:title];
+    
+    UiObject *newobj = uic_object_new_toplevel();
+    newobj->widget = (__bridge void*)sub;
+    
+    UiContainerX *container = ui_create_container(newobj, sub);
+    uic_object_push_container(newobj, container);
+    
+    return newobj;
+}
+
+@end
+
+int64_t ui_nstabview_get(UiInteger *i) {
+    UiTopTabView *tabview = (__bridge UiTopTabView*)i->obj;
+    i->value = [tabview indexOfTabViewItem:tabview.selectedTabViewItem];
+    return i->value;
+}
+
+void ui_nstabview_set(UiInteger *i, int64_t value) {
+    UiTopTabView *tabview = (__bridge UiTopTabView*)i->obj;
+    [tabview selectTab:(int)value];
+    i->value = [tabview indexOfTabViewItem:tabview.selectedTabViewItem];
+}
index 747b469ec6d3db6837e5fcad379a2d55e5dca040..04941cbbf56f53095b3253e2bdb33d666d47b671 100644 (file)
@@ -204,6 +204,7 @@ NSToolbarItem* ui_nstoolbaritem_create_toggle(UiObject *obj, UiToolbarToggleItem
             
         }
         i->obj = (__bridge void*)seg;
+        printf("seg: %p\n", seg);
         i->get = ui_toolbar_seg_toggleitem_get;
         i->set = ui_toolbar_seg_toggleitem_set;
     }
index 74895914a07049a8598e5c994caaacab85936056..29534fa408aec0e39d57aab34b32b706e4d0ce30 100644 (file)
 
 @end
 
+@interface UiLinkButtonData : NSObject
+@property UiObject *obj;
+@property (weak) NSTextField *textfield;
+@property (strong) NSString *label;
+@property (strong) NSString *uri;
+@property BOOL visited;
+@property ui_callback onclick;
+@property void *onclickdata;
+
+- (id)init:(UiObject*)obj textfield:(NSTextField*)textfield;
+- (void)setLinkDataFromJson:(const char*)jsonStr;
+- (void)buildLink;
+
+@end
+
 
 int64_t ui_togglebutton_get(UiInteger *i);
 void ui_togglebutton_set(UiInteger *i, int64_t value);
@@ -49,3 +64,6 @@ void ui_switch_set(UiInteger *i, int64_t value);
 
 int64_t ui_radiobuttons_get(UiInteger *i);
 void ui_radiobuttons_set(UiInteger *i, int64_t value);
+
+char* ui_linkbutton_get(UiString *s);
+void ui_linkbutton_set(UiString *s, const char *str);
index e8e753d4795f42270a76c449397eeb8a0e46b21b..4a7f25995f73afd42214f116b452feaba458e828 100644 (file)
 #import "Container.h"
 #import <objc/runtime.h>
 
+#import <cx/buffer.h>
+#import <cx/json.h>
+
 UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
     NSButton *button = [[NSButton alloc] init];
+    button.translatesAutoresizingMaskIntoConstraints = NO;
     if(args->label) {
         NSString *label = [[NSString alloc] initWithUTF8String:args->label];
         button.title = label;
@@ -272,3 +276,187 @@ void ui_radiobuttons_set(UiInteger *i, int64_t value) {
         index++;
     }
 }
+
+
+/* --------------------------- Link Button --------------------------- */
+
+@implementation UiLinkButtonData
+
+- (id)init:(UiObject*)obj textfield:(NSTextField*)textfield {
+    _obj = obj;
+    _textfield = textfield;
+    return self;
+}
+
+- (void)setLinkDataFromJson:(const char*)jsonStr {
+    CxJson json;
+    cxJsonInit(&json, NULL);
+    cxJsonFill(&json, jsonStr);
+    
+    CxJsonValue *value;
+    if(cxJsonNext(&json, &value) == CX_JSON_NO_ERROR) {
+        if(cxJsonIsObject(value)) {
+            CxJsonValue *label = cxJsonObjGet(value, "label");
+            CxJsonValue *uri = cxJsonObjGet(value, "uri");
+            CxJsonValue *visited = cxJsonObjGet(value, "visited");
+            if(label) {
+                char *str = cxJsonIsString(label) ? cxJsonAsString(label) : NULL;
+                if(str) {
+                    _label = [[NSString alloc]initWithUTF8String:str];
+                } else {
+                    _label = nil;
+                }
+            }
+            if(uri) {
+                char *str = cxJsonIsString(uri) ? cxJsonAsString(uri) : NULL;
+                if(str) {
+                    _uri = [[NSString alloc]initWithUTF8String:str];
+                } else {
+                    _uri = nil;
+                }
+            }
+            if(visited) {
+                _visited = cxJsonIsBool(visited) ? cxJsonAsBool(visited) : FALSE;
+            }
+        }
+        cxJsonValueFree(value);
+    }
+    cxJsonDestroy(&json);
+    
+    [self buildLink];
+}
+
+- (void)buildLink {
+    NSString *label = _label ? _label : @"";
+    
+    NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:label];
+    [attrString beginEditing];
+    if(_uri) {
+        [attrString addAttribute:NSLinkAttributeName value:_uri range:NSMakeRange(0, attrString.length)];
+    }
+    [attrString addAttribute:NSForegroundColorAttributeName value:[NSColor systemBlueColor] range:NSMakeRange(0, attrString.length)];
+    [attrString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, attrString.length)];
+    [attrString endEditing];
+
+    [_textfield setAttributedStringValue:attrString];
+}
+
+@end
+
+static char* create_linkbutton_jsonvalue(const char *label, const char *uri, UiBool include_null, UiBool visited, UiBool set_visited) {
+    CxJsonValue *obj = cxJsonCreateObj(NULL);
+    if(label) {
+        cxJsonObjPutString(obj, CX_STR("label"), label);
+    } else if(include_null) {
+        cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL);
+    }
+    
+    if(uri) {
+        cxJsonObjPutString(obj, CX_STR("uri"), uri);
+    } else if(include_null) {
+        cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL);
+    }
+    
+    if(set_visited) {
+        cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE);
+    }
+    
+    CxJsonWriter writer = cxJsonWriterCompact();
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND);
+    cxJsonWrite(&buf, obj, (cx_write_func)cxBufferWrite, &writer);
+    cxJsonValueFree(obj);
+    cxBufferTerminate(&buf);
+    
+    return buf.space;
+}
+
+UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) {
+    NSTextField *label = [[NSTextField alloc] init];
+    label.editable = NO;
+    label.bezeled = NO;
+    label.drawsBackground = NO;
+    label.allowsEditingTextAttributes = YES;
+    label.selectable = YES;
+    
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ui_container_add(obj, label, &layout);
+    
+    UiLinkButtonData *data = [[UiLinkButtonData alloc]init:obj textfield:label];
+    objc_setAssociatedObject(label, "linkdata", data, OBJC_ASSOCIATION_RETAIN);
+    
+    UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+    if(var) {
+        UiString *s = var->value;
+        s->obj = (__bridge void*)data;
+        s->get = ui_linkbutton_get;
+        s->set = ui_linkbutton_set;
+        
+        if(s->value.ptr) {
+            [data setLinkDataFromJson:s->value.ptr];
+        }
+    }
+    
+    return (__bridge void*)label;
+}
+
+char* ui_linkbutton_get(UiString *s) {
+    return NULL; // TODO
+}
+
+void ui_linkbutton_set(UiString *s, const char *str) {
+    UiLinkButtonData *data = (__bridge UiLinkButtonData*)s->obj;
+    [data setLinkDataFromJson:str];
+}
+
+
+
+void ui_linkbutton_value_set(UiString *str, const char *label, const char *uri) {
+    char *value = create_linkbutton_jsonvalue(label, uri, TRUE, FALSE, TRUE);
+    ui_set(str, value);
+    free(value);
+}
+
+void ui_linkbutton_value_set_label(UiString *str, const char *label) {
+    char *value = create_linkbutton_jsonvalue(label, NULL, FALSE, FALSE, TRUE);
+    ui_set(str, value);
+    free(value);
+}
+
+void ui_linkbutton_value_set_uri(UiString *str, const char *uri) {
+    char *value = create_linkbutton_jsonvalue(NULL, uri, FALSE, FALSE, TRUE);
+    ui_set(str, value);
+    free(value);
+}
+
+void ui_linkbutton_value_set_visited(UiString *str, UiBool visited) {
+    char *value = create_linkbutton_jsonvalue(NULL, NULL, FALSE, visited, TRUE);
+    ui_set(str, value);
+    free(value);
+}
+
+// TODO
+
+void ui_linkbutton_set_label(UIWIDGET button, const char *label) {
+    
+}
+
+void ui_linkbutton_set_uri(UIWIDGET button, const char *label) {
+    
+}
+
+void ui_linkbutton_set_visited(UIWIDGET button, UiBool visited) {
+    
+}
+
+char* ui_linkbutton_get_label(UIWIDGET button) {
+    return NULL;
+}
+
+char* ui_linkbutton_get_uri(UIWIDGET button) {
+    return NULL;
+}
+
+UiBool ui_linkbutton_get_visited(UIWIDGET button) {
+    return FALSE;
+}
index 1ff66b12d560f08af46befc48f7ccbefdd03cab3..ed2dc67b92f2126271d09b9141c9267a43a09efd 100644 (file)
 
 #import "../ui/container.h"
 
-#define ui_lb2bool(b) ((b) == UI_LAYOUT_TRUE ? TRUE : FALSE)
-#define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE)
 
 typedef struct UiLayout UiLayout;
-typedef enum UiLayoutBool UiLayoutBool;
-
-enum UiLayoutBool {
-    UI_LAYOUT_UNDEFINED = 0,
-    UI_LAYOUT_TRUE,
-    UI_LAYOUT_FALSE,
-};
-
-struct UiLayout {
-    UiBool       fill;
-    //UiBool       newline;
-    //char         *label;
-    UiBool       hexpand;
-    UiBool       vexpand;
-    UiBool       hfill;
-    UiBool       vfill;
-    //int          width;
-    int          colspan;
-    int          rowspan;
-};
+
 
 #define UI_INIT_LAYOUT(args) (UiLayout) {\
     .fill = args->fill, \
@@ -61,28 +40,37 @@ struct UiLayout {
     .vexpand = args->vexpand, \
     .hfill = args->hfill, \
     .vfill = args->vfill, \
+    .margin = args->margin, \
+    .margin_left = args->margin_left, \
+    .margin_right = args->margin_right, \
+    .margin_top = args->margin_top, \
+    .margin_bottom = args->margin_bottom, \
     .colspan = args->colspan, \
     .rowspan = args->rowspan }
 
 
 @protocol Container
 
-@property UiLayout uilayout;
-@property const char *label;
-@property UiBool newline;
+@property UiContainerX *container;
 
-- (void) addView:(NSView*)view;
+- (void) addView:(NSView*)view layout:(UiLayout*)layout;
 
 @end
 
-@interface BoxContainer : NSStackView<Container>
 
-- (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing;
+@interface FrameContainer : NSBox<Container>
+
+- (id)init:(NSString*)title;
 
 @end
 
+@interface ScrollViewContainer : NSScrollView<Container>
 
+@property NSView<Container> *child;
 
+- (id)init:(UiSubContainerType)subContainer columnSpacing:(int)columnSpacing rowSpacing:(int)rowSpacing;
+
+@end
 
 
 UiContainerX* ui_create_container(UiObject *obj, id<Container> container);
index c73c429f90b3bfd2c0462de459fca8ae2c0d77a8..e7bcf0c49593be59fadf7073f2aa085d24b5bab1 100644 (file)
 
 #import "Container.h"
 #import "GridLayout.h"
-
-/* ------------------------- container classes ------------------------- */
-
-@implementation BoxContainer
-
-@synthesize label=_label;
-@synthesize uilayout=_uilayout;
-@synthesize newline=_newline;
-
-- (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing {
-    self = [super init];
-    _label = NULL;
-    _uilayout = (UiLayout){ 0 };
-    _newline = false;
-    
-    self.distribution = NSStackViewDistributionFillProportionally;
-    self.spacing = spacing;
-    
-    self.orientation = orientation;
-    if(orientation == NSUserInterfaceLayoutOrientationHorizontal) {
-        self.alignment = NSLayoutAttributeHeight;
-    } else {
-        self.alignment = NSLayoutAttributeWidth;
-    }
-    
-    
-    return self;
-}
-
-- (void) addView:(NSView*)view {
-    UiBool fill = _uilayout.fill;
-    
-    [self addArrangedSubview:view];
-    
-    if(self.orientation == NSUserInterfaceLayoutOrientationHorizontal) {
-        [view.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES;
-        if(!fill) {
-            NSSize isize = view.intrinsicContentSize;
-            [view.widthAnchor constraintEqualToConstant:isize.width].active = YES;
-        }
-    } else {
-        [view.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES;
-        if(!fill) {
-            NSSize isize = view.intrinsicContentSize;
-            NSRect frame = view.frame;
-            CGFloat height = isize.height > 0 ? isize.height : frame.size.height;
-            if(height == 0) {
-                printf("debug");
-            }
-            if(height > 0) {
-                [view.heightAnchor constraintEqualToConstant:height].active = YES;
-            }
-        }
-    }
-    
-    // at the moment, only the fill layout option needs to be reset
-    _uilayout.fill = UI_DEFAULT;
-}
-
-@end
-
-
+#import "BoxContainer.h"
+#import "TabView.h"
 
 /* -------------------- public container functions --------------------- */
 
 static UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, NSUserInterfaceLayoutOrientation orientation) {
     BoxContainer *box = [[BoxContainer alloc] init:orientation spacing:args->spacing];
     box.translatesAutoresizingMaskIntoConstraints = false;
+    UiContainerX *container = ui_create_container(obj, box);
     
     // add box to the parent
     UiLayout layout = UI_INIT_LAYOUT(args);
     ui_container_add(obj, box, &layout);
     
     // add new box to the obj container chain
-    uic_object_push_container(obj, ui_create_container(obj, box));
+    uic_object_push_container(obj, container);
     
     return (__bridge void*)box;
 }
@@ -118,17 +59,133 @@ UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args) {
 UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
     GridLayout *grid = [[GridLayout alloc] init];
     grid.translatesAutoresizingMaskIntoConstraints = false;
+    grid.columnspacing = args->columnspacing;
+    grid.rowspacing = args->rowspacing;
+    UiContainerX *container = ui_create_container(obj, grid);
+    grid.container = container;
     
     // add box to the parent
     UiLayout layout = UI_INIT_LAYOUT(args);
     ui_container_add(obj, grid, &layout);
     
     // add new box to the obj container chain
-    uic_object_push_container(obj, ui_create_container(obj, grid));
+    uic_object_push_container(obj, container);
     
     return (__bridge void*)grid;
 }
 
+UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
+    NSString *title = args->label ? [[NSString alloc]initWithUTF8String:args->label] : nil;
+    FrameContainer *frame = [[FrameContainer alloc] init:title];
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ui_container_add(obj, frame, &layout);
+    
+    // add container to the chain
+    UiContainerX *container;
+    UiLayout subLayout = {0};
+    switch(args->subcontainer) {
+        default: {
+            // UI_CONTAINER_NO_SUB
+            container = ui_create_container(obj, frame);
+            break;
+        }
+        case UI_CONTAINER_VBOX: {
+            BoxContainer *box = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationVertical spacing:args->spacing];
+            box.translatesAutoresizingMaskIntoConstraints = false;
+            [frame addView:box layout:&subLayout];
+            container = ui_create_container(obj, box);
+            break;
+        }
+        case UI_CONTAINER_HBOX: {
+            BoxContainer *box = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationHorizontal spacing:args->spacing];
+            box.translatesAutoresizingMaskIntoConstraints = false;
+            [frame addView:box layout:&subLayout];
+            container = ui_create_container(obj, box);
+            break;
+        }
+        case UI_CONTAINER_GRID: {
+            GridLayout *grid = [[GridLayout alloc] init];
+            grid.translatesAutoresizingMaskIntoConstraints = false;
+            grid.columnspacing = args->columnspacing;
+            grid.rowspacing = args->rowspacing;
+            [frame addView:grid layout:&subLayout];
+            container = ui_create_container(obj, grid);
+            break;
+        }
+    }
+    
+    uic_object_push_container(obj, container);
+    
+    return (__bridge void*)frame;
+}
+
+UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs *args) {
+    return ui_frame_create(obj, args); // TODO
+}
+
+UIWIDGET ui_scrolledwindow_create(UiObject *obj, UiFrameArgs *args) {
+    int colspacing = args->spacing;
+    int rowspacing = args->spacing;
+    if(args->subcontainer == UI_CONTAINER_GRID) {
+        colspacing = args->columnspacing;
+        rowspacing = args->rowspacing;
+    }
+    ScrollViewContainer *scrollview = [[ScrollViewContainer alloc]init:args->subcontainer columnSpacing:colspacing rowSpacing:rowspacing];
+    scrollview.hasVerticalScroller = YES;
+    scrollview.scrollerStyle = NSScrollerStyleOverlay;
+    scrollview.autohidesScrollers = YES;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ui_container_add(obj, scrollview, &layout);
+    
+    UiContainerX *container = ui_create_container(obj, scrollview);
+    uic_object_push_container(obj, container);
+    
+    return (__bridge void*)scrollview;
+}
+
+UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args) {
+    NSView<TabView, Container> *tabview;
+    switch(args->tabview) {
+        default: tabview = [[UiTopTabView alloc]init:obj args:args]; break;
+    }
+    
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ui_container_add(obj, tabview, &layout);
+    
+    UiContainerX *container = ui_create_container(obj, tabview);
+    uic_object_push_container(obj, container);
+    
+    return (__bridge void*)tabview;
+}
+
+void ui_tab_create(UiObject *obj, const char* title) {
+    UiContainerX *ctn = obj->container_end;
+    id<TabView> tabview = (__bridge id<TabView>)ctn->container;
+    NSString *s = title ? [[NSString alloc]initWithUTF8String:title] : @"";
+    NSView<Container> *sub = [tabview createTab:-1 title:s];
+    
+    UiContainerX *container = ui_create_container(obj, sub);
+    uic_object_push_container(obj, container);
+}
+
+void ui_tabview_select(UIWIDGET tabview, int tab) {
+    id<TabView> tabv = (__bridge id<TabView>)tabview;
+    [tabv selectTab:tab];
+}
+
+void ui_tabview_remove(UIWIDGET tabview, int tab) {
+    id<TabView> tabv = (__bridge id<TabView>)tabview;
+    [tabv removeTab:tab];
+}
+
+UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) {
+    id<TabView> tabv = (__bridge id<TabView>)tabview;
+    NSString *s = name ? [[NSString alloc]initWithUTF8String:name] : @"";
+    return [tabv addTab:tab_index title:s];
+}
+
+
+
 
 void ui_container_begin_close(UiObject *obj) {
     UiContainerX *ct = obj->container_end;
@@ -144,6 +201,94 @@ int ui_container_finish(UiObject *obj) {
     return 1;
 }
 
+/* -------------------------- Frame Container -------------------------- */
+
+@implementation FrameContainer
+
+@synthesize container = _container;
+
+- (id)init:(NSString*)title {
+    self = [super init];
+    self.title = title;
+    self.boxType = NSBoxPrimary;
+    if(title != nil) {
+        self.titlePosition = NSAtTop;
+    }
+    return self;
+}
+
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
+    [self.contentView addSubview:view];
+    view.translatesAutoresizingMaskIntoConstraints = NO;
+    [NSLayoutConstraint activateConstraints:@[
+        [view.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:0],
+        [view.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:0],
+        [view.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-0],
+        [view.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-0]
+    ]];
+}
+
+@end
+
+
+/* -------------------------- Expander Container -------------------------- */
+
+// TODO
+
+
+/* ------------------------ ScrollView Container ------------------------ */
+
+@implementation ScrollViewContainer
+
+@synthesize container = _container;
+
+- (id)init:(UiSubContainerType)subContainer columnSpacing:(int)columnSpacing rowSpacing:(int)rowSpacing {
+    self = [super init];
+    
+    if(subContainer != UI_CONTAINER_NO_SUB) {
+        GridLayout *child;
+        switch(subContainer) {
+            default:
+            case UI_CONTAINER_VBOX: {
+                child = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationVertical spacing:columnSpacing];
+                break;
+            }
+            case UI_CONTAINER_HBOX: {
+                child = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationHorizontal spacing:columnSpacing];
+                break;
+            }
+            case UI_CONTAINER_GRID: {
+                child = [[GridLayout alloc]init];
+                child.columnspacing = columnSpacing;
+                child.rowspacing = rowSpacing;
+                break;
+            }
+        }
+        child.translatesAutoresizingMaskIntoConstraints = NO;
+        
+        self.documentView = child;
+        [child.widthAnchor constraintEqualToAnchor:self.contentView.widthAnchor].active = YES;
+        
+        _child = child;
+    }
+    
+    
+    return self;
+}
+
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
+    if(_child != nil) {
+        _child.container = self.container; // required, otherwise child has no container and can't access the newline property
+        view.translatesAutoresizingMaskIntoConstraints = NO;
+        [_child addView:view layout:layout];
+    } else {
+        self.documentView = view;
+        [view.widthAnchor constraintEqualToAnchor:self.contentView.widthAnchor].active = YES;
+    }
+}
+
+@end
+
 /* ------------------------- private functions ------------------------- */
 
 UiContainerX* ui_create_container(UiObject *obj, id<Container> container) {
@@ -152,24 +297,20 @@ UiContainerX* ui_create_container(UiObject *obj, id<Container> container) {
     ctn->close = 0;
     ctn->prev = NULL;
     ctn->next = NULL;
+    container.container = ctn;
     return ctn;
 }
 
 void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout) {
     UiContainerX *ctn = obj->container_end;
     id<Container> container = (__bridge id<Container>)ctn->container;
-    container.uilayout = *layout;
-    [container addView:view];
-}
-
-/* ---------------------- public layout functions ----------------------- */
-
-void ui_newline(UiObject *obj) {
-    UiContainerX *ctn = obj->container_end;
-    if(ctn) {
-        id<Container> container = (__bridge id<Container>)ctn->container;
-        container.newline = TRUE;
-    } else {
-        fprintf(stderr, "Error: obj has no container\n");
+    UiLayout adjustedLayout = *layout;
+    if(adjustedLayout.margin > 0) {
+        adjustedLayout.margin_left = adjustedLayout.margin;
+        adjustedLayout.margin_right = adjustedLayout.margin;
+        adjustedLayout.margin_top = adjustedLayout.margin;
+        adjustedLayout.margin_bottom = adjustedLayout.margin;
     }
+    [container addView:view layout:&adjustedLayout];
 }
+
diff --git a/ui/cocoa/entry.h b/ui/cocoa/entry.h
new file mode 100644 (file)
index 0000000..b0cb2fd
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+#import "Container.h"
+
+#import "../ui/entry.h"
+
+@interface UiSpinBox : NSObject<NSTextFieldDelegate>
+
+@property UiObject *obj;
+@property (weak) NSTextField *textfield;
+@property (weak) NSStepper *stepper;
+@property BOOL isInteger;
+@property ui_callback onchange;
+@property void* onchangedata;
+@property UiObserver **observers;
+
+- (UiSpinBox*)init;
+
+- (void)valueChanged;
+- (void)stepperChanged:(id)sender;
+- (void) controlTextDidChange:(NSNotification *) obj;
+
+@end
+
+int64_t ui_spinbutton_getint(UiInteger *i);
+void ui_spinbutton_setint(UiInteger *i, int64_t val);
+
+double ui_spinbutton_getdouble(UiDouble *d);
+void ui_spinbutton_setdouble(UiDouble *d, double val);
+
+double ui_spinbutton_getrangeval(UiRange *r);
+void ui_spinbutton_setrangeval(UiRange *r, double val);
+void ui_spinbutton_setrange(UiRange *r, double min, double max);
+void ui_spinbutton_setextent(UiRange *r, double extent);
diff --git a/ui/cocoa/entry.m b/ui/cocoa/entry.m
new file mode 100644 (file)
index 0000000..9bccc06
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "entry.h"
+#import <objc/runtime.h>
+
+@implementation UiSpinBox
+
+- (UiSpinBox*)init {
+    return self;
+}
+
+- (void)valueChanged {
+    float value = _stepper.doubleValue;
+    UiEvent e;
+    e.obj = _obj;
+    e.window = e.obj->window;
+    e.document = e.obj->ctx->document;
+    e.eventdata = NULL;
+    e.eventdatatype = 0;
+    e.intval = (int)value;
+    e.set = ui_get_setop();
+    
+    if(_onchange) {
+        _onchange(&e, _onchangedata);
+    }
+    
+    if(_observers) {
+        UiObserver *observer = *_observers;
+        ui_notify_evt(observer, &e);
+    }
+}
+
+- (void)stepperChanged:(id)sender {
+    if(_isInteger) {
+        _textfield.integerValue = _stepper.integerValue;
+    } else {
+        _textfield.doubleValue = _stepper.doubleValue;
+    }
+    [self valueChanged];
+}
+
+- (void) controlTextDidChange:(NSNotification *)obj {
+    if(_isInteger) {
+        _stepper.integerValue = _textfield.integerValue;
+    } else {
+        _stepper.doubleValue = _textfield.doubleValue;
+    }
+    [self valueChanged];
+}
+
+@end
+
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
+    double min = args->min;
+    double max = args->max != 0 ? args->max : 1000;
+    
+    UiVar *var = NULL;
+    UiVarType vartype = 0;
+    if(args->varname) {
+        var = uic_get_var(obj->ctx, args->varname);
+        if(var) {
+            vartype = var->type;
+        } else {
+            var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, args->varname, UI_VAR_RANGE);
+            vartype = UI_VAR_RANGE;
+        }
+    }
+    
+    if(!var) {
+        if(args->intvalue) {
+            var = uic_widget_var(obj->ctx, obj->ctx, args->intvalue, NULL, UI_VAR_INTEGER);
+            vartype = UI_VAR_INTEGER;
+        } else if(args->doublevalue) {
+            var = uic_widget_var(obj->ctx, obj->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE);
+            vartype = UI_VAR_DOUBLE;
+        } else if(args->rangevalue) {
+            var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, NULL, UI_VAR_RANGE);
+            vartype = UI_VAR_RANGE;
+        }
+    }
+    
+    if(vartype == UI_VAR_RANGE) {
+        UiRange *r = var->value;
+        min = r->min;
+        max = r->max;
+    }
+    if(args->step == 0) {
+        args->step = 1;
+    }
+    
+    // create and setup textfield for number input
+    NSTextField *textfield = [[NSTextField alloc] init];
+    textfield.translatesAutoresizingMaskIntoConstraints = NO;
+    
+    if(!args->hfill || args->width > 0) {
+        textfield.translatesAutoresizingMaskIntoConstraints = NO;
+        int width = args->width > 0 ? args->width : 100;
+        [[textfield.widthAnchor constraintEqualToConstant:width] setActive:YES];
+    }
+    
+    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
+    formatter.numberStyle = NSNumberFormatterDecimalStyle;
+    formatter.allowsFloats = vartype != UI_VAR_INTEGER;
+    formatter.minimumFractionDigits = args->digits;
+    formatter.maximumFractionDigits = args->digits;
+
+    textfield.formatter = formatter;
+
+    // create view containing the textfield and stepper
+    NSView *view = [[NSView alloc]init];
+    view.translatesAutoresizingMaskIntoConstraints = NO;
+    
+    NSStepper *stepper = [[NSStepper alloc] init];
+    stepper.translatesAutoresizingMaskIntoConstraints = NO;
+    
+    [view addSubview:textfield];
+    [view addSubview:stepper];
+
+    [NSLayoutConstraint activateConstraints:@[
+        [textfield.leadingAnchor constraintEqualToAnchor:view.leadingAnchor],
+        [textfield.topAnchor constraintEqualToAnchor:view.topAnchor],
+        [textfield.bottomAnchor constraintEqualToAnchor:view.bottomAnchor],
+        
+        [stepper.trailingAnchor constraintEqualToAnchor:view.trailingAnchor],
+        [stepper.topAnchor constraintEqualToAnchor:view.topAnchor],
+        [stepper.bottomAnchor constraintEqualToAnchor:view.bottomAnchor],
+        
+        [textfield.trailingAnchor constraintEqualToAnchor:stepper.leadingAnchor]
+    ]];
+    
+    UiLayout layout = UI_INIT_LAYOUT(args);
+    ui_container_add(obj, view, &layout);
+    
+    // create the spinbox object, that handles all textfield and stepper events
+    UiSpinBox *spinbox = [[UiSpinBox alloc]init];
+    spinbox.obj = obj;
+    spinbox.textfield = textfield;
+    spinbox.stepper = stepper;
+    spinbox.onchange = args->onchange;
+    spinbox.onchangedata = args->onchangedata;
+    spinbox.isInteger = vartype == UI_VAR_INTEGER;
+    objc_setAssociatedObject(stepper, "ui_spinbox", spinbox, OBJC_ASSOCIATION_RETAIN);
+    
+    stepper.minValue = min;
+    stepper.maxValue = max;
+    stepper.increment = args->step;
+    stepper.target = spinbox;
+    stepper.action = @selector(stepperChanged:);
+    textfield.delegate = spinbox;
+    
+    UiObserver **obs = NULL;
+    if(var) {
+        void *varObj = (__bridge void*)spinbox;
+        switch(vartype) {
+            default: break;
+            case UI_VAR_INTEGER: {
+                UiInteger *i = var->value;
+                i->get = ui_spinbutton_getint;
+                i->set = ui_spinbutton_setint;
+                i->obj = varObj;
+                obs = &i->observers;
+                
+                stepper.integerValue = i->value;
+                textfield.integerValue = i->value;
+                break;
+            }
+            case UI_VAR_DOUBLE: {
+                UiDouble *d = var->value;
+                d->get = ui_spinbutton_getdouble;
+                d->set = ui_spinbutton_setdouble;
+                d->obj = varObj;
+                obs = &d->observers;
+                
+                stepper.doubleValue = d->value;
+                textfield.doubleValue = d->value;
+                break;
+            }
+            case UI_VAR_RANGE: {
+                UiRange *r = var->value;
+                r->get = ui_spinbutton_getrangeval;
+                r->set = ui_spinbutton_setrangeval;
+                r->setrange = ui_spinbutton_setrange;
+                r->setextent = ui_spinbutton_setextent;
+                r->obj = varObj;
+                obs = &r->observers;
+                
+                stepper.doubleValue = r->value;
+                textfield.doubleValue = r->value;
+                break;
+            }
+        }
+    }
+    spinbox.observers = obs;
+    
+    return (__bridge void*)textfield;
+}
+
+int64_t ui_spinbutton_getint(UiInteger *i) {
+    UiSpinBox *spinbox = (__bridge UiSpinBox*)i->obj;
+    i->value = spinbox.stepper.integerValue;
+    return i->value;
+}
+
+void ui_spinbutton_setint(UiInteger *i, int64_t val) {
+    UiSpinBox *spinbox = (__bridge UiSpinBox*)i->obj;
+    i->value = val;
+    spinbox.stepper.integerValue = val;
+    spinbox.textfield.integerValue = val;
+}
+
+double ui_spinbutton_getdouble(UiDouble *d) {
+    UiSpinBox *spinbox = (__bridge UiSpinBox*)d->obj;
+    d->value = spinbox.stepper.doubleValue;
+    return d->value;
+}
+
+void ui_spinbutton_setdouble(UiDouble *d, double val) {
+    UiSpinBox *spinbox = (__bridge UiSpinBox*)d->obj;
+    d->value = val;
+    spinbox.stepper.doubleValue = val;
+    spinbox.textfield.doubleValue = val;
+}
+
+double ui_spinbutton_getrangeval(UiRange *r) {
+    UiSpinBox *spinbox = (__bridge UiSpinBox*)r->obj;
+    r->value = spinbox.stepper.doubleValue;
+    return r->value;
+}
+
+void ui_spinbutton_setrangeval(UiRange *r, double val) {
+    UiSpinBox *spinbox = (__bridge UiSpinBox*)r->obj;
+    r->value = val;
+    spinbox.stepper.doubleValue = val;
+    spinbox.textfield.doubleValue = val;
+}
+
+void ui_spinbutton_setrange(UiRange *r, double min, double max) {
+    UiSpinBox *spinbox = (__bridge UiSpinBox*)r->obj;
+    spinbox.stepper.minValue = min;
+    spinbox.stepper.maxValue = max;
+}
+
+void ui_spinbutton_setextent(UiRange *r, double extent) {
+    UiSpinBox *spinbox = (__bridge UiSpinBox*)r->obj;
+    spinbox.stepper.increment = extent;
+}
index f29a6500b44f2ce7f9c70e1fabb637417a4ac393..44e90451f7437c5b53c797a8e9e7e88765a84acb 100644 (file)
@@ -51,7 +51,7 @@ static UIWIDGET create_label(UiObject *obj, UiLabelArgs *args) {
         label.stringValue = str;
     }
     
-    UiLayout layout = UI_INIT_LAYOUT(args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     ui_container_add(obj, label, &layout);
     
     UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
index 03557b714a36099eed65c91f37285ad5426734cb..c498de5408b74f7a36fb5a8c02648ba272e5455e 100644 (file)
@@ -55,3 +55,67 @@ void ui_tableview_setselection(UiList *list, UiListSelection selection);
 void ui_dropdown_update(UiList *list, int i);
 UiListSelection ui_dropdown_getselection(UiList *list);
 void ui_dropdown_setselection(UiList *list, UiListSelection selection);
+
+@class UiSourceList;
+
+@interface UiSourceListItem : NSObject
+@property (weak) UiSourceList *sourcelist;
+@property (weak) UiSourceListItem *parent;
+@property (strong) NSString *label;
+@property (strong) NSString *badge;
+
+@property (strong) NSMutableArray<UiSourceListItem*> *items;
+@property UiVar *var;
+@property UiSubList *sublist;
+
+@property int sublistIndex;
+@property int sublistStartRow;
+@property int rownum;
+
+@property void *eventdata;
+
+/*
+ * Initialize a section item
+ */
+- (id)init:(UiSubListItem*)item parent:(UiSourceListItem*)parent;
+/*
+ * Initialize a child item
+ */
+- (id)init:(UiSourceList*)sourcelist sublist:(UiSubList*)sublist;
+- (BOOL)isSection;
+- (void)update:(int)row;
+
+@end
+
+
+@interface UiSourceList : NSObject <NSOutlineViewDataSource, NSOutlineViewDelegate>
+
+@property UiObject *obj;
+@property (weak) NSOutlineView *outlineView;
+@property CxList *sublists;
+@property UiVar *dynamic_sublists;
+@property ui_sublist_getvalue_func getvalue;
+@property void *getvaluedata;
+@property ui_callback onactivate;
+@property void *onactivatedata;
+@property ui_callback onbuttonclick;
+@property void *onbuttonclickdata;
+
+@property (strong) NSMutableArray<UiSourceListItem*> *sections;
+
+- (id)init:(UiObject*)obj outline:(NSOutlineView*)view;
+
+- (void)update:(int)row;
+
+@end
+
+@interface UiSourceListRow : NSTableRowView
+
+@property NSTrackingArea *trackingArea;
+@property NSView *disclosureButton;
+@property BOOL hover;
+@property BOOL showDisclosureButton;
+
+@end
+
+void ui_sourcelist_update(UiList *list, int row);
index 6c4e39b51709967bd2f2006c327231bad22cd333..de2e98ecaaa0db903ccb1db4164b1c67b9dc9ea3 100644 (file)
 #import "ListDelegate.h"
 #import <objc/runtime.h>
 
+#import <inttypes.h>
+#import <limits.h>
+
+#import <cx/array_list.h>
+
 static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
     ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata;
     return getvalue(elm, col);
@@ -354,3 +359,449 @@ void ui_dropdown_setselection(UiList *list, UiListSelection selection) {
         [combobox selectItemAtIndex: -1];
     }
 }
+
+
+/* --------------------------- SourceList --------------------------- */
+
+static void sublist_free(const CxAllocator *a, UiSubList *sl) {
+    cxFree(a, (char*)sl->varname);
+    cxFree(a, (char*)sl->header);
+}
+
+static UiSubList copy_sublist(const CxAllocator *a, UiSubList *sl) {
+    UiSubList new_sl;
+    new_sl.value = sl->value;
+    new_sl.varname = sl->varname ? cx_strdup_a(a, cx_str(sl->varname)).ptr : NULL;
+    new_sl.header = sl->header ? cx_strdup_a(a, cx_str(sl->header)).ptr : NULL;
+    new_sl.separator = sl->separator;
+    new_sl.userdata = sl->userdata;
+    return new_sl;
+}
+
+static CxList* copy_sublists(const CxAllocator *a, UiSourceListArgs *args) {
+    if(args->sublists) {
+        size_t max = args->numsublists;
+        if(max == 0) {
+            max = INT_MAX;
+        }
+        
+        CxList *sublists = cxArrayListCreate(a, NULL, sizeof(UiSubList), args->numsublists);
+        sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_free;
+        
+        for(int i=0;i<max;i++) {
+            UiSubList *sl = &args->sublists[i];
+            if(sl->value == NULL && sl->varname == NULL) {
+                break;
+            }
+            
+            UiSubList new_sl = copy_sublist(a, sl);
+            cxListAdd(sublists, &new_sl);
+        }
+        
+        return sublists;
+    }
+    return NULL;
+}
+
+UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
+    // create views
+    NSScrollView *scrollview = [[NSScrollView alloc] init];
+    scrollview.autoresizingMask = NSViewWidthSizable;
+    scrollview.hasVerticalScroller = YES;
+    scrollview.hasHorizontalScroller = NO;
+    scrollview.autohidesScrollers = YES;
+    
+    NSOutlineView *outline = [[NSOutlineView alloc]init];
+    NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:@"x"];
+    [outline addTableColumn:column];
+    outline.outlineTableColumn = column;
+    outline.headerView = NULL;
+    outline.rowSizeStyle = NSTableViewRowSizeStyleDefault;
+    outline.usesAutomaticRowHeights = YES;
+    outline.indentationPerLevel = 0;
+    
+    outline.style = NSTableViewStyleSourceList;
+    
+    // Make background transparent so vibrancy shows through
+    scrollview.drawsBackground = NO;
+
+    scrollview.documentView = outline;
+    
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ui_container_add(obj, scrollview, &layout);
+    
+    // datasource and delegate
+    UiSourceList *data = [[UiSourceList alloc] init:obj outline:outline];
+    data.sublists = copy_sublists(obj->ctx->allocator, args);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST);
+    if(var) {
+        UiList *list = var->value;
+        list->obj = (__bridge void*)data;
+        list->update = ui_sourcelist_update;
+    }
+    data.dynamic_sublists = var;
+    data.getvalue = args->getvalue;
+    data.getvaluedata = args->getvaluedata;
+    data.onactivate = args->onactivate;
+    data.onactivatedata = args->onactivatedata;
+    data.onbuttonclick = args->onbuttonclick;
+    data.onactivatedata = args->onbuttonclickdata;
+    [data update:-1];
+    outline.dataSource = data;
+    outline.delegate = data;
+    
+    [data update:-1];
+    
+    objc_setAssociatedObject(outline, "ui_datasource", data, OBJC_ASSOCIATION_RETAIN);
+    
+    return (__bridge void*)scrollview;
+}
+
+void ui_sourcelist_update(UiList *list, int row) {
+    UiSourceList *sourcelist = (__bridge UiSourceList*)list->obj;
+    [sourcelist update:row];
+}
+
+
+/*
+ * Data Source and Delegate for the sourcelist NSOutlineView
+ */
+@implementation UiSourceList
+
+- (id)init:(UiObject*)obj outline:(NSOutlineView*)view {
+    _obj = obj;
+    _outlineView = view;
+    _sections = [[NSMutableArray alloc] initWithCapacity:16];
+    return self;
+}
+
+- (void)dealloc {
+    cxListFree(_sublists);
+}
+
+- (void)update:(int)row {
+    // TODO: check row
+    
+    [_sections removeAllObjects];
+    
+    CxIterator i = cxListIterator(_sublists);
+    int index = 0;
+    int rownum = 0;
+    cx_foreach(UiSubList *, sl, i) {
+        UiSourceListItem *section = [[UiSourceListItem alloc] init:self sublist:sl];
+        section.sublistIndex = index;
+        section.rownum = rownum;
+        section.sublistStartRow = rownum;
+        [section update:-1];
+        [_sections addObject:section];
+        index++;
+        rownum += 1 + section.items.count;
+    }
+    
+    [_outlineView reloadData];
+    [_outlineView expandItem:nil expandChildren:YES];
+}
+
+// NSOutlineViewDataSource implementation
+
+- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
+    if(item == nil) {
+        return _sections.count;
+    } else {
+        UiSourceListItem *i = item;
+        return i.items.count;
+    }
+}
+
+- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
+    UiSourceListItem *i = item;
+    return [i isSection] ? YES : NO;
+}
+
+- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
+    UiSourceListItem *i = item;
+    if(i) {
+        return [i.items objectAtIndex:index];
+    }
+    return [_sections objectAtIndex:index];
+}
+
+- (void)outlineView:(NSOutlineView *)outlineView
+     setObjectValue:(id)object
+     forTableColumn:(NSTableColumn *)tableColumn
+             byItem:(id)item
+{
+    
+}
+
+// NSOutlineViewDelegate implementation
+
+- (NSView *)outlineView:(NSOutlineView *)outlineView
+     viewForTableColumn:(NSTableColumn *)tableColumn
+                   item:(id)item
+{
+    UiSourceListItem *i = item;
+    
+    NSTableCellView *cell = [[NSTableCellView alloc] init];
+    cell.identifier = @"cell";
+    // Icon
+    NSImageView *iconView = [[NSImageView alloc] initWithFrame:NSZeroRect];
+    iconView.translatesAutoresizingMaskIntoConstraints = NO;
+    [cell addSubview:iconView];
+    cell.imageView = iconView;
+
+    // Label
+    //NSTextField *textField = [NSTextField labelWithString:@""];
+    NSTextField *textField = [[NSTextField alloc] initWithFrame:NSZeroRect];
+    textField.translatesAutoresizingMaskIntoConstraints = NO;
+    textField.bezeled = NO;
+    textField.editable = NO;
+    textField.drawsBackground = NO;
+    textField.selectable = NO;
+    textField.lineBreakMode = NSLineBreakByTruncatingTail;
+    
+    
+    [cell addSubview:textField];
+    cell.textField = textField;
+    
+    if([i isSection]) {
+        NSFont *font = [NSFont boldSystemFontOfSize:[NSFont systemFontSize]*0.85];
+        //NSFont *font = [NSFont preferredFontForTextStyle:NSFontTextStyleCaption1 options:@{}];
+        NSDictionary *attrs = @{
+            NSFontAttributeName: font,
+            NSForegroundColorAttributeName: [NSColor tertiaryLabelColor]
+        };
+        textField.attributedStringValue = [[NSAttributedString alloc] initWithString:i.label attributes:attrs];
+        
+        // Layout constraints
+        [NSLayoutConstraint activateConstraints:@[
+            [iconView.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+            [iconView.bottomAnchor constraintEqualToAnchor:cell.bottomAnchor constant:-1],
+
+            [textField.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+            [textField.bottomAnchor constraintEqualToAnchor:cell.bottomAnchor constant:-1],
+            [textField.trailingAnchor constraintEqualToAnchor:cell.trailingAnchor constant:0],
+        ]];
+    } else {
+        textField.stringValue = i.label;
+        
+        // Layout constraints
+        [NSLayoutConstraint activateConstraints:@[
+            [iconView.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+            [iconView.centerYAnchor constraintEqualToAnchor:cell.centerYAnchor],
+
+            [textField.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+            [textField.centerYAnchor constraintEqualToAnchor:cell.centerYAnchor],
+            [textField.trailingAnchor constraintEqualToAnchor:cell.trailingAnchor constant:0],
+        ]];
+    }
+    
+    return cell;
+}
+
+- (NSTableRowView *) outlineView:(NSOutlineView *) outlineView
+                  rowViewForItem:(id)item {
+    UiSourceListItem *it = item;
+    UiSourceListRow *row = [[UiSourceListRow alloc]init];
+    if([it isSection] && it.sublist->header) {
+        row.showDisclosureButton = YES;
+    }
+    return row;
+}
+
+- (BOOL) outlineView:(NSOutlineView *) outlineView
+    shouldSelectItem:(id)item
+{
+    UiSourceListItem *i = item;
+    return [i isSection] ? NO : YES;
+}
+
+- (CGFloat) outlineView:(NSOutlineView *) outlineView
+      heightOfRowByItem:(id) item
+{
+    UiSourceListItem *i = item;
+    CGFloat rowHeight = outlineView.rowHeight;
+    if([i isSection]) {
+        if(i.sublist->header) {
+            rowHeight += i.sublistIndex == 0 ? -12 : 4;
+        } else {
+            rowHeight = i.sublistIndex == 0 ? 0.1 : 12;
+        }
+    }
+    return rowHeight;
+}
+
+- (void) outlineViewSelectionDidChange:(NSNotification *) notification {
+    UiEvent event;
+    event.obj = _obj;
+    event.window = event.obj->window;
+    event.document = event.obj->ctx->document;
+    event.eventdata = NULL;
+    event.eventdatatype = 0;
+    event.intval = 0;
+    event.set = ui_get_setop();
+    
+    UiSubListEventData sublistEvent;
+    
+    NSInteger selectedRow = _outlineView.selectedRow;
+    if(selectedRow >= 0) {
+        UiSourceListItem *item = [_outlineView itemAtRow:selectedRow];
+        UiSourceListItem *parent = item.parent;
+        UiSubList *sublist = parent != nil ? parent.sublist : item.sublist;
+        UiVar *var = parent != nil ? parent.var : item.var;
+        if(item && var) {
+            sublistEvent.list = var->value;
+            sublistEvent.sublist_index = parent ? parent.sublistIndex : item.sublistIndex;
+            sublistEvent.row_index = (int)selectedRow - item.sublistStartRow - 1;
+            sublistEvent.sublist_userdata = sublist ? sublist->userdata : NULL;
+            sublistEvent.event_data = item.eventdata;
+            sublistEvent.row_data = sublistEvent.list->get(sublistEvent.list, sublistEvent.row_index);
+            
+            event.eventdata = &sublistEvent;
+            event.eventdatatype = UI_EVENT_DATA_SUBLIST;
+        }
+    }
+    
+    if(_onactivate) {
+        _onactivate(&event, _onactivatedata);
+    }
+}
+
+@end
+
+/*
+ * Outline datasource item
+ * Is used for sections (sublists) and individual items
+ */
+@implementation UiSourceListItem
+
+- (id)init:(UiSourceList*)sourcelist sublist:(UiSubList*)sublist {
+    _sourcelist = sourcelist;
+    _sublist = sublist;
+    _items = [[NSMutableArray alloc]initWithCapacity:16];
+    if(sublist->header) {
+        _label = [[NSString alloc]initWithUTF8String:sublist->header];
+    } else {
+        _label = @"";
+    }
+    UiVar *var = uic_widget_var(sourcelist.obj->ctx,
+                                sourcelist.obj->ctx,
+                                sublist->value,
+                                sublist->varname,
+                                UI_VAR_LIST);
+    _var = var;
+    return self;
+}
+
+- (id)init:(UiSubListItem*)item parent:(UiSourceListItem*)parent {
+    _parent = parent;
+    if(item->label) {
+        _label = [[NSString alloc]initWithUTF8String:item->label];
+    } else {
+        _label = @"";
+    }
+    _eventdata = item->eventdata;
+    return self;
+}
+
+- (BOOL)isSection {
+    return _sublist != NULL;
+}
+
+- (void)update:(int)row {
+    // TODO: check row
+    
+    [_items removeAllObjects];
+    if(_var == NULL) {
+        return;
+    }
+    UiList *list = _var->value;
+    void *elm = list->first(list);
+    int index = 0;
+    while(elm) {
+        UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
+        if(_sourcelist.getvalue) {
+            _sourcelist.getvalue(list, _sublist->userdata, elm, index, &item, _sourcelist.getvaluedata);
+        } else {
+            item.label = strdup(elm);
+        }
+        
+        UiSourceListItem *it = [[UiSourceListItem alloc] init:&item parent:self];
+        it.sublistIndex = index;
+        it.rownum = self.rownum + index;
+        it.sublistStartRow = _parent ? _parent.sublistStartRow : _sublistStartRow;
+        [_items addObject:it];
+        
+        elm = list->next(list);
+        index++;
+    }
+}
+
+@end
+
+/*
+ * Custom NSTableRowView implementation
+ * Moves the disclosure button to the right side
+ * Handles mouse hover events (for hiding the disclosure button)
+ */
+@implementation UiSourceListRow
+
+- (void)layout {
+    [super layout];
+
+    for (NSView *subview in self.subviews) {
+        if ([subview.identifier isEqualToString:NSOutlineViewDisclosureButtonKey] ||
+            [subview.identifier isEqualToString:NSOutlineViewShowHideButtonKey])
+        {
+            NSRect frame = subview.frame;
+            frame.origin.x = self.bounds.size.width - frame.size.width - 16.0;
+            subview.frame = frame;
+            
+            if(!_hover) {
+                subview.hidden = YES;
+            }
+            
+            if(subview != _disclosureButton) {
+                // init disclosure button
+                _disclosureButton = (NSButton*)subview;
+                if ([subview isKindOfClass:[NSButton class]]) {
+                    NSButton *button = (NSButton*)subview;
+                    button.contentTintColor = [NSColor tertiaryLabelColor];
+                }
+            }
+            
+            
+        } else if ([subview.identifier isEqualToString:@"cell"]) {
+            NSRect frame = subview.frame;
+            frame.origin.x = 16;
+            subview.frame = frame;
+        }
+    }
+}
+
+- (void)updateTrackingAreas {
+    [super updateTrackingAreas];
+    if(_trackingArea != nil) {
+        [self removeTrackingArea:_trackingArea];
+    }
+    _trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds
+                                                 options:NSTrackingMouseEnteredAndExited |
+                                                         NSTrackingActiveInActiveApp |
+                                                         NSTrackingInVisibleRect
+                                                   owner:self
+                                                userInfo:nil];
+    [self addTrackingArea:_trackingArea];
+}
+
+- (void)mouseEntered:(NSEvent *)event {
+    _hover = YES;
+    _disclosureButton.hidden = _showDisclosureButton ? NO : YES;
+}
+
+- (void)mouseExited:(NSEvent *)event {
+    _hover = NO;
+    _disclosureButton.hidden = YES;
+}
+
+@end
index a9a0b4b69397d72d454c3dde94d681fe50c8cf45..b0326be9867be55e72bd1c00972ee480dae2a5ec 100644 (file)
@@ -32,8 +32,9 @@ COCOA_OBJPRE = $(OBJ_DIR)/$(COCOA_SRC_DIR)
 COCOAOBJ = toolkit.o
 COCOAOBJ += AppDelegate.o
 COCOAOBJ += GridLayout.o
+COCOAOBJ += BoxContainer.o
 COCOAOBJ += EventData.o
-COCOAOBJ += UiJob.o
+COCOAOBJ += UiThread.o
 COCOAOBJ += MainWindow.o
 COCOAOBJ += WindowManager.o
 COCOAOBJ += window.o
@@ -42,6 +43,13 @@ COCOAOBJ += button.o
 COCOAOBJ += text.o
 COCOAOBJ += menu.o
 COCOAOBJ += Toolbar.o
+COCOAOBJ += ListDelegate.o
+COCOAOBJ += ListDataSource.o
+COCOAOBJ += label.o
+COCOAOBJ += list.o
+COCOAOBJ += widget.o
+COCOAOBJ += image.o
+COCOAOBJ += entry.o
 
 TOOLKITOBJS += $(COCOAOBJ:%=$(COCOA_OBJPRE)%)
 TOOLKITSOURCE += $(COCOAOBJ:%.o=cocoa/%.m)
index 366f7a37987bb0bb82e5d79100f2cc3a956c2ef9..324d6e7560478747b32a79e4c5259f1a278021e4 100644 (file)
@@ -182,7 +182,14 @@ static UIWIDGET textfield_create(UiObject *obj, UiTextFieldArgs *args, BOOL pass
         textfield = [[NSSecureTextField alloc] init];
     } else {
         textfield = [[NSTextField alloc] init];
-    } 
+    }
+    
+    if(!args->hfill || args->width > 0) {
+        textfield.translatesAutoresizingMaskIntoConstraints = NO;
+        int width = args->width > 0 ? args->width : 100;
+        [[textfield.widthAnchor constraintEqualToConstant:width] setActive:YES];
+    }
+    
     
     if(frameless) {
         [textfield setBezeled: NO];
index fd003336bffc4f380f95504b466e36fafcc46375..da755d3634db876bd32b4b435cb4ad44f9f9c68e 100644 (file)
 
 @end
 
+@protocol UiToplevelObject
+
+- (BOOL) getIsVisible;
+- (void) setVisible:(BOOL)visible;
+
+@end
+
 void ui_cocoa_onstartup(void);
 void ui_cocoa_onopen(const char *file);
 void ui_cocoa_onexit(void);
index 4df07900165582799b2a8159c36dc375e49c79f8..33332a55e2823a2dad540793ad33013afa9f53a8 100644 (file)
@@ -64,7 +64,6 @@ void ui_init(const char *appname, int argc, char **argv) {
     
     uic_init_global_context();
 
-    uic_docmgr_init();
     uic_menu_init();
     uic_toolbar_init();
 
@@ -149,13 +148,22 @@ void ui_main(void) {
 
 void ui_show(UiObject *obj) {
     if(obj->wobj) {
-        NSWindow *window = (__bridge NSWindow*)obj->wobj;
-        [window makeKeyAndOrderFront:nil];
+        id<UiToplevelObject> window = (__bridge id<UiToplevelObject>)obj->wobj;
+        
+        if(![window getIsVisible]) {
+            obj->ref++;
+        }
+        
+        [window setVisible:YES];
     }
 }
 
 void ui_close(UiObject *obj) {
-
+    // TODO: unref, window close, ...
+    if(obj->wobj) {
+        id<UiToplevelObject> window = (__bridge id<UiToplevelObject>)obj->wobj;
+        [window setVisible:NO];
+    }
 }
 
 /* ------------------- Job Control / Threadpool functions ------------------- */
diff --git a/ui/cocoa/widget.h b/ui/cocoa/widget.h
new file mode 100644 (file)
index 0000000..5857c86
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+#import "container.h"
+#import "../ui/widget.h"
diff --git a/ui/cocoa/widget.m b/ui/cocoa/widget.m
new file mode 100644 (file)
index 0000000..1c2e90a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "widget.h"
+
+/* genereal widget functions */
+
+void ui_set_enabled(UIWIDGET widget, int enabled) {
+    NSControl *control = (__bridge NSControl*)widget;
+    control.enabled = enabled;
+}
+
+void ui_set_show_all(UIWIDGET widget, int value) {
+    // TODO: is this relevant?
+}
+
+void ui_set_visible(UIWIDGET widget, int visible) {
+    NSView *view = (__bridge NSView*)widget;
+    view.hidden = !visible;
+}
+
+
+
+/* custom widget */
+
+UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
+    UIWIDGET widget = create_widget(obj, args, userdata);
+    
+    NSView *view = (__bridge NSView*)widget;
+    UiLayout layout = UI_INIT_LAYOUT(args);
+    ui_container_add(obj, view, &layout);
+    
+    return widget;
+}
index 478b59172337ae82e91dd1b7822fa06c3e55ef82..329d38cf97d87ef21ecdf062828d97c54aae07dd 100644 (file)
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#import "toolkit.h"
\ No newline at end of file
+#import "toolkit.h"
+#import "../ui/window.h"
+
+@interface UiDialogWindow : NSPanel<UiToplevelObject>
+
+@property UiObject *obj;
+@property NSWindow *parent;
+@property UiTri modal;
+@property ui_callback onclick;
+@property void *onclickdata;
+
+@end
index 8e2c81f7deb16f074327f9f94add72753ffa4ca1..811226e685986481d43f06620f365dcf9de4dd1f 100644 (file)
@@ -30,6 +30,8 @@
 
 #import "MainWindow.h"
 #import "WindowManager.h"
+#import "BoxContainer.h"
+#import "EventData.h"
 
 #import <objc/runtime.h>
 
 #include "../common/context.h"
 #include "../common/menu.h"
 #include "../common/toolbar.h"
+#include "../common/object.h"
 
 #include <cx/mempool.h>
 
 
-static UiObject* create_window(const char *title, BOOL simple) {
-    CxMempool *mp = cxMempoolCreateSimple(256);
-    UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject));
-    obj->ref = 0;
+static UiObject* create_window(const char *title, BOOL simple, BOOL sidebar, BOOL splitview) {
+    UiObject *obj = uic_object_new_toplevel();
     
-    obj->ctx = uic_context(obj, mp);
-    
-    MainWindow *window = [[MainWindow alloc] init:obj];
+    MainWindow *window = [[MainWindow alloc] init:obj withSidebar:sidebar withSplitview:splitview];
     [[WindowManager sharedWindowManager] addWindow:window];
-    window.releasedWhenClosed = false;
+    window.releasedWhenClosed = false; // TODO: we still need a cleanup strategy
     
     obj->wobj = (__bridge void*)window;
     
@@ -64,13 +63,358 @@ static UiObject* create_window(const char *title, BOOL simple) {
 }
 
 UiObject* ui_window(const char *title, void *window_data) {
-    UiObject *obj = create_window(title, FALSE);
+    UiObject *obj = create_window(title, FALSE, FALSE, FALSE);
     obj->window = window_data;
     return obj;
 }
 
 UiObject* ui_simple_window(const char *title, void *window_data) {
-    UiObject *obj = create_window(title, TRUE);
+    UiObject *obj = create_window(title, TRUE, FALSE, FALSE);
+    obj->window = window_data;
+    return obj;
+}
+
+UiObject* ui_sidebar_window(const char *title, void *window_data) {
+    UiObject *obj = create_window(title, FALSE, TRUE, FALSE);
     obj->window = window_data;
     return obj;
 }
+
+UiObject* ui_splitview_window(const char *title, UiBool sidebar) {
+    return create_window(title, FALSE, sidebar, TRUE);
+}
+
+/* --------------------------------- File Dialogs --------------------------------- */
+
+void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) {
+    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
+    if((mode & UI_FILEDIALOG_SELECT_MULTI) == UI_FILEDIALOG_SELECT_MULTI) {
+        openPanel.allowsMultipleSelection = YES;
+    }
+    if((mode & UI_FILEDIALOG_SELECT_FOLDER) == UI_FILEDIALOG_SELECT_FOLDER) {
+        openPanel.canChooseFiles = NO;
+        openPanel.canChooseDirectories = YES;
+    }
+    
+    NSWindow *window = (__bridge NSWindow*)obj->wobj;
+    [openPanel beginSheetModalForWindow:window completionHandler:^(NSModalResponse result) {
+        UiEvent event;
+        event.obj = obj;
+        event.window = obj->window;
+        event.document = obj->ctx->document;
+        event.intval = 0;
+        event.set = 0;
+        
+        UiFileList flist = { NULL, 0 };
+        event.eventdata = &flist;
+        event.eventdatatype = UI_EVENT_DATA_FILE_LIST;
+        
+        if(result == NSModalResponseOK) {
+            NSArray<NSURL *> *urls = [openPanel URLs];
+            flist.files = calloc(urls.count, sizeof(char*));
+            for(NSURL *url in urls) {
+                if([url isFileURL]) {
+                    flist.files[flist.nfiles++] = strdup(url.path.UTF8String);
+                }
+            }
+        }
+        
+        if(file_selected_callback) {
+            file_selected_callback(&event, cbdata);
+        }
+        ui_filelist_free(flist);
+    }];
+}
+
+void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata) {
+    NSSavePanel *savePanel = [NSSavePanel savePanel];
+    if(name) {
+        NSString *nameStr = [[NSString alloc] initWithUTF8String:name];
+        [savePanel setNameFieldStringValue: nameStr];
+    }
+    
+    NSWindow *window = (__bridge NSWindow*)obj->wobj;
+    [savePanel beginSheetModalForWindow:window completionHandler:^(NSModalResponse result) {
+        UiEvent event;
+        event.obj = obj;
+        event.window = obj->window;
+        event.document = obj->ctx->document;
+        event.intval = 0;
+        event.set = 0;
+        
+        UiFileList flist = { NULL, 0 };
+        event.eventdata = &flist;
+        event.eventdatatype = UI_EVENT_DATA_FILE_LIST;
+        
+        if(result == NSModalResponseOK) {
+            NSURL *url = [savePanel URL];
+            if([url isFileURL]) {
+                NSString *path = url.path;
+                flist.files = malloc(sizeof(char*));
+                flist.files[0] = strdup(path.UTF8String);
+                flist.nfiles = 1;
+            }
+            file_selected_callback(NULL, NULL);
+        }
+        if(file_selected_callback) {
+            file_selected_callback(&event, cbdata);
+        }
+        ui_filelist_free(flist);
+    }];
+}
+
+/* ------------------------------------- Dialog ------------------------------------- */
+
+void ui_dialog_create(UiObject *parent, UiDialogArgs *args) {
+    NSAlert *dialog = [[NSAlert alloc] init];
+    
+    if(args->title) {
+        dialog.messageText = [[NSString alloc]initWithUTF8String:args->title];
+    }
+    if(args->content) {
+        dialog.informativeText = [[NSString alloc]initWithUTF8String:args->content];
+    }
+    NSTextField *textfield = nil;
+    if(args->input) {
+        NSRect frame = NSMakeRect(0,0,300,22);
+        textfield = args->password ? [[NSSecureTextField alloc] initWithFrame:frame] : [[NSTextField alloc]initWithFrame:frame];
+        if(args->input_value) {
+            textfield.stringValue = [[NSString alloc]initWithUTF8String:args->input_value];
+        }
+        dialog.accessoryView = textfield;
+    }
+    
+    int b = 0;
+    int b1 = -1;
+    int b2 = -1;
+    if(args->button1_label) {
+        [dialog addButtonWithTitle:[[NSString alloc]initWithUTF8String:args->button1_label]];
+        b1 = b++;
+    }
+    if(args->button2_label) {
+        [dialog addButtonWithTitle:[[NSString alloc]initWithUTF8String:args->button2_label]];
+        b2 = b;
+    }
+    if(args->closebutton_label) {
+        [dialog addButtonWithTitle:[[NSString alloc]initWithUTF8String:args->closebutton_label]];
+    }
+    
+    ui_callback callback = args->result;
+    void *userdata = args->resultdata;
+    
+    NSWindow *window = (__bridge NSWindow*)parent->wobj;
+    [dialog beginSheetModalForWindow:window completionHandler:^(NSModalResponse returnCode) {
+        UiEvent event;
+        event.obj = parent;
+        event.window = event.obj->window;
+        event.document = event.obj->ctx->document;
+        event.eventdata = NULL;
+        event.eventdatatype = 0;
+        event.set = 0;
+        event.intval = 0;
+        
+        long ret = returnCode - NSAlertFirstButtonReturn;
+        if(ret == b1) {
+            event.intval = 1;
+        } else if(ret == b2) {
+            event.intval = 2;
+        }
+        
+        NSString *value = nil;
+        if(textfield) {
+            value = textfield.stringValue;
+            event.eventdata = (void*)value.UTF8String;
+            event.eventdatatype = UI_EVENT_DATA_STRING;
+        }
+        
+        if(callback) {
+            callback(&event, userdata);
+        }
+    }];
+    
+}
+
+/* ------------------------------------- Dialog Window ------------------------------------- */
+
+@implementation UiDialogWindow
+
+- (BOOL) getIsVisible {
+    return self.isVisible;
+}
+
+- (void) setVisible:(BOOL)visible {
+    //[self makeKeyAndOrderFront:nil];
+    if(visible) {
+        [_parent beginSheet:self completionHandler:^(NSModalResponse returnCode) {
+            // TODO: close event
+        }];
+    } else {
+        [self.sheetParent endSheet:self returnCode:NSModalResponseCancel];
+    }
+}
+
+- (void)cancelOperation:(id)sender {
+    [self.sheetParent endSheet:self returnCode:NSModalResponseCancel];
+    // TODO: close event
+}
+
+@end
+
+UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
+    UiObject *obj = uic_object_new_toplevel();
+    UiDialogWindow *panel = [[UiDialogWindow alloc] initWithContentRect:NSMakeRect(0, 0, args->width, args->height)
+                                                 styleMask:(NSWindowStyleMaskTitled |
+                                                             NSWindowStyleMaskClosable |
+                                                             NSWindowStyleMaskResizable |
+                                                             NSWindowStyleMaskUtilityWindow)
+                                                   backing:NSBackingStoreBuffered
+                                                     defer:NO];
+    panel.parent = (__bridge NSWindow*)parent->wobj;
+    panel.obj = obj;
+    panel.modal = args->modal;
+    panel.onclick = args->onclick;
+    panel.onclickdata = args->onclickdata;
+    [panel center];
+    [[WindowManager sharedWindowManager] addWindow:panel];
+    obj->wobj = (__bridge void*)panel;
+    
+    NSView *content = panel.contentView;
+    
+    // Create a view for the dialog window buttons (lbutton1, lbutton2, rbutton3, rbutton4)
+    NSView *buttonArea = [[NSView alloc]init];
+    buttonArea.translatesAutoresizingMaskIntoConstraints = NO;
+    [content addSubview:buttonArea];
+    [NSLayoutConstraint activateConstraints:@[
+        [buttonArea.bottomAnchor constraintEqualToAnchor:content.bottomAnchor constant:-10],
+        [buttonArea.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:10],
+        [buttonArea.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-10],
+        [buttonArea.heightAnchor constraintEqualToConstant:20]
+    ]];
+    
+    NSButton *lbutton1 = nil;
+    if(args->lbutton1) {
+        lbutton1 = [[NSButton alloc]init];
+        lbutton1.title = [[NSString alloc]initWithUTF8String:args->lbutton1];
+        lbutton1.translatesAutoresizingMaskIntoConstraints = NO;
+        [buttonArea addSubview:lbutton1];
+        [NSLayoutConstraint activateConstraints:@[
+            [lbutton1.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0],
+            [lbutton1.leadingAnchor constraintEqualToAnchor:buttonArea.leadingAnchor constant:0]
+        ]];
+        
+        EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata];
+        event.obj = obj;
+        event.value = 1;
+        lbutton1.target = event;
+        lbutton1.action = @selector(handleEvent:);
+        objc_setAssociatedObject(lbutton1, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+    }
+    NSButton *lbutton2 = nil;
+    if(args->lbutton2) {
+        lbutton2 = [[NSButton alloc]init];
+        lbutton2.title = [[NSString alloc]initWithUTF8String:args->lbutton2];
+        lbutton2.translatesAutoresizingMaskIntoConstraints = NO;
+        [buttonArea addSubview:lbutton2];
+        NSLayoutXAxisAnchor *anchor = lbutton1 != nil ? lbutton1.trailingAnchor : buttonArea.leadingAnchor;
+        int off = lbutton1 != nil ? 4 : 0;
+        [NSLayoutConstraint activateConstraints:@[
+            [lbutton2.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0],
+            [lbutton2.leadingAnchor constraintEqualToAnchor:anchor constant:off]
+        ]];
+        
+        EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata];
+        event.obj = obj;
+        event.value = 2;
+        lbutton2.target = event;
+        lbutton2.action = @selector(handleEvent:);
+        objc_setAssociatedObject(lbutton2, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+    }
+    
+    NSButton *rbutton4 = nil;
+    if(args->rbutton4) {
+        rbutton4 = [[NSButton alloc]init];
+        rbutton4.title = [[NSString alloc]initWithUTF8String:args->rbutton4];
+        rbutton4.translatesAutoresizingMaskIntoConstraints = NO;
+        [buttonArea addSubview:rbutton4];
+        [NSLayoutConstraint activateConstraints:@[
+            [rbutton4.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0],
+            [rbutton4.trailingAnchor constraintEqualToAnchor:buttonArea.trailingAnchor constant:0]
+        ]];
+        
+        EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata];
+        event.obj = obj;
+        event.value = 2;
+        rbutton4.target = event;
+        rbutton4.action = @selector(handleEvent:);
+        objc_setAssociatedObject(rbutton4, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+    }
+    NSButton *rbutton3 = nil;
+    if(args->rbutton3) {
+        rbutton3 = [[NSButton alloc]init];
+        rbutton3.title = [[NSString alloc]initWithUTF8String:args->rbutton3];
+        rbutton3.translatesAutoresizingMaskIntoConstraints = NO;
+        [buttonArea addSubview:rbutton3];
+        NSLayoutXAxisAnchor *anchor = rbutton4 != nil ? rbutton4.leadingAnchor : buttonArea.trailingAnchor;
+        int off = rbutton4 != nil ? -4 : 0;
+        [NSLayoutConstraint activateConstraints:@[
+            [rbutton3.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0],
+            [rbutton3.trailingAnchor constraintEqualToAnchor:anchor constant:off]
+        ]];
+        
+        EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata];
+        event.obj = obj;
+        event.value = 2;
+        rbutton3.target = event;
+        rbutton3.action = @selector(handleEvent:);
+        objc_setAssociatedObject(rbutton3, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+    }
+    switch(args->default_button) {
+        default: break;
+        case 1: if(lbutton1 != nil) lbutton1.keyEquivalent = @"\r"; break;
+        case 2: if(lbutton2 != nil) lbutton2.keyEquivalent = @"\r"; break;
+        case 3: if(rbutton3 != nil) rbutton3.keyEquivalent = @"\r"; break;
+        case 4: if(rbutton4 != nil) rbutton4.keyEquivalent = @"\r"; break;
+    }
+    
+    // space between left and right buttons
+    NSView *space = [[NSView alloc]init];
+    space.translatesAutoresizingMaskIntoConstraints = NO;
+    [buttonArea addSubview:space];
+    NSLayoutXAxisAnchor *leftAnchor = buttonArea.leadingAnchor;
+    NSLayoutXAxisAnchor *rightAnchor = buttonArea.trailingAnchor;
+    if(lbutton2 != nil) {
+        leftAnchor = lbutton2.trailingAnchor;
+    } else if(lbutton1 != nil) {
+        leftAnchor = lbutton1.trailingAnchor;
+    }
+    
+    if(rbutton3 != nil) {
+        rightAnchor = rbutton3.leadingAnchor;
+    } else if(rbutton4 != nil) {
+        rightAnchor = rbutton4.leadingAnchor;
+    }
+    [NSLayoutConstraint activateConstraints:@[
+        [space.topAnchor constraintEqualToAnchor:buttonArea.topAnchor],
+        [space.leadingAnchor constraintEqualToAnchor:leftAnchor constant:10],
+        [space.trailingAnchor constraintEqualToAnchor:rightAnchor constant:-10]
+    ]];
+    
+    // dialog window main content
+    BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0];
+    vbox.translatesAutoresizingMaskIntoConstraints = NO;
+    [content addSubview:vbox];
+    
+    [NSLayoutConstraint activateConstraints:@[
+        [vbox.topAnchor constraintEqualToAnchor:content.topAnchor constant:0],
+        [vbox.leadingAnchor constraintEqualToAnchor:content.leadingAnchor],
+        [vbox.trailingAnchor constraintEqualToAnchor:content.trailingAnchor],
+        [vbox.bottomAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0]
+    ]];
+     
+    UiContainerX *container = ui_create_container(obj, vbox);
+    vbox.container = container;
+    uic_object_push_container(obj, container);
+    
+    return obj;
+}
+
index 3ef591fae8f138f6f1a2f47fff71e5882f9b38dd..796919ba834fd4797e69ff2935d0ca4c5cf1b770 100644 (file)
@@ -195,10 +195,6 @@ void ui_menuitem_args_set_label(UiMenuItemArgs *args, const char *label) {
     args->label = strdup(label);
 }
 
-void ui_menuitem_args_set_stockid(UiMenuItemArgs *args, const char *stockid) {
-    args->stockid = strdup(stockid);
-}
-
 void ui_menuitem_args_set_icon(UiMenuItemArgs *args, const char *icon) {
     args->icon = strdup(icon);
 }
@@ -213,7 +209,6 @@ void ui_menuitem_args_set_onclickdata(UiMenuItemArgs *args, void *onclickdata) {
 
 void ui_menuitem_args_free(UiMenuItemArgs *args) {
     free((void*)args->label);
-    free((void*)args->stockid);
     free((void*)args->icon);
     free(args);
 }
@@ -231,10 +226,6 @@ void ui_menutoggleitem_args_set_label(UiMenuToggleItemArgs *args, const char *la
     args->label = strdup(label);
 }
 
-void ui_menutoggleitem_args_set_stockid(UiMenuToggleItemArgs *args, const char *stockid) {
-    args->stockid = strdup(stockid);
-}
-
 void ui_menutoggleitem_args_set_icon(UiMenuToggleItemArgs *args, const char *icon) {
     args->icon = strdup(icon);
 }
@@ -253,7 +244,6 @@ void ui_menutoggleitem_args_set_onchangedata(UiMenuToggleItemArgs *args, void *o
 
 void ui_menutoggleitem_args_free(UiMenuToggleItemArgs *args) {
     free((void*)args->label);
-    free((void*)args->stockid);
     free((void*)args->icon);
     free((void*)args->varname);
     free(args);
@@ -304,14 +294,14 @@ void ui_toolbar_item_args_set_label(UiToolbarItemArgs *args, const char *label)
     args->label = strdup(label);
 }
 
-void ui_toolbar_item_args_set_stockid(UiToolbarItemArgs *args, const char *stockid) {
-    args->stockid = strdup(stockid);
-}
-
 void ui_toolbar_item_args_set_icon(UiToolbarItemArgs *args, const char *icon) {
     args->icon = strdup(icon);
 }
 
+void ui_toolbar_item_args_set_tooltip(UiToolbarItemArgs *args, const char *tooltip) {
+    args->tooltip = strdup(tooltip);
+}
+
 void ui_toolbar_item_args_set_onclick(UiToolbarItemArgs *args, ui_callback callback) {
     args->onclick = callback;
 }
@@ -320,13 +310,16 @@ void ui_toolbar_item_args_set_onclickdata(UiToolbarItemArgs *args, void *onclick
     args->onclickdata = onclickdata;
 }
 
-void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *groups) {
-    // TODO
+void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 void ui_toolbar_item_args_free(UiToolbarItemArgs *args) {
     free((void*)args->label);
-    free((void*)args->stockid);
     free((void*)args->icon);
+    free((void*)args->tooltip);
+    free((void*)args->groups);
     free(args);
 }
 
@@ -338,47 +331,42 @@ UiToolbarToggleItemArgs* ui_toolbar_toggleitem_args_new(void) {
     return args;
 }
 
-
 void ui_toolbar_toggleitem_args_set_label(UiToolbarToggleItemArgs *args, const char *label) {
     args->label = strdup(label);
 }
 
-
-void ui_toolbar_toggleitem_args_set_stockid(UiToolbarToggleItemArgs *args, const char *stockid) {
-    args->stockid = strdup(stockid);
-}
-
-
 void ui_toolbar_toggleitem_args_set_icon(UiToolbarToggleItemArgs *args, const char *icon) {
     args->icon = strdup(icon);
 }
 
+void ui_toolbar_toggleitem_args_set_tooltip(UiToolbarToggleItemArgs *args, const char *tooltip) {
+    args->tooltip = strdup(tooltip);
+}
 
 void ui_toolbar_toggleitem_args_set_varname(UiToolbarToggleItemArgs *args, const char *varname) {
     args->varname = strdup(varname);
 }
 
-
 void ui_toolbar_toggleitem_args_set_onchange(UiToolbarToggleItemArgs *args, ui_callback callback) {
     args->onchange = callback;
 }
 
-
 void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args, void *onchangedata) {
     args->onchangedata = onchangedata;
 }
 
-
-void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *groups) {
-    // TODO
+void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args,int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
-
 void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args) {
     free((void*)args->label);
-    free((void*)args->stockid);
     free((void*)args->icon);
+    free((void*)args->tooltip);
     free((void*)args->varname);
+    free((void*)args->groups);
     free(args);
 }
 
@@ -391,26 +379,22 @@ UiToolbarMenuArgs* ui_toolbar_menu_args_new(void) {
     return args;
 }
 
-
 void ui_toolbar_menu_args_set_label(UiToolbarMenuArgs *args, const char *label) {
     args->label = strdup(label);
 }
 
-
-void ui_toolbar_menu_args_set_stockid(UiToolbarMenuArgs *args, const char *stockid) {
-    args->stockid = strdup(stockid);
-}
-
-
 void ui_toolbar_menu_args_set_icon(UiToolbarMenuArgs *args, const char *icon) {
     args->icon = strdup(icon);
 }
 
+void ui_toolbar_menu_args_set_tooltip(UiToolbarMenuArgs *args, const char *tooltip) {
+    args->tooltip = strdup(tooltip);
+}
 
 void ui_toolbar_menu_args_free(UiToolbarMenuArgs *args) {
     free((void*)args->label);
-    free((void*)args->stockid);
     free((void*)args->icon);
+    free((void*)args->tooltip);
     free(args);
 }
 
@@ -424,7 +408,7 @@ UiContainerArgs* ui_container_args_new(void) {
 }
 
 void ui_container_args_set_fill(UiContainerArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 void ui_container_args_set_hexpand(UiContainerArgs *args, UiBool value) {
@@ -496,6 +480,22 @@ void ui_container_args_set_margin(UiContainerArgs *args, int value) {
     args->margin = value;
 }
 
+void ui_container_args_set_margin_left(UiContainerArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_container_args_set_margin_right(UiContainerArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_container_args_set_margin_top(UiContainerArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_container_args_set_margin_bottom(UiContainerArgs *args, int value) {
+    args->margin_bottom = value;
+}
+
 
 void ui_container_args_set_spacing(UiContainerArgs *args, int value) {
     args->spacing = value;
@@ -529,7 +529,7 @@ UiFrameArgs* ui_frame_args_new(void) {
 
 
 void ui_frame_args_set_fill(UiFrameArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -557,6 +557,26 @@ void ui_frame_args_set_override_defaults(UiFrameArgs *args, UiBool value) {
     args->override_defaults = value;
 }
 
+void ui_frame_args_set_margin(UiFrameArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_frame_args_set_margin_left(UiFrameArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_frame_args_set_margin_right(UiFrameArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_frame_args_set_margin_top(UiFrameArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_frame_args_set_margin_bottom(UiFrameArgs *args, int value) {
+    args->margin_bottom = value;
+}
+
 
 void ui_frame_args_set_colspan(UiFrameArgs *args, int colspan) {
     args->colspan = colspan;
@@ -577,11 +597,13 @@ void ui_frame_args_set_style_class(UiFrameArgs *args, const char *classname) {
     args->style_class = strdup(classname);
 }
 
-
-void ui_frame_args_set_margin(UiFrameArgs *args, int value) {
-    args->margin = value;
+void ui_frame_args_set_subcontainer(UiFrameArgs *args, UiSubContainerType subcontainer) {
+    args->subcontainer = subcontainer;
 }
 
+void ui_frame_args_set_padding(UiFrameArgs *args, int value) {
+    args->padding = value;
+}
 
 void ui_frame_args_set_spacing(UiFrameArgs *args, int value) {
     args->spacing = value;
@@ -607,7 +629,6 @@ void ui_frame_args_set_label(UiFrameArgs *args, const char *label) {
     args->label = strdup(label);
 }
 
-
 void ui_frame_args_free(UiFrameArgs *args) {
     free((void*)args->name);
     free((void*)args->style_class);
@@ -624,27 +645,38 @@ UiSidebarArgs* ui_sidebar_args_new(void) {
     return args;
 }
 
-
 void ui_sidebar_args_set_name(UiSidebarArgs *args, const char *name) {
     args->name = strdup(name);
 }
 
-
 void ui_sidebar_args_set_style_class(UiSidebarArgs *args, const char *classname) {
     args->style_class = strdup(classname);
 }
 
-
 void ui_sidebar_args_set_margin(UiSidebarArgs *args, int value) {
     args->margin = value;
 }
 
+void ui_sidebar_args_set_margin_left(UiSidebarArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_sidebar_args_set_margin_right(UiSidebarArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_sidebar_args_set_margin_top(UiSidebarArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_sidebar_args_set_margin_bottom(UiSidebarArgs *args, int value) {
+    args->margin_bottom = value;
+}
 
 void ui_sidebar_args_set_spacing(UiSidebarArgs *args, int value) {
     args->spacing = value;
 }
 
-
 void ui_sidebar_args_free(UiSidebarArgs *args) {
     free((void*)args->name);
     free((void*)args->style_class);
@@ -660,61 +692,66 @@ UiSplitPaneArgs* ui_splitpane_args_new(void) {
     return args;
 }
 
-
 void ui_splitpane_args_set_fill(UiSplitPaneArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
-
 void ui_splitpane_args_set_hexpand(UiSplitPaneArgs *args, UiBool value) {
     args->hexpand = value;
 }
 
-
 void ui_splitpane_args_set_vexpand(UiSplitPaneArgs *args, UiBool value) {
     args->vexpand = value;
 }
 
-
 void ui_splitpane_args_set_hfill(UiSplitPaneArgs *args, UiBool value) {
     args->hfill = value;
 }
 
-
 void ui_splitpane_args_set_vfill(UiSplitPaneArgs *args, UiBool value) {
     args->vfill = value;
 }
 
-
 void ui_splitpane_args_set_override_defaults(UiSplitPaneArgs *args, UiBool value) {
     args->override_defaults = value;
 }
 
-
 void ui_splitpane_args_set_colspan(UiSplitPaneArgs *args, int colspan) {
     args->colspan = colspan;
 }
 
-
 void ui_splitpane_args_set_rowspan(UiSplitPaneArgs *args, int rowspan) {
     args->rowspan = rowspan;
 }
 
-
 void ui_splitpane_args_set_name(UiSplitPaneArgs *args, const char *name) {
     args->name = strdup(name);
 }
 
-
 void ui_splitpane_args_set_style_class(UiSplitPaneArgs *args, const char *classname) {
     args->style_class = strdup(classname);
 }
 
-
 void ui_splitpane_args_set_margin(UiSplitPaneArgs *args, int value) {
     args->margin = value;
 }
 
+void ui_splitpane_args_set_margin_left(UiSplitPaneArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_splitpane_args_set_margin_right(UiSplitPaneArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_splitpane_args_set_margin_top(UiSplitPaneArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_splitpane_args_set_margin_bottom(UiSplitPaneArgs *args, int value) {
+    args->margin_bottom = value;
+}
+
 
 void ui_splitpane_args_set_spacing(UiSplitPaneArgs *args, int value) {
     args->spacing = value;
@@ -735,6 +772,9 @@ void ui_splitpane_args_set_initial_position(UiSplitPaneArgs *args, int pos) {
     args->initial_position = pos;
 }
 
+void ui_splitpane_args_set_position_property(UiSplitPaneArgs *args, const char *propname) {
+    args->position_property = strdup(propname);
+}
 
 void ui_splitpane_args_set_varname(UiSplitPaneArgs *args, const char *varname) {
     args->varname = strdup(varname);
@@ -749,8 +789,125 @@ void ui_splitpane_args_set_max_panes(UiSplitPaneArgs *args, int max) {
     args->max_panes = max;
 }
 
-
 void ui_splitpane_args_free(UiSplitPaneArgs *args) {
+    free((void*)args->name);
+    free((void*)args->style_class);
+    free((void*)args->varname);
+    free((void*)args->position_property);
+    free(args);
+}
+
+
+/* ---------------------------- UiTabViewArgs ---------------------------- */
+
+UiTabViewArgs* ui_tabview_args_new(void) {
+    UiTabViewArgs *args = malloc(sizeof(UiTabViewArgs));
+    memset(args, 0, sizeof(UiTabViewArgs));
+    return args;
+}
+
+void ui_tabview_args_set_fill(UiTabViewArgs *args, UiBool fill) {
+    args->fill = fill;
+}
+
+void ui_tabview_args_set_hexpand(UiTabViewArgs *args, UiBool value) {
+    args->hexpand = value;
+}
+
+void ui_tabview_args_set_vexpand(UiTabViewArgs *args, UiBool value) {
+    args->vexpand = value;
+}
+
+void ui_tabview_args_set_hfill(UiTabViewArgs *args, UiBool value) {
+    args->hfill = value;
+}
+
+void ui_tabview_args_set_vfill(UiTabViewArgs *args, UiBool value) {
+    args->vfill = value;
+}
+
+void ui_tabview_args_set_override_defaults(UiTabViewArgs *args, UiBool value) {
+    args->override_defaults = value;
+}
+
+void ui_tabview_args_set_colspan(UiTabViewArgs *args, int colspan) {
+    args->colspan = colspan;
+}
+
+void ui_tabview_args_set_rowspan(UiTabViewArgs *args, int rowspan) {
+    args->rowspan = rowspan;
+}
+
+void ui_tabview_args_set_name(UiTabViewArgs *args, const char *name) {
+    args->name = strdup(name);
+}
+
+void ui_tabview_args_set_style_class(UiTabViewArgs *args, const char *classname) {
+    args->style_class = strdup(classname);
+}
+
+void ui_tabview_args_set_margin(UiTabViewArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_tabview_args_set_margin_left(UiTabViewArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_tabview_args_set_margin_right(UiTabViewArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_tabview_args_set_margin_top(UiTabViewArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_tabview_args_set_margin_bottom(UiTabViewArgs *args, int value) {
+    args->margin_bottom = value;
+}
+
+void ui_tabview_args_set_padding(UiTabViewArgs *args, int value) {
+    args->padding = value;
+}
+
+void ui_tabview_args_set_spacing(UiTabViewArgs *args, int value) {
+    args->spacing = value;
+}
+
+void ui_tabview_args_set_columnspacing(UiTabViewArgs *args, int value) {
+    args->columnspacing = value;
+}
+
+
+void ui_tabview_args_set_rowspacing(UiTabViewArgs *args, int value) {
+    args->rowspacing = value;
+}
+
+void ui_tabview_args_set_type(UiTabViewArgs *args, UiTabViewType tabview) {
+    args->tabview = tabview;
+}
+
+void ui_tabview_args_set_onchange(UiTabViewArgs *args, ui_callback cb) {
+    args->onchange = cb;
+}
+
+void ui_tabview_args_set_onchangedata(UiTabViewArgs *args, void *userdata) {
+    args->onchangedata = userdata;
+}
+
+void ui_tabview_args_set_varname(UiTabViewArgs *args, const char *varname) {
+    args->varname = strdup(varname);
+}
+
+void ui_tabview_args_set_value(UiTabViewArgs *args, UiInteger *value) {
+    args->value = value;
+}
+
+void ui_tabview_args_set_subcontainer(UiTabViewArgs *args, UiSubContainerType subcontainer) {
+    args->subcontainer = subcontainer;
+}
+
+void ui_tabview_args_free(UiTabViewArgs *args) {
     free((void*)args->name);
     free((void*)args->style_class);
     free((void*)args->varname);
@@ -768,7 +925,7 @@ UiWidgetArgs* ui_widget_args_new(void) {
 
 
 void ui_widget_args_set_fill(UiWidgetArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -796,6 +953,25 @@ void ui_widget_args_set_override_defaults(UiWidgetArgs *args, UiBool value) {
     args->override_defaults = value;
 }
 
+void ui_widget_args_set_margin(UiWidgetArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_widget_args_set_margin_left(UiWidgetArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_widget_args_set_margin_right(UiWidgetArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_widget_args_set_margin_top(UiWidgetArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_widget_args_set_margin_bottom(UiWidgetArgs *args, int value) {
+    args->margin_bottom = value;
+}
 
 void ui_widget_args_set_colspan(UiWidgetArgs *args, int colspan) {
     args->colspan = colspan;
@@ -832,36 +1008,49 @@ UiLabelArgs* ui_label_args_new(void) {
     return args;
 }
 
-
 void ui_label_args_set_fill(UiLabelArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
-
 void ui_label_args_set_hexpand(UiLabelArgs *args, UiBool value) {
     args->hexpand = value;
 }
 
-
 void ui_label_args_set_vexpand(UiLabelArgs *args, UiBool value) {
     args->vexpand = value;
 }
 
-
 void ui_label_args_set_hfill(UiLabelArgs *args, UiBool value) {
     args->hfill = value;
 }
 
-
 void ui_label_args_set_vfill(UiLabelArgs *args, UiBool value) {
     args->vfill = value;
 }
 
-
 void ui_label_args_set_override_defaults(UiLabelArgs *args, UiBool value) {
     args->override_defaults = value;
 }
 
+void ui_label_args_set_margin(UiLabelArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_label_args_set_margin_left(UiLabelArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_label_args_set_margin_right(UiLabelArgs *args, int value){
+    args->margin_right = value;
+}
+
+void ui_label_args_set_margin_top(UiLabelArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_label_args_set_margin_bottom(UiLabelArgs *args, int value) {
+    args->margin_bottom = value;
+}
 
 void ui_label_args_set_colspan(UiLabelArgs *args, int colspan) {
     args->colspan = colspan;
@@ -872,12 +1061,10 @@ void ui_label_args_set_rowspan(UiLabelArgs *args, int rowspan) {
     args->rowspan = rowspan;
 }
 
-
 void ui_label_args_set_name(UiLabelArgs *args, const char *name) {
     args->name = strdup(name);
 }
 
-
 void ui_label_args_set_style_class(UiLabelArgs *args, const char *classname) {
     args->style_class = strdup(classname);
 }
@@ -886,7 +1073,6 @@ void ui_label_args_set_label(UiLabelArgs *args, const char *label){
     args->label = strdup(label);
 }
 
-
 void ui_label_args_set_align(UiLabelArgs *args, UiAlignment align) {
     args->align = align;
 }
@@ -923,7 +1109,7 @@ UiProgressbarArgs* ui_progressbar_args_new(void) {
 
 
 void ui_progressbar_args_set_fill(UiProgressbarArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -951,6 +1137,25 @@ void ui_progressbar_args_set_override_defaults(UiProgressbarArgs *args, UiBool v
     args->override_defaults = value;
 }
 
+void ui_progressbar_args_set_margin(UiProgressbarArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_progressbar_args_set_margin_left(UiProgressbarArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_progressbar_args_set_margin_right(UiProgressbarArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_progressbar_args_set_margin_top(UiProgressbarArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_progressbar_args_set_margin_bottom(UiProgressbarArgs *args, int value) {
+    args->margin_bottom = value;
+}
 
 void ui_progressbar_args_set_colspan(UiProgressbarArgs *args, int colspan) {
     args->colspan = colspan;
@@ -1004,7 +1209,7 @@ UiProgressbarSpinnerArgs* ui_progress_spinner_args_new(void) {
 }
 
 void ui_progress_spinner_args_set_fill(UiProgressbarSpinnerArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 void ui_progress_spinner_args_set_hexpand(UiProgressbarSpinnerArgs *args, UiBool value) {
@@ -1027,6 +1232,26 @@ void ui_progress_spinner_args_set_override_defaults(UiProgressbarSpinnerArgs *ar
     args->override_defaults = value;
 }
 
+void ui_progress_spinner_args_set_margin(UiProgressbarSpinnerArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_progress_spinner_args_set_margin_left(UiProgressbarSpinnerArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_progress_spinner_args_set_margin_right(UiProgressbarSpinnerArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_progress_spinner_args_set_margin_top(UiProgressbarSpinnerArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_progress_spinner_args_set_margin_bottom(UiProgressbarSpinnerArgs *args, int value) {
+    args->margin_bottom = value;
+}
+
 void ui_progress_spinner_args_set_colspan(UiProgressbarSpinnerArgs *args, int colspan) {
     args->colspan = colspan;
 }
@@ -1069,7 +1294,7 @@ UiButtonArgs* ui_button_args_new(void) {
 
 
 void ui_button_args_set_fill(UiButtonArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1097,6 +1322,25 @@ void ui_button_args_set_override_defaults(UiButtonArgs *args, UiBool value) {
     args->override_defaults = value;
 }
 
+void ui_button_args_set_margin(UiButtonArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_button_args_set_margin_left(UiButtonArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_button_args_set_margin_right(UiButtonArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_button_args_set_margin_top(UiButtonArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_button_args_set_margin_bottom(UiButtonArgs *args, int value) {
+    args->margin_bottom = value;
+}
 
 void ui_button_args_set_colspan(UiButtonArgs *args, int colspan) {
     args->colspan = colspan;
@@ -1107,12 +1351,10 @@ void ui_button_args_set_rowspan(UiButtonArgs *args, int rowspan) {
     args->rowspan = rowspan;
 }
 
-
 void ui_button_args_set_name(UiButtonArgs *args, const char *name) {
     args->name = strdup(name);
 }
 
-
 void ui_button_args_set_style_class(UiButtonArgs *args, const char *classname) {
     args->style_class = strdup(classname);
 }
@@ -1121,16 +1363,13 @@ void ui_button_args_set_label(UiButtonArgs *args, const char *label){
     args->label = strdup(label);
 }
 
-
-void ui_button_args_set_stockid(UiButtonArgs *args, const char *stockid){
-    args->stockid = strdup(stockid);
-}
-
-
 void ui_button_args_set_icon(UiButtonArgs *args, const char *icon){
     args->icon = strdup(icon);
 }
 
+void ui_button_args_set_tooltip(UiButtonArgs *args, const char *tooltip) {
+    args->tooltip = strdup(tooltip);
+}
 
 void ui_button_args_set_labeltype(UiButtonArgs *args, int labeltype){
     args->labeltype = labeltype;
@@ -1140,21 +1379,22 @@ void ui_button_args_set_onclick(UiButtonArgs *args, ui_callback callback){
     args->onclick = callback;
 }
 
-
 void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata){
     args->onclickdata = onclickdata;
 }
 
-void ui_button_args_set_groups(UiButtonArgs *args, int *groups){
-    // TODO
+void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_button_args_free(UiButtonArgs *args) {
     free((void*)args->name);
     free((void*)args->style_class);
     free((void*)args->label);
-    free((void*)args->stockid);
     free((void*)args->icon);
+    free((void*)args->tooltip);
     free((void*)args->groups);
     free(args);
 }
@@ -1169,42 +1409,54 @@ UiToggleArgs* ui_toggle_args_new(void) {
     return args;
 }
 
-
 void ui_toggle_args_set_fill(UiToggleArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
-
 void ui_toggle_args_set_hexpand(UiToggleArgs *args, UiBool value) {
     args->hexpand = value;
 }
 
-
 void ui_toggle_args_set_vexpand(UiToggleArgs *args, UiBool value) {
     args->vexpand = value;
 }
 
-
 void ui_toggle_args_set_hfill(UiToggleArgs *args, UiBool value) {
     args->hfill = value;
 }
 
-
 void ui_toggle_args_set_vfill(UiToggleArgs *args, UiBool value) {
     args->vfill = value;
 }
 
-
 void ui_toggle_args_set_override_defaults(UiToggleArgs *args, UiBool value) {
     args->override_defaults = value;
 }
 
+void ui_toggle_args_set_margin(UiToggleArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_toggle_args_set_margin_left(UiToggleArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_toggle_args_set_margin_right(UiToggleArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_toggle_args_set_margin_top(UiToggleArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_toggle_args_set_margin_bottom(UiToggleArgs *args, int value) {
+    args->margin_bottom = value;
+}
 
 void ui_toggle_args_set_colspan(UiToggleArgs *args, int colspan) {
     args->colspan = colspan;
 }
 
-
 void ui_toggle_args_set_rowspan(UiToggleArgs *args, int rowspan) {
     args->rowspan = rowspan;
 }
@@ -1214,7 +1466,6 @@ void ui_toggle_args_set_name(UiToggleArgs *args, const char *name) {
     args->name = strdup(name);
 }
 
-
 void ui_toggle_args_set_style_class(UiToggleArgs *args, const char *classname) {
     args->style_class = strdup(classname);
 }
@@ -1223,16 +1474,13 @@ void ui_toggle_args_set_label(UiToggleArgs *args, const char *label){
     args->label = strdup(label);
 }
 
-
-void ui_toggle_args_set_stockid(UiToggleArgs *args, const char *stockid){
-    args->stockid = strdup(stockid);
-}
-
-
 void ui_toggle_args_set_icon(UiToggleArgs *args, const char *icon){
     args->icon = strdup(icon);
 }
 
+void ui_toggle_args_set_tooltip(UiToggleArgs *args, const char *tooltip) {
+    args->tooltip = strdup(tooltip);
+}
 
 void ui_toggle_args_set_labeltype(UiToggleArgs *args, int labeltype){
     args->labeltype = labeltype;
@@ -1242,7 +1490,6 @@ void ui_toggle_args_set_onchange(UiToggleArgs *args, ui_callback callback){
     args->onchange = callback;
 }
 
-
 void ui_toggle_args_set_onchangedata(UiToggleArgs *args, void *onchangedata){
     args->onchangedata = onchangedata;
 }
@@ -1259,16 +1506,18 @@ void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group) {
     args->enable_group = group;
 }
 
-void ui_toggle_args_set_groups(UiToggleArgs *args, int *groups){
-    // TODO
+void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_toggle_args_free(UiToggleArgs *args) {
     free((void*)args->name);
     free((void*)args->style_class);
     free((void*)args->label);
-    free((void*)args->stockid);
     free((void*)args->icon);
+    free((void*)args->tooltip);
     free((void*)args->varname);
     free((void*)args->groups);
     free(args);
@@ -1285,7 +1534,7 @@ UiLinkButtonArgs* ui_linkbutton_args_new(void) {
 
 
 void ui_linkbutton_args_set_fill(UiLinkButtonArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1313,6 +1562,25 @@ void ui_linkbutton_args_set_override_defaults(UiLinkButtonArgs *args, UiBool val
     args->override_defaults = value;
 }
 
+void ui_linkbutton_args_set_margin(UiLinkButtonArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_linkbutton_args_set_margin_left(UiLinkButtonArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_linkbutton_args_set_margin_right(UiLinkButtonArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_linkbutton_args_set_margin_top(UiLinkButtonArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_linkbutton_args_set_margin_bottom(UiLinkButtonArgs *args, int value) {
+    args->margin_bottom = value;
+}
 
 void ui_linkbutton_args_set_colspan(UiLinkButtonArgs *args, int colspan) {
     args->colspan = colspan;
@@ -1365,8 +1633,10 @@ void ui_linkbutton_args_set_value(UiLinkButtonArgs *args, UiString *value) {
     args->value = value;
 }
 
-void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *groups){
-    // TODO
+void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_linkbutton_args_free(UiLinkButtonArgs *args) {
@@ -1389,7 +1659,7 @@ UiListArgs* ui_list_args_new(void) {
 }
 
 void ui_list_args_set_fill(UiListArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 void ui_list_args_set_hexpand(UiListArgs *args, UiBool value) {
@@ -1412,6 +1682,26 @@ void ui_list_args_set_override_defaults(UiListArgs *args, UiBool value) {
     args->override_defaults = value;
 }
 
+void ui_list_args_set_margin(UiListArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_list_args_set_margin_left(UiListArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_list_args_set_margin_right(UiListArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_list_args_set_margin_top(UiListArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_list_args_set_margin_bottom(UiListArgs *args, int value) {
+    args->margin_bottom = value;
+}
+
 void ui_list_args_set_colspan(UiListArgs *args, int colspan) {
     args->colspan = colspan;
 }
@@ -1461,6 +1751,14 @@ void ui_list_args_set_getvalue_data(UiListArgs *args, void *userdata) {
     args->getvalue2data = userdata;
 }
 
+void ui_list_args_set_getstyle_func(UiListArgs *args, ui_getstylefunc getstyle) {
+    args->getstyle = getstyle;
+}
+
+void ui_list_args_set_getstyle_data(UiListArgs *args, void *userdata) {
+    args->getstyledata = userdata;
+}
+
 void ui_list_args_set_onactivate(UiListArgs *args, ui_callback callback) {
     args->onactivate = callback;
 }
@@ -1501,6 +1799,14 @@ void ui_list_args_set_ondropdata(UiListArgs *args, void *userdata) {
     args->ondropdata = userdata;
 }
 
+void ui_list_args_set_onsave(UiListArgs *args, ui_list_savefunc onsave) {
+    args->onsave = onsave;
+}
+
+void ui_list_args_set_onsavedata(UiListArgs *args, void *userdata) {
+    args->onsavedata = userdata;
+}
+
 void ui_list_args_set_multiselection(UiListArgs *args, UiBool multiselection) {
     args->multiselection = multiselection;
 }
@@ -1509,8 +1815,10 @@ void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder)
     args->contextmenu = menubuilder;
 }
 
-void ui_list_args_set_groups(UiListArgs *args, int *groups) {
-    // TODO
+void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_list_args_free(UiListArgs *args) {
@@ -1523,6 +1831,7 @@ void ui_list_args_free(UiListArgs *args) {
         }
         free(args->static_elements);
     }
+    free((void*)args->groups);
     free(args);
 }
 
@@ -1538,7 +1847,7 @@ UiSourceListArgs* ui_sourcelist_args_new(void) {
 
 
 void ui_sourcelist_args_set_fill(UiSourceListArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1566,6 +1875,25 @@ void ui_sourcelist_args_set_override_defaults(UiSourceListArgs *args, UiBool val
     args->override_defaults = value;
 }
 
+void ui_sourcelist_args_set_margin(UiSourceListArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_sourcelist_args_set_margin_left(UiSourceListArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_sourcelist_args_set_margin_right(UiSourceListArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_sourcelist_args_set_margin_top(UiSourceListArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_sourcelist_args_set_margin_bottom(UiSourceListArgs *args, int value) {
+    args->margin_bottom = value;
+}
 
 void ui_sourcelist_args_set_colspan(UiSourceListArgs *args, int colspan) {
     args->colspan = colspan;
@@ -1630,12 +1958,20 @@ void ui_sourcelist_args_set_onbuttonclickdata(UiSourceListArgs *args, void *user
     args->onbuttonclickdata = userdata;
 }
 
+void ui_sourcelist_args_set_contextmenu(UiSourceListArgs *args, UiMenuBuilder *menubuilder) {
+    args->contextmenu = menubuilder;
+}
+
+void ui_sourcelist_args_set_header_is_item(UiSourceListArgs *args, UiBool value) {
+    args->header_is_item = value;
+}
 
 void ui_sourcelist_args_free(UiSourceListArgs *args) {
     free((void*)args->name);
     free((void*)args->style_class);
     free((void*)args->varname);
     free((void*)args->sublists);
+    free((void*)args->groups);
     free(args);
 }
 
@@ -1650,7 +1986,7 @@ UiTextAreaArgs* ui_textarea_args_new(void) {
 
 
 void ui_textarea_args_set_fill(UiTextAreaArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1678,6 +2014,26 @@ void ui_textarea_args_set_override_defaults(UiTextAreaArgs *args, UiBool value)
     args->override_defaults = value;
 }
 
+void ui_textarea_args_set_margin(UiTextAreaArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_textarea_args_set_margin_left(UiTextAreaArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_textarea_args_set_margin_right(UiTextAreaArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_textarea_args_set_margin_top(UiTextAreaArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_textarea_args_set_margin_bottom(UiTextAreaArgs *args, int value) {
+    args->margin_bottom = value;
+}
+
 
 void ui_textarea_args_set_colspan(UiTextAreaArgs *args, int colspan) {
     args->colspan = colspan;
@@ -1715,8 +2071,10 @@ void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value) {
     args->value = value;
 }
 
-void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *groups){
-    // TODO
+void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_textarea_args_free(UiTextAreaArgs *args) {
@@ -1739,7 +2097,7 @@ UiTextFieldArgs* ui_textfield_args_new(void) {
 
 
 void ui_textfield_args_set_fill(UiTextFieldArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1767,6 +2125,26 @@ void ui_textfield_args_set_override_defaults(UiTextFieldArgs *args, UiBool value
     args->override_defaults = value;
 }
 
+void ui_textfield_args_set_margin(UiTextFieldArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_textfield_args_set_margin_left(UiTextFieldArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_textfield_args_set_margin_right(UiTextFieldArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_textfield_args_set_margin_top(UiTextFieldArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_textfield_args_set_margin_bottom(UiTextFieldArgs *args, int value) {
+    args->margin_bottom = value;
+}
+
 
 void ui_textfield_args_set_colspan(UiTextFieldArgs *args, int colspan) {
     args->colspan = colspan;
@@ -1813,8 +2191,10 @@ void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value) {
     args->value = value;
 }
 
-void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *groups){
-    // TODO
+void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_textfield_args_free(UiTextFieldArgs *args) {
@@ -1826,6 +2206,129 @@ void ui_textfield_args_free(UiTextFieldArgs *args) {
 }
 
 
+/* ------------------------- UiSpinBoxArgs ----------------------------*/
+
+UiSpinBoxArgs* ui_spinbox_args_new(void) {
+    UiSpinBoxArgs *args = malloc(sizeof(UiSpinBoxArgs));
+    memset(args, 0, sizeof(UiSpinBoxArgs));
+    return args;
+}
+
+void ui_spinbox_args_set_fill(UiSpinBoxArgs *args, UiBool fill) {
+    args->fill = fill;
+}
+
+void ui_spinbox_args_set_hexpand(UiSpinBoxArgs *args, UiBool value) {
+    args->hexpand = value;
+}
+
+void ui_spinbox_args_set_vexpand(UiSpinBoxArgs *args, UiBool value) {
+    args->vexpand = value;
+}
+
+void ui_spinbox_args_set_hfill(UiSpinBoxArgs *args, UiBool value) {
+    args->hfill = value;
+}
+
+void ui_spinbox_args_set_vfill(UiSpinBoxArgs *args, UiBool value) {
+    args->vfill = value;
+}
+
+void ui_spinbox_args_set_override_defaults(UiSpinBoxArgs *args, UiBool value) {
+    args->override_defaults = value;
+}
+
+void ui_spinbox_args_set_colspan(UiSpinBoxArgs *args, int colspan) {
+    args->colspan = colspan;
+}
+
+void ui_spinbox_args_set_rowspan(UiSpinBoxArgs *args, int rowspan) {
+    args->rowspan = rowspan;
+}
+
+void ui_spinbox_args_set_name(UiSpinBoxArgs *args, const char *name) {
+    args->name = strdup(name);
+}
+
+void ui_spinbox_args_set_style_class(UiSpinBoxArgs *args, const char *classname) {
+    args->style_class = strdup(classname);
+}
+
+void ui_spinbox_args_set_onchange(UiSpinBoxArgs *args, ui_callback callback) {
+    args->onchange = callback;
+}
+
+void ui_spinbox_args_set_onchangedata(UiSpinBoxArgs *args, void *onchangedata) {
+    args->onchangedata = onchangedata;
+}
+
+void ui_spinbox_args_set_margin(UiSpinBoxArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_spinbox_args_set_margin_left(UiSpinBoxArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_spinbox_args_set_margin_right(UiSpinBoxArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_spinbox_args_set_margin_top(UiSpinBoxArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_spinbox_args_set_margin_bottom(UiSpinBoxArgs *args, int value) {
+    args->margin_bottom = value;
+}
+
+void ui_spinbox_args_set_min(UiSpinBoxArgs *args, double min) {
+    args->min = min;
+}
+
+void ui_spinbox_args_set_max(UiSpinBoxArgs *args, double max) {
+    args->max = max;
+}
+
+void ui_spinbox_args_set_step(UiSpinBoxArgs *args, double step) {
+    args->step = step;
+}
+
+void ui_spinbox_args_set_digits(UiSpinBoxArgs *args, int digits) {
+    args->digits = digits;
+}
+
+void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varname) {
+    args->varname = strdup(varname);
+}
+
+void ui_spinbox_args_set_intvalue(UiSpinBoxArgs *args, UiInteger *value) {
+    args->intvalue = value;
+}
+
+void ui_spinbox_args_set_doublevalue(UiSpinBoxArgs *args, UiDouble *value) {
+    args->doublevalue = value;
+}
+
+void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value) {
+    args->rangevalue = value;
+}
+
+void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_spinbox_args_free(UiSpinBoxArgs *args) {
+    free((void*)args->name);
+    free((void*)args->style_class);
+    free((void*)args->varname);
+    free((void*)args->groups);
+    free(args);
+}
+
+
 /* ------------------------- UiWebviewArgs ----------------------------*/
 
 UiWebviewArgs* ui_webview_args_new(void) {
@@ -1836,7 +2339,7 @@ UiWebviewArgs* ui_webview_args_new(void) {
 
 
 void ui_webview_args_set_fill(UiWebviewArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1864,6 +2367,25 @@ void ui_webview_args_set_override_defaults(UiWebviewArgs *args, UiBool value) {
     args->override_defaults = value;
 }
 
+void ui_webview_args_set_margin(UiWebviewArgs *args, int value) {
+    args->margin = value;
+}
+
+void ui_webview_args_set_margin_left(UiWebviewArgs *args, int value) {
+    args->margin_left = value;
+}
+
+void ui_webview_args_set_margin_right(UiWebviewArgs *args, int value) {
+    args->margin_right = value;
+}
+
+void ui_webview_args_set_margin_top(UiWebviewArgs *args, int value) {
+    args->margin_top = value;
+}
+
+void ui_webview_args_set_margin_bottom(UiWebviewArgs *args, int value) {
+    args->margin_bottom = value;
+}
 
 void ui_webview_args_set_colspan(UiWebviewArgs *args, int colspan) {
     args->colspan = colspan;
@@ -1892,8 +2414,10 @@ void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value) {
     args->value = value;
 }
 
-void ui_webview_args_set_groups(UiWebviewArgs *args, int *groups){
-    // TODO
+void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_webview_args_free(UiWebviewArgs *args) {
index a4bebe8fcce8c489708ad6c0bcbb525bf8b965d7..e5331939787307de0ffbcebd317c7c88b1dcd6e2 100644 (file)
@@ -33,6 +33,7 @@
 #include "../ui/container.h"
 #include "../ui/display.h"
 #include "../ui/button.h"
+#include "../ui/entry.h"
 #include "../ui/menu.h"
 #include "../ui/toolbar.h"
 #include "../ui/tree.h"
@@ -79,7 +80,6 @@ UIEXPORT void ui_dialogwindow_args_free(UiDialogWindowArgs *args);
     
 UIEXPORT UiMenuItemArgs* ui_menuitem_args_new(void);
 UIEXPORT void ui_menuitem_args_set_label(UiMenuItemArgs *args, const char *label);
-UIEXPORT void ui_menuitem_args_set_stockid(UiMenuItemArgs *args, const char *stockid);
 UIEXPORT void ui_menuitem_args_set_icon(UiMenuItemArgs *args, const char *icon);
 UIEXPORT void ui_menuitem_args_set_onclick(UiMenuItemArgs *args, ui_callback callback);
 UIEXPORT void ui_menuitem_args_set_onclickdata(UiMenuItemArgs *args, void *onclickdata);
@@ -87,7 +87,6 @@ UIEXPORT void ui_menuitem_args_free(UiMenuItemArgs *args);
 
 UIEXPORT UiMenuToggleItemArgs* ui_menutoggleitem_args_new(void);
 UIEXPORT void ui_menutoggleitem_args_set_label(UiMenuToggleItemArgs *args, const char *label);
-UIEXPORT void ui_menutoggleitem_args_set_stockid(UiMenuToggleItemArgs *args, const char *stockid);
 UIEXPORT void ui_menutoggleitem_args_set_icon(UiMenuToggleItemArgs *args, const char *icon);
 UIEXPORT void ui_menutoggleitem_args_set_varname(UiMenuToggleItemArgs *args, const char *varname);
 UIEXPORT void ui_menutoggleitem_args_set_onchange(UiMenuToggleItemArgs *args, ui_callback callback);
@@ -104,27 +103,27 @@ UIEXPORT void ui_menuitemlist_args_free(UiMenuItemListArgs *args);
 
 UIEXPORT UiToolbarItemArgs* ui_toolbar_item_args_new(void);
 UIEXPORT void ui_toolbar_item_args_set_label(UiToolbarItemArgs *args, const char *label);
-UIEXPORT void ui_toolbar_item_args_set_stockid(UiToolbarItemArgs *args, const char *stockid);
 UIEXPORT void ui_toolbar_item_args_set_icon(UiToolbarItemArgs *args, const char *icon);
+UIEXPORT void ui_toolbar_item_args_set_tooltip(UiToolbarItemArgs *args, const char *tooltip);
 UIEXPORT void ui_toolbar_item_args_set_onclick(UiToolbarItemArgs *args, ui_callback callback);
 UIEXPORT void ui_toolbar_item_args_set_onclickdata(UiToolbarItemArgs *args, void *onclickdata);
-UIEXPORT void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *groups);
+UIEXPORT void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates);
 UIEXPORT void ui_toolbar_item_args_free(UiToolbarItemArgs *args);
 
 UIEXPORT UiToolbarToggleItemArgs* ui_toolbar_toggleitem_args_new(void);
 UIEXPORT void ui_toolbar_toggleitem_args_set_label(UiToolbarToggleItemArgs *args, const char *label);
-UIEXPORT void ui_toolbar_toggleitem_args_set_stockid(UiToolbarToggleItemArgs *args, const char *stockid);
 UIEXPORT void ui_toolbar_toggleitem_args_set_icon(UiToolbarToggleItemArgs *args, const char *icon);
+UIEXPORT void ui_toolbar_toggleitem_args_set_tooltip(UiToolbarToggleItemArgs *args, const char *tooltip);
 UIEXPORT void ui_toolbar_toggleitem_args_set_varname(UiToolbarToggleItemArgs *args, const char *varname);
 UIEXPORT void ui_toolbar_toggleitem_args_set_onchange(UiToolbarToggleItemArgs *args, ui_callback callback);
 UIEXPORT void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args, void *onchangedata);
-UIEXPORT void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *groups);
+UIEXPORT void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *states, int numstates);
 UIEXPORT void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args);
 
 UIEXPORT UiToolbarMenuArgs* ui_toolbar_menu_args_new(void);
 UIEXPORT void ui_toolbar_menu_args_set_label(UiToolbarMenuArgs *args, const char *label);
-UIEXPORT void ui_toolbar_menu_args_set_stockid(UiToolbarMenuArgs *args, const char *stockid);
 UIEXPORT void ui_toolbar_menu_args_set_icon(UiToolbarMenuArgs *args, const char *icon);
+UIEXPORT void ui_toolbar_menu_args_set_tooltip(UiToolbarMenuArgs *args, const char *tooltip);
 UIEXPORT void ui_toolbar_menu_args_free(UiToolbarMenuArgs *args);
     
 UIEXPORT UiContainerArgs* ui_container_args_new(void);
@@ -134,6 +133,11 @@ UIEXPORT void ui_container_args_set_vexpand(UiContainerArgs *args, UiBool value)
 UIEXPORT void ui_container_args_set_hfill(UiContainerArgs *args, UiBool value);
 UIEXPORT void ui_container_args_set_vfill(UiContainerArgs *args, UiBool value);
 UIEXPORT void ui_container_args_set_override_defaults(UiContainerArgs *args, UiBool value);
+UIEXPORT void ui_container_args_set_margin(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_margin_left(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_margin_right(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_margin_top(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_margin_right(UiContainerArgs *args, int value);
 UIEXPORT void ui_container_args_set_colspan(UiContainerArgs *args, int colspan);
 UIEXPORT void ui_container_args_set_rowspan(UiContainerArgs *args, int rowspan);
 UIEXPORT void ui_container_args_set_def_hexpand(UiContainerArgs *args, UiBool value);
@@ -142,13 +146,11 @@ UIEXPORT void ui_container_args_set_def_hfill(UiContainerArgs *args, UiBool valu
 UIEXPORT void ui_container_args_set_def_vfill(UiContainerArgs *args, UiBool value);
 UIEXPORT void ui_container_args_set_name(UiContainerArgs *args, const char *name);
 UIEXPORT void ui_container_args_set_style_class(UiContainerArgs *args, const char *classname);
-UIEXPORT void ui_container_args_set_margin(UiContainerArgs *args, int value);
 UIEXPORT void ui_container_args_set_spacing(UiContainerArgs *args, int value);
 UIEXPORT void ui_container_args_set_columnspacing(UiContainerArgs *args, int value);
 UIEXPORT void ui_container_args_set_rowspacing(UiContainerArgs *args, int value);
 UIEXPORT void ui_container_args_free(UiContainerArgs *args);
     
-
 UIEXPORT UiFrameArgs* ui_frame_args_new(void);
 UIEXPORT void ui_frame_args_set_fill(UiFrameArgs *args, UiBool fill);
 UIEXPORT void ui_frame_args_set_hexpand(UiFrameArgs *args, UiBool value);
@@ -156,11 +158,17 @@ UIEXPORT void ui_frame_args_set_vexpand(UiFrameArgs *args, UiBool value);
 UIEXPORT void ui_frame_args_set_hfill(UiFrameArgs *args, UiBool value);
 UIEXPORT void ui_frame_args_set_vfill(UiFrameArgs *args, UiBool value);
 UIEXPORT void ui_frame_args_set_override_defaults(UiFrameArgs *args, UiBool value);
+UIEXPORT void ui_frame_args_set_margin(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_margin_left(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_margin_right(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_margin_top(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_margin_bottom(UiFrameArgs *args, int value);
 UIEXPORT void ui_frame_args_set_colspan(UiFrameArgs *args, int colspan);
 UIEXPORT void ui_frame_args_set_rowspan(UiFrameArgs *args, int rowspan);
 UIEXPORT void ui_frame_args_set_name(UiFrameArgs *args, const char *name);
 UIEXPORT void ui_frame_args_set_style_class(UiFrameArgs *args, const char *classname);
-UIEXPORT void ui_frame_args_set_margin(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_subcontainer(UiFrameArgs *args, UiSubContainerType subcontainer);
+UIEXPORT void ui_frame_args_set_padding(UiFrameArgs *args, int value);
 UIEXPORT void ui_frame_args_set_spacing(UiFrameArgs *args, int value);
 UIEXPORT void ui_frame_args_set_columnspacing(UiFrameArgs *args, int value);
 UIEXPORT void ui_frame_args_set_rowspacing(UiFrameArgs *args, int value);
@@ -172,6 +180,10 @@ UIEXPORT UiSidebarArgs* ui_sidebar_args_new(void);
 UIEXPORT void ui_sidebar_args_set_name(UiSidebarArgs *args, const char *name);
 UIEXPORT void ui_sidebar_args_set_style_class(UiSidebarArgs *args, const char *classname);
 UIEXPORT void ui_sidebar_args_set_margin(UiSidebarArgs *args, int value);
+UIEXPORT void ui_sidebar_args_set_margin_left(UiSidebarArgs *args, int value);
+UIEXPORT void ui_sidebar_args_set_margin_right(UiSidebarArgs *args, int value);
+UIEXPORT void ui_sidebar_args_set_margin_top(UiSidebarArgs *args, int value);
+UIEXPORT void ui_sidebar_args_set_margin_bottom(UiSidebarArgs *args, int value);
 UIEXPORT void ui_sidebar_args_set_spacing(UiSidebarArgs *args, int value);
 UIEXPORT void ui_sidebar_args_free(UiSidebarArgs *args);
 
@@ -182,20 +194,53 @@ UIEXPORT void ui_splitpane_args_set_vexpand(UiSplitPaneArgs *args, UiBool value)
 UIEXPORT void ui_splitpane_args_set_hfill(UiSplitPaneArgs *args, UiBool value);
 UIEXPORT void ui_splitpane_args_set_vfill(UiSplitPaneArgs *args, UiBool value);
 UIEXPORT void ui_splitpane_args_set_override_defaults(UiSplitPaneArgs *args, UiBool value);
+UIEXPORT void ui_splitpane_args_set_margin(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_margin_left(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_margin_right(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_margin_top(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_margin_bottom(UiSplitPaneArgs *args, int value);
 UIEXPORT void ui_splitpane_args_set_colspan(UiSplitPaneArgs *args, int colspan);
 UIEXPORT void ui_splitpane_args_set_rowspan(UiSplitPaneArgs *args, int rowspan);
 UIEXPORT void ui_splitpane_args_set_name(UiSplitPaneArgs *args, const char *name);
 UIEXPORT void ui_splitpane_args_set_style_class(UiSplitPaneArgs *args, const char *classname);
-UIEXPORT void ui_splitpane_args_set_margin(UiSplitPaneArgs *args, int value);
 UIEXPORT void ui_splitpane_args_set_spacing(UiSplitPaneArgs *args, int value);
 UIEXPORT void ui_splitpane_args_set_columnspacing(UiSplitPaneArgs *args, int value);
 UIEXPORT void ui_splitpane_args_set_rowspacing(UiSplitPaneArgs *args, int value);
 UIEXPORT void ui_splitpane_args_set_initial_position(UiSplitPaneArgs *args, int pos);
+UIEXPORT void ui_splitpane_args_set_position_property(UiSplitPaneArgs *args, const char *propname);
 UIEXPORT void ui_splitpane_args_set_varname(UiSplitPaneArgs *args, const char *varname);
 UIEXPORT void ui_splitpane_args_set_value(UiSplitPaneArgs *args, UiInteger *value);
 UIEXPORT void ui_splitpane_args_set_max_panes(UiSplitPaneArgs *args, int max);
 UIEXPORT void ui_splitpane_args_free(UiSplitPaneArgs *args);
 
+UIEXPORT UiTabViewArgs* ui_tabview_args_new(void);
+UIEXPORT void ui_tabview_args_set_fill(UiTabViewArgs *args, UiBool fill);
+UIEXPORT void ui_tabview_args_set_hexpand(UiTabViewArgs *args, UiBool value);
+UIEXPORT void ui_tabview_args_set_vexpand(UiTabViewArgs *args, UiBool value);
+UIEXPORT void ui_tabview_args_set_hfill(UiTabViewArgs *args, UiBool value);
+UIEXPORT void ui_tabview_args_set_vfill(UiTabViewArgs *args, UiBool value);
+UIEXPORT void ui_tabview_args_set_override_defaults(UiTabViewArgs *args, UiBool value);
+UIEXPORT void ui_tabview_args_set_margin(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_margin_left(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_margin_right(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_margin_top(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_margin_bottom(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_colspan(UiTabViewArgs *args, int colspan);
+UIEXPORT void ui_tabview_args_set_rowspan(UiTabViewArgs *args, int rowspan);
+UIEXPORT void ui_tabview_args_set_name(UiTabViewArgs *args, const char *name);
+UIEXPORT void ui_tabview_args_set_style_class(UiTabViewArgs *args, const char *classname);
+UIEXPORT void ui_tabview_args_set_padding(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_spacing(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_columnspacing(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_rowspacing(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_type(UiTabViewArgs *args, UiTabViewType tabview);
+UIEXPORT void ui_tabview_args_set_onchange(UiTabViewArgs *args, ui_callback cb);
+UIEXPORT void ui_tabview_args_set_onchangedata(UiTabViewArgs *args, void *userdata);
+UIEXPORT void ui_tabview_args_set_varname(UiTabViewArgs *args, const char *varname);
+UIEXPORT void ui_tabview_args_set_value(UiTabViewArgs *args, UiInteger *value);
+UIEXPORT void ui_tabview_args_set_subcontainer(UiTabViewArgs *args, UiSubContainerType subcontainer);
+UIEXPORT void ui_tabview_args_free(UiTabViewArgs *args);
+
 UIEXPORT UiWidgetArgs* ui_widget_args_new(void);
 UIEXPORT void ui_widget_args_set_fill(UiWidgetArgs *args, UiBool fill);
 UIEXPORT void ui_widget_args_set_hexpand(UiWidgetArgs *args, UiBool value);
@@ -203,6 +248,11 @@ UIEXPORT void ui_widget_args_set_vexpand(UiWidgetArgs *args, UiBool value);
 UIEXPORT void ui_widget_args_set_hfill(UiWidgetArgs *args, UiBool value);
 UIEXPORT void ui_widget_args_set_vfill(UiWidgetArgs *args, UiBool value);
 UIEXPORT void ui_widget_args_set_override_defaults(UiWidgetArgs *args, UiBool value);
+UIEXPORT void ui_widget_args_set_margin(UiWidgetArgs *args, int value);
+UIEXPORT void ui_widget_args_set_margin_left(UiWidgetArgs *args, int value);
+UIEXPORT void ui_widget_args_set_margin_right(UiWidgetArgs *args, int value);
+UIEXPORT void ui_widget_args_set_margin_top(UiWidgetArgs *args, int value);
+UIEXPORT void ui_widget_args_set_margin_bottom(UiWidgetArgs *args, int value);
 UIEXPORT void ui_widget_args_set_colspan(UiWidgetArgs *args, int colspan);
 UIEXPORT void ui_widget_args_set_rowspan(UiWidgetArgs *args, int rowspan);
 UIEXPORT void ui_widget_args_set_name(UiWidgetArgs *args, const char *name);
@@ -216,6 +266,11 @@ UIEXPORT void ui_label_args_set_vexpand(UiLabelArgs *args, UiBool value);
 UIEXPORT void ui_label_args_set_hfill(UiLabelArgs *args, UiBool value);
 UIEXPORT void ui_label_args_set_vfill(UiLabelArgs *args, UiBool value);
 UIEXPORT void ui_label_args_set_override_defaults(UiLabelArgs *args, UiBool value);
+UIEXPORT void ui_label_args_set_margin(UiLabelArgs *args, int value);
+UIEXPORT void ui_label_args_set_margin_left(UiLabelArgs *args, int value);
+UIEXPORT void ui_label_args_set_margin_right(UiLabelArgs *args, int value);
+UIEXPORT void ui_label_args_set_margin_top(UiLabelArgs *args, int value);
+UIEXPORT void ui_label_args_set_margin_bottom(UiLabelArgs *args, int value);
 UIEXPORT void ui_label_args_set_colspan(UiLabelArgs *args, int colspan);
 UIEXPORT void ui_label_args_set_rowspan(UiLabelArgs *args, int rowspan);
 UIEXPORT void ui_label_args_set_name(UiLabelArgs *args, const char *name);
@@ -234,6 +289,11 @@ UIEXPORT void ui_progressbar_args_set_vexpand(UiProgressbarArgs *args, UiBool va
 UIEXPORT void ui_progressbar_args_set_hfill(UiProgressbarArgs *args, UiBool value);
 UIEXPORT void ui_progressbar_args_set_vfill(UiProgressbarArgs *args, UiBool value);
 UIEXPORT void ui_progressbar_args_set_override_defaults(UiProgressbarArgs *args, UiBool value);
+UIEXPORT void ui_progressbar_args_set_margin(UiProgressbarArgs *args, int value);
+UIEXPORT void ui_progressbar_args_set_margin_left(UiProgressbarArgs *args, int value);
+UIEXPORT void ui_progressbar_args_set_margin_right(UiProgressbarArgs *args, int value);
+UIEXPORT void ui_progressbar_args_set_margin_top(UiProgressbarArgs *args, int value);
+UIEXPORT void ui_progressbar_args_set_margin_bottom(UiProgressbarArgs *args, int value);
 UIEXPORT void ui_progressbar_args_set_colspan(UiProgressbarArgs *args, int colspan);
 UIEXPORT void ui_progressbar_args_set_rowspan(UiProgressbarArgs *args, int rowspan);
 UIEXPORT void ui_progressbar_args_set_name(UiProgressbarArgs *args, const char *name);
@@ -251,6 +311,11 @@ UIEXPORT void ui_progress_spinner_args_set_vexpand(UiProgressbarSpinnerArgs *arg
 UIEXPORT void ui_progress_spinner_args_set_hfill(UiProgressbarSpinnerArgs *args, UiBool value);
 UIEXPORT void ui_progress_spinner_args_set_vfill(UiProgressbarSpinnerArgs *args, UiBool value);
 UIEXPORT void ui_progress_spinner_args_set_override_defaults(UiProgressbarSpinnerArgs *args, UiBool value);
+UIEXPORT void ui_progress_spinner_args_set_margin(UiProgressbarSpinnerArgs *args, int value);
+UIEXPORT void ui_progress_spinner_args_set_margin_left(UiProgressbarSpinnerArgs *args, int value);
+UIEXPORT void ui_progress_spinner_args_set_margin_right(UiProgressbarSpinnerArgs *args, int value);
+UIEXPORT void ui_progress_spinner_args_set_margin_top(UiProgressbarSpinnerArgs *args, int value);
+UIEXPORT void ui_progress_spinner_args_set_margin_bottom(UiProgressbarSpinnerArgs *args, int value);
 UIEXPORT void ui_progress_spinner_args_set_colspan(UiProgressbarSpinnerArgs *args, int colspan);
 UIEXPORT void ui_progress_spinner_args_set_rowspan(UiProgressbarSpinnerArgs *args, int rowspan);
 UIEXPORT void ui_progress_spinner_args_set_name(UiProgressbarSpinnerArgs *args, const char *name);
@@ -266,17 +331,22 @@ UIEXPORT void ui_button_args_set_vexpand(UiButtonArgs *args, UiBool value);
 UIEXPORT void ui_button_args_set_hfill(UiButtonArgs *args, UiBool value);
 UIEXPORT void ui_button_args_set_vfill(UiButtonArgs *args, UiBool value);
 UIEXPORT void ui_button_args_set_override_defaults(UiButtonArgs *args, UiBool value);
+UIEXPORT void ui_button_args_set_margin(UiButtonArgs *args, int value);
+UIEXPORT void ui_button_args_set_margin_left(UiButtonArgs *args, int value);
+UIEXPORT void ui_button_args_set_margin_right(UiButtonArgs *args, int value);
+UIEXPORT void ui_button_args_set_margin_top(UiButtonArgs *args, int value);
+UIEXPORT void ui_button_args_set_margin_bottom(UiButtonArgs *args, int value);
 UIEXPORT void ui_button_args_set_colspan(UiButtonArgs *args, int colspan);
 UIEXPORT void ui_button_args_set_rowspan(UiButtonArgs *args, int rowspan);
 UIEXPORT void ui_button_args_set_name(UiButtonArgs *args, const char *name);
 UIEXPORT void ui_button_args_set_style_class(UiButtonArgs *args, const char *classname);
 UIEXPORT void ui_button_args_set_label(UiButtonArgs *args, const char *label);
-UIEXPORT void ui_button_args_set_stockid(UiButtonArgs *args, const char *stockid);
 UIEXPORT void ui_button_args_set_icon(UiButtonArgs *args, const char *icon);
+UIEXPORT void ui_button_args_set_tooltip(UiButtonArgs *args, const char *tooltip);
 UIEXPORT void ui_button_args_set_labeltype(UiButtonArgs *args, int labeltype);
 UIEXPORT void ui_button_args_set_onclick(UiButtonArgs *args, ui_callback callback);
 UIEXPORT void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata);
-UIEXPORT void ui_button_args_set_groups(UiButtonArgs *args, int *groups);
+UIEXPORT void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates);
 UIEXPORT void ui_button_args_free(UiButtonArgs *args);
 
 UIEXPORT UiToggleArgs* ui_toggle_args_new(void);
@@ -286,20 +356,25 @@ UIEXPORT void ui_toggle_args_set_vexpand(UiToggleArgs *args, UiBool value);
 UIEXPORT void ui_toggle_args_set_hfill(UiToggleArgs *args, UiBool value);
 UIEXPORT void ui_toggle_args_set_vfill(UiToggleArgs *args, UiBool value);
 UIEXPORT void ui_toggle_args_set_override_defaults(UiToggleArgs *args, UiBool value);
+UIEXPORT void ui_toggle_args_set_margin(UiToggleArgs *args, int value);
+UIEXPORT void ui_toggle_args_set_margin_left(UiToggleArgs *args, int value);
+UIEXPORT void ui_toggle_args_set_margin_right(UiToggleArgs *args, int value);
+UIEXPORT void ui_toggle_args_set_margin_top(UiToggleArgs *args, int value);
+UIEXPORT void ui_toggle_args_set_margin_bottom(UiToggleArgs *args, int value);
 UIEXPORT void ui_toggle_args_set_colspan(UiToggleArgs *args, int colspan);
 UIEXPORT void ui_toggle_args_set_rowspan(UiToggleArgs *args, int rowspan);
 UIEXPORT void ui_toggle_args_set_name(UiToggleArgs *args, const char *name);
 UIEXPORT void ui_toggle_args_set_style_class(UiToggleArgs *args, const char *classname);
 UIEXPORT void ui_toggle_args_set_label(UiToggleArgs *args, const char *label);
-UIEXPORT void ui_toggle_args_set_stockid(UiToggleArgs *args, const char *stockid);
 UIEXPORT void ui_toggle_args_set_icon(UiToggleArgs *args, const char *icon);
+UIEXPORT void ui_toggle_args_set_tooltip(UiToggleArgs *args, const char *tooltip);
 UIEXPORT void ui_toggle_args_set_labeltype(UiToggleArgs *args, int labeltype);
 UIEXPORT void ui_toggle_args_set_onchange(UiToggleArgs *args, ui_callback callback);
 UIEXPORT void ui_toggle_args_set_onchangedata(UiToggleArgs *args, void *onchangedata);
 UIEXPORT void ui_toggle_args_set_varname(UiToggleArgs *args, const char *varname);
 UIEXPORT void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value);
 UIEXPORT void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group);
-UIEXPORT void ui_toggle_args_set_groups(UiToggleArgs *args, int *groups);
+UIEXPORT void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates);
 UIEXPORT void ui_toggle_args_free(UiToggleArgs *args);
 
 UIEXPORT UiLinkButtonArgs* ui_linkbutton_args_new(void);
@@ -309,6 +384,11 @@ UIEXPORT void ui_linkbutton_args_set_vexpand(UiLinkButtonArgs *args, UiBool valu
 UIEXPORT void ui_linkbutton_args_set_hfill(UiLinkButtonArgs *args, UiBool value);
 UIEXPORT void ui_linkbutton_args_set_vfill(UiLinkButtonArgs *args, UiBool value);
 UIEXPORT void ui_linkbutton_args_set_override_defaults(UiLinkButtonArgs *args, UiBool value);
+UIEXPORT void ui_linkbutton_args_set_margin(UiLinkButtonArgs *args, int value);
+UIEXPORT void ui_linkbutton_args_set_margin_left(UiLinkButtonArgs *args, int value);
+UIEXPORT void ui_linkbutton_args_set_margin_right(UiLinkButtonArgs *args, int value);
+UIEXPORT void ui_linkbutton_args_set_margin_top(UiLinkButtonArgs *args, int value);
+UIEXPORT void ui_linkbutton_args_set_margin_bottom(UiLinkButtonArgs *args, int value);
 UIEXPORT void ui_linkbutton_args_set_colspan(UiLinkButtonArgs *args, int colspan);
 UIEXPORT void ui_linkbutton_args_set_rowspan(UiLinkButtonArgs *args, int rowspan);
 UIEXPORT void ui_linkbutton_args_set_name(UiLinkButtonArgs *args, const char *name);
@@ -321,7 +401,7 @@ UIEXPORT void ui_linkbutton_args_set_onclick(UiLinkButtonArgs *args, ui_callback
 UIEXPORT void ui_linkbutton_args_set_onclickdata(UiLinkButtonArgs *args, void *userdata);
 UIEXPORT void ui_linkbutton_args_set_nofollow(UiLinkButtonArgs *args, UiBool value);
 UIEXPORT void ui_linkbutton_args_set_type(UiLinkButtonArgs *args, UiLinkType type);
-UIEXPORT void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *groups);
+UIEXPORT void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates);
 UIEXPORT void ui_linkbutton_args_free(UiLinkButtonArgs *args);
 
 UIEXPORT UiListArgs* ui_list_args_new(void);
@@ -331,6 +411,11 @@ UIEXPORT void ui_list_args_set_vexpand(UiListArgs *args, UiBool value);
 UIEXPORT void ui_list_args_set_hfill(UiListArgs *args, UiBool value);
 UIEXPORT void ui_list_args_set_vfill(UiListArgs *args, UiBool value);
 UIEXPORT void ui_list_args_set_override_defaults(UiListArgs *args, UiBool value);
+UIEXPORT void ui_list_args_set_margin(UiListArgs *args, int value);
+UIEXPORT void ui_list_args_set_margin_left(UiListArgs *args, int value);
+UIEXPORT void ui_list_args_set_margin_right(UiListArgs *args, int value);
+UIEXPORT void ui_list_args_set_margin_top(UiListArgs *args, int value);
+UIEXPORT void ui_list_args_set_margin_bottom(UiListArgs *args, int value);
 UIEXPORT void ui_list_args_set_colspan(UiListArgs *args, int colspan);
 UIEXPORT void ui_list_args_set_rowspan(UiListArgs *args, int rowspan);
 UIEXPORT void ui_list_args_set_name(UiListArgs *args, const char *name);
@@ -342,6 +427,8 @@ UIEXPORT void ui_list_args_set_static_elements(UiListArgs *args, char **strarray
 UIEXPORT void ui_list_args_set_getvalue_func(UiListArgs *args, ui_getvaluefunc getvalue);
 UIEXPORT void ui_list_args_set_getvalue_func2(UiListArgs *args, ui_getvaluefunc2 getvalue);
 UIEXPORT void ui_list_args_set_getvalue_data(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_getstyle_func(UiListArgs *args, ui_getstylefunc getstyle);
+UIEXPORT void ui_list_args_set_getstyle_data(UiListArgs *args, void *userdata);
 UIEXPORT void ui_list_args_set_onactivate(UiListArgs *args, ui_callback callback);
 UIEXPORT void ui_list_args_set_onactivatedata(UiListArgs *args, void *userdata);
 UIEXPORT void ui_list_args_set_onselection(UiListArgs *args, ui_callback callback);
@@ -352,9 +439,11 @@ UIEXPORT void ui_list_args_set_ondragcomplete(UiListArgs *args, ui_callback call
 UIEXPORT void ui_list_args_set_ondragcompletedata(UiListArgs *args, void *userdata);
 UIEXPORT void ui_list_args_set_ondrop(UiListArgs *args, ui_callback callback);
 UIEXPORT void ui_list_args_set_ondropdata(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_onsave(UiListArgs *args, ui_list_savefunc onsave);
+UIEXPORT void ui_list_args_set_onsavedata(UiListArgs *args, void *userdata);
 UIEXPORT void ui_list_args_set_multiselection(UiListArgs *args, UiBool multiselection);
 UIEXPORT void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder);
-UIEXPORT void ui_list_args_set_groups(UiListArgs *args, int *groups);
+UIEXPORT void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates);
 UIEXPORT void ui_list_args_free(UiListArgs *args);
 
 UIEXPORT UiSourceListArgs* ui_sourcelist_args_new(void);
@@ -364,6 +453,11 @@ UIEXPORT void ui_sourcelist_args_set_vexpand(UiSourceListArgs *args, UiBool valu
 UIEXPORT void ui_sourcelist_args_set_hfill(UiSourceListArgs *args, UiBool value);
 UIEXPORT void ui_sourcelist_args_set_vfill(UiSourceListArgs *args, UiBool value);
 UIEXPORT void ui_sourcelist_args_set_override_defaults(UiSourceListArgs *args, UiBool value);
+UIEXPORT void ui_sourcelist_args_set_margin(UiSourceListArgs *args, int value);
+UIEXPORT void ui_sourcelist_args_set_margin_left(UiSourceListArgs *args, int value);
+UIEXPORT void ui_sourcelist_args_set_margin_right(UiSourceListArgs *args, int value);
+UIEXPORT void ui_sourcelist_args_set_margin_top(UiSourceListArgs *args, int value);
+UIEXPORT void ui_sourcelist_args_set_margin_bottom(UiSourceListArgs *args, int value);
 UIEXPORT void ui_sourcelist_args_set_colspan(UiSourceListArgs *args, int colspan);
 UIEXPORT void ui_sourcelist_args_set_rowspan(UiSourceListArgs *args, int rowspan);
 UIEXPORT void ui_sourcelist_args_set_name(UiSourceListArgs *args, const char *name);
@@ -377,6 +471,8 @@ UIEXPORT void ui_sourcelist_args_set_onactivate(UiSourceListArgs *args, ui_callb
 UIEXPORT void ui_sourcelist_args_set_onactivatedata(UiSourceListArgs *args, void *userdata);
 UIEXPORT void ui_sourcelist_args_set_onbuttonclick(UiSourceListArgs *args, ui_callback callback);
 UIEXPORT void ui_sourcelist_args_set_onbuttonclickdata(UiSourceListArgs *args, void *userdata);
+UIEXPORT void ui_sourcelist_args_set_contextmenu(UiSourceListArgs *args, UiMenuBuilder *menubuilder);
+UIEXPORT void ui_sourcelist_args_set_header_is_item(UiSourceListArgs *args, UiBool value);
 UIEXPORT void ui_sourcelist_args_free(UiSourceListArgs *args);
 
 UIEXPORT UiTextAreaArgs* ui_textarea_args_new(void);
@@ -386,6 +482,11 @@ UIEXPORT void ui_textarea_args_set_vexpand(UiTextAreaArgs *args, UiBool value);
 UIEXPORT void ui_textarea_args_set_hfill(UiTextAreaArgs *args, UiBool value);
 UIEXPORT void ui_textarea_args_set_vfill(UiTextAreaArgs *args, UiBool value);
 UIEXPORT void ui_textarea_args_set_override_defaults(UiTextAreaArgs *args, UiBool value);
+UIEXPORT void ui_textarea_args_set_margin(UiTextAreaArgs *args, int value);
+UIEXPORT void ui_textarea_args_set_margin_left(UiTextAreaArgs *args, int value);
+UIEXPORT void ui_textarea_args_set_margin_right(UiTextAreaArgs *args, int value);
+UIEXPORT void ui_textarea_args_set_margin_top(UiTextAreaArgs *args, int value);
+UIEXPORT void ui_textarea_args_set_margin_bottom(UiTextAreaArgs *args, int value);
 UIEXPORT void ui_textarea_args_set_colspan(UiTextAreaArgs *args, int colspan);
 UIEXPORT void ui_textarea_args_set_rowspan(UiTextAreaArgs *args, int rowspan);
 UIEXPORT void ui_textarea_args_set_name(UiTextAreaArgs *args, const char *name);
@@ -394,7 +495,7 @@ UIEXPORT void ui_textarea_args_set_onchange(UiTextAreaArgs *args, ui_callback ca
 UIEXPORT void ui_textarea_args_set_onchangedata(UiTextAreaArgs *args, void *onchangedata);
 UIEXPORT void ui_textarea_args_set_varname(UiTextAreaArgs *args, const char *varname);
 UIEXPORT void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value);
-UIEXPORT void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *groups);
+UIEXPORT void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates);
 UIEXPORT void ui_textarea_args_free(UiTextAreaArgs *args);
 
 UIEXPORT UiTextFieldArgs* ui_textfield_args_new(void);
@@ -404,6 +505,11 @@ UIEXPORT void ui_textfield_args_set_vexpand(UiTextFieldArgs *args, UiBool value)
 UIEXPORT void ui_textfield_args_set_hfill(UiTextFieldArgs *args, UiBool value);
 UIEXPORT void ui_textfield_args_set_vfill(UiTextFieldArgs *args, UiBool value);
 UIEXPORT void ui_textfield_args_set_override_defaults(UiTextFieldArgs *args, UiBool value);
+UIEXPORT void ui_textfield_args_set_margin(UiTextFieldArgs *args, int value);
+UIEXPORT void ui_textfield_args_set_margin_left(UiTextFieldArgs *args, int value);
+UIEXPORT void ui_textfield_args_set_margin_right(UiTextFieldArgs *args, int value);
+UIEXPORT void ui_textfield_args_set_margin_top(UiTextFieldArgs *args, int value);
+UIEXPORT void ui_textfield_args_set_margin_bottom(UiTextFieldArgs *args, int value);
 UIEXPORT void ui_textfield_args_set_colspan(UiTextFieldArgs *args, int colspan);
 UIEXPORT void ui_textfield_args_set_rowspan(UiTextFieldArgs *args, int rowspan);
 UIEXPORT void ui_textfield_args_set_name(UiTextFieldArgs *args, const char *name);
@@ -414,9 +520,38 @@ UIEXPORT void ui_textfield_args_set_onactivate(UiTextFieldArgs *args, ui_callbac
 UIEXPORT void ui_textfield_args_set_onactivatedata(UiTextFieldArgs *args, void *onactivatedata);
 UIEXPORT void ui_textfield_args_set_varname(UiTextFieldArgs *args, const char *varname);
 UIEXPORT void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value);
-UIEXPORT void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *groups);
+UIEXPORT void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates);
 UIEXPORT void ui_textfield_args_free(UiTextFieldArgs *args);
 
+UIEXPORT UiSpinBoxArgs* ui_spinbox_args_new(void);
+UIEXPORT void ui_spinbox_args_set_fill(UiSpinBoxArgs *args, UiBool fill);
+UIEXPORT void ui_spinbox_args_set_hexpand(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_vexpand(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_hfill(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_vfill(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_override_defaults(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_margin(UiSpinBoxArgs *args, int value);
+UIEXPORT void ui_spinbox_args_set_margin_left(UiSpinBoxArgs *args, int value);
+UIEXPORT void ui_spinbox_args_set_margin_right(UiSpinBoxArgs *args, int value);
+UIEXPORT void ui_spinbox_args_set_margin_top(UiSpinBoxArgs *args, int value);
+UIEXPORT void ui_spinbox_args_set_margin_bottom(UiSpinBoxArgs *args, int value);
+UIEXPORT void ui_spinbox_args_set_colspan(UiSpinBoxArgs *args, int colspan);
+UIEXPORT void ui_spinbox_args_set_rowspan(UiSpinBoxArgs *args, int rowspan);
+UIEXPORT void ui_spinbox_args_set_name(UiSpinBoxArgs *args, const char *name);
+UIEXPORT void ui_spinbox_args_set_style_class(UiSpinBoxArgs *args, const char *classname);
+UIEXPORT void ui_spinbox_args_set_onchange(UiSpinBoxArgs *args, ui_callback callback);
+UIEXPORT void ui_spinbox_args_set_onchangedata(UiSpinBoxArgs *args, void *onchangedata);
+UIEXPORT void ui_spinbox_args_set_min(UiSpinBoxArgs *args, double min);
+UIEXPORT void ui_spinbox_args_set_max(UiSpinBoxArgs *args, double max);
+UIEXPORT void ui_spinbox_args_set_step(UiSpinBoxArgs *args, double step);
+UIEXPORT void ui_spinbox_args_set_digits(UiSpinBoxArgs *args, int digits);
+UIEXPORT void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varname);
+UIEXPORT void ui_spinbox_args_set_intvalue(UiSpinBoxArgs *args, UiInteger *value);
+UIEXPORT void ui_spinbox_args_set_doublevalue(UiSpinBoxArgs *args, UiDouble *value);
+UIEXPORT void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value);
+UIEXPORT void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates);
+UIEXPORT void ui_spinbox_args_free(UiSpinBoxArgs *args);
+
 UIEXPORT UiWebviewArgs* ui_webview_args_new(void);
 UIEXPORT void ui_webview_args_set_fill(UiWebviewArgs *args, UiBool fill);
 UIEXPORT void ui_webview_args_set_hexpand(UiWebviewArgs *args, UiBool value);
@@ -424,13 +559,18 @@ UIEXPORT void ui_webview_args_set_vexpand(UiWebviewArgs *args, UiBool value);
 UIEXPORT void ui_webview_args_set_hfill(UiWebviewArgs *args, UiBool value);
 UIEXPORT void ui_webview_args_set_vfill(UiWebviewArgs *args, UiBool value);
 UIEXPORT void ui_webview_args_set_override_defaults(UiWebviewArgs *args, UiBool value);
+UIEXPORT void ui_webview_args_set_margin(UiWebviewArgs *args, int value);
+UIEXPORT void ui_webview_args_set_margin_left(UiWebviewArgs *args, int value);
+UIEXPORT void ui_webview_args_set_margin_right(UiWebviewArgs *args, int value);
+UIEXPORT void ui_webview_args_set_margin_top(UiWebviewArgs *args, int value);
+UIEXPORT void ui_webview_args_set_margin_bottom(UiWebviewArgs *args, int value);
 UIEXPORT void ui_webview_args_set_colspan(UiWebviewArgs *args, int colspan);
 UIEXPORT void ui_webview_args_set_rowspan(UiWebviewArgs *args, int rowspan);
 UIEXPORT void ui_webview_args_set_name(UiWebviewArgs *args, const char *name);
 UIEXPORT void ui_webview_args_set_style_class(UiWebviewArgs *args, const char *classname);
 UIEXPORT void ui_webview_args_set_varname(UiWebviewArgs *args, const char *varname);
 UIEXPORT void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value);
-UIEXPORT void ui_webview_args_set_groups(UiWebviewArgs *args, int *groups);
+UIEXPORT void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates);
 UIEXPORT void ui_webview_args_free(UiWebviewArgs *args);
 
 #ifdef __cplusplus
diff --git a/ui/common/container.c b/ui/common/container.c
new file mode 100644 (file)
index 0000000..6f0dde9
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "container.h"
+#include "object.h"
+
+void ui_end_new(UiObject *obj) {
+    if(!obj->container_end) {
+        return;
+    }
+    UiContainerX *rm = obj->container_end;
+    uic_object_pop_container(obj);
+    ui_free(obj->ctx, rm);
+}
+
+void ui_newline(UiObject *obj) {
+    UiContainerX *container = obj->container_end;
+    if(container) {
+        container->newline = TRUE;
+    }
+}
+
+void uic_layout_setup_expand_fill(
+        UiLayout *layout,
+        UiBool def_hexpand,
+        UiBool def_vexpand,
+        UiBool def_hfill,
+        UiBool def_vfill)
+{
+    if(layout->fill) {
+        layout->hfill = TRUE;
+        layout->vfill = TRUE;
+        layout->hexpand = TRUE;
+        layout->vexpand = TRUE;
+        return;
+    }
+    
+    if(!layout->override_defaults) {
+        if(def_hexpand) {
+            layout->hexpand = TRUE;
+        }
+        if(def_hfill) {
+            layout->hfill = TRUE;
+        }
+        if(def_vexpand) {
+            layout->vexpand = TRUE;
+        }
+        if(def_vfill) {
+            layout->vfill = TRUE;
+        }
+    }
+}
+
+void uic_layout_setup_margin(UiLayout *layout) {
+    int margin = layout->margin;
+    if(margin > 0) {
+        layout->margin_left = margin;
+        layout->margin_right = margin;
+        layout->margin_top = margin;
+        layout->margin_bottom = margin;
+    }
+}
diff --git a/ui/common/container.h b/ui/common/container.h
new file mode 100644 (file)
index 0000000..6f102ed
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_CONTAINER_H
+#define UIC_CONTAINER_H
+
+#include "../ui/container.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * prepares the layout horizontal and vertical fill/expand settings
+ * based on fill and defaults
+ */
+void uic_layout_setup_expand_fill(
+        UiLayout *layout,
+        UiBool def_hexpand,
+        UiBool def_vexpand,
+        UiBool def_hfill,
+        UiBool def_vfill);
+
+/*
+ * adjusts margin_* if margin > 0
+ */
+void uic_layout_setup_margin(UiLayout *layout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_CONTAINER_H */
+
index d0cd53d5302c7ac34d53cfd5021c65744b558044..6c0cbcc1ab6fc3cd3ba500909387a20c8e209398 100644 (file)
@@ -38,6 +38,7 @@
 
 #include "context.h"
 #include "../ui/window.h"
+#include "../ui/widget.h"
 #include "document.h"
 #include "types.h"
 
@@ -102,21 +103,19 @@ void uic_context_attach_document(UiContext *ctx, void *document) {
     UiContext *doc_ctx = ui_document_context(document);
     doc_ctx->parent = ctx;
     
-    // check if any parent context has an unbound variable with the same name
-    // as any document variable
+    // if a document variable has the same name as a parent variable,
+    // move the bindings to the document
     UiContext *var_ctx = ctx;
     while(var_ctx) {
-        if(var_ctx->vars_unbound &&  cxMapSize(var_ctx->vars_unbound) > 0) {
-            CxMapIterator i = cxMapIterator(var_ctx->vars_unbound);
-            cx_foreach(CxMapEntry*, entry, i) {
-                printf("attach %s\n", entry->key->data);
-                UiVar *var = entry->value;
-                UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key);
-                if(docvar) {
-                    // bind var to document var
-                    uic_copy_binding(var, docvar, TRUE);
-                    cxIteratorFlagRemoval(i);
-                }
+        CxMapIterator i = cxMapIterator(var_ctx->vars);
+        cx_foreach(CxMapEntry*, entry, i) {
+            printf("attach %.*s\n", (int)entry->key->len, (char*)entry->key->data);
+            UiVar *var = entry->value;
+            UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key);
+            if(docvar) {
+                // bind var to document var
+                uic_copy_binding(var, docvar, TRUE);
+                cxIteratorFlagRemoval(i);
             }
         }
         
@@ -129,10 +128,10 @@ static void uic_context_unbind_vars(UiContext *ctx) {
     cx_foreach(CxMapEntry*, entry, mi) {
         UiVar *var = entry->value;
         // var->from && var->from_ctx && var->from_ctx != ctx
+        uic_save_var(var);
         if(var->from) {
-            uic_save_var2(var);
             uic_copy_binding(var, var->from, FALSE);
-            cxMapPut(var->from->from_ctx->vars_unbound, *entry->key, var->from);
+            cxMapPut(var->from->from_ctx->vars, *entry->key, var->from);
             var->from = NULL;
         }
     }
@@ -199,13 +198,6 @@ UiVar* uic_get_var(UiContext *ctx, const char *name) {
 }
 
 UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) {
-    if(ctx->vars_unbound) {
-        UiVar *unbound = cxMapGet(ctx->vars_unbound, name);
-        if(unbound) {
-            return unbound;
-        }
-    }
-    
     UiVar *var = uic_get_var(ctx, name);
     if(var) {
         if(var->type == type) {
@@ -224,10 +216,7 @@ UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) {
 
     cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var);
 
-    if(!ctx->vars_unbound) {
-        ctx->vars_unbound = cxHashMapCreate(ctx->allocator, CX_STORE_POINTERS, 16);
-    }
-    cxMapPut(ctx->vars_unbound, name, var);
+    cxMapPut(ctx->vars, name, var);
     
     return var;
 }
@@ -278,7 +267,7 @@ void* uic_create_value(UiContext *ctx, UiVarType type) {
 }
 
 
-UiVar* uic_widget_var(UiContext* toplevel, UiContext* current, void* value, const char* varname, UiVarType type) {
+UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type) {
     if (value) {
         return uic_create_value_var(current, value);
     }
@@ -383,7 +372,7 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
     ui_setop_enable(FALSE);
 }
 
-void uic_save_var2(UiVar *var) {
+void uic_save_var(UiVar *var) {
     switch(var->type) {
         case UI_VAR_SPECIAL: break;
         case UI_VAR_INTEGER: uic_int_save(var->value); break;
@@ -542,6 +531,9 @@ void uic_check_group_widgets(UiContext *ctx) {
 }
 
 void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) {
+    if(enable == NULL) {
+        enable = (ui_enablefunc)ui_set_enabled;
+    }
     // get groups
     CxList *groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
     va_list ap;
@@ -557,6 +549,22 @@ void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable,
     cxListFree(groups);
 }
 
+void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, int *groups, int ngroups) {
+    if(enable == NULL) {
+        enable = (ui_enablefunc)ui_set_enabled;
+    }
+    CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), ngroups);
+    for(int i=0;i<ngroups;i++) {
+        cxListAdd(ls, groups+i);
+    }
+    uic_add_group_widget(ctx, widget, enable, ls);
+    cxListFree(ls);
+}
+
+void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, int *states, int nstates) {
+    ui_widget_set_groups2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates);
+}
+
 size_t uic_group_array_size(const int *groups) {
     int i;
     for(i=0;groups[i] >= 0;i++) { }
index 4fdcfe64e4c0a0bb8564648a4a50bde7fd5243e1..f5170902d2a5a2c657936a9af0cc047fb929f76b 100644 (file)
@@ -67,8 +67,7 @@ struct UiContext {
     void          *document;
     CxList        *documents;
     
-    CxMap         *vars; // manually created context vars
-    CxMap         *vars_unbound; // unbound vars created by widgets
+    CxMap         *vars;
     
     CxList        *groups; // int list
     CxList        *group_widgets; // UiGroupWidget list
@@ -92,7 +91,6 @@ struct UiContext {
     void          *close_data;
 };
 
-// UiVar replacement, rename it to UiVar when finished
 struct UiVar {
     void      *value;
     void      *original_value;
@@ -133,10 +131,10 @@ UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type);
 UiVar* uic_create_value_var(UiContext *ctx, void *value);
 void* uic_create_value(UiContext *ctx, UiVarType type);
 
-UiVar* uic_widget_var(UiContext* toplevel, UiContext* current, void* value, const char* varname, UiVarType type);
+UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type);
 
 void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc);
-void uic_save_var2(UiVar *var);
+void uic_save_var(UiVar *var);
 void uic_unbind_var(UiVar *var);
 
 void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value);
index bd69073f5a9c5112cd4495b160c0bd65e5dff72b..3eb5874d870d9989fd28d3b6fbbb0f5ce45ac2c3 100644 (file)
 #include <cx/mempool.h>
 
 
-static CxMap *documents;
 
-void uic_docmgr_init() {
-    if (!documents) {
-        documents = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32);
-    }
-}
 
 void* ui_document_new(size_t size) {
     CxMempool *mp = cxMempoolCreateSimple(256);
     const CxAllocator *a = mp->allocator;
     UiContext *ctx = uic_context(NULL, mp);
     
-    void *document = cxCalloc(a, size, 1);
-    cxMapPut(documents, cx_hash_key(&document, sizeof(void*)), ctx);
-    return document;
+    UiDoc *document = cxCalloc(a, sizeof(UiDoc) + size, 1);
+    document->ctx = ctx;
+    return &document->doc;
 }
 
 void ui_document_destroy(void *doc) {
@@ -74,7 +68,9 @@ void ui_document_destroy(void *doc) {
 
 UiContext* ui_document_context(void *doc) {
     if(doc) {
-        return cxMapGet(documents, cx_hash_key(&doc, sizeof(void*)));
+        char *docPtr = doc;
+        UiDoc *document = (UiDoc*)(docPtr - sizeof(void*));
+        return document->ctx;
     } else {
         return NULL;
     }
index 0f439d5876936352cddfdbe4a351b0ea7111b2fd..b98c810b26bae5952fe9bff0d6da62e073a88328 100644 (file)
 extern "C" {
 #endif
 
-void uic_docmgr_init();
+typedef struct UiDoc {
+    UiContext *ctx;
+    char doc[];
+} UiDoc;
+    
 void uic_document_addvar(void *doc, char *name, int type, size_t vs);
 
 
index 31f81713e3c6e5a2d2a8afd6244f20ed2dc47f6c..cdf3a8ca421e821fac558023eef84f43f12d457e 100644 (file)
@@ -100,7 +100,7 @@ void ui_menu_create(const char *label) {
     menu->item.next = NULL;
     menu->item.type = UI_MENU;
     
-    menu->label        = label;
+    menu->label        = nl_strdup(label);
     menu->items_begin  = NULL;
     menu->items_end    = NULL;
     menu->parent       = NULL;    
@@ -133,7 +133,6 @@ void ui_menuitem_create(UiMenuItemArgs *args) {
     item->item.type = UI_MENU_ITEM;
 
     item->label = nl_strdup(args->label);
-    item->stockid = nl_strdup(args->stockid);
     item->icon = nl_strdup(args->icon);
     item->userdata = args->onclickdata;
     item->callback = args->onclick;
@@ -160,7 +159,6 @@ void ui_menu_toggleitem_create(UiMenuToggleItemArgs *args) {
     item->item.type = UI_MENU_CHECK_ITEM;
 
     item->label = nl_strdup(args->label);
-    item->stockid = nl_strdup(args->stockid);
     item->icon = nl_strdup(args->icon);
     item->varname = nl_strdup(args->varname);
     item->userdata = args->onchangedata;
@@ -178,7 +176,6 @@ void ui_menu_radioitem_create(UiMenuToggleItemArgs *args) {
     item->item.type = UI_MENU_RADIO_ITEM;
 
     item->label = nl_strdup(args->label);
-    item->stockid = nl_strdup(args->stockid);
     item->icon = nl_strdup(args->icon);
     item->varname = nl_strdup(args->varname);
     item->userdata = args->onchangedata;
@@ -258,6 +255,7 @@ void ui_contextmenu_builder(UiMenuBuilder **out_builder) {
     builder->menus_begin = NULL;
     builder->menus_end = NULL;
     builder->current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+    builder->ref = 1;
     current_builder = builder;
     *out_builder = builder;
     
@@ -271,6 +269,7 @@ static void free_menuitem(UiMenuItemI *item) {
         default: break;
         case UI_MENU: {
             UiMenu *menu = (UiMenu*)item;
+            free(menu->label);
             UiMenuItemI *m = menu->items_begin;
             while(m) {
                 UiMenuItemI *next = m->next;
@@ -283,7 +282,6 @@ static void free_menuitem(UiMenuItemI *item) {
             UiMenuItem *i = (UiMenuItem*)item;
             free(i->groups);
             free(i->label);
-            free(i->stockid);
             free(i->icon);
             break;
         }
@@ -291,7 +289,6 @@ static void free_menuitem(UiMenuItemI *item) {
             UiMenuCheckItem *i = (UiMenuCheckItem*)item;
             free(i->groups);
             free(i->label);
-            free(i->stockid);
             free(i->icon);
             free(i->varname);
             break;
@@ -300,9 +297,8 @@ static void free_menuitem(UiMenuItemI *item) {
             UiMenuRadioItem *i = (UiMenuRadioItem*)item;
             free(i->groups);
             free(i->label);
-            free(i->stockid);
             free(i->icon);
-            //free(i->varname);
+            free(i->varname);
             break;
         }
         case UI_MENU_ITEM_LIST: {
@@ -329,3 +325,13 @@ void ui_menubuilder_free(UiMenuBuilder *builder) {
     cxListFree(builder->current);
     free(builder);
 }
+
+void ui_menubuilder_ref(UiMenuBuilder *builder) {
+    builder->ref++;
+}
+
+void ui_menubuilder_unref(UiMenuBuilder *builder) {
+    if(--builder->ref <= 0) {
+        ui_menubuilder_free(builder);
+    }
+}
index 1ed01b00354958195331238afffcbc9ee10c379e..e9dce38f518b0d0aa5ce9c705adece897da085b0 100644 (file)
@@ -66,7 +66,7 @@ struct UiMenuItemI {
 
 struct UiMenu {
     UiMenuItemI    item;
-    const char     *label;
+    char           *label;
     UiMenuItemI    *items_begin;
     UiMenuItemI    *items_end;
     UiMenu         *parent;
@@ -77,7 +77,6 @@ struct UiMenuItem {
     UiMenuItemI    item;
     ui_callback    callback;
     char           *label;
-    char           *stockid;
     char           *icon;
     void           *userdata;
     int            *groups;
@@ -87,7 +86,6 @@ struct UiMenuItem {
 struct UiMenuCheckItem {
     UiMenuItemI    item;
     char           *label;
-    char           *stockid;
     char           *icon;
     char           *varname;
     ui_callback    callback;
@@ -99,7 +97,6 @@ struct UiMenuCheckItem {
 struct UiMenuRadioItem {
     UiMenuItemI    item;
     char           *label;
-    char           *stockid;
     char           *icon;
     char           *varname;
     ui_callback    callback;
@@ -123,6 +120,7 @@ struct UiMenuBuilder {
     UiMenu *menus_begin;
     UiMenu *menus_end;
     CxList *current;
+    int ref;
 };
 
 void uic_menu_init(void);
index e7981c4687f2c3d82823a2df8701847c4e21309d..6d6b54788b2c98393e934b2a436091cd42032a91 100644 (file)
@@ -74,32 +74,6 @@ void uic_object_destroyed(UiObject *obj) {
     }
 }
 
-void ui_end(UiObject *obj) {
-    if(!obj->next) {
-        return;
-    }
-    
-    UiObject *prev = NULL;
-    while(obj->next) {
-        prev = obj;
-        obj = obj->next;
-    }
-    
-    if(prev) {
-        // TODO: free last obj
-        prev->next = NULL;
-    }
-}
-
-void ui_end_new(UiObject *obj) {
-    if(!obj->container_end) {
-        return;
-    }
-    UiContainerX *rm = obj->container_end;
-    uic_object_pop_container(obj);
-    ui_free(obj->ctx, rm);
-}
-
 void ui_object_ref(UiObject *obj) {
     obj->ref++;
 }
@@ -136,14 +110,11 @@ UiObject* uic_object_new_toplevel(void) {
     CxMempool *mp = cxMempoolCreateSimple(256);
     UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject));
     obj->ctx = uic_context(obj, mp);
+    obj->ctx->parent = ui_global_context();
     uic_object_created(obj);
     return obj;
 }
 
-UiObject* uic_object_new(UiObject *toplevel, UIWIDGET widget) {
-    return uic_ctx_object_new(toplevel->ctx, widget);
-}
-
 UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget) {
     UiObject *newobj = cxCalloc(ctx->allocator, 1, sizeof(UiObject));
     newobj->ctx = ctx;
@@ -152,26 +123,6 @@ UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget) {
     return newobj;
 }
 
-void uic_obj_add(UiObject *toplevel, UiObject *ctobj) {
-    UiObject *current = uic_current_obj(toplevel);
-    current->next = ctobj;
-}
-
-UiObject* uic_current_obj(UiObject *toplevel) {
-    if(!toplevel) {
-        return NULL;
-    }
-    UiObject *obj = toplevel;
-    while(obj->next) {
-        obj = obj->next;
-    }
-    return obj;
-}
-
-UiContainer* uic_get_current_container(UiObject *obj) {
-    return uic_current_obj(obj)->container;
-}
-
 void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer) {
     newcontainer->prev = toplevel->container_end;
     if(toplevel->container_end) {
index 45cf6837043fd8db56f4d44d1daec50f14aadf5e..a899a8346b41eaa3a032efad37b3962bbfbd88eb 100644 (file)
@@ -48,10 +48,6 @@ void uic_object_destroy(UiObject *obj);
 UiObject* uic_object_new_toplevel(void);
 UiObject* uic_object_new(UiObject *toplevel, UIWIDGET widget);
 UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget);
-void uic_obj_add(UiObject *toplevel, UiObject *ctobj);
-UiObject* uic_current_obj(UiObject *toplevel);
-
-UiContainer* uic_get_current_container(UiObject *obj); // deprecated
 
 void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer);
 void uic_object_pop_container(UiObject *toplevel);
index a126b56ab8289a46254d4454650b021fc5b4987f..4d401b54643e134cc4fc7275f965b749da4b836b 100644 (file)
@@ -32,6 +32,7 @@ COMMON_OBJPRE = $(OBJ_DIR)$(COMMON_SRC_DIR)
 COMMON_OBJ = context$(OBJ_EXT)
 COMMON_OBJ += document$(OBJ_EXT)
 COMMON_OBJ += object$(OBJ_EXT)
+COMMON_OBJ += container$(OBJ_EXT)
 COMMON_OBJ += types$(OBJ_EXT)
 COMMON_OBJ += properties$(OBJ_EXT)
 COMMON_OBJ += menu$(OBJ_EXT)
index 0866923d505c63f9767978011b28e9977b977cdb..28676fb08fe84c06d1e5c501351a7fbdc3d75496 100644 (file)
@@ -73,7 +73,7 @@ char* ui_getappdir(void) {
 #define UI_ENV_HOME "USERPROFILE"
 #endif
 
-char* ui_configfile(char *name) {
+char* ui_configfile(const char *name) {
     const char *appname = ui_appname();
     if(!appname) {
         return NULL;
@@ -128,6 +128,7 @@ static int ui_mkdir(char *path) {
 
 void uic_load_app_properties() {
     application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128);
+    application_properties->collection.simple_destructor = free;
     
     if(!ui_appname()) {
         // applications without name cannot load app properties
@@ -202,7 +203,11 @@ const char* ui_get_property(const char *name) {
 }
 
 void ui_set_property(const char *name, const char *value) {
-    cxMapPut(application_properties, name, (char*)value);
+    if(value) {
+        cxMapPut(application_properties, name, strdup(value));
+    } else {
+        cxMapRemove(application_properties, name);
+    }
 }
 
 const char* ui_set_default_property(const char *name, const char *value) {
@@ -241,12 +246,14 @@ static char* uic_concat_path(const char *base, const char *p, const char *ext) {
 
 #ifndef UI_COCOA
 
-void ui_locales_dir(char *path) {
-    locales_dir = path;
+void ui_locales_dir(const char *path) {
+    free(locales_dir);
+    locales_dir = path ? strdup(path) : NULL;
 }
 
-void ui_pixmaps_dir(char *path) {
-    pixmaps_dir = path;
+void ui_pixmaps_dir(const char *path) {
+    free(pixmaps_dir);
+    pixmaps_dir = path ? strdup(path) : NULL;
 }
 
 char* uic_get_image_path(const char *imgfilename) {
@@ -257,7 +264,7 @@ char* uic_get_image_path(const char *imgfilename) {
     }
 }
 
-void ui_load_lang(char *locale) {
+void ui_load_lang(const char *locale) {
     if(!locale) {
         locale = "en_EN";
     }
@@ -319,12 +326,12 @@ int uic_load_language_file(const char *path) {
     return 0;
 }
 
-char* uistr(char *name) {
+char* uistr(const char *name) {
     char *value = uistr_n(name);
     return value ? value : "missing string";
 }
 
-char* uistr_n(char *name) {
+char* uistr_n(const char *name) {
     if(!language) {
         return NULL;
     }
index 199dc41b254bae6381cc8dce8851f98e61f9f291..5164f0cfc03a967ff64e597a9e18dca3f6497343 100644 (file)
 #include "toolbar.h"\r
 #include "menu.h"\r
 \r
+#include <stdio.h>\r
 #include <string.h>\r
 \r
 static CxMap* toolbar_items;\r
 \r
-static CxList* toolbar_defaults[3]; // 0: left 1: center 2: right\r
+static CxList* toolbar_defaults[8]; // 0: left 1: center 2: right\r
 \r
 static UiToolbarMenuItem* ui_appmenu;\r
 \r
 \r
 void uic_toolbar_init(void) {\r
     toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);\r
-    toolbar_defaults[0] = cxLinkedListCreateSimple(CX_STORE_POINTERS);\r
-    toolbar_defaults[1] = cxLinkedListCreateSimple(CX_STORE_POINTERS);\r
-    toolbar_defaults[2] = cxLinkedListCreateSimple(CX_STORE_POINTERS);\r
+    for(int i=0;i<8;i++) {\r
+        toolbar_defaults[i] = cxLinkedListCreateSimple(CX_STORE_POINTERS);\r
+    }\r
 }\r
 \r
 static char* nl_strdup(const char* str) {\r
@@ -52,8 +53,8 @@ static char* nl_strdup(const char* str) {
 static UiToolbarItemArgs itemargs_copy(UiToolbarItemArgs *args, size_t *ngroups) {\r
     UiToolbarItemArgs newargs;\r
     newargs.label = nl_strdup(args->label);\r
-    newargs.stockid = nl_strdup(args->stockid);\r
     newargs.icon = nl_strdup(args->icon);\r
+    newargs.tooltip = nl_strdup(args->tooltip);\r
     newargs.onclick = args->onclick;\r
     newargs.onclickdata = args->onclickdata;\r
     newargs.groups = uic_copy_groups(args->groups, ngroups);\r
@@ -71,8 +72,8 @@ void ui_toolbar_item_create(const char* name, UiToolbarItemArgs *args) {
 static UiToolbarToggleItemArgs toggleitemargs_copy(UiToolbarToggleItemArgs *args, size_t *ngroups) {\r
     UiToolbarToggleItemArgs newargs;\r
     newargs.label = nl_strdup(args->label);\r
-    newargs.stockid = nl_strdup(args->stockid);\r
     newargs.icon = nl_strdup(args->icon);\r
+    newargs.tooltip = nl_strdup(args->tooltip);\r
     newargs.varname = nl_strdup(args->varname);\r
     newargs.onchange = args->onchange;\r
     newargs.onchangedata = args->onchangedata;\r
@@ -90,8 +91,8 @@ void ui_toolbar_toggleitem_create(const char* name, UiToolbarToggleItemArgs *arg
 static UiToolbarMenuArgs menuargs_copy(UiToolbarMenuArgs *args) {\r
     UiToolbarMenuArgs newargs;\r
     newargs.label = nl_strdup(args->label);\r
-    newargs.stockid = nl_strdup(args->stockid);\r
     newargs.icon = nl_strdup(args->icon);\r
+    newargs.tooltip = nl_strdup(args->tooltip);\r
     return newargs;\r
 }\r
 \r
@@ -120,7 +121,7 @@ CxMap* uic_get_toolbar_items(void) {
 }\r
 \r
 CxList* uic_get_toolbar_defaults(enum UiToolbarPos pos) {\r
-    if (pos >= 0 && pos < 3) {\r
+    if (pos >= 0 && pos < 8) {\r
         return toolbar_defaults[pos];\r
     }\r
     return NULL;\r
@@ -128,15 +129,19 @@ CxList* uic_get_toolbar_defaults(enum UiToolbarPos pos) {
 \r
 void ui_toolbar_add_default(const char* name, enum UiToolbarPos pos) {\r
     char* cp = strdup(name);\r
-    if (pos >= 0 && pos < 3) {\r
+    if (pos >= 0 && pos < 8) {\r
         cxListAdd(toolbar_defaults[pos], cp);\r
     } else {\r
-        // TODO: error\r
+        fprintf(stderr, "Error: illegal toolbar position: %d\n", (int)pos);\r
     }\r
 }\r
 \r
 UiBool uic_toolbar_isenabled(void) {\r
-    return cxListSize(toolbar_defaults[0]) + cxListSize(toolbar_defaults[1]) + cxListSize(toolbar_defaults[2]) > 0;\r
+    size_t size = 0;\r
+    for(int i=0;i<8;i++) {\r
+        size += cxListSize(toolbar_defaults[i]);\r
+    }\r
+    return size > 0;\r
 }\r
 \r
 UiToolbarItemI* uic_toolbar_get_item(const char* name) {\r
index 5f5685f231d2002fd3babd4d089fc23c45ad39c9..3d3d94fa776f46293a37ebb37a26433fcf34aee4 100644 (file)
@@ -220,6 +220,7 @@ UiModel* ui_model(UiContext *ctx, ...) {
     va_end(ap);
     
     size_t len = cxListSize(cols);
+    info->alloc = len;
     info->columns = len;
     info->types = ui_calloc(ctx, len, sizeof(UiModelType));
     info->titles = ui_calloc(ctx, len, sizeof(char*));
@@ -229,7 +230,7 @@ UiModel* ui_model(UiContext *ctx, ...) {
     CxIterator iter = cxListIterator(cols);
     cx_foreach(UiColumn*, c, iter) {
         info->types[i] = c->type;
-        info->titles[i] = c->name;
+        info->titles[i] = ui_strdup(ctx, c->name);
         i++;
     }
     cxListFree(cols);
@@ -237,6 +238,30 @@ UiModel* ui_model(UiContext *ctx, ...) {
     return info;
 }
 
+#define UI_MODEL_DEFAULT_ALLOC_SIZE 16
+
+UiModel* ui_model_new(UiContext *ctx) {
+    UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel));
+    info->alloc = UI_MODEL_DEFAULT_ALLOC_SIZE;
+    info->types = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(UiModelType));
+    info->titles = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(char*));
+    info->columnsize = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(int));
+    return info;
+}
+
+void ui_model_add_column(UiContext *ctx, UiModel *model, UiModelType type, const char *title, int width) {
+    if(model->columns >= model->alloc) {
+        model->alloc += UI_MODEL_DEFAULT_ALLOC_SIZE;
+        model->types = ui_realloc(ctx, model->types, model->alloc * sizeof(UiModelType));
+        model->titles = ui_realloc(ctx, model->titles, model->alloc * sizeof(char*));
+        model->columnsize = ui_realloc(ctx, model->columnsize, model->alloc * sizeof(int));
+    }
+    model->types[model->columns] = type;
+    model->titles[model->columns] = ui_strdup(ctx, title);
+    model->columnsize[model->columns] = width;
+    model->columns++;
+}
+
 UiModel* ui_model_copy(UiContext *ctx, UiModel* model) {
     const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator;
 
@@ -258,6 +283,9 @@ UiModel* ui_model_copy(UiContext *ctx, UiModel* model) {
 
 void ui_model_free(UiContext *ctx, UiModel *mi) {
     const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator;
+    for(int i=0;i<mi->columns;i++) {
+        ui_free(ctx, mi->titles[i]);
+    }
     cxFree(a, mi->types);
     cxFree(a, mi->titles);
     cxFree(a, mi->columnsize);
@@ -389,7 +417,7 @@ char* ui_string_get(UiString* s) {
         return s->get ? s->get(s) : s->value.ptr;
     }
     else {
-        return 0;
+        return NULL;
     }
 }
 
@@ -419,6 +447,69 @@ char* ui_text_get(UiText* s) {
         return s->get ? s->get(s) : s->value.ptr;
     }
     else {
+        return NULL;
+    }
+}
+
+void ui_range_set(UiRange *r, double value) {
+    if (r) {
+        if (r->set) {
+            r->set(r, value);
+        } else {
+            r->value = value;
+        }
+    }
+}
+
+void ui_range_set_range(UiRange *r, double min, double max) {
+    if (r) {
+        if (r->setrange) {
+            r->setrange(r, min, max);
+        } else {
+            r->min = min;
+            r->max = max;
+        }
+    }
+}
+
+void ui_range_set_extent(UiRange *r, double extent) {
+    if (r) {
+        if (r->setextent) {
+            r->setextent(r, extent);
+        } else {
+            r->extent = extent;
+        }
+    }
+}
+
+double ui_range_get(UiRange *r) {
+    if (r) {
+        return r->get ? r->get(r) : r->value;
+    } else {
+        return 0;
+    }
+}
+
+double ui_range_get_min(UiRange *r) {
+    if (r) {
+        return r->min;
+    } else {
+        return 0;
+    }
+}
+
+double ui_range_get_max(UiRange *r) {
+    if (r) {
+        return r->max;
+    } else {
+        return 0;
+    }
+}
+
+double ui_range_get_extent(UiRange *r) {
+    if (r) {
+        return r->extent;
+    } else {
         return 0;
     }
 }
@@ -580,6 +671,8 @@ void uic_range_unbind(UiRange *r) {
 
 void uic_list_unbind(UiList *l) {
     l->update = NULL;
+    l->getselection = NULL;
+    l->setselection = NULL;
     l->obj = NULL;
 }
 
@@ -599,10 +692,12 @@ UIEXPORT UiListSelection ui_list_getselection(UiList *list) {
 }
 
 UIEXPORT void ui_list_setselection(UiList *list, int index) {
-    if (list->setselection && index >= 0) {
-        UiListSelection sel;
-        sel.count = 1;
-        sel.rows = &index;
+    if (list->setselection) {
+        UiListSelection sel = { 0, NULL };
+        if(index >= 0) {
+            sel.count = 1;
+            sel.rows = &index;
+        }
         list->setselection(list, sel);
     }
 }
index 6d5be8190d337fb0ce79a009476cf8fae78a0283..50ecf407f9e15928741a8e9d857de6cd26f768f8 100644 (file)
@@ -134,6 +134,17 @@ int ui_srclist_size(UiList *list) {
     return ui_list_count(list);
 }
 
+/*
+ * numerates all sublists and sets the sublist index as userdata
+ */
+void ui_srclist_generate_sublist_num_data(UiList *list) {
+    CxList *cxlist = list->data;
+    CxIterator i = cxListIterator(cxlist);
+    cx_foreach(UiSubList *, sublist, i) {
+        sublist->userdata = (void*)i.index;
+    }
+}
+
 
 /* ---------------------------- UiSubListEventData ---------------------------- */
 
@@ -211,6 +222,10 @@ void ui_sublist_item_set_button_label(UiSubListItem *item, const char *button_la
     item->button_label = button_label ? strdup(button_label) : NULL;
 }
 
+void ui_sublist_item_set_button_menu(UiSubListItem *item, UiMenuBuilder *menu) {
+    item->button_menu = menu;
+}
+
 void ui_sublist_item_set_badge(UiSubListItem *item, const char *badge) {
     item->badge = badge ? strdup(badge) : NULL;
 }
@@ -236,6 +251,15 @@ int* ui_list_selection_get_rows(UiListSelection *sel) {
     return sel->rows;
 }
 
+UIEXPORT void ui_list_set_selected_indices(UiList *list, int *indices, int num) {
+    UiListSelection sel;
+    sel.rows = indices;
+    sel.count = num;
+    if(list->setselection) {
+        list->setselection(list, sel);
+    }
+}
+
 void ui_list_selection_free(UiListSelection *sel) {
     ui_listselection_free(*sel);
     free(sel);
@@ -253,3 +277,59 @@ char* ui_filelist_get(UiFileList *flist, int index) {
     }
     return NULL;
 }
+
+/* ---------------------------- UiTextStyle ---------------------------- */
+
+void ui_textstyle_set_bold(UiTextStyle *style, UiBool set) {
+    if(set) {
+        style->text_style |= UI_TEXT_STYLE_BOLD;
+    } else {
+        style->text_style &= ~UI_TEXT_STYLE_BOLD;
+    }
+}
+
+void ui_textstyle_set_underline(UiTextStyle *style, UiBool set) {
+    if(set) {
+        style->text_style |= UI_TEXT_STYLE_UNDERLINE;
+    } else {
+        style->text_style &= ~UI_TEXT_STYLE_UNDERLINE;
+    }
+}
+
+void ui_textstyle_set_italic(UiTextStyle *style, UiBool set) {
+    if(set) {
+        style->text_style |= UI_TEXT_STYLE_ITALIC;
+    } else {
+        style->text_style &= ~UI_TEXT_STYLE_ITALIC;
+    }
+}
+
+void ui_textstyle_set_color(UiTextStyle *style, int r, int g, int b) {
+    style->fg_set = TRUE;
+    style->fg.red = r;
+    style->fg.green = g;
+    style->fg.blue = b;
+}
+
+void ui_textstyle_enable_color(UiTextStyle *style, UiBool enable) {
+    style->fg_set = enable;
+}
+
+
+/* ---------------------------- UiCellValue ---------------------------- */
+
+UiBool ui_cell_value_is_string(UiCellValue *value) {
+   return value->type == UI_STRING_EDITABLE;
+}
+
+UiBool ui_cell_value_is_int(UiCellValue *value) {
+    return FALSE; // TODO
+}
+
+const char* ui_cell_value_get_string(UiCellValue *value) {
+    return value->string;
+}
+
+int64_t ui_cell_value_get_int(UiCellValue *value) {
+    return value->i;
+}
index ec8d2d9c928906ced4ae1ba066dd211c34836952..88fd8c813b538aabd1100b85c8fa0d7103ae748c 100644 (file)
@@ -58,6 +58,7 @@ UIEXPORT void ui_srclist_insert(UiList *list, int index, UiSubList *item);
 UIEXPORT void ui_srclist_remove(UiList *list, int index);
 UIEXPORT void ui_srclist_clear(UiList *list);
 UIEXPORT int ui_srclist_size(UiList *list);
+UIEXPORT void ui_srclist_generate_sublist_num_data(UiList *list);
 
 UIEXPORT UiList* ui_sublist_event_get_list(UiSubListEventData *event);
 UIEXPORT int ui_sublist_event_get_sublist_index(UiSubListEventData *event);
@@ -77,11 +78,23 @@ UIEXPORT int ui_event_get_set(UiEvent *event);
 UIEXPORT UiListSelection* ui_list_get_selection_allocated(UiList *list);
 UIEXPORT int ui_list_selection_get_count(UiListSelection *sel);
 UIEXPORT int* ui_list_selection_get_rows(UiListSelection *sel);
+UIEXPORT void ui_list_set_selected_indices(UiList *list, int *indices, int num);
 UIEXPORT void ui_list_selection_free(UiListSelection *sel);
 
 UIEXPORT int ui_filelist_count(UiFileList *flist);
 UIEXPORT char* ui_filelist_get(UiFileList *flist, int index);
 
+UIEXPORT void ui_textstyle_set_bold(UiTextStyle *style, UiBool set);
+UIEXPORT void ui_textstyle_set_underline(UiTextStyle *style, UiBool set);
+UIEXPORT void ui_textstyle_set_italic(UiTextStyle *style, UiBool set);
+UIEXPORT void ui_textstyle_set_color(UiTextStyle *style, int r, int g, int b);
+UIEXPORT void ui_textstyle_enable_color(UiTextStyle *style, UiBool enable);
+
+UIEXPORT UiBool ui_cell_value_is_string(UiCellValue *value);
+UIEXPORT UiBool ui_cell_value_is_int(UiCellValue *value);
+UIEXPORT const char* ui_cell_value_get_string(UiCellValue *value);
+UIEXPORT int64_t ui_cell_value_get_int(UiCellValue *value);
+
 
 #ifdef __cplusplus
 }
index 84d9608e76fcb82ab6511264c1cc728660afc0e6..6db5141b63ee8d714bb50c44b64fce5ad06a33ac 100644 (file)
@@ -60,6 +60,7 @@ GtkWidget* ui_create_button(
         UiObject *obj,
         const char *label,
         const char *icon,
+        const char *tooltip,
         ui_callback onclick,
         void *userdata,
         int event_value,
@@ -67,6 +68,9 @@ GtkWidget* ui_create_button(
 {
     GtkWidget *button = gtk_button_new_with_label(label);
     ui_button_set_icon_name(button, icon);
+    if(tooltip) {
+        gtk_widget_set_tooltip_text(button, tooltip);
+    }
     
     if(onclick) {
         UiEventData *event = malloc(sizeof(UiEventData));
@@ -100,12 +104,12 @@ GtkWidget* ui_create_button(
 }
 
 UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    GtkWidget *button = ui_create_button(obj, args->label, args->icon, args->onclick, args->onclickdata, 0, FALSE);
+    GtkWidget *button = ui_create_button(obj, args->label, args->icon, args->tooltip, args->onclick, args->onclickdata, 0, FALSE);
     ui_set_name_and_style(button, args->name, args->style_class);
     ui_set_widget_groups(obj->ctx, button, args->groups);
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, button);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, button, &layout);
     return button;
 }
 
@@ -181,6 +185,7 @@ void ui_setup_togglebutton(
         GtkWidget *togglebutton,
         const char *label,
         const char *icon,
+        const char *tooltip,
         const char *varname,
         UiInteger *value,
         ui_callback onchange,
@@ -191,6 +196,9 @@ void ui_setup_togglebutton(
         gtk_button_set_label(GTK_BUTTON(togglebutton), label);
     }
     ui_button_set_icon_name(togglebutton, icon);
+    if(tooltip) {
+        gtk_widget_set_tooltip_text(togglebutton, tooltip);
+    }
     
     ui_bind_togglebutton(
             obj,
@@ -220,8 +228,7 @@ void ui_bind_togglebutton(
         void (*enable_state_func)(void*, void*),
         int enable_state)
 {
-    UiObject* current = uic_current_obj(obj);
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, value, varname, UI_VAR_INTEGER);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER);
     if (var) {
         UiInteger* value = (UiInteger*)var->value;
         value->obj = widget;
@@ -291,13 +298,12 @@ void ui_bind_togglebutton(
 }
 
 static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     ui_setup_togglebutton(
             obj,
             widget,
             args->label,
             args->icon,
+            args->tooltip,
             args->varname,
             args->value,
             args->onchange,
@@ -306,8 +312,9 @@ static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleAr
     ui_set_name_and_style(widget, args->name, args->style_class);
     ui_set_widget_groups(obj->ctx, widget, args->groups);
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, widget);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
     
     return widget;
 }
@@ -350,9 +357,7 @@ static void ui_checkbox_enable_state(GtkCheckButton *widget, UiEventData *event)
     }
 }
 
-UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
+UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) { 
     GtkWidget *widget = gtk_check_button_new_with_label(args->label); 
     ui_bind_togglebutton(
             obj,
@@ -370,8 +375,9 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
     ui_set_name_and_style(widget, args->name, args->style_class);
     ui_set_widget_groups(obj->ctx, widget, args->groups);
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, widget);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
     
     return widget;
 }
@@ -382,14 +388,99 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
 }
 #endif
 
+
+#if GTK_MAJOR_VERSION >= 3
+
+static void switch_changed(
+        GObject *gobject,
+        GParamSpec *pspec,
+        UiVarEventData *event)
+{
+    GtkSwitch *sw = GTK_SWITCH (gobject);
+    gboolean active = gtk_switch_get_active (sw);
+    
+    UiEvent e;
+    e.obj = event->obj;
+    e.document = e.obj->ctx->document;
+    e.window = e.obj->window;
+    e.eventdata = NULL;
+    e.eventdatatype = 0;
+    e.set = ui_get_setop();
+    
+    if(event->callback) {
+        event->callback(&e, event->userdata);
+    }
+    if(event->var) {
+        UiInteger *i = event->var->value;
+        ui_notify_evt(i->observers, &e);
+    }
+}
+
 UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
-#ifdef UI_GTK3
-    return NULL; // TODO
+    GtkWidget *widget = gtk_switch_new();
+    ui_set_name_and_style(widget, args->name, args->style_class);
+    ui_set_widget_groups(obj->ctx, widget, args->groups);
+    
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+    if(var) {
+        UiInteger *value = var->value;
+        value->obj = widget;
+        value->get = ui_switch_get;
+        value->set = ui_switch_set;
+        
+        if(value->value) {
+            gtk_switch_set_active(GTK_SWITCH(widget), TRUE);
+        }
+        
+        
+    }
+    
+    UiVarEventData *event = malloc(sizeof(UiVarEventData));
+    event->obj = obj;
+    event->callback = args->onchange;
+    event->userdata = args->onchangedata;
+    event->var = var;
+    event->observers = NULL;
+    
+    g_signal_connect(
+            widget,
+            "notify::active",
+            G_CALLBACK(switch_changed),
+            event);
+    
+    g_signal_connect(
+            widget,
+            "destroy",
+            G_CALLBACK(ui_destroy_vardata),
+            event);
+    
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
+    
+    return widget;
+}
+
+int64_t ui_switch_get(UiInteger *value) {
+    GtkSwitch *sw = GTK_SWITCH((GtkWidget*)value->obj);
+    value->value = gtk_switch_get_active(sw);
+    return value->value;
+}
+
+void ui_switch_set(UiInteger *value, int64_t i) {
+    GtkSwitch *sw = GTK_SWITCH((GtkWidget*)value->obj);
+    value->value = i;
+    gtk_switch_set_active(sw, i);
+}
+    
 #else
+
+UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
     return ui_checkbox_create(obj, args);
-#endif
 }
 
+#endif
+
 #if GTK_MAJOR_VERSION >= 4
 #define RADIOBUTTON_NEW(group, label) gtk_check_button_new_with_label(label)
 #define RADIOBUTTON_SET_GROUP(button, group) 
@@ -434,12 +525,10 @@ static void destroy_radiobutton(GtkWidget *w, UiRadioButtonData *data) {
 }
 
 UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     GSList *rg = NULL;
     UiInteger *rgroup;
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_INTEGER);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
     
     UiBool first = FALSE;
     if(var) {
@@ -515,8 +604,9 @@ UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) {
                 event);
     }
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, rbutton);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, rbutton, &layout);
     
     return rbutton;
 }
@@ -715,7 +805,7 @@ static char* linkbutton_get_value(UiLinkButton *link) {
     return create_linkbutton_jsonvalue(label, uri, TRUE, visited, TRUE);
 }
 
-static void linkbutton_clicked(GtkWidget *widget, UiLinkButton *data) {
+static void linkbutton_callback(GtkWidget *widget, UiLinkButton *data) {
     if(data->onclick) {
         UiEvent e;
         e.obj = data->obj;
@@ -729,8 +819,24 @@ static void linkbutton_clicked(GtkWidget *widget, UiLinkButton *data) {
     }
 }
 
+static void linkbutton_clicked(GtkWidget *widget, UiLinkButton *data) {
+    linkbutton_callback(widget, data);
+    if(data->link) {   
+#if GTK_CHECK_VERSION(4, 0, 0)
+        GtkUriLauncher *launcher = gtk_uri_launcher_new (data->link);
+        gtk_uri_launcher_launch (launcher, NULL, NULL, NULL, NULL);
+        g_object_unref (launcher);
+#elif GTK_CHECK_VERSION(3, 22, 0)
+        GError *error = NULL;
+        gtk_show_uri_on_window(NULL, data->link, GDK_CURRENT_TIME, &error);
+#elif
+        // TODO: call xdg-open
+#endif
+    }
+}
+
 static gboolean linkbutton_activate_link(GtkLinkButton *self, UiLinkButton *data) {
-    linkbutton_clicked(data->widget, data);
+    linkbutton_callback(data->widget, data);
     return data->nofollow;
 }
 
@@ -760,6 +866,12 @@ UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) {
                 data);
     }
     gtk_button_set_label(GTK_BUTTON(button), args->label);
+#if GTK_CHECK_VERSION(4, 0, 0)
+    gtk_button_set_can_shrink(GTK_BUTTON(button), TRUE);
+#elif GTK_MAJOR_VERSION == 3
+    GtkWidget *child = gtk_bin_get_child(GTK_BIN(button));
+    gtk_label_set_ellipsize(GTK_LABEL(child), PANGO_ELLIPSIZE_END);
+#endif
     g_object_set_data(G_OBJECT(button), "ui_linkbutton", data);
     g_signal_connect(
             button,
@@ -769,8 +881,7 @@ UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) {
     
     data->widget = button;
     
-    UiObject* current = uic_current_obj(obj);
-    UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING);
+    UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
     if(var) {
         UiString *str = var->value;
         char *current_value = ui_get(str);
@@ -785,8 +896,9 @@ UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) {
     
     ui_set_name_and_style(button, args->name, args->style_class);
     ui_set_widget_groups(obj->ctx, button, args->groups);
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, button);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, button, &layout);
     
     return button;
 }
@@ -806,6 +918,11 @@ void ui_linkbutton_set(UiString *s, const char *str) {
     if(s->value.free) {
         s->value.free(s->value.ptr);
     }
+#if GTK_MAJOR_VERSION == 3
+    UiLinkButton *data = s->obj;
+    GtkWidget *child = gtk_bin_get_child(GTK_BIN(data->widget));
+    gtk_label_set_ellipsize(GTK_LABEL(child), PANGO_ELLIPSIZE_END);
+#endif
 }
 
 
index 4b60f494e52adead5fe76b1effcdfeb761689e89..ce94b90c7dc04259e087553a2a195d35a949f1b5 100644 (file)
@@ -55,6 +55,7 @@ GtkWidget* ui_create_button(
         UiObject *obj,
         const char *label,
         const char *icon,
+        const char *tooltip,
         ui_callback onclick,
         void *userdata,
         int event_value,
@@ -65,6 +66,7 @@ void ui_setup_togglebutton(
         GtkWidget *togglebutton,
         const char *label,
         const char *icon,
+        const char *tooltip,
         const char *varname,
         UiInteger *value,
         ui_callback onchange,
@@ -96,6 +98,9 @@ UIWIDGET ui_radiobutton_var(UiObject *obj, char *label, UiVar *var);
 
 void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event);
 
+int64_t ui_switch_get(UiInteger *value);
+void ui_switch_set(UiInteger *value, int64_t i);
+
 int64_t ui_radiobutton_get(UiInteger *value);
 void ui_radiobutton_set(UiInteger *value, int64_t i);
 
index 7a98bf475561f6ac720ac58e38b2b0cb4a6b32d1..9ca07f959e0883c2adb962ed6b2ea44b64cecc91 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <limits.h>
 
 #include "container.h"
 
 #include "../common/context.h"
 #include "../common/object.h"
+#include "../common/container.h"
+
+#include "../ui/properties.h"
 
 
 void ui_container_begin_close(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
+    UiContainerX *ct = obj->container_end;
     ct->close = 1;
 }
 
 int ui_container_finish(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
+    UiContainerX *ct = obj->container_end;
     if(ct->close) {
-        ui_end(obj);
+        ui_end_new(obj);
         return 0;
     }
     return 1;
@@ -70,7 +74,7 @@ GtkWidget* ui_gtk_hbox_new(int spacing) {
 
 GtkWidget* ui_subcontainer_create(
         UiSubContainerType type,
-        UiObject *newobj,
+        UiObject *obj,
         int spacing,
         int columnspacing,
         int rowspacing,
@@ -78,38 +82,39 @@ GtkWidget* ui_subcontainer_create(
 {
     GtkWidget *sub = NULL;
     GtkWidget *add = NULL;
+    UiContainerX *container = NULL;
     switch(type) {
         default: {
             sub = ui_gtk_vbox_new(spacing);
-            add = ui_box_set_margin(sub, margin);
-            newobj->container = ui_box_container(newobj, sub, type);
-            newobj->widget = sub;
+            add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0);
+            container = ui_box_container(obj, sub, type);
             break;
         }
         case UI_CONTAINER_HBOX: {
             sub = ui_gtk_hbox_new(spacing);
-            add = ui_box_set_margin(sub, margin);
-            newobj->container = ui_box_container(newobj, sub, type);
-            newobj->widget = sub;
+            add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0);
+            container = ui_box_container(obj, sub, type);
             break;
         }
         case UI_CONTAINER_GRID: {
             sub = ui_create_grid_widget(columnspacing, rowspacing);
-            add = ui_box_set_margin(sub, margin);
-            newobj->container = ui_grid_container(newobj, sub, FALSE, FALSE, FALSE, FALSE);
-            newobj->widget = sub;
+            add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0);
+            container = ui_grid_container(obj, sub, FALSE, FALSE, FALSE, FALSE);
             break;
         }
         case UI_CONTAINER_NO_SUB: {
             break;
         }
     }
+    if(container) {
+        uic_object_push_container(obj, container);
+    }
     return add;
 }
 
 
 /* -------------------- Box Container -------------------- */
-UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) {
+UiContainerX* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) {
     UiBoxContainer *ct = cxCalloc(
             obj->ctx->allocator,
             1,
@@ -117,12 +122,14 @@ UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType
     ct->container.widget = box;
     ct->container.add = ui_box_container_add;
     ct->type = type;
-    return (UiContainer*)ct;
+    return (UiContainerX*)ct;
 }
 
-void ui_box_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_box_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
     UiBoxContainer *bc = (UiBoxContainer*)ct;
-    UiBool fill = ct->layout.fill;
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+    
+    UiBool fill = layout->fill;
     if(bc->has_fill && fill) {
         fprintf(stderr, "UiError: container has 2 filled widgets");
         fill = FALSE;
@@ -149,11 +156,10 @@ void ui_box_container_add(UiContainer *ct, GtkWidget *widget) {
     gtk_box_pack_start(GTK_BOX(ct->widget), widget, expand, fill, 0);
 #endif
     
-    ui_reset_layout(ct->layout);
     ct->current = widget;
 }
 
-UiContainer* ui_grid_container(
+UiContainerX* ui_grid_container(
         UiObject *obj,
         GtkWidget *grid,
         UiBool def_hexpand,
@@ -173,140 +179,72 @@ UiContainer* ui_grid_container(
     ct->container.add = ui_grid_container_add;
     UI_GTK_V2(ct->width = 0);
     UI_GTK_V2(ct->height = 1);
-    return (UiContainer*)ct;
+    return (UiContainerX*)ct;
 }
 
+
 #if GTK_MAJOR_VERSION >= 3
-void ui_grid_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
     UiGridContainer *grid = (UiGridContainer*)ct;
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
     
-    if(ct->layout.newline) {
+    if(ct->container.newline) {
         grid->x = 0;
         grid->y++;
-        ct->layout.newline = FALSE;
+        ct->container.newline = FALSE;
     }
     
-    int hexpand = FALSE;
-    int vexpand = FALSE;
-    int hfill = FALSE;
-    int vfill = FALSE;
-    if(!ct->layout.override_defaults) {
-        if(grid->def_hexpand) {
-            hexpand = TRUE;
-            hfill = TRUE;
-        } else if(grid->def_hfill) {
-            hfill = TRUE;
-        }
-        if(grid->def_vexpand) {
-            vexpand = TRUE;
-            vfill = TRUE;
-        } else if(grid->def_vfill) {
-            vfill = TRUE;
-        }
-    }
-    
-    UiBool fill = ct->layout.fill;
-    if(ct->layout.hexpand) {
-        hexpand = TRUE;
-        hfill = TRUE;
-    } else if(ct->layout.hfill) {
-        hfill = TRUE;
-    }
-    if(ct->layout.vexpand) {
-        vexpand = TRUE;
-        vfill = TRUE;
-    } else if(ct->layout.vfill) {
-        vfill = TRUE;
-    }
-    if(fill) {
-        hfill = TRUE;
-        vfill = TRUE;
-    }
+    uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill);
     
-    if(!hfill) {
+    if(!layout->hfill) {
         gtk_widget_set_halign(widget, GTK_ALIGN_START);
     }
-    if(!vfill) {
+    if(!layout->vfill) {
         gtk_widget_set_valign(widget, GTK_ALIGN_START);
     }
     
-    gtk_widget_set_hexpand(widget, hexpand);
-    gtk_widget_set_vexpand(widget, vexpand);
+    gtk_widget_set_hexpand(widget, layout->hexpand);
+    gtk_widget_set_vexpand(widget, layout->vexpand);
     
-    int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1;
-    int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1;
+    int colspan = layout->colspan > 0 ? layout->colspan : 1;
+    int rowspan = layout->rowspan > 0 ? layout->rowspan : 1;
     
     gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, colspan, rowspan);
     grid->x += colspan;
     
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
+    grid->container.current = widget;
 }
 #endif
 #ifdef UI_GTK2
-void ui_grid_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget) {
     UiGridContainer *grid = (UiGridContainer*)ct;
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
     
-    if(ct->layout.newline) {
+    if(ct->container.newline) {
         grid->x = 0;
         grid->y++;
-        ct->layout.newline = FALSE;
+        ct->container.newline = FALSE;
     }
     
-    int hexpand = FALSE;
-    int vexpand = FALSE;
-    int hfill = FALSE;
-    int vfill = FALSE;
-    if(!ct->layout.override_defaults) {
-        if(grid->def_hexpand) {
-            hexpand = TRUE;
-            hfill = TRUE;
-        } else if(grid->def_hfill) {
-            hfill = TRUE;
-        }
-        if(grid->def_vexpand) {
-            vexpand = TRUE;
-            vfill = TRUE;
-        } else if(grid->def_vfill) {
-            vfill = TRUE;
-        }
-    }
-    
-    UiBool fill = ct->layout.fill;
-    if(ct->layout.hexpand) {
-        hexpand = TRUE;
-        hfill = TRUE;
-    } else if(ct->layout.hfill) {
-        hfill = TRUE;
-    }
-    if(ct->layout.vexpand) {
-        vexpand = TRUE;
-        vfill = TRUE;
-    } else if(ct->layout.vfill) {
-        vfill = TRUE;
-    }
-    if(fill) {
-        hfill = TRUE;
-        vfill = TRUE;
-    }
+    uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill);
     
     GtkAttachOptions xoptions = 0;
     GtkAttachOptions yoptions = 0;
-    if(hexpand) {
+    if(layout->hexpand) {
         xoptions = GTK_EXPAND;
     }
-    if(hfill) {
+    if(layout->hfill) {
         xoptions |= GTK_FILL;
     }
-    if(vexpand) {
+    if(layout->vexpand) {
         yoptions = GTK_EXPAND;
     }
-    if(vfill) {
+    if(layout->vfill) {
         yoptions |= GTK_FILL;
     }
     
-    int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1;
-    int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1;
+    int colspan = layout->colspan > 0 ? layout->colspan : 1;
+    int rowspan = layout->rowspan > 0 ? layout->rowspan : 1;
     // TODO: use colspan/rowspan
     
     gtk_table_attach(GTK_TABLE(ct->widget), widget, grid->x, grid->x+1, grid->y, grid->y+1, xoptions, yoptions, 0, 0);
@@ -318,116 +256,138 @@ void ui_grid_container_add(UiContainer *ct, GtkWidget *widget) {
         gtk_table_resize(GTK_TABLE(ct->widget), grid->width, grid->height);
     }
     
-    ui_reset_layout(ct->layout);
     ct->current = widget;
 }
 #endif
 
-UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame) {
-    UiContainer *ct = cxCalloc(
+UiContainerX* ui_frame_container(UiObject *obj, GtkWidget *frame) {
+    UiContainerPrivate *ct = cxCalloc(
             obj->ctx->allocator,
             1,
-            sizeof(UiContainer));
+            sizeof(UiContainerPrivate));
     ct->widget = frame;
     ct->add = ui_frame_container_add;
-    return ct;
+    return (UiContainerX*)ct;
 }
 
-void ui_frame_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_frame_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
     FRAME_SET_CHILD(ct->widget, widget);
+    ct->current = widget;
 }
 
-UiContainer* ui_expander_container(UiObject *obj, GtkWidget *expander) {
-    UiContainer *ct = cxCalloc(
+UiContainerX* ui_expander_container(UiObject *obj, GtkWidget *expander) {
+    UiContainerPrivate *ct = cxCalloc(
             obj->ctx->allocator,
             1,
-            sizeof(UiContainer));
+            sizeof(UiContainerPrivate));
     ct->widget = expander;
     ct->add = ui_expander_container_add;
-    return ct;
+    return (UiContainerX*)ct;
 }
 
-void ui_expander_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_expander_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
     EXPANDER_SET_CHILD(ct->widget, widget);
+    ct->current = widget;
 }
 
-void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_scrolledwindow_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
     // TODO: check if the widget implements GtkScrollable
     SCROLLEDWINDOW_SET_CHILD(ct->widget, widget);
-    ui_reset_layout(ct->layout);
     ct->current = widget;
 }
 
-UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) {
-    UiContainer *ct = cxCalloc(
+UiContainerX* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) {
+    UiContainerPrivate *ct = cxCalloc(
             obj->ctx->allocator,
             1,
-            sizeof(UiContainer));
+            sizeof(UiContainerPrivate));
     ct->widget = scrolledwindow;
     ct->add = ui_scrolledwindow_container_add;
-    return ct;
+    return (UiContainerX*)ct;
 }
 
-UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview) {
+UiContainerX* ui_tabview_container(UiObject *obj, GtkWidget *tabview) {
     UiTabViewContainer *ct = cxCalloc(
             obj->ctx->allocator,
             1,
             sizeof(UiTabViewContainer));
     ct->container.widget = tabview;
     ct->container.add = ui_tabview_container_add;
-    return (UiContainer*)ct;
+    return (UiContainerX*)ct;
 }
 
-void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_tabview_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
     UiGtkTabView *data = ui_widget_get_tabview_data(ct->widget);
     if(!data) {
         fprintf(stderr, "UI Error: widget is not a tabview");
         return;
     }
-    data->add_tab(ct->widget, -1, ct->layout.label, widget);
+    widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+    data->add_tab(ct->widget, -1, layout->label, widget);
     
-    ui_reset_layout(ct->layout);
     ct->current = widget;
 }
 
+#ifdef UI_GTK2
 
+static void alignment_child_visibility_changed(GtkWidget *widget, gpointer user_data) {
+    gtk_widget_set_visible(gtk_widget_get_parent(widget), gtk_widget_get_visible(widget));
+}
 
-GtkWidget* ui_box_set_margin(GtkWidget *box, int margin) {
-    GtkWidget *ret = box;
+#endif
+
+GtkWidget* ui_gtk_set_margin(GtkWidget *widget, int margin, int margin_left, int margin_right, int margin_top, int margin_bottom) {
+    if(margin > 0) {
+        margin_left = margin;
+        margin_right = margin;
+        margin_top = margin;
+        margin_bottom = margin;
+    }
+    GtkWidget *ret = widget;
 #if GTK_MAJOR_VERSION >= 3
-#if GTK_MAJOR_VERSION * 1000 + GTK_MINOR_VERSION >= 3012
-    gtk_widget_set_margin_start(box, margin);
-    gtk_widget_set_margin_end(box, margin);
+#if GTK_CHECK_VERSION(3, 12, 0)
+    gtk_widget_set_margin_start(widget, margin_left);
+    gtk_widget_set_margin_end(widget, margin_right);
 #else
-    gtk_widget_set_margin_left(box, margin);
-    gtk_widget_set_margin_right(box, margin);
+    gtk_widget_set_margin_left(widget, margin_left);
+    gtk_widget_set_margin_right(widget, margin_right);
 #endif
-    gtk_widget_set_margin_top(box, margin);
-    gtk_widget_set_margin_bottom(box, margin);
+    gtk_widget_set_margin_top(widget, margin_top);
+    gtk_widget_set_margin_bottom(widget, margin_bottom);
 #elif defined(UI_GTK2)
     GtkWidget *a = gtk_alignment_new(0.5, 0.5, 1, 1);
-    gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin, margin, margin, margin);
-    gtk_container_add(GTK_CONTAINER(a), box);
+    gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin_top, margin_bottom, margin_left, margin_right);
+    gtk_container_add(GTK_CONTAINER(a), widget);
+    g_signal_connect(
+                widget,
+                "show",
+                G_CALLBACK(alignment_child_visibility_changed),
+                NULL);
+    g_signal_connect(
+                widget,
+                "hide",
+                G_CALLBACK(alignment_child_visibility_changed),
+                NULL);
     ret = a;
 #endif
     return ret;
 }
 
 UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, UiSubContainerType type) {
-    UiObject *current = uic_current_obj(obj);
-    UiContainer *ct = current->container;
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     GtkWidget *box = type == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args->spacing) : ui_gtk_hbox_new(args->spacing);
     ui_set_name_and_style(box, args->name, args->style_class);
-    GtkWidget *widget = args->margin > 0 ? ui_box_set_margin(box, args->margin) : box;
-    ct->add(ct, widget);
+    ct->add(ct, box, &layout);
     
-    UiObject *newobj = uic_object_new(obj, box);
-    newobj->container = ui_box_container(obj, box, type);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_box_container(obj, box, type);
+    uic_object_push_container(obj, container);
     
-    return widget;
+    return box;
 }
 
 UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) {
@@ -452,82 +412,113 @@ GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing) {
 }
 
 UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     GtkWidget *widget;
     
     GtkWidget *grid = ui_create_grid_widget(args->columnspacing, args->rowspacing);
     ui_set_name_and_style(grid, args->name, args->style_class);
-    widget = ui_box_set_margin(grid, args->margin);
-    current->container->add(current->container, widget);
+    ct->add(ct, grid, &layout);
     
-    UiObject *newobj = uic_object_new(obj, grid);
-    newobj->container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill);
+    uic_object_push_container(obj, container);
     
-    return widget;
+    return grid;
+}
+
+static void frame_create_subcontainer(UiObject *obj, UiFrameArgs *args) {
+    switch(args->subcontainer) {
+        default:
+        case UI_CONTAINER_VBOX: {
+            UiContainerArgs sub_args = { .spacing = args->spacing, .margin = args->padding };
+            ui_vbox_create(obj, &sub_args);
+            break;
+        }
+        case UI_CONTAINER_HBOX: {
+            UiContainerArgs sub_args = { .spacing = args->spacing, .margin = args->padding };
+            ui_hbox_create(obj, &sub_args);
+            break;
+        }
+        case UI_CONTAINER_GRID: {
+            UiContainerArgs sub_args = { .columnspacing = args->columnspacing, .rowspacing = args->rowspacing, .margin = args->padding };
+            ui_grid_create(obj, &sub_args);
+            break;
+        }
+        case UI_CONTAINER_NO_SUB: {
+            break; // NOOP
+        }
+    }
 }
 
 UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     GtkWidget *frame = gtk_frame_new(args->label);
-    UiObject *newobj = uic_object_new(obj, frame);
-    GtkWidget *sub = ui_subcontainer_create(args->subcontainer, newobj, args->spacing, args->columnspacing, args->rowspacing, args->margin);
+    ct->add(ct, frame, &layout);
+    
+    GtkWidget *sub = ui_subcontainer_create(
+            args->subcontainer,
+            obj, args->spacing,
+            args->columnspacing,
+            args->rowspacing,
+            args->padding);
     if(sub) {
         FRAME_SET_CHILD(frame, sub);
     } else {
-        newobj->widget = frame;
-        newobj->container = ui_frame_container(obj, frame);
+        UiContainerX *container = ui_frame_container(obj, frame);
+        uic_object_push_container(obj, container);
     }
-    current->container->add(current->container, frame);
-    uic_obj_add(obj, newobj);
     
     return frame;
 }
 
 UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     GtkWidget *expander = gtk_expander_new(args->label);
     gtk_expander_set_expanded(GTK_EXPANDER(expander), args->isexpanded);
-    UiObject *newobj = uic_object_new(obj, expander);
-    GtkWidget *sub = ui_subcontainer_create(args->subcontainer, newobj, args->spacing, args->columnspacing, args->rowspacing, args->margin);
+    ct->add(ct, expander, &layout);
+    
+    GtkWidget *sub = ui_subcontainer_create(
+            args->subcontainer,
+            obj, args->spacing,
+            args->columnspacing,
+            args->rowspacing,
+            args->padding);
     if(sub) {
         EXPANDER_SET_CHILD(expander, sub);
     } else {
-        newobj->widget = expander;
-        newobj->container = ui_expander_container(obj, expander);
+        UiContainerX *container = ui_expander_container(obj, expander);
+        uic_object_push_container(obj, container);
     }
-    current->container->add(current->container, expander);
-    uic_obj_add(obj, newobj);
     
     return expander;
 }
 
 
 UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     GtkWidget *sw = SCROLLEDWINDOW_NEW();
     ui_set_name_and_style(sw, args->name, args->style_class);
-    GtkWidget *widget = ui_box_set_margin(sw, args->margin);
-    current->container->add(current->container, widget);
-    
-    UiObject *newobj = uic_object_new(obj, sw);
-    GtkWidget *sub = ui_subcontainer_create(args->subcontainer, newobj, args->spacing, args->columnspacing, args->rowspacing, args->margin);
+    ct->add(ct, sw, &layout);
+    
+    GtkWidget *sub = ui_subcontainer_create(
+            args->subcontainer,
+            obj, args->spacing,
+            args->columnspacing,
+            args->rowspacing,
+            args->padding);
     if(sub) {
         SCROLLEDWINDOW_SET_CHILD(sw, sub);
     } else {
-        newobj->widget = sw;
-        newobj->container = ui_scrolledwindow_container(obj, sw);
+        UiContainerX *container = ui_scrolledwindow_container(obj, sw);
+        uic_object_push_container(obj, container);
     }
     
-    uic_obj_add(obj, newobj);
-    
     return sw;
 }
 
@@ -736,7 +727,7 @@ typedef void (*ui_tabview_set_func)(UiInteger*, int64_t);
 UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs *args) {
     UiGtkTabView *data = malloc(sizeof(UiGtkTabView));
     memset(data, 0, sizeof(UiGtkTabView));
-    data->margin = args->margin;
+    data->padding = args->padding;
     data->spacing = args->spacing;
     data->columnspacing = args->columnspacing;
     data->rowspacing = args->rowspacing;
@@ -796,9 +787,8 @@ UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs *args) {
         }
     }
     
-    UiObject* current = uic_current_obj(obj);
     if(args->value || args->varname) {
-        UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_INTEGER);
+        UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
         UiInteger *i = var->value;
         i->get = getfunc;
         i->set = setfunc;
@@ -807,29 +797,56 @@ UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs *args) {
     
     g_object_set_data(G_OBJECT(widget), "ui_tabview", data);
     data->widget = data_widget;
-    data->subcontainer = args->subcontainer;
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, widget);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
     
-    UiObject *newobj = uic_object_new(obj, widget);
-    newobj->container = ui_tabview_container(obj, widget);
-    uic_obj_add(obj, newobj);
-    data->obj = newobj;
+    UiContainerX *container = ui_tabview_container(obj, widget);
+    uic_object_push_container(obj, container);
     
     return widget;
 }
 
+static GtkWidget* create_tab(UiObject *obj, UiGtkTabView *tabview, const char *title, int tab) {
+    UiContainerX *container;
+    GtkWidget *sub;
+    switch(tabview->subcontainer) {
+        default: {
+            sub = ui_gtk_vbox_new(tabview->spacing);
+            container = ui_box_container(obj, sub, tabview->subcontainer);
+            break;
+        }
+        case UI_CONTAINER_HBOX: {
+            sub = ui_gtk_hbox_new(tabview->spacing);
+            container = ui_box_container(obj, sub, tabview->subcontainer);
+            break;
+        }
+        case UI_CONTAINER_GRID: {
+            sub = ui_create_grid_widget(tabview->columnspacing, tabview->rowspacing);
+            container = ui_grid_container(obj, sub, FALSE, FALSE, FALSE, FALSE);
+            break;
+        }
+    }
+    
+    uic_object_push_container(obj, container);
+    
+    GtkWidget *widget = ui_gtk_set_margin(sub, tabview->padding, 0, 0, 0, 0);
+    tabview->add_tab(tabview->widget, tab, title, widget);
+    
+    return sub;
+}
+
 void ui_tab_create(UiObject* obj, const char* title) {
-    UiObject* current = uic_current_obj(obj);
-    UiGtkTabView *data = ui_widget_get_tabview_data(current->widget);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; 
+    GtkWidget *tabview = ct->widget;
+    UiGtkTabView *data = ui_widget_get_tabview_data(tabview);
     if(!data) {
         fprintf(stderr, "UI Error: widget is not a tabview\n");
         return;
     }
     
-    UiObject *newobj = ui_tabview_add(current->widget, title, -1);
-    current->next = newobj;
+    create_tab(obj, data, title, -1);
 }
 
 
@@ -859,31 +876,8 @@ UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) {
         return NULL;
     }
     
-    UiObject *newobj = cxCalloc(data->obj->ctx->allocator, 1, sizeof(UiObject));
-    newobj->ctx = data->obj->ctx;
-    
-    GtkWidget *sub;
-    switch(data->subcontainer) {
-        default: {
-            sub = ui_gtk_vbox_new(data->spacing);
-            newobj->container = ui_box_container(newobj, sub, data->subcontainer);
-            break;
-        }
-        case UI_CONTAINER_HBOX: {
-            sub = ui_gtk_hbox_new(data->spacing);
-            newobj->container = ui_box_container(newobj, sub, data->subcontainer);
-            break;
-        }
-        case UI_CONTAINER_GRID: {
-            sub = ui_create_grid_widget(data->columnspacing, data->rowspacing);
-            newobj->container = ui_grid_container(newobj, sub, FALSE, FALSE, FALSE, FALSE);
-            break;
-        }
-    }
-    newobj->widget = sub;
-    GtkWidget *widget = ui_box_set_margin(sub, data->margin);
-    
-    data->add_tab(data->widget, tab_index, name, widget);
+    UiObject *newobj = uic_object_new_toplevel();
+    newobj->widget = create_tab(newobj, data, name, tab_index);
     
     return newobj;
 }
@@ -892,20 +886,16 @@ UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) {
 /* -------------------- Headerbar -------------------- */
 
 static void hb_set_part(UiObject *obj, int part) {
-    UiObject* current = uic_current_obj(obj);
-    GtkWidget *headerbar = current->widget;
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    GtkWidget *headerbar = ct->widget;
     
     UiHeaderbarContainer *hb = cxCalloc(
             obj->ctx->allocator,
             1,
             sizeof(UiHeaderbarContainer));
-    memcpy(hb, current->container, sizeof(UiHeaderbarContainer));
-    
-    UiObject *newobj = uic_object_new(obj, headerbar);
-    newobj->container = (UiContainer*)hb;
-    uic_obj_add(obj, newobj);
-    
+    memcpy(hb, ct, sizeof(UiHeaderbarContainer));
     hb->part = part;
+    uic_object_push_container(obj, (UiContainerX*)hb);
 }
 
 void ui_headerbar_start_create(UiObject *obj) {
@@ -921,74 +911,70 @@ void ui_headerbar_end_create(UiObject *obj) {
 }
 
 UIWIDGET ui_headerbar_fallback_create(UiObject *obj, UiHeaderbarArgs *args) {
-    UiObject *current = uic_current_obj(obj);
-    UiContainer *ct = current->container;
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     GtkWidget *box = ui_gtk_hbox_new(args->alt_spacing);
     ui_set_name_and_style(box, args->name, args->style_class);
-    ct->add(ct, box);
+    ct->add(ct, box, &layout);
     
-    UiObject *newobj = uic_object_new(obj, box);
-    newobj->container = ui_headerbar_fallback_container(obj, box);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_headerbar_fallback_container(obj, box);
+    uic_object_push_container(obj, container);
     
     return box;
 }
 
 static void hb_fallback_set_part(UiObject *obj, int part) {
-    UiObject* current = uic_current_obj(obj);
-    GtkWidget *headerbar = current->widget;
+     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    GtkWidget *headerbar = ct->widget;
     
-    UiObject *newobj = uic_object_new(obj, headerbar);
-    newobj->container = ui_headerbar_container(obj, headerbar);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_headerbar_container(obj, headerbar);
+    uic_object_push_container(obj, container);
     
-    UiHeaderbarContainer *hb = (UiHeaderbarContainer*)newobj->container;
+    UiHeaderbarContainer *hb = (UiHeaderbarContainer*)container;
     hb->part = part;
 }
 
-UiContainer* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar) {
+UiContainerX* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar) {
     UiHeaderbarContainer *ct = cxCalloc(
             obj->ctx->allocator,
             1,
             sizeof(UiHeaderbarContainer));
     ct->container.widget = headerbar;
     ct->container.add = ui_headerbar_fallback_container_add;
-    return (UiContainer*)ct;
+    return (UiContainerX*)ct;
 }
 
-void ui_headerbar_fallback_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_headerbar_fallback_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
     UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct;
     BOX_ADD(ct->widget, widget);
 }
 
 #if GTK_CHECK_VERSION(3, 10, 0)
 
-UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs *args) {
+UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs *args) {  
     GtkWidget *headerbar = g_object_get_data(G_OBJECT(obj->widget), "ui_headerbar");
     if(!headerbar) {
         return ui_headerbar_fallback_create(obj, args);
     }
     
-    UiObject *newobj = uic_object_new(obj, headerbar);
-    newobj->container = ui_headerbar_container(obj, headerbar);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_headerbar_container(obj, headerbar);
+    uic_object_push_container(obj, container);
     
     return headerbar;    
 }
 
-UiContainer* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar) {
+UiContainerX* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar) {
     UiHeaderbarContainer *ct = cxCalloc(
             obj->ctx->allocator,
             1,
             sizeof(UiHeaderbarContainer));
     ct->container.widget = headerbar;
     ct->container.add = ui_headerbar_container_add;
-    return (UiContainer*)ct;
+    return (UiContainerX*)ct;
 }
 
-void ui_headerbar_container_add(UiContainer *ct, GtkWidget *widget) {
+void ui_headerbar_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
     UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct;
     if(hb->part == 0) {
         UI_HEADERBAR_PACK_START(ct->widget, widget);
@@ -1023,12 +1009,11 @@ UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
     }
     
     GtkWidget *box = ui_gtk_vbox_new(args->spacing);
-    ui_box_set_margin(box, args->margin);
+    ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom);
     adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), box);
     
-    UiObject *newobj = uic_object_new(obj, box);
-    newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+    uic_object_push_container(obj, container);
     
     return box;
 }
@@ -1037,17 +1022,44 @@ UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
     GtkWidget *sidebar_vbox = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar");
     
     GtkWidget *box = ui_gtk_vbox_new(args->spacing);
-    ui_box_set_margin(box, args->margin);
+    ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom);
     BOX_ADD_EXPAND(sidebar_vbox, box);
     
-    UiObject *newobj = uic_object_new(obj, box);
-    newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
-    uic_obj_add(obj, newobj);
+    UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+    uic_object_push_container(obj, container);
     
     return box;
 }
 #endif
 
+/* ------------------------ Split Window Panels ------------------------ */
+
+static UIWIDGET splitwindow_panel(UiObject *obj, GtkWidget *pbox, UiSidebarArgs *args) {
+    if(!pbox) {
+        fprintf(stderr, "Error: window is not a splitview window\n");
+        return NULL;
+    }
+    
+    GtkWidget *box = ui_gtk_vbox_new(args->spacing);
+    ui_set_name_and_style(box, args->name, args->style_class);
+    ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom);
+    BOX_ADD_EXPAND(pbox, box);
+    
+    UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+    uic_object_push_container(obj, container);
+    
+    return box;
+}
+
+UIWIDGET ui_left_panel_create(UiObject *obj, UiSidebarArgs *args) {
+    return splitwindow_panel(obj, g_object_get_data(G_OBJECT(obj->widget), "ui_left_panel"), args);
+}
+
+UIWIDGET ui_right_panel_create(UiObject *obj, UiSidebarArgs *args) {
+    return splitwindow_panel(obj, g_object_get_data(G_OBJECT(obj->widget), "ui_right_panel"), args);
+}
+
+
 /* -------------------- Splitpane -------------------- */
 
 static GtkWidget* create_paned(UiOrientation orientation) {
@@ -1065,23 +1077,55 @@ static GtkWidget* create_paned(UiOrientation orientation) {
     return NULL;
 }
 
-
+static void save_pane_pos(GtkWidget *widget, char *property_name) {
+    int pos = gtk_paned_get_position(GTK_PANED(widget));
+    char buf[32];
+    snprintf(buf, 32, "%d", pos);
+    ui_set_property(property_name, buf);
+    free(property_name);
+}
 
 static UIWIDGET splitpane_create(UiObject *obj, UiOrientation orientation, UiSplitPaneArgs *args) {
-    UiObject* current = uic_current_obj(obj);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     GtkWidget *pane0 = create_paned(orientation);
-    
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, pane0);
+    ct->add(ct, pane0, &layout);
     
     int max = args->max_panes == 0 ? 2 : args->max_panes;
     
-    UiObject *newobj = uic_object_new(obj, pane0);
-    newobj->container = ui_splitpane_container(obj, pane0, orientation, max, args->initial_position);
-    uic_obj_add(obj, newobj);
+    if(args->position_property) {
+        const char *pos_str = ui_get_property(args->position_property);
+        if(pos_str) {
+            char *end;
+            long pos = strtol(pos_str, &end, 10);
+            if(*end == '\0') {
+                args->initial_position = (int)pos;
+            }
+        }
+        
+        g_signal_connect(
+                pane0,
+                "destroy",
+                G_CALLBACK(save_pane_pos),
+                strdup(args->position_property));
+    }
+    
+    UiSplitPane *splitpane = ui_create_splitpane_data(pane0, orientation, max, args->initial_position);
+    UiContainerX *container = ui_splitpane_container(obj, pane0, splitpane);
+    uic_object_push_container(obj, container);
     
-    g_object_set_data(G_OBJECT(pane0), "ui_splitpane", newobj->container);
+    g_object_set_data(G_OBJECT(pane0), "ui_splitpane", splitpane);
+    
+    UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+    if(var) {
+        UiInteger *i = var->value;
+        splitpane->initial_position = i->value;
+        
+        i->obj = splitpane;
+        i->get = ui_splitpane_get;
+        i->set = ui_splitpane_set;
+    }
     
     return pane0;
 }
@@ -1094,20 +1138,27 @@ UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) {
     return splitpane_create(obj, UI_VERTICAL, args);
 }
 
-UiContainer* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiOrientation orientation, int max, int init) {
-    UiSplitPaneContainer *ct = ui_calloc(obj->ctx, 1, sizeof(UiSplitPaneContainer));
-    ct->container.widget = pane;
-    ct->container.add = ui_splitpane_container_add;
+UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation, int max, int init) {
+    UiSplitPane *ct = malloc(sizeof(UiSplitPane));
     ct->current_pane = pane;
     ct->orientation = orientation;
     ct->max = max;
     ct->initial_position = init;
     ct->children = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
-    return (UiContainer*)ct;
+    return ct;
+}
+
+UiContainerX* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiSplitPane *data) {
+    UiSplitPaneContainer *ct = ui_calloc(obj->ctx, 1, sizeof(UiSplitPaneContainer));
+    ct->container.widget = pane;
+    ct->container.add = ui_splitpane_container_add;
+    ct->splitpane = data;
+    return (UiContainerX*)ct;
 }
 
-void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget) {
-    UiSplitPaneContainer *s = (UiSplitPaneContainer*)ct;
+void ui_splitpane_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+    UiSplitPaneContainer *sct = (UiSplitPaneContainer*)ct;
+    UiSplitPane *s = sct->splitpane;
     
     if(s->nchildren >= s->max) {
         fprintf(stderr, "splitpane: maximum number of children reached\n");
@@ -1138,14 +1189,26 @@ void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget) {
     }
 }
 
+int64_t ui_splitpane_get(UiInteger *i) {
+    UiSplitPane *s = i->obj;
+    i->value = gtk_paned_get_position(GTK_PANED(s->current_pane));
+    return i->value;
+}
+
+void ui_splitpane_set(UiInteger *i, int64_t value) {
+    UiSplitPane *s = i->obj;
+    i->value = value;
+    gtk_paned_set_position(GTK_PANED(s->current_pane), (int)value);
+}
+
 UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBool visible) {
-    UiSplitPaneContainer *ct = g_object_get_data(G_OBJECT(splitpane), "ui_splitpane");
-    if(!ct) {
+    UiSplitPane *s = g_object_get_data(G_OBJECT(splitpane), "ui_splitpane");
+    if(!s) {
         fprintf(stderr, "UI Error: not a splitpane\n");
         return;
     }
     
-    GtkWidget *w = cxListAt(ct->children, child_index);
+    GtkWidget *w = cxListAt(s->children, child_index);
     if(w) {
         gtk_widget_set_visible(w, visible);
     }
@@ -1199,13 +1262,12 @@ static void update_itemlist(UiList *list, int c) {
         UiObject *item_obj = cxMapGet(ct->current_items, key);
         if(item_obj) {
             // re-add previously created widget
-            ui_box_container_add(ct->container, item_obj->widget);
+            UiLayout layout = {0};
+            ui_box_container_add(ct->container, item_obj->widget, &layout);
         } else {
             // create new widget and object for this list element
-            CxMempool *mp = cxMempoolCreateSimple(256);
-            const CxAllocator *a = mp->allocator;
-            UiObject *obj = cxCalloc(a, 1, sizeof(UiObject));
-            obj->ctx = uic_context(obj, mp);
+            UiObject *obj = uic_object_new_toplevel();
+            obj->ctx->parent = ct->parent->ctx;
             obj->window = NULL;
             obj->widget = ui_subcontainer_create(
                     ct->subcontainer,
@@ -1214,7 +1276,8 @@ static void update_itemlist(UiList *list, int c) {
                     ct->columnspacing,
                     ct->rowspacing,
                     ct->margin);
-            ui_box_container_add(ct->container, obj->widget);
+            UiLayout layout = {0};
+            ui_box_container_add(ct->container, obj->widget, &layout);
             if(ct->create_ui) {
                 ct->create_ui(obj, index, elm, ct->userdata);
             }
@@ -1236,19 +1299,17 @@ static void destroy_itemlist_container(GtkWidget *w, UiGtkItemListContainer *con
 }
 
 UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) {
-    UiObject *current = uic_current_obj(obj);
-    UiContainer *ct = current->container;
-    UI_APPLY_LAYOUT2(current, args);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     GtkWidget *box = args->container == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args->spacing) : ui_gtk_hbox_new(args->spacing);
     ui_set_name_and_style(box, args->name, args->style_class);
-    GtkWidget *widget = args->margin > 0 ? ui_box_set_margin(box, args->margin) : box;
-    ct->add(ct, widget);
+    ct->add(ct, box, &layout);
     
     UiGtkItemListContainer *container = malloc(sizeof(UiGtkItemListContainer));
     container->parent = obj;
     container->widget = box;
-    container->container = ui_box_container(current, box, args->container);
+    container->container = (UiContainerPrivate*)ui_box_container(obj, box, args->container);
     container->create_ui = args->create_ui;
     container->userdata = args->userdata;
     container->subcontainer = args->subcontainer;
@@ -1261,7 +1322,7 @@ UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) {
     container->rowspacing = args->sub_rowspacing;
     container->remove_items = TRUE;
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_LIST);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_LIST);
     if(var) {
         UiList *list = var->value;
         list->obj = container;
@@ -1278,56 +1339,3 @@ UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) {
 }
 
 
-
-/*
- * -------------------- Layout Functions --------------------
- * 
- * functions for setting layout attributes for the current container
- *
- */
-
-void ui_layout_fill(UiObject *obj, UiBool fill) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.fill = fill;
-}
-
-void ui_layout_hexpand(UiObject *obj, UiBool expand) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.hexpand = expand;
-}
-
-void ui_layout_vexpand(UiObject *obj, UiBool expand) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.vexpand = expand;
-}
-
-void ui_layout_hfill(UiObject *obj, UiBool fill) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.hfill = fill;
-}
-
-void ui_layout_vfill(UiObject *obj, UiBool fill) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.vfill = fill;
-}
-
-UIEXPORT void ui_layout_override_defaults(UiObject *obj, UiBool d) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.override_defaults = d;
-}
-
-void ui_layout_colspan(UiObject* obj, int cols) {
-    UiContainer* ct = uic_get_current_container(obj);
-    ct->layout.colspan = cols;
-}
-
-void ui_layout_rowspan(UiObject* obj, int rows) {
-    UiContainer* ct = uic_get_current_container(obj);
-    ct->layout.rowspan = rows;
-}
-
-void ui_newline(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.newline = TRUE;
-}
-
index 5f447662477881ec25a9b4c8ca8bfabd8bd37d9d..2f2510c51a3a505fc66400d71aeef7e80dd46f6e 100644 (file)
@@ -45,45 +45,30 @@ extern "C" {
   
 #define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout))
     
-typedef void (*ui_container_add_f)(UiContainer*, GtkWidget*);
 
 typedef struct UiDocumentView UiDocumentView;
 
-
-typedef struct UiLayout UiLayout;
-struct UiLayout {
-    UiBool       fill;
-    UiBool       newline;
-    char         *label;
-    UiBool       hexpand;
-    UiBool       vexpand;
-    UiBool       hfill;
-    UiBool       vfill;
-    UiBool       override_defaults;
-    int          width;
-    int          colspan;
-    int          rowspan;
-};
-
-struct UiContainer {
+typedef struct UiContainerPrivate UiContainerPrivate;
+struct UiContainerPrivate {
+    UiContainerX container;
     GtkWidget *widget;
     UIMENU menu;
-    GtkWidget *current;
+    GtkWidget *current; // TODO: remove
     
-    void (*add)(UiContainer*, GtkWidget*);
+    void (*add)(UiContainerPrivate*, GtkWidget*, UiLayout *layout);
     UiLayout layout;
     
     int close;
 };
 
 typedef struct UiBoxContainer {
-    UiContainer container;
+    UiContainerPrivate container;
     UiSubContainerType type;
     UiBool has_fill;
 } UiBoxContainer;
 
 typedef struct UiGridContainer {
-    UiContainer container;
+    UiContainerPrivate container;
     UiBool def_hexpand;
     UiBool def_vexpand;
     UiBool def_hfill;
@@ -97,7 +82,7 @@ typedef struct UiGridContainer {
 } UiGridContainer;
 
 typedef struct UiTabViewContainer {
-    UiContainer container;
+    UiContainerPrivate container;
 } UiTabViewContainer;
 
 typedef void (*ui_select_tab_func)(UIWIDGET widget, int tab);
@@ -110,7 +95,7 @@ typedef struct UiGtkTabView {
     ui_select_tab_func remove_tab;
     ui_add_tab_func add_tab;
     UiSubContainerType subcontainer;
-    int margin;
+    int padding;
     int spacing;
     int columnspacing;
     int rowspacing;
@@ -118,9 +103,7 @@ typedef struct UiGtkTabView {
     void *onchangedata;
 } UiGtkTabView;
 
-
-typedef struct UiSplitPaneContainer {
-    UiContainer container;
+typedef struct UiSplitPane {
     GtkWidget *current_pane;
     CxList *children;
     UiOrientation orientation;
@@ -128,10 +111,15 @@ typedef struct UiSplitPaneContainer {
     int max;
     int nchildren;
     int initial_position;
+} UiSplitPane;
+
+typedef struct UiSplitPaneContainer {
+    UiContainerPrivate container;
+    UiSplitPane *splitpane;
 } UiSplitPaneContainer;
 
 typedef struct UiHeaderbarContainer {
-    UiContainer container;
+    UiContainerPrivate container;
     GtkWidget *centerbox;
     int part;
     UiHeaderbarAlternative alternative; /* only used by fallback headerbar */
@@ -140,7 +128,7 @@ typedef struct UiHeaderbarContainer {
 typedef struct UiGtkItemListContainer {
     UiObject *parent;
     GtkWidget *widget;
-    UiContainer *container;
+    UiContainerPrivate *container;
     void (*create_ui)(UiObject *, int, void *, void *);
     void *userdata;
     UiSubContainerType subcontainer;
@@ -163,52 +151,52 @@ GtkWidget* ui_subcontainer_create(
         int rowspacing,
         int margin);
 
-UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame);
-void ui_frame_container_add(UiContainer *ct, GtkWidget *widget);
-
-GtkWidget* ui_box_set_margin(GtkWidget *box, int margin);
+GtkWidget* ui_gtk_set_margin(GtkWidget *widget, int margin, int margin_left, int margin_right, int margin_top, int margin_bottom);
 UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, UiSubContainerType type);
 
-UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type);
-void ui_box_container_add(UiContainer *ct, GtkWidget *widget);
+UiContainerX* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type);
+void ui_box_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
 
 GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing);
-UiContainer* ui_grid_container(
+UiContainerX* ui_grid_container(
         UiObject *obj,
         GtkWidget *grid,
         UiBool def_hexpand,
         UiBool def_vexpand,
         UiBool def_hfill,
         UiBool def_vfill);
-void ui_grid_container_add(UiContainer *ct, GtkWidget *widget);
+void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
 
-UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame);
-void ui_frame_container_add(UiContainer *ct, GtkWidget *widget);
+UiContainerX* ui_frame_container(UiObject *obj, GtkWidget *frame);
+void ui_frame_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
 
-UiContainer* ui_expander_container(UiObject *obj, GtkWidget *expander);
-void ui_expander_container_add(UiContainer *ct, GtkWidget *widget);
+UiContainerX* ui_expander_container(UiObject *obj, GtkWidget *expander);
+void ui_expander_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
 
-UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow);
-void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget);
+UiContainerX* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow);
+void ui_scrolledwindow_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
 
-UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview);
-void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget);
+UiContainerX* ui_tabview_container(UiObject *obj, GtkWidget *tabview);
+void ui_tabview_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
 
-UiContainer* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiOrientation orientation, int max, int init);
-void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget);
+UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation, int max, int init);
+UiContainerX* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiSplitPane *data);
+void ui_splitpane_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
 
+int64_t ui_splitpane_get(UiInteger *i);
+void ui_splitpane_set(UiInteger *i, int64_t value);
 
 UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview);
 
 void ui_gtk_notebook_select_tab(GtkWidget *widget, int tab);
 
 #if GTK_CHECK_VERSION(3, 10, 0)
-UiContainer* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar);
-void ui_headerbar_container_add(UiContainer *ct, GtkWidget *widget);
+UiContainerX* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar);
+void ui_headerbar_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
 #endif
 
-UiContainer* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar);
-void ui_headerbar_fallback_container_add(UiContainer *ct, GtkWidget *widget);
+UiContainerX* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar);
+void ui_headerbar_fallback_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
 
 #ifdef __cplusplus
 }
index 32c4afd78472e6b68fc446c036b842c9e5e62736..1f8c39e1a32104d73638e17975cd07d2a1a54814 100644 (file)
@@ -47,8 +47,6 @@ static void set_alignment(GtkWidget *widget, float xalign, float yalign) {
 }
 
 UIWIDGET ui_label_create(UiObject *obj, UiLabelArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     const char *css_class = NULL;
     char *markup = NULL;
     if(args->label) {
@@ -105,7 +103,7 @@ UIWIDGET ui_label_create(UiObject *obj, UiLabelArgs *args) {
     }
     
 
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
     if(var) {
         UiString* value = (UiString*)var->value;
         value->obj = widget;
@@ -113,8 +111,9 @@ UIWIDGET ui_label_create(UiObject *obj, UiLabelArgs *args) {
         value->set = ui_label_set;
     }
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, widget);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
     
     return widget;
 }
@@ -147,10 +146,12 @@ void ui_label_set(UiString *s, const char *value) {
     }
 }
 
+/*
 UIWIDGET ui_space_deprecated(UiObject *obj) {
     GtkWidget *widget = gtk_label_new("");
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, widget);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
     
     return widget;
 }
@@ -161,12 +162,14 @@ UIWIDGET ui_separator_deprecated(UiObject *obj) {
 #else
     GtkWidget *widget = gtk_hseparator_new();
 #endif
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, widget);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
     
     return widget;
 }
-
+*/
 /* ------------------------- progress bar ------------------------- */
 
 typedef struct UiProgressBarRange {
@@ -175,8 +178,6 @@ typedef struct UiProgressBarRange {
 } UiProgressBarRange;
 
 UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     GtkWidget *progressbar = gtk_progress_bar_new();
     if(args->max > args->min) {
         UiProgressBarRange *range = malloc(sizeof(UiProgressBarRange));
@@ -191,7 +192,7 @@ UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs *args) {
     }
     
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_DOUBLE);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_DOUBLE);
     if(var && var->value) {
         UiDouble *value = var->value;
         value->get = ui_progressbar_get;
@@ -200,8 +201,9 @@ UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs *args) {
         ui_progressbar_set(value, value->value);
     }
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, progressbar);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, progressbar, &layout);
     
     return progressbar;
 }
@@ -229,11 +231,9 @@ void ui_progressbar_set(UiDouble *d, double value) {
 /* ------------------------- progress spinner ------------------------- */
 
 UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     GtkWidget *spinner = gtk_spinner_new();
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_INTEGER);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
     if(var && var->value) {
         UiInteger *value = var->value;
         value->get = ui_spinner_get;
@@ -242,8 +242,9 @@ UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs *args
         ui_spinner_set(value, value->value);
     }
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, spinner);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, spinner, &layout);
     
     return spinner;
 }
index fc85c324b7c6fef631815587c1825d577969d789..c067bc0c75652586afdf5694601f65a05ebb94b7 100644 (file)
 
 
 #if GTK_MAJOR_VERSION >= 3
-static void ui_drawingarea_draw(
-        GtkDrawingArea *area,
-        cairo_t *cr,
-        int width,
-        int height,
-        gpointer data)
-{
+void ui_drawingarea_draw(UiDrawingArea *drawingarea, cairo_t *cr, int width, int height) {
     UiCairoGraphics g;
     g.g.width = width;
     g.g.height = height;
-    g.widget = GTK_WIDGET(area);
+    g.widget = drawingarea->widget;;
     g.cr = cr;
     
-    UiDrawEvent *event = data;
-    UiEvent ev;
-    ev.obj = event->obj;
-    ev.window = event->obj->window;
-    ev.document = event->obj->ctx->document;
+    UiEvent event;
+    event.obj = drawingarea->obj;
+    event.window = event.obj->window;
+    event.document = event.obj->ctx->document;
+    event.eventdata = NULL;
+    event.eventdatatype = 0;
+    event.intval = 0;
+    event.set = 0;
     
-    event->callback(&ev, &g.g, event->userdata);
+    if(drawingarea->draw) {
+        drawingarea->draw(&event, &g.g, drawingarea->drawdata);
+    }
 }
 #endif
 
+/*
 #if UI_GTK3
 gboolean ui_drawingarea_expose(GtkWidget *w, cairo_t *cr, void *data) {
     int width = gtk_widget_get_allocated_width(w);
@@ -85,9 +85,11 @@ gboolean ui_canvas_expose(GtkWidget *w, GdkEventExpose *e, void *data) {
     return FALSE;
 }
 #endif
+*/
 
 // function from graphics.h
 
+/*
 void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event) {
 #if GTK_MAJOR_VERSION >= 4
     gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(widget), ui_drawingarea_draw, event, NULL);
@@ -103,7 +105,7 @@ void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event) {
             event);
 #endif
 }
-
+*/
 
 PangoContext *ui_get_pango_context(UiGraphics *g) {
     UiCairoGraphics *gr = (UiCairoGraphics*)g;
@@ -130,7 +132,7 @@ void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2) {
     cairo_stroke(gr->cr);
 }
 
-void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, int fill) {
+void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, UiBool fill) {
     UiCairoGraphics *gr = (UiCairoGraphics*)g;
     cairo_set_line_width(gr->cr, 1);
     cairo_rectangle(gr->cr, x + 0.5, y + 0.5 , w, h);
index 6be96a7c22626c894fe5f8b8faa2bcc3c7681f13..a1ae8d7f366501249a81c738bd5d72e00e0563e6 100644 (file)
@@ -41,7 +41,7 @@ typedef struct UiCairoGraphics {
     cairo_t    *cr;
 } UiCairoGraphics;
 
-// ui_canvas_expose
+void ui_drawingarea_draw(UiDrawingArea *drawingarea, cairo_t *cr, int width, int height);
 
 #ifdef __cplusplus
 }
index 3c83f111d8f944b362bc05f45cb55d347ee1e6ea..b411191cae49057d08a708463a81d830c608cf7a 100644 (file)
 #include "entry.h"
 
 
-UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
-    double min = 0;
-    double max = 1000;
-    
-    UiObject* current = uic_current_obj(obj);
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
+    double min = args->min;
+    double max = args->max != 0 ? args->max : 1000;
     
     UiVar *var = NULL;
+    UiVarType vartype = 0;
     if(args->varname) {
         var = uic_get_var(obj->ctx, args->varname);
+        if(var) {
+            vartype = var->type;
+        } else {
+            var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, args->varname, UI_VAR_RANGE);
+            vartype = UI_VAR_RANGE;
+        }
     }
     
     if(!var) {
         if(args->intvalue) {
-            var = uic_widget_var(obj->ctx, current->ctx, args->intvalue, NULL, UI_VAR_INTEGER);
+            var = uic_widget_var(obj->ctx, obj->ctx, args->intvalue, NULL, UI_VAR_INTEGER);
+            vartype = UI_VAR_INTEGER;
         } else if(args->doublevalue) {
-            var = uic_widget_var(obj->ctx, current->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE);
+            var = uic_widget_var(obj->ctx, obj->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE);
+            vartype = UI_VAR_DOUBLE;
         } else if(args->rangevalue) {
-            var = uic_widget_var(obj->ctx, current->ctx, args->rangevalue, NULL, UI_VAR_RANGE);
+            var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, NULL, UI_VAR_RANGE);
+            vartype = UI_VAR_RANGE;
         }
     }
     
-    if(var && var->type == UI_VAR_RANGE) {
+    if(vartype == UI_VAR_RANGE) {
         UiRange *r = var->value;
         min = r->min;
         max = r->max;
@@ -72,11 +80,16 @@ UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
     GtkWidget *spin = gtk_spin_button_new_with_range(min, max, args->step);
     ui_set_name_and_style(spin, args->name, args->style_class);
     ui_set_widget_groups(obj->ctx, spin, args->groups);
+    
+    if(args->width > 0) {
+        gtk_widget_set_size_request(spin, args->width, -1);
+    }
+    
     gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), args->digits);
     UiObserver **obs = NULL;
     if(var) {
         double value = 0;
-        switch(var->type) {
+        switch(vartype) {
             default: break;
             case UI_VAR_INTEGER: {
                 UiInteger *i = var->value;
@@ -129,8 +142,9 @@ UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
             G_CALLBACK(ui_destroy_vardata),
             event);
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, spin);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, spin, &layout);
     
     return spin;
 }
index 6883575f3b3b1194633de428adafe4c1f1848baf..33fd186ae63c885f8f9fa8c7192d0ef2148ee496 100644 (file)
 #include "container.h"
 #include "../common/object.h"
 
-UIWIDGET ui_drawingarea(UiObject *obj, ui_drawfunc f, void *userdata) {
+#if GTK_CHECK_VERSION(3, 0, 0)
+#include "draw_cairo.h"
+#endif
+
+static void destroy_drawingarea(GtkWidget *widget, UiDrawingArea *drawingarea) {
+    free(drawingarea);
+}
+
+static void drawingarea_draw(UiDrawingArea *drawingarea, cairo_t *cr, int width, int height) {
+    UiEvent event;
+    event.obj = drawingarea->obj;
+    event.window = event.obj->window;
+    event.document = event.obj->ctx->document;
+    event.eventdata = NULL;
+    event.eventdatatype = 0;
+    event.intval = 0;
+    event.set = 0;
+    
+    
+}
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+static void drawfunc(
+        GtkDrawingArea *area,
+        cairo_t *cr,
+        int width,
+        int  height,
+        gpointer userdata)
+{
+    ui_drawingarea_draw(userdata, cr, width, height);
+}
+#elif GTK_CHECK_VERSION(3, 0, 0)
+gboolean draw_callback(GtkWidget *widget, cairo_t *cr, UiDrawingArea *drawingarea) {
+    int width = gtk_widget_get_allocated_width(widget);
+    int height = gtk_widget_get_allocated_height(widget);
+    ui_drawingarea_draw(drawingarea, cr, width, height);
+    return FALSE;
+}
+#endif
+
+UIWIDGET ui_drawingarea_create(UiObject *obj, UiDrawingAreaArgs *args) {
     GtkWidget *widget = gtk_drawing_area_new();
+    ui_set_name_and_style(widget, args->name, args->style_class);
     
-    if(f) {
-        UiDrawEvent *event = malloc(sizeof(UiDrawEvent));
-        event->obj = obj;
-        event->callback = f;
-        event->userdata = userdata;
-        ui_connect_draw_handler(widget, event);
-    }
+#if GTK_CHECK_VERSION(4, 0, 0)
+    gtk_drawing_area_set_content_width(GTK_DRAWING_AREA(widget), args->width > 0 ? args->width : 100);
+    gtk_drawing_area_set_content_height(GTK_DRAWING_AREA(widget), args->height > 0 ? args->height : 100);
+#else
+    int w = args->width > 0 ? args->width : 100;
+    int h = args->height > 0 ? args->height : 100;
+    gtk_widget_set_size_request(widget, w, h);
+#endif
+    
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
+    
+    UiDrawingArea *drawingarea = malloc(sizeof(UiDrawingArea));
+    drawingarea->obj = obj;
+    drawingarea->widget = widget;
+    drawingarea->draw = args->draw;
+    drawingarea->drawdata = args->drawdata;
+    drawingarea->onclick = args->onclick;
+    drawingarea->onclickdata = args->onclickdata;
+    drawingarea->onmotion = args->onmotion;
+    drawingarea->onmotiondata = args->onmotiondata;
+    
+#if GTK_CHECK_VERSION(4, 0, 0)
+    gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(widget), drawfunc, drawingarea, NULL);
+#elif GTK_CHECK_VERSION(3, 0, 0)
+    g_signal_connect(
+            widget,
+            "draw",
+            G_CALLBACK(draw_callback),
+            NULL);
+#endif
     
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, widget);
+    g_signal_connect(
+                widget,
+                "destroy",
+                G_CALLBACK(destroy_drawingarea),
+                drawingarea);
     
     return widget;
 }
@@ -142,7 +211,7 @@ void ui_text_setstringl(UiTextLayout *layout, char *str, int len) {
     pango_layout_set_text(layout->layout, str, len);
 }
 
-void ui_text_setfont(UiTextLayout *layout, char *font, int size) {
+void ui_text_setfont(UiTextLayout *layout, const char *font, int size) {
     PangoFontDescription *fontDesc;
     fontDesc = pango_font_description_from_string(font);
     pango_font_description_set_size(fontDesc, size * PANGO_SCALE);
index 67616bd9dd29bc53fea9f7e596d566395966daec..fb8953952188f8d5141f1ddfcf6b88e357008ab6 100644 (file)
 extern "C" {
 #endif
 
-typedef struct UiDrawEvent {
-    ui_drawfunc callback;
-    UiObject    *obj;
-    void        *userdata;
-} UiDrawEvent;
+
+typedef struct UiDrawingArea {
+    UiObject *obj;
+    GtkWidget *widget;
+    ui_drawfunc draw;
+    void *drawdata;
+    ui_callback onclick;
+    void *onclickdata;
+    ui_callback onmotion;
+    void *onmotiondata;
+} UiDrawingArea;
 
 struct UiTextLayout {
     PangoLayout *layout;
 };
 
 // implemented in draw_*.h
-void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event);
+//void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event);
 PangoContext *ui_get_pango_context(UiGraphics *g);
 
 #ifdef __cplusplus
index f53dbd56dafd8be6469495cf371b6b467439209e..3cd76d0b41cd2ba4bd85b44ecb3b67a9c35e7f77 100644 (file)
 #include "button.h"
 #include "menu.h"
 
+#include "../ui/properties.h"
+
 #if GTK_CHECK_VERSION(3, 10, 0)
     
-void ui_fill_headerbar(UiObject *obj, GtkWidget *headerbar) {
+void ui_fill_headerbar(UiObject *obj, GtkWidget *sidebar_headerbar, GtkWidget *main_headerbar, GtkWidget *right_headerbar) {
+    CxList *sidebar_left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_SIDEBAR_LEFT);
+    CxList *sidebar_right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_SIDEBAR_RIGHT);
+    
     CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT);
     CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER);
     CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT);
     
-    ui_headerbar_add_items(obj, headerbar, left_defaults, UI_TOOLBAR_LEFT);
-    ui_headerbar_add_items(obj, headerbar, center_defaults, UI_TOOLBAR_CENTER);
-    
+    CxList *rightpanel_left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_LEFT);
+    CxList *rightpanel_center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_CENTER);
+    CxList *rightpanel_right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_RIGHT);
+
     UiToolbarMenuItem *appmenu = uic_get_appmenu();
-    if(appmenu) {
-        ui_add_headerbar_menu(headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT);
+    const char *appmenu_pos_str = ui_get_property("ui.gtk.window.appmenu.position");
+    int appmenu_pos = UI_TOOLBAR_RIGHT;
+    if(sidebar_headerbar) {
+        appmenu_pos = UI_TOOLBAR_SIDEBAR_RIGHT;
+    } else if(right_headerbar) {
+        appmenu_pos = UI_TOOLBAR_RIGHTPANEL_RIGHT;
+    }
+    if(appmenu_pos_str) {
+        if(!strcmp(appmenu_pos_str, "sidebar") && sidebar_headerbar) {
+            appmenu_pos = UI_TOOLBAR_SIDEBAR_RIGHT;
+        } else if(!strcmp(appmenu_pos_str, "main")) {
+            appmenu_pos = UI_TOOLBAR_RIGHT;
+        } else if(!strcmp(appmenu_pos_str, "rightpanel") && right_headerbar) {
+            appmenu_pos = UI_TOOLBAR_RIGHTPANEL_RIGHT;
+        }
+    }
+    
+    // main toolbar
+    ui_headerbar_add_items(obj, main_headerbar, left_defaults, UI_TOOLBAR_LEFT);
+    ui_headerbar_add_items(obj, main_headerbar, center_defaults, UI_TOOLBAR_CENTER);
+    
+    if(appmenu && appmenu_pos == UI_TOOLBAR_RIGHT) {
+        ui_add_headerbar_menu(main_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT);
+    }
+    ui_headerbar_add_items(obj, main_headerbar, right_defaults, UI_TOOLBAR_RIGHT);
+    
+    // sidebar
+    if(sidebar_headerbar) {
+        // ui_headerbar_add_items pos parameter uses only UI_TOOLBAR_LEFT, UI_TOOLBAR_CENTER, UI_TOOLBAR_RIGHT
+        ui_headerbar_add_items(obj, sidebar_headerbar, sidebar_left_defaults, UI_TOOLBAR_LEFT);
+
+        if(appmenu && appmenu_pos == UI_TOOLBAR_SIDEBAR_RIGHT) {
+            ui_add_headerbar_menu(sidebar_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT);
+        }
+        ui_headerbar_add_items(obj, sidebar_headerbar, sidebar_right_defaults, UI_TOOLBAR_RIGHT);
+    }
+    
+    // right panel
+    if(right_headerbar) {
+        ui_headerbar_add_items(obj, right_headerbar, rightpanel_left_defaults, UI_TOOLBAR_LEFT);
+        ui_headerbar_add_items(obj, right_headerbar, rightpanel_center_defaults, UI_TOOLBAR_CENTER);
+
+        if(appmenu && appmenu_pos == UI_TOOLBAR_RIGHTPANEL_RIGHT) {
+            ui_add_headerbar_menu(right_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT);
+        }
+        ui_headerbar_add_items(obj, right_headerbar, rightpanel_right_defaults, UI_TOOLBAR_RIGHT);
     }
-    ui_headerbar_add_items(obj, headerbar, right_defaults, UI_TOOLBAR_RIGHT);
 }
 
 static void create_item(UiObject *obj, GtkWidget *headerbar, GtkWidget *box, UiToolbarItemI *i, enum UiToolbarPos pos) {
@@ -114,7 +163,7 @@ void ui_add_headerbar_item(
         UiObject *obj,
         enum UiToolbarPos pos)
 {
-    GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.onclick, item->args.onclickdata, 0, FALSE);
+    GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.tooltip, item->args.onclick, item->args.onclickdata, 0, FALSE);
     ui_set_widget_groups(obj->ctx, button, item->args.groups);
     WIDGET_ADD_CSS_CLASS(button, "flat");
     headerbar_add(headerbar, box, button, pos);
@@ -130,7 +179,7 @@ void ui_add_headerbar_toggleitem(
     GtkWidget *button = gtk_toggle_button_new();
     ui_set_widget_groups(obj->ctx, button, item->args.groups);
     WIDGET_ADD_CSS_CLASS(button, "flat");
-    ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0);
+    ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.tooltip, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0);
     headerbar_add(headerbar, box, button, pos);
 }
 
index acea9fed4787d0bea7c19c2d3b950ed677669b7b..93028ea515f7cbfac7aa9f6645b8de64540e5b83 100644 (file)
@@ -58,7 +58,7 @@ extern "C" {
 #endif
 #endif
     
-void ui_fill_headerbar(UiObject *obj, GtkWidget *headerbar);
+void ui_fill_headerbar(UiObject *obj, GtkWidget *sidebar_headerbar, GtkWidget *main_headerbar, GtkWidget *right_headerbar);
 
 void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos);
 
index 425af82ef311bb96c428b9c51ae1b04cf64899bc..e60dbdd46deaca14043eff25cefaa765d7294ea2 100644 (file)
@@ -64,8 +64,6 @@ static gboolean imageviewer_draw(GtkWidget *widget, cairo_t *cr, gpointer userda
 #endif
 
 UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs *args) {
-    UiObject *current = uic_current_obj(obj);
-    
     GtkWidget *drawingarea = gtk_drawing_area_new();
     GtkWidget *toplevel;
     GtkWidget *widget = drawingarea;
@@ -111,7 +109,7 @@ UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs *args) {
     
     g_object_set_data_full(G_OBJECT(drawingarea), "uiimageviewer", imgviewer, (GDestroyNotify)imageviewer_destroy);
     
-    UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_GENERIC);
+    UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC);
     imgviewer->var = var;
     imgviewer->widget = drawingarea;
     
@@ -187,8 +185,9 @@ UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs *args) {
         ui_widget_set_contextmenu(widget, menu);
     }
        
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, toplevel);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, toplevel, &layout);
     
     return toplevel;
 }
index 41ed5d0711db1c86062ce67e616eeb619585a651..e4612bfbfb3ad3ab705657a262ae352295a161e9 100644 (file)
@@ -92,6 +92,11 @@ static UiListView* create_listview(UiObject *obj, UiListArgs *args) {
     tableview->ondropdata = args->ondropdata;
     tableview->selection.count = 0;
     tableview->selection.rows = NULL;
+    tableview->current_row = -1;
+    tableview->getstyle = args->getstyle;
+    tableview->getstyledata = args->getstyledata;
+    tableview->onsave = args->onsave;
+    tableview->onsavedata = args->onsavedata;
     
     if(args->getvalue2) {
         tableview->getvalue = args->getvalue2;
@@ -102,7 +107,7 @@ static UiListView* create_listview(UiObject *obj, UiListArgs *args) {
     } else {
         tableview->getvalue = null_getvalue;
     }
-    
+      
     return tableview;
 }
 
@@ -140,6 +145,58 @@ ObjWrapper* obj_wrapper_new(void* data, int i) {
 
 /* END GObject wrapper for generic pointers */
 
+typedef struct UiCellEntry {
+    GtkEntry *entry;
+    UiListView *listview;
+    char *previous_value;
+    int row;
+    int col;
+} UiCellEntry;
+
+static void cell_save_value(UiCellEntry *data, int restore) {
+    if(data->listview && data->listview->onsave) {
+        UiVar *var = data->listview->var;
+        UiList *list = var ? var->value : NULL;
+        const char *str = ENTRY_GET_TEXT(data->entry);
+        UiCellValue value;
+        value.string = str;
+        value.type = UI_STRING_EDITABLE;
+        if(data->listview->onsave(list, data->row, data->col, &value, data->listview->onsavedata)) {
+            free(data->previous_value);
+            data->previous_value = strdup(str);
+        } else if(restore) {
+            ENTRY_SET_TEXT(data->entry, data->previous_value);
+        }
+    }
+}
+
+static void cell_entry_leave_focus(
+        GtkEventControllerFocus *self,
+        UiCellEntry *data)
+{
+    // TODO: use a different singal to track focus
+    //       we only want to call cell_save_value, when another entry is selected,
+    //       not when the window loses focus or something like that
+    cell_save_value(data, TRUE);
+}
+
+static void cell_entry_destroy(GtkWidget *object, UiCellEntry *data) {
+    free(data->previous_value);
+    free(data);
+}
+
+static void cell_entry_unmap(GtkWidget *w, UiCellEntry *data) {
+    const char *text = ENTRY_GET_TEXT(w);
+    cell_save_value(data, FALSE);
+}
+
+static void cell_entry_activate(
+        GtkEntry *self,
+        UiCellEntry *data)
+{
+    cell_save_value(data, TRUE);
+}
+
 static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
     UiColData *col = userdata;
     UiModel *model = col->listview->model;
@@ -156,26 +213,125 @@ static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item,
     } else if(type == UI_ICON) {
         GtkWidget *image = gtk_image_new();
         gtk_list_item_set_child(item, image);
-    } else {
+    } else if(type == UI_STRING_EDITABLE) {
+        GtkWidget *textfield = gtk_entry_new();
+        gtk_widget_add_css_class(textfield, "ui-table-entry");
+        gtk_list_item_set_child(item, textfield);
+        
+        UiCellEntry *entry_data = malloc(sizeof(UiCellEntry));
+        entry_data->entry = GTK_ENTRY(textfield);
+        entry_data->listview = NULL;
+        entry_data->previous_value = NULL;
+        entry_data->col = 0;
+        entry_data->row = 0;
+        g_object_set_data(G_OBJECT(textfield), "ui_entry_data", entry_data);
+        
+        g_signal_connect(
+                textfield,
+                "destroy",
+                G_CALLBACK(cell_entry_destroy),
+                entry_data);
+        g_signal_connect(
+                textfield,
+                "activate",
+                G_CALLBACK(cell_entry_activate),
+                entry_data);
+        g_signal_connect(
+                textfield,
+                "unmap",
+                G_CALLBACK(cell_entry_unmap),
+                entry_data);
+        
+        GtkEventController *focus_controller = gtk_event_controller_focus_new();
+        g_signal_connect(focus_controller, "leave", G_CALLBACK(cell_entry_leave_focus), entry_data);
+        gtk_widget_add_controller(textfield, focus_controller);
+    } else if(type == UI_BOOL_EDITABLE) {
+        GtkWidget *checkbox = gtk_check_button_new();
+        gtk_list_item_set_child(item, checkbox);
+    }else {
         GtkWidget *label = gtk_label_new(NULL);
         gtk_label_set_xalign(GTK_LABEL(label), 0);
         gtk_list_item_set_child(item, label);
     }
 }
 
-static void column_factory_bind(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
+PangoAttrList* textstyle2pangoattributes(UiTextStyle style) {
+    PangoAttrList *attr = pango_attr_list_new();
+    
+    if(style.text_style & UI_TEXT_STYLE_BOLD) {
+        pango_attr_list_insert(attr, pango_attr_weight_new(PANGO_WEIGHT_BOLD));
+    }
+    if(style.text_style & UI_TEXT_STYLE_ITALIC) {
+        pango_attr_list_insert(attr, pango_attr_style_new(PANGO_STYLE_ITALIC));
+    }
+    if(style.text_style & UI_TEXT_STYLE_UNDERLINE) {
+        pango_attr_list_insert(attr, pango_attr_underline_new(PANGO_UNDERLINE_SINGLE));
+    }
+    
+    // foreground color, convert from 8bit to 16bit
+    guint16 r = (guint16)style.fg.red   * 257;
+    guint16 g = (guint16)style.fg.green * 257;
+    guint16 b = (guint16)style.fg.blue  * 257;
+    pango_attr_list_insert(attr, pango_attr_foreground_new(r, g, b));
+    
+    return attr;
+}
+
+static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, gpointer userdata) {
     UiColData *col = userdata;
     UiList *list = col->listview->var ? col->listview->var->value : NULL;
     UiListView *listview = col->listview;
-    
+     
     ObjWrapper *obj = gtk_list_item_get_item(item);
     UiModel *model = col->listview->model;
     UiModelType type = model->types[col->model_column];
     
+    // cache the GtkListItem
+    CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
+    UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
+    if(row) {
+        if(row->items[col->model_column] == NULL) {
+            row->bound++;
+        }
+    } else {
+        row = calloc(1, sizeof(UiRowItems) + listview->numcolumns * sizeof(GtkListItem*));
+        cxMapPut(listview->bound_rows, row_key, row);
+        row->bound = 1;
+    }
+    row->items[col->model_column] = item;
+    
     UiBool freevalue = FALSE;
     void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue);
     GtkWidget *child = gtk_list_item_get_child(item);
     
+    PangoAttrList *attributes = NULL;
+    UiTextStyle style = { 0, 0 };
+    if(listview->getstyle) { 
+        // query current row style, if it wasn't already queried
+        if(obj->i != listview->current_row) {
+            listview->current_row = obj->i;
+            listview->row_style = (UiTextStyle){ 0, 0 };
+            listview->apply_row_style = listview->getstyle(list, obj->data, obj->i, -1, listview->getstyledata, &listview->row_style);
+            style = listview->row_style;
+            if(listview->apply_row_style) {
+                pango_attr_list_unref(listview->current_row_attributes);
+                listview->current_row_attributes = textstyle2pangoattributes(style);
+            }
+        }
+        
+        int style_col = col->data_column;
+        if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
+            style_col++; // col->data_column is the icon, we need the next col for the label
+        }
+        
+        // get the column style
+        if(listview->getstyle(list, obj->data, obj->i, style_col, listview->getstyledata, &style)) {
+            attributes = textstyle2pangoattributes(style);
+        } else if(listview->apply_row_style) {
+            attributes = listview->current_row_attributes;
+        }
+    }    
+    
     switch(type) {
         case UI_STRING_FREE: {
             freevalue = TRUE;
@@ -185,6 +341,7 @@ static void column_factory_bind(GtkListItemFactory *factory, GtkListItem *item,
             if(freevalue) {
                 free(data);
             }
+            gtk_label_set_attributes(GTK_LABEL(child), attributes);
             break;
         }
         case UI_INTEGER: {
@@ -192,6 +349,7 @@ static void column_factory_bind(GtkListItemFactory *factory, GtkListItem *item,
             char buf[32];
             snprintf(buf, 32, "%d", (int)intvalue);
             gtk_label_set_label(GTK_LABEL(child), buf);
+            gtk_label_set_attributes(GTK_LABEL(child), attributes);
             break;
         }
         case UI_ICON: {
@@ -217,14 +375,61 @@ static void column_factory_bind(GtkListItemFactory *factory, GtkListItem *item,
             }
             if(data2 && label) {
                 gtk_label_set_label(GTK_LABEL(label), data2);
+                gtk_label_set_attributes(GTK_LABEL(label), attributes);
             }
             if(freevalue) {
                 free(data2);
             }
             break;
         }
+        case UI_STRING_EDITABLE: {
+            UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data");
+            if(entry) {
+                entry->listview = col->listview;
+                entry->row = obj->i;
+                entry->col = col->data_column;
+                entry->previous_value = strdup(data);
+            }
+            ENTRY_SET_TEXT(child, data);
+            break;
+        }
+        case UI_BOOL_EDITABLE: {
+            intptr_t i = (intptr_t)data;
+            gtk_check_button_set_active(GTK_CHECK_BUTTON(child), (gboolean)i);
+            break;
+        }
+    }
+    
+    if(attributes != listview->current_row_attributes) {
+        pango_attr_list_unref(attributes);
+    }
+}
+
+static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) {
+    ObjWrapper *obj = gtk_list_item_get_item(item);
+    UiListView *listview = col->listview;
+    CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
+    UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
+    if(row) {
+        row->items[col->model_column] = NULL;
+        row->bound--;
+        if(row->bound == 0) {
+            cxMapRemove(listview->bound_rows, row_key);
+        }
+    } // else: should not happen
+    
+    GtkWidget *child = gtk_list_item_get_child(item);
+    UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data");
+    if(entry) {
+        cell_save_value(entry, FALSE);
+        entry->listview = NULL;
+        free(entry->previous_value);
+        entry->previous_value = NULL;
+    } else if(GTK_IS_CHECK_BUTTON(child)) {
+        
     }
 }
+    
 
 static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) {
     GtkSelectionModel *selection_model;
@@ -240,8 +445,6 @@ static GtkSelectionModel* create_selection_model(UiListView *listview, GListStor
 }
 
 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     // to simplify things and share code with ui_table_create, we also
     // use a UiModel for the listview
     UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
@@ -253,10 +456,14 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
         listview->getvalue = str_getvalue;
     }
     
+    listview->numcolumns = 1;
     listview->columns = malloc(sizeof(UiColData));
     listview->columns->listview = listview;
     listview->columns->data_column = 0;
     listview->columns->model_column = 0;
+    
+    listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
+    listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
      
     GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
     g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
@@ -265,7 +472,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
     GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection);
     GtkWidget *view = gtk_list_view_new(GTK_SELECTION_MODEL(selection_model), factory);
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
     
     // init listview
     listview->widget = view;
@@ -314,19 +521,26 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
             GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS  
     SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, scroll_area);
+    if(args->width > 0 || args->height > 0) {
+        int width = args->width;
+        int height = args->height;
+        if(width == 0) {
+            width = -1;
+        }
+        if(height == 0) {
+            height = -1;
+        }
+        gtk_widget_set_size_request(scroll_area, width, height);
+    }
     
-    // ct->current should point to view, not scroll_area, to make it possible
-    // to add a context menu
-    current->container->current = view;
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, scroll_area, &layout);
     
     return scroll_area;
 }
 
 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     // to simplify things and share code with ui_tableview_create, we also
     // use a UiModel for the listview
     UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
@@ -339,19 +553,26 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
         listview->getvalue = str_getvalue;
     }
     
+    listview->numcolumns = 1;
     listview->columns = malloc(sizeof(UiColData));
     listview->columns->listview = listview;
     listview->columns->data_column = 0;
     listview->columns->model_column = 0;
     
+    listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
+    listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
+    
     GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
     g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
     g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
     
     GtkWidget *view = gtk_drop_down_new(G_LIST_MODEL(ls), NULL);
     gtk_drop_down_set_factory(GTK_DROP_DOWN(view), factory);
+    if(args->width > 0) {
+        gtk_widget_set_size_request(view, args->width, -1);
+    }
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
     
     // init listview
     listview->widget = view;
@@ -386,8 +607,10 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
     }
     
     // add widget to parent 
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, view);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, view, &layout);
+    
     return view;
 }
 
@@ -401,8 +624,6 @@ void ui_combobox_select(UIWIDGET dropdown, int index) {
 }
 
 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
     //g_list_store_append(ls, v1);
     
@@ -413,7 +634,7 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args->multiselection);
     GtkWidget *view = gtk_column_view_new(GTK_SELECTION_MODEL(selection_model));
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
     
     // init tableview
     tableview->widget = view;
@@ -432,6 +653,10 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     int columns = model ? model->columns : 0;
     
     tableview->columns = calloc(columns, sizeof(UiColData));
+    tableview->numcolumns = columns;
+    
+    tableview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
+    tableview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
     
     int addi = 0;
     for(int i=0;i<columns;i++) {
@@ -490,12 +715,21 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
             GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS  
     SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, scroll_area);
+    if(args->width > 0 || args->height > 0) {
+        int width = args->width;
+        int height = args->height;
+        if(width == 0) {
+            width = -1;
+        }
+        if(height == 0) {
+            height = -1;
+        }
+        gtk_widget_set_size_request(scroll_area, width, height);
+    }
     
-    // ct->current should point to view, not scroll_area, to make it possible
-    // to add a context menu
-    current->container->current = view;
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, scroll_area, &layout);
     
     return scroll_area;
 }
@@ -631,21 +865,20 @@ void ui_listview_update2(UiList *list, int i) {
     } else {
         void *value = list->get(list, i);
         if(value) {
-            ObjWrapper *obj = obj_wrapper_new(value, i);
-            UiListSelection sel = list->getselection(list);
-            // TODO: if index i is selected, the selection is lost
-            // is it possible to update the item without removing it?
-            // workaround: save selection and reapply it
-            int count = g_list_model_get_n_items(G_LIST_MODEL(view->liststore));
-            if(count <= i) {
-                g_list_store_splice(view->liststore, i, 0, (void **)&obj, 1);
-            } else {
-                g_list_store_splice(view->liststore, i, 1, (void **)&obj, 1);
+            ObjWrapper *obj = g_list_model_get_item(G_LIST_MODEL(view->liststore), i);
+            if(obj) {
+                obj->data = value;
             }
-            if(sel.count > 0) {
-                list->setselection(list, sel);
+            
+            CxHashKey row_key = cx_hash_key(&i, sizeof(int));
+            UiRowItems *row = cxMapGet(view->bound_rows, row_key);
+            if(row) {
+                for(int c=0;c<view->numcolumns;c++) {
+                    if(row->items[c] != NULL) {
+                        column_factory_bind(NULL, row->items[c], &view->columns[c]);
+                    }
+                }
             }
-            ui_listselection_free(sel);
         }
     }
 }
@@ -709,14 +942,40 @@ void ui_combobox_setselection(UiList *list, UiListSelection selection) {
 
 static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIter *iter, UiList *list, void *elm, int row) {
     UiModel *model = listview->model;
+    ui_getstylefunc getstyle = listview->getstyle;
+    
+    // get the row style
+    UiBool style_set = FALSE;
+    UiTextStyle style = { 0, 0 };
+    if(getstyle) {
+        style_set = getstyle(list, elm, row, -1, listview->getstyledata, &style);
+    }
+    
     // set column values
     int c = 0;
     for(int i=0;i<model->columns;i++,c++) {
         UiBool freevalue = FALSE;
         void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue);
+        
+        UiModelType type = model->types[i];
+        
+        if(getstyle) {
+            // in case the column is icon+text, only get a style for the text column
+            int style_col = c;
+            if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
+                style_col++;
+            }
+            
+            // Get the individual column style 
+            // The column style overrides the row style, however if no column style
+            // is provided, we stick with the row style
+            if(getstyle(list, elm, row, style_col, listview->getstyledata, &style)) {
+                style_set = TRUE;
+            }
+        }
 
         GValue value = G_VALUE_INIT;
-        switch(model->types[i]) {
+        switch(type) {
             case UI_STRING_FREE: {
                 freevalue = TRUE;
             } 
@@ -789,13 +1048,64 @@ static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIt
         }
 
         gtk_list_store_set_value(store, iter, c, &value);
+        
+        if(style_set) {
+            int soff = listview->style_offset + i*6;
+            
+            GValue style_set_value = G_VALUE_INIT;
+            g_value_init(&style_set_value, G_TYPE_BOOLEAN);
+            g_value_set_boolean(&style_set_value, TRUE);
+            gtk_list_store_set_value(store, iter, soff, &style_set_value);
+            
+            GValue style_weight_value = G_VALUE_INIT;
+            g_value_init(&style_weight_value, G_TYPE_INT);
+            if(style.text_style & UI_TEXT_STYLE_BOLD) {
+                g_value_set_int(&style_weight_value, 600);
+            } else {
+                g_value_set_int(&style_weight_value, 400);
+            }
+            gtk_list_store_set_value(store, iter, soff + 1, &style_weight_value);
+            
+            GValue style_underline_value = G_VALUE_INIT;
+            g_value_init(&style_underline_value, G_TYPE_INT);
+            if(style.text_style & UI_TEXT_STYLE_UNDERLINE) {
+                g_value_set_int(&style_underline_value, PANGO_UNDERLINE_SINGLE);
+            } else {
+                g_value_set_int(&style_underline_value, PANGO_UNDERLINE_NONE);
+            }
+            gtk_list_store_set_value(store, iter, soff + 2, &style_underline_value);
+            
+            GValue style_italic_value = G_VALUE_INIT;
+            g_value_init(&style_italic_value, G_TYPE_INT);
+            if(style.text_style & UI_TEXT_STYLE_ITALIC) {
+                g_value_set_int(&style_italic_value, PANGO_STYLE_ITALIC);
+            } else {
+                g_value_set_int(&style_italic_value, PANGO_STYLE_NORMAL);
+            }
+            gtk_list_store_set_value(store, iter, soff + 3, &style_italic_value);
+            
+            GValue style_fgset_value = G_VALUE_INIT;
+            g_value_init(&style_fgset_value, G_TYPE_BOOLEAN);
+            g_value_set_boolean(&style_fgset_value, style.fg_set);
+            gtk_list_store_set_value(store, iter, soff + 4, &style_fgset_value);
+            
+            if(style.fg_set) {
+                char buf[8];
+                snprintf(buf, 8, "#%02X%02X%02X", (int)style.fg.red, (int)style.fg.green, (int)style.fg.blue);
+                
+                GValue style_fg_value = G_VALUE_INIT;
+                g_value_init(&style_fg_value, G_TYPE_STRING);
+                g_value_set_string(&style_fg_value, buf);
+                gtk_list_store_set_value(store, iter, soff + 5, &style_fg_value);
+            }
+        }
     }
 }
 
 static GtkListStore* create_list_store(UiListView *listview, UiList *list) {
     UiModel *model = listview->model;
     int columns = model->columns;
-    GType types[2*columns];
+    GType *types = calloc(columns*8, sizeof(GType));
     int c = 0;
     for(int i=0;i<columns;i++,c++) {
         switch(model->types[i]) {
@@ -810,8 +1120,18 @@ static GtkListStore* create_list_store(UiListView *listview, UiList *list) {
             }
         }
     }
+    int s = 0;
+    for(int i=0;i<columns;i++) {
+        types[listview->style_offset+s] = G_TYPE_BOOLEAN; s++; // *-set
+        types[listview->style_offset+s] = G_TYPE_INT; s++;     // weight
+        types[listview->style_offset+s] = G_TYPE_INT; s++;     // underline
+        types[listview->style_offset+s] = G_TYPE_INT; s++;     // style
+        types[listview->style_offset+s] = G_TYPE_BOOLEAN; s++; // foreground-set
+        types[listview->style_offset+s] = G_TYPE_STRING; s++;  // foreground
+    }
     
-    GtkListStore *store = gtk_list_store_newv(c, types);
+    GtkListStore *store = gtk_list_store_newv(c+s, types);
+    free(types);
     
     if(list) {
         void *elm = list->first(list);
@@ -833,8 +1153,6 @@ static GtkListStore* create_list_store(UiListView *listview, UiList *list) {
 
 
 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     // create treeview
     GtkWidget *view = gtk_tree_view_new();
     ui_set_name_and_style(view, args->name, args->style_class);
@@ -857,6 +1175,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
     UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
     
     UiListView *listview = create_listview(obj, args);
+    listview->style_offset = 1;
     if(!args->getvalue && !args->getvalue2) {
         listview->getvalue = str_getvalue;
     }
@@ -867,7 +1186,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
                 G_CALLBACK(ui_listview_destroy),
                 listview);
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
     
     // init listview
     listview->widget = view;
@@ -927,12 +1246,21 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
             GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS  
     SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, scroll_area);
+    if(args->width > 0 || args->height > 0) {
+        int width = args->width;
+        int height = args->height;
+        if(width == 0) {
+            width = -1;
+        }
+        if(height == 0) {
+            height = -1;
+        }
+        gtk_widget_set_size_request(scroll_area, width, height);
+    }
     
-    // ct->current should point to view, not scroll_area, to make it possible
-    // to add a context menu
-    current->container->current = view;
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, scroll_area, &layout);
     
     return scroll_area;
 }
@@ -949,18 +1277,28 @@ void ui_combobox_select(UIWIDGET dropdown, int index) {
 }
 
 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     // create treeview
     GtkWidget *view = gtk_tree_view_new();
     
     UiModel *model = args->model;
     int columns = model ? model->columns : 0;
     
+    // find the last data column index
     int addi = 0;
-    for(int i=0;i<columns;i++) {
+    int style_offset = 0;
+    int i = 0;
+    for(;i<columns;i++) {
+        if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) {
+            addi++;
+        }
+    }
+    style_offset = i+addi;
+    
+    // create columns and init cell renderers
+    addi = 0;
+    for(i=0;i<columns;i++) {
         GtkTreeViewColumn *column = NULL;
-        if(model->types[i] == UI_ICON_TEXT) {
+        if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) {
             column = gtk_tree_view_column_new();
             gtk_tree_view_column_set_title(column, model->titles[i]);
             
@@ -971,8 +1309,21 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
             gtk_tree_view_column_pack_start(column, iconrenderer, FALSE);
             
             
-            gtk_tree_view_column_add_attribute(column, iconrenderer, "pixbuf", i);
-            gtk_tree_view_column_add_attribute(column, textrenderer, "text", i+1);
+            gtk_tree_view_column_add_attribute(column, iconrenderer, "pixbuf", addi + i);
+            gtk_tree_view_column_add_attribute(column, textrenderer, "text", addi + i+1);
+            
+            if(args->getstyle) {
+                int soff = style_offset + i*6;
+                gtk_tree_view_column_add_attribute(column, textrenderer, "weight-set", soff);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "underline-set", soff);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "style-set", soff);
+                
+                gtk_tree_view_column_add_attribute(column, textrenderer, "weight", soff + 1);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "underline", soff + 2);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "style", soff + 3);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "foreground-set", soff + 4);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "foreground", soff + 5);
+            }
             
             addi++;
         } else if (model->types[i] == UI_ICON) {
@@ -984,13 +1335,26 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
                 i + addi,
                 NULL);
         } else {
-            GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+            GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
             column = gtk_tree_view_column_new_with_attributes(
                 model->titles[i],
-                renderer,
+                textrenderer,
                 "text",
                 i + addi,
                 NULL);
+            
+            if(args->getstyle) {
+                int soff = style_offset + i*6;
+                gtk_tree_view_column_add_attribute(column, textrenderer, "weight-set", soff);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "underline-set", soff);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "style-set", soff);
+                
+                gtk_tree_view_column_add_attribute(column, textrenderer, "weight", soff + 1);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "underline", soff + 2);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "style", soff + 3);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "foreground-set", soff + 4);
+                gtk_tree_view_column_add_attribute(column, textrenderer, "foreground", soff + 5);
+            }
         }
         
         int colsz = model->columnsize[i];
@@ -1011,7 +1375,7 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     
 #endif
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
     
     //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL);
     //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL);
@@ -1019,6 +1383,8 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     // add TreeView as observer to the UiList to update the TreeView if the
     // data changes
     UiListView *tableview = create_listview(obj, args);
+    tableview->widget = view;
+    tableview->style_offset = style_offset;
     g_signal_connect(
                 view,
                 "destroy",
@@ -1082,6 +1448,18 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
             GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS  
     SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
     
+    if(args->width > 0 || args->height > 0) {
+        int width = args->width;
+        int height = args->height;
+        if(width == 0) {
+            width = -1;
+        }
+        if(height == 0) {
+            height = -1;
+        }
+        gtk_widget_set_size_request(scroll_area, width, height);
+    }
+    
     if(args->contextmenu) {
         UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, scroll_area);
 #if GTK_MAJOR_VERSION >= 4
@@ -1091,12 +1469,9 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
 #endif
     }
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, scroll_area);
-    
-    // ct->current should point to view, not scroll_area, to make it possible
-    // to add a context menu
-    current->container->current = view;
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, scroll_area, &layout);
     
     return scroll_area;
 }
@@ -1142,18 +1517,20 @@ void ui_listview_setselection(UiList *list, UiListSelection selection) {
 /* --------------------------- ComboBox ---------------------------  */
 
 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     GtkWidget *combobox = gtk_combo_box_new();
+    if(args->width > 0) {
+        gtk_widget_set_size_request(combobox, args->width, -1);
+    }
     
     ui_set_name_and_style(combobox, args->name, args->style_class);
     ui_set_widget_groups(obj->ctx, combobox, args->groups);
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, combobox);
-    current->container->current = combobox;
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, combobox, &layout);
     
     UiListView *listview = create_listview(obj, args);
     listview->widget = combobox;
+    listview->style_offset = 1;
     listview->model = ui_model(obj->ctx, UI_STRING, "", -1);
     g_signal_connect(
                 combobox,
@@ -1161,7 +1538,7 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
                 G_CALLBACK(ui_listview_destroy),
                 listview);
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
     UiList *list = var ? var->value : NULL;
     GtkListStore *listmodel = create_list_store(listview, list);
     if(var) {
@@ -1676,6 +2053,8 @@ void ui_listview_destroy(GtkWidget *w, UiListView *v) {
     }
 #if GTK_CHECK_VERSION(4, 10, 0)
     free(v->columns);
+    pango_attr_list_unref(v->current_row_attributes);
+    cxMapFree(v->bound_rows);
 #endif
     free(v->selection.rows);
     free(v);
@@ -1684,6 +2063,18 @@ void ui_listview_destroy(GtkWidget *w, UiListView *v) {
 
 /* ------------------------------ Source List ------------------------------ */
 
+static ui_sourcelist_update_func sourcelist_update_finished_callback;
+
+void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb) {
+    sourcelist_update_finished_callback = cb;
+}
+
+static void ui_sourcelist_update_finished(void) {
+    if(sourcelist_update_finished_callback) {
+        sourcelist_update_finished_callback();
+    }
+}
+
 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) {
     cxListFree(v->sublists);
     free(v);
@@ -1710,7 +2101,7 @@ static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpo
     if(sublist->separator) {
         GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
         gtk_list_box_row_set_header(row, separator);
-    } else if(sublist->header) {
+    } else if(sublist->header && !listbox->header_is_item) {
         GtkWidget *header = gtk_label_new(sublist->header);
         gtk_widget_set_halign(header, GTK_ALIGN_START);
         if(row == listbox->first_row) {
@@ -1773,8 +2164,6 @@ static void add_sublist(UiListBox *uilistbox, CxList *sublists, UiSubList *subli
 }
 
 UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
 #ifdef UI_GTK3
     GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL);
 #else
@@ -1793,12 +2182,14 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
     
     ui_set_name_and_style(listbox, args->name, args->style_class);
     ui_set_widget_groups(obj->ctx, listbox, args->groups);
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, scroll_area);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, scroll_area, &layout);
     
     UiListBox *uilistbox = malloc(sizeof(UiListBox));
     uilistbox->obj = obj;
     uilistbox->listbox = GTK_LIST_BOX(listbox);
+    uilistbox->header_is_item = args->header_is_item;
     uilistbox->getvalue = args->getvalue;
     uilistbox->getvaluedata = args->getvaluedata;
     uilistbox->onactivate = args->onactivate;
@@ -1827,7 +2218,7 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
         // fill items
         ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists));
     } else {
-        UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST);
+        UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST);
         if(var) {
             UiList *list = var->value;
             list->obj = uilistbox;
@@ -1920,9 +2311,11 @@ void ui_listbox_update(UiListBox *listbox, int from, int to) {
         ui_listbox_update_sublist(listbox, sublist, pos);
         pos += sublist->numitems;
     }
+    
+    ui_sourcelist_update_finished();
 }
 
-static void listbox_button_clicked(GtkWidget *widget, UiEventDataExt *data) {
+static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) {
     UiListBoxSubList *sublist = data->customdata0;
     
     UiSubListEventData eventdata;
@@ -1945,21 +2338,41 @@ static void listbox_button_clicked(GtkWidget *widget, UiEventDataExt *data) {
     if(data->callback2) {
         data->callback2(&event, data->userdata2);
     }
+    
+    if(data->customdata3) {
+        UIMENU menu = data->customdata3;
+        g_object_set_data(G_OBJECT(button), "ui-button-popup", menu);
+        gtk_popover_popup(GTK_POPOVER(menu));
+    }
 }
 
-static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
+#if GTK_CHECK_VERSION(3, 0, 0)
+static void button_popover_closed(GtkPopover *popover, GtkWidget *button) {
+    g_object_set_data(G_OBJECT(button), "ui-button-popup", NULL);
+    if(g_object_get_data(G_OBJECT(button), "ui-button-invisible")) {
+        g_object_set_data(G_OBJECT(button), "ui-button-invisible", NULL);
+        gtk_widget_set_visible(button, FALSE);
+    }
+}
+#endif
+
+static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
+    UiBool is_header = index < 0;
+    
     GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
     if(item->icon) {
         GtkWidget *icon = ICON_IMAGE(item->icon);
         BOX_ADD(hbox, icon);
     }
     GtkWidget *label = gtk_label_new(item->label);
+    if(is_header) {
+        WIDGET_ADD_CSS_CLASS(label, "ui-listbox-header-row");
+    }
     gtk_widget_set_halign(label, GTK_ALIGN_START);
     BOX_ADD_EXPAND(hbox, label);
     if(item->badge) {
         
     }
-    GtkWidget *row = gtk_list_box_row_new();
     LISTBOX_ROW_SET_CHILD(row, hbox);
     
     // signals 
@@ -1975,6 +2388,9 @@ static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *subli
     event->userdata2 = listbox->onbuttonclickdata;
     event->value0 = index;
     
+    // TODO: semi-memory leak when listbox_fill_row is called again for the same row
+    // each row update will create a new UiEventDataExt object and a separate destroy handler
+    
     g_signal_connect(
             row,
             "destroy",
@@ -1987,7 +2403,7 @@ static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *subli
     if(item->badge) {
         GtkWidget *badge = gtk_label_new(item->badge);
         WIDGET_ADD_CSS_CLASS(badge, "ui-badge");
-#if GTK_CHECK_VERSION(4, 0, 0)
+#if GTK_CHECK_VERSION(3, 14, 0)
         gtk_widget_set_valign(badge, GTK_ALIGN_CENTER);
         BOX_ADD(hbox, badge);
 #else
@@ -2009,11 +2425,107 @@ static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *subli
                 G_CALLBACK(listbox_button_clicked),
                 event
                 );
+        gtk_widget_set_visible(button, FALSE);
+        
+        g_object_set_data(G_OBJECT(row), "ui-listbox-row-button", button);
+        
+        // menu
+        if(item->button_menu) {
+            UIMENU menu = ui_contextmenu_create(item->button_menu, listbox->obj, button);
+            event->customdata3 = menu;
+            g_signal_connect(menu, "closed", G_CALLBACK(button_popover_closed), button);
+            ui_menubuilder_unref(item->button_menu);
+        }
+    }
+}
+
+static void update_sublist_item(UiListBox *listbox, UiListBoxSubList *sublist, int index) {
+    int header_row = listbox->header_is_item && sublist->header ? 1 : 0;
+    GtkListBoxRow *row = gtk_list_box_get_row_at_index(listbox->listbox, sublist->startpos + index + header_row);
+    if(!row) {
+        return;
     }
+    UiList *list = sublist->var->value;
+    if(!list) {
+        return;
+    }
+    
+    void *elm = list->get(list, index);
+    UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
+    if(listbox->getvalue) {
+        listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata);
+    } else {
+        item.label = strdup(elm);
+    }
+    
+    LISTBOX_ROW_REMOVE_CHILD(row);
     
-    return row;
+    listbox_fill_row(listbox, GTK_WIDGET(row), sublist, &item, index);
+    
+     // cleanup
+    free(item.label);
+    free(item.icon);
+    free(item.button_label);
+    free(item.button_icon);
+    free(item.badge);
+}
+
+static void listbox_row_on_enter(GtkWidget *row) {
+    GtkWidget *button = g_object_get_data(G_OBJECT(row), "ui-listbox-row-button");
+    if(button) {
+        gtk_widget_set_visible(button, TRUE);
+    }
 }
 
+static void listbox_row_on_leave(GtkWidget *row) {
+    GtkWidget *button = g_object_get_data(G_OBJECT(row), "ui-listbox-row-button");
+    if(button) {  
+        if(!g_object_get_data(G_OBJECT(button), "ui-button-popup")) {
+            gtk_widget_set_visible(button, FALSE);
+        } else {
+            g_object_set_data(G_OBJECT(button), "ui-button-invisible", (void*)1);
+        }
+    }
+}
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+static void listbox_row_enter(
+        GtkEventControllerMotion* self,
+        gdouble x,
+        gdouble y,
+        GtkWidget *row)
+{
+    listbox_row_on_enter(row);
+}
+
+static void listbox_row_leave(
+        GtkEventControllerMotion* self,
+        GtkWidget *row)
+{
+    listbox_row_on_leave(row);
+}
+#else
+static gboolean listbox_row_enter(
+        GtkWidget *row,
+        GdkEventCrossing event,
+        gpointer user_data)
+{
+    listbox_row_on_enter(row);
+    return FALSE;
+}
+
+
+static gboolean listbox_row_leave(
+        GtkWidget *row,
+        GdkEventCrossing *event,
+        gpointer user_data)
+{
+    listbox_row_on_leave(row);
+    return FALSE;
+}
+
+#endif
+
 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) {
     // clear sublist
     CxIterator r = cxListIterator(sublist->widgets);
@@ -2033,22 +2545,32 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si
         return;
     }
     
-    size_t index = 0;
+    int index = 0;
     void *elm = list->first(list);
+    void *first = elm;
     
-    if(!elm && sublist->header) {
+    if(sublist->header && !listbox->header_is_item && !elm) {
         // empty row for header
         GtkWidget *row = gtk_list_box_row_new();
         cxListAdd(sublist->widgets, row);
         g_object_set_data(G_OBJECT(row), "ui_listbox", listbox);
         g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist);
-        intptr_t rowindex = listbox_insert_index + index;
-        g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex);
+        //intptr_t rowindex = listbox_insert_index + index;
+        //g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex);
         gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index);
         sublist->numitems = 1;
         return;
     }
     
+    int first_index = 0;
+    int header_row = 0;
+    if(listbox->header_is_item && sublist->header) {
+        index = -1;
+        first_index = -1;
+        header_row = 1;
+        elm = sublist->header;
+    }
+    
     while(elm) {
         UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
         if(listbox->getvalue) {
@@ -2057,9 +2579,25 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si
             item.label = strdup(elm);
         }
         
+        if(item.label == NULL && index == -1 && sublist->header) {
+            item.label = strdup(sublist->header);
+        }
+        
         // create listbox item
-        GtkWidget *row = create_listbox_row(listbox, sublist, &item, (int)index);
-        if(index == 0) {
+        GtkWidget *row = gtk_list_box_row_new();
+#if GTK_CHECK_VERSION(4, 0, 0)
+        GtkEventController *motion_controller = gtk_event_controller_motion_new();
+        gtk_widget_add_controller(GTK_WIDGET(row), motion_controller);
+        g_signal_connect(motion_controller, "enter", G_CALLBACK(listbox_row_enter), row);
+        g_signal_connect(motion_controller, "leave", G_CALLBACK(listbox_row_leave), row);
+#else
+        gtk_widget_set_events(GTK_WIDGET(row), GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+        g_signal_connect(row, "enter-notify-event", G_CALLBACK(listbox_row_enter), NULL);
+        g_signal_connect(row, "leave-notify-event", G_CALLBACK(listbox_row_leave), NULL);
+#endif
+        
+        listbox_fill_row(listbox, row, sublist, &item, index);
+        if(index == first_index) {
             // first row in the sublist, set ui_listbox data to the row
             // which is then used by the headerfunc
             g_object_set_data(G_OBJECT(row), "ui_listbox", listbox);
@@ -2070,9 +2608,9 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si
                 listbox->first_row = GTK_LIST_BOX_ROW(row);
             }
         }
-        intptr_t rowindex = listbox_insert_index + index;
-        g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex);
-        gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index);
+        //intptr_t rowindex = listbox_insert_index + index;
+        //g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex);
+        gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index + header_row);
         cxListAdd(sublist->widgets, row);
         
         // cleanup
@@ -2083,7 +2621,7 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si
         free(item.badge);
         
         // next row
-        elm = list->next(list);
+        elm = index >= 0 ? list->next(list) : first;
         index++;
     }
     
@@ -2092,14 +2630,19 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si
 
 void ui_listbox_list_update(UiList *list, int i) {
     UiListBoxSubList *sublist = list->obj;
-    ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos);
-    size_t pos = 0;
-    CxIterator it = cxListIterator(sublist->listbox->sublists);
-    cx_foreach(UiListBoxSubList *, ls, it) {
-        ls->startpos = pos;
-        pos += sublist->numitems;
+    if(i < 0) {
+        ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos);
+        size_t pos = 0;
+        CxIterator it = cxListIterator(sublist->listbox->sublists);
+        cx_foreach(UiListBoxSubList *, ls, it) {
+            ls->startpos = pos;
+            pos += ls->numitems;
+        }
+    } else {
+        update_sublist_item(sublist->listbox, sublist, i);
     }
     
+    ui_sourcelist_update_finished();
 }
 
 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
@@ -2114,7 +2657,7 @@ void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user
     eventdata.sublist_index = sublist->index;
     eventdata.row_index = data->value0;
     eventdata.sublist_userdata = sublist->userdata;
-    eventdata.row_data = eventdata.list->get(eventdata.list, eventdata.row_index);
+    eventdata.row_data = eventdata.row_index >= 0 ? eventdata.list->get(eventdata.list, eventdata.row_index) : NULL;
     eventdata.event_data = data->customdata2;
     
     UiEvent event;
index 7878b4f67fcbaac650f75d9ecde3655cce105226..06f7fac6d7db4fe9e11640002afb53c3f4fae5b5 100644 (file)
@@ -40,6 +40,13 @@ extern "C" {
     
 typedef struct UiColData UiColData;
 
+#if GTK_CHECK_VERSION(4, 10, 0)
+typedef struct UiRowItems {
+    int bound;
+    GtkListItem *items[];
+} UiRowItems;
+#endif
+
 typedef struct UiListView {
     UiObject          *obj;
     GtkWidget         *widget;
@@ -47,12 +54,22 @@ typedef struct UiListView {
     UiModel           *model;
     ui_getvaluefunc2  getvalue;
     void              *getvaluedata;
+    ui_getstylefunc   getstyle;
+    void              *getstyledata;
     char              **elements;
     size_t            nelm;
+    int               current_row;
+    UiTextStyle       row_style;
+    UiBool            apply_row_style;
 #if GTK_CHECK_VERSION(4, 10, 0)
+    CxMap             *bound_rows;
     GListStore        *liststore;
     GtkSelectionModel *selectionmodel;
     UiColData         *columns;
+    int               numcolumns;
+    PangoAttrList     *current_row_attributes;
+#else
+    int               style_offset;
 #endif
     ui_callback       onactivate;
     void              *onactivatedata;
@@ -64,6 +81,8 @@ typedef struct UiListView {
     void              *ondragcompletedata;
     ui_callback       ondrop;
     void              *ondropdata;
+    ui_list_savefunc  onsave;
+    void              *onsavedata;
     UiListSelection   selection;
     
 } UiListView;
@@ -107,6 +126,7 @@ struct UiListBox {
     ui_callback              onbuttonclick;
     void                     *onbuttonclickdata;
     GtkListBoxRow            *first_row;
+    UiBool                   header_is_item;
 };
 
 
index 521bf91d9f8f05e24ea77a5407d6133599b409d4..c399d85726579a886daaf5731abc25264ceed6d1 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "menu.h"
 #include "toolkit.h"
+#include "widget.h"
 #include "../common/context.h"
 #include "../common/menu.h"
 #include "../common/types.h"
@@ -42,6 +43,7 @@
 
 #include <cx/linked_list.h>
 #include <cx/array_list.h>
+#include <cx/printf.h>
 
 #if GTK_MAJOR_VERSION <= 3
 
@@ -135,44 +137,6 @@ void add_menuitem_widget(GtkWidget *parent, int index, UiMenuItemI *item, UiObje
     }
 }
 
-/*
-void add_menuitem_st_widget(
-        GtkWidget *parent,
-        int index,
-        UiMenuItemI *item,
-        UiObject *obj)
-{
-    UiStMenuItem *i = (UiStMenuItem*)item;
-    
-    GtkWidget *widget = gtk_image_menu_item_new_from_stock(i->stockid, obj->ctx->accel_group);
-    
-    if(i->callback != NULL) {
-        UiEventData *event = malloc(sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = i->userdata;
-        event->callback = i->callback;
-        event->value = 0;
-
-        g_signal_connect(
-                widget,
-                "activate",
-                G_CALLBACK(ui_menu_event_wrapper),
-                event);
-        g_signal_connect(
-                widget,
-                "destroy",
-                G_CALLBACK(ui_destroy_userdata),
-                event);
-    }
-    
-    gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget);
-    
-    if(i->groups) {
-        uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, i->groups);
-    }
-}
-*/
-
 void add_menuseparator_widget(
         GtkWidget *parent,
         int index,
@@ -214,25 +178,6 @@ void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *
     // TODO
 }
 
-/*
-void add_checkitemnv_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
-    UiCheckItemNV *ci = (UiCheckItemNV*)item;
-    GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label);
-    gtk_menu_shell_append(GTK_MENU_SHELL(p), widget);
-    
-    UiVar *var = uic_create_var(obj->ctx, ci->varname, UI_VAR_INTEGER);
-    if(var) {
-        UiInteger *value = var->value;
-        value->obj = widget;
-        value->get = ui_checkitem_get;
-        value->set = ui_checkitem_set;
-        value = 0;
-    } else {
-        // TODO: error
-    }
-}
-*/
-
 static void menuitem_list_remove_binding(void *obj) {
     UiActiveMenuItemList *ls = obj;
     UiList *list = ls->var->value;
@@ -261,8 +206,8 @@ void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObje
     ls->oldcount = 0;
     ls->getvalue = il->getvalue;
     
-    //UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
-    UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
+    UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+    //UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
     ls->var = var;
     if(var) {
         UiList *list = var->value;
@@ -468,6 +413,9 @@ void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj)
     GMenu *section = NULL;
     while(it) {
         if(it->type == UI_MENU_SEPARATOR) {
+            if(section) {
+                g_object_unref(section);
+            }
             section = g_menu_new();
             g_menu_append_section(parent, NULL, G_MENU_MODEL(section));
             index++;
@@ -481,6 +429,9 @@ void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj)
         }
         it = it->next;
     }
+    if(section) {
+        g_object_unref(section);
+    }
 }
 
 void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
@@ -488,6 +439,7 @@ void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *ob
     GMenu *menu = g_menu_new();
     ui_gmenu_add_menu_items(menu, 0, mi, obj);
     g_menu_append_submenu(parent, mi->label, G_MENU_MODEL(menu));
+    g_object_unref(menu);
 }
 
 static void action_enable(GSimpleAction *action, int enabled) {
@@ -499,6 +451,7 @@ void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject
      
     GSimpleAction *action = g_simple_action_new(item->id, NULL);
     g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
+    g_object_unref(action);
     
     if(i->groups) {
         CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups);
@@ -529,7 +482,8 @@ void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject
     }
     
     char action_name[32];
-    snprintf(action_name, 32, "win.%s", item->id); 
+    snprintf(action_name, 32, "win.%s", item->id);
+    
     g_menu_append(parent, i->label, action_name);
 }
 
@@ -543,8 +497,161 @@ void ui_gmenu_add_checkitem(GMenu *p, int index, UiMenuItemI *item, UiObject *ob
     // TODO
 }
 
-void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) {
+
+
+typedef struct UiCallbackData {
+    ui_callback callback;
+    void *userdata;
+} UiCallbackData;
+
+static void radiogroup_remove_binding(void *obj) {
+    UiMenuRadioGroup *group = obj;
+    if(group->var) {
+        UiInteger *i = group->var->value;
+        CxList *bindings = i->obj;
+        if(bindings) {
+            (void)cxListFindRemove(bindings, obj);
+        }
+    }
+}
+
+static void stateful_action_notify_group(UiMenuRadioGroup *group, UiInteger *i) {
+    UiEvent event;
+    event.obj = group->obj;
+    event.window = event.obj->window;
+    event.document = event.obj->ctx->document;
+    event.eventdata = NULL;
+    event.eventdatatype = 0;
+    event.intval = (int)i->value;
+    event.set = ui_get_setop();
+    
+    CxIterator iter = cxListIterator(group->callbacks);
+    cx_foreach(UiCallbackData *, cb, iter) {
+        if(cb->callback) {
+            cb->callback(&event, cb->userdata);
+        }
+    }
+    
+    UiObserver *obs = i->observers;
+    while(obs) {
+        if(obs->callback) {
+            obs->callback(&event, obs->data);
+        }
+        obs = obs->next;
+    }
+}
+
+static void ui_action_set_state(UiInteger *i, int64_t value) {
+    i->value = value;
+    CxList *bindings = i->obj;
+    CxIterator iter = cxListIterator(bindings);
+    cx_foreach(UiMenuRadioGroup *, group, iter) {
+        char buf[32];
+        snprintf(buf, 32, "%d", (int)value);
+        GVariant *state = g_variant_new_string(buf);
+        g_action_change_state(G_ACTION(group->action), state);
+        stateful_action_notify_group(group, i);
+    }
+}
+
+static int64_t ui_action_get_state(UiInteger *i) {
+    return i->value;
+}
+
+static UiMenuRadioGroup* create_radio_group(UiObject *obj, UiMenuRadioItem *item, GSimpleAction *action) {
+    UiMenuRadioGroup *group = cxZalloc(obj->ctx->allocator, sizeof(UiMenuRadioGroup));
+    group->callbacks = cxArrayListCreate(obj->ctx->allocator, NULL, sizeof(UiCallbackData), 8);
+    group->var = uic_create_var(ui_global_context(), item->varname, UI_VAR_INTEGER);
+    group->obj = obj;
+    group->action = action;
+    if(group->var) {
+        UiInteger *i = group->var->value;
+        CxList *bindings = i->obj;
+        if(!bindings) {
+            bindings = cxLinkedListCreate(group->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+            i->obj = bindings;
+            i->set = ui_action_set_state;
+            i->get = ui_action_get_state;
+        }
+        cxListAdd(bindings, group);
+        // the destruction of the toplevel obj must remove the binding
+        uic_context_add_destructor(obj->ctx, radiogroup_remove_binding, group);
+    }
+    return group;
+}
+
+static void stateful_action_activate(
+        GSimpleAction *action,
+        GVariant      *state,
+        UiMenuRadioGroup *group)
+{
+    g_simple_action_set_state(action, state);
+    
+    UiVar *var = group->var;
+    if(!var) {
+        return;
+    }
+    UiInteger *i = var->value;
+    
+    gsize len;
+    const char *s = g_variant_get_string(state, &len);
+    cxstring value = cx_strn(s, len);
+    int v;
+    if(!cx_strtoi(value, &v, 10)) {
+        i->value = v;
+    }
     
+    CxList *bindings = i->obj;
+    CxIterator iter = cxListIterator(bindings);
+    cx_foreach(UiMenuRadioGroup *, gr, iter) {
+        stateful_action_notify_group(gr, i);
+    }
+}
+
+
+void ui_gmenu_add_radioitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
+    UiMenuRadioItem *i = (UiMenuRadioItem*)item;
+    
+    if(!i->varname) {
+        return;
+    }
+
+    // All radio buttons with the name varname use the same GAction
+    UiMenuRadioGroup *group = NULL;
+    GAction *action = g_action_map_lookup_action(obj->ctx->action_map, i->varname);
+    if(!action) {
+        GVariant *state = g_variant_new_string("0");
+        GSimpleAction *newAction = g_simple_action_new_stateful(i->varname, G_VARIANT_TYPE_STRING, state);
+        g_action_map_add_action(obj->ctx->action_map, G_ACTION(newAction));
+        g_object_unref(newAction);
+
+        group = create_radio_group(obj, i, newAction);
+        g_object_set_data(G_OBJECT(newAction), "ui_radiogroup", group);
+
+        g_signal_connect(
+                newAction,
+                "change-state",
+                G_CALLBACK(stateful_action_activate),
+                group);
+    } else {
+        group = g_object_get_data(G_OBJECT(action), "ui_radiogroup");
+        if(!group) {
+            fprintf(stderr, "Error: missing ui_radiogroup property for action %s\n", i->varname);
+            return; // error, should not happen
+        }
+    }
+    
+    size_t item_index = cxListSize(group->callbacks);
+    
+    UiCallbackData cb;
+    cb.callback = i->callback;
+    cb.userdata = i->userdata;
+    cxListAdd(group->callbacks, &cb);
+    
+    
+    cxmutstr action_name = cx_asprintf("win.%s::%d", i->varname, (int)item_index);
+    g_menu_append(parent, i->label, action_name.ptr);
+    free(action_name.ptr);
 }
 
 static void menuitem_list_remove_binding(void *obj) {
@@ -576,8 +683,13 @@ void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject
     ls->oldcount = 0;
     ls->getvalue = il->getvalue;
     
-    //UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
-    UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
+    GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i"));
+    g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
+    g_object_unref(action);
+    snprintf(ls->action, 32, "win.%s", item->id);
+    
+    UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+    //UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
     ls->var = var;
     if(var) {
         UiList *list = var->value;
@@ -604,10 +716,6 @@ void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject
     ls->callback = il->callback;
     ls->userdata = il->userdata;
     
-    GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i"));
-    g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
-    snprintf(ls->action, 32, "win.%s", item->id);
-    
     
     UiEventData *event = malloc(sizeof(UiEventData));
     event->obj = obj;
@@ -650,6 +758,10 @@ void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* par
     UiVar *var = event->customdata;
     UiList *list = var->value;
     
+    if(!event->callback) {
+        return;
+    }
+    
     UiEvent evt;
     evt.obj = event->obj;
     evt.window = event->obj->window;
@@ -687,6 +799,7 @@ void ui_update_gmenu_item_list(UiActiveGMenuItemList *list) {
         GVariant *v = g_variant_new("i", i);
         g_menu_item_set_action_and_target_value(item, list->action, v);
         g_menu_insert_item(list->menu, list->index+i, item);
+        g_object_unref(item);
         
         elm = ui_list_next(ls);
         i++;
@@ -706,6 +819,7 @@ UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, GtkWidget *w
     GMenu *menu = g_menu_new();
     ui_gmenu_add_menu_items(menu, 0, builder->menus_begin, obj);
     GtkWidget *contextmenu = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu));
+    g_object_unref(menu);
     gtk_popover_set_has_arrow(GTK_POPOVER(contextmenu), FALSE);
     gtk_widget_set_halign(contextmenu, GTK_ALIGN_START);
     gtk_widget_set_parent(GTK_WIDGET(contextmenu), widget);
index dc6240cdb55dd409c929150f09748bdb410ec600..a7f65e7316f946efce7e1006f833f0f9c570dc0d 100644 (file)
@@ -98,6 +98,14 @@ struct UiActiveGMenuItemList {
     void             *userdata;
 };
 
+typedef struct UiMenuRadioGroup {
+    UiObject      *obj;
+    CxList        *callbacks;
+    UiVar         *var;
+    GSimpleAction *action;
+} UiMenuRadioGroup;
+
+
 void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj);
 
 void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj);
index 45c5bfc19e60bafca0cbda4164328b3bdb4c6598..e33b24acd3985d4df21fbb8d46d1e5d2512c8ebb 100644 (file)
@@ -76,8 +76,10 @@ static UIWIDGET ui_scrollbar(UiObject *obj, UiOrientation orientation, UiRange *
                 event);
     }
     
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, scrollbar);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    //UiLayout layout = UI_ARGS2LAYOUT(args);
+    UiLayout layout = {0};
+    ct->add(ct, scrollbar, &layout);
     
     return scrollbar;
 }
@@ -126,7 +128,7 @@ void   ui_scrollbar_setextent(UiRange *range, double extent) {
 #else
     gtk_adjustment_set_page_size(a, extent);
 #endif
-#if GTK_MAJOR_VERSION * 100 + GTK_MIMOR_VERSION < 318
+#if !GTK_CHECK_VERSION(3, 18, 0)
     gtk_adjustment_changed(a);
 #endif
     range->extent = extent;
index ef07daf37d1eee1700a1f524acdbcde7ab707c4a..085964278b1b93527d7c7f92580d4e1e88187526 100644 (file)
@@ -108,8 +108,7 @@ static GtkTextBuffer* create_textbuffer(UiTextArea *textarea) {
 }
 
 UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_TEXT);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_TEXT);
     
     GtkWidget *text_area = gtk_text_view_new();
     ui_set_name_and_style(text_area, args->name, args->style_class);
@@ -145,6 +144,18 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
             GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS  
     SCROLLEDWINDOW_SET_CHILD(scroll_area, text_area);
     
+    if(args->width > 0 || args->height > 0) {
+        int width = args->width;
+        int height = args->height;
+        if(width == 0) {
+            width = -1;
+        }
+        if(height == 0) {
+            height = -1;
+        }
+        gtk_widget_set_size_request(scroll_area, width, height);
+    }
+    
     // font and padding
     //PangoFontDescription *font;
     //font = pango_font_description_from_string("Monospace");
@@ -155,8 +166,9 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
     gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text_area), 2);
     
     // add
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, scroll_area);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, scroll_area, &layout);
     
     // bind value
     if(var) {
@@ -598,8 +610,7 @@ static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool passwor
     ui_set_name_and_style(textfield, args->name, args->style_class);
     ui_set_widget_groups(obj->ctx, textfield, args->groups);
     
-    UiObject* current = uic_current_obj(obj);
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
     
     UiTextField *uitext = malloc(sizeof(UiTextField));
     uitext->obj = obj;
@@ -616,10 +627,11 @@ static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool passwor
                 uitext);
     
     if(args->width > 0) {
-        // TODO: gtk4
-#if GTK_MAJOR_VERSION <= 3
-        gtk_entry_set_width_chars(GTK_ENTRY(textfield), args->width);
-#endif
+        // An early implementation used gtk_entry_set_width_chars,
+        // but that is not available on gtk4 and other toolkits
+        // also don't have this. Setting the width in pixels can
+        // be implemented on all platforms
+        gtk_widget_set_size_request(textfield, args->width, -1);
     }
     if(frameless) {
         // TODO: gtk2legacy workaroud
@@ -629,8 +641,9 @@ static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool passwor
         gtk_entry_set_visibility(GTK_ENTRY(textfield), FALSE);
     }
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, textfield);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, textfield, &layout);
     
     if(var) {
         UiString *value = var->value;
@@ -921,8 +934,6 @@ static gboolean ui_path_textfield_key_controller(
 }
 
 UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     UiPathTextField *pathtf = malloc(sizeof(UiPathTextField));
     memset(pathtf, 0, sizeof(UiPathTextField));
     pathtf->obj = obj;
@@ -945,8 +956,9 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
     pathtf->stack = gtk_stack_new();
     gtk_widget_set_name(pathtf->stack, "path-textfield-box");
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, pathtf->stack);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, pathtf->stack, &layout);
     
     pathtf->entry_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
     pathtf->entry = gtk_entry_new();
@@ -978,7 +990,7 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
     gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->entry_box);
     
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
     if (var) {
         UiString* value = (UiString*)var->value;
         value->obj = pathtf;
@@ -1082,8 +1094,6 @@ static GtkWidget* create_path_button_box() {
 }
 
 UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     UiPathTextField *pathtf = malloc(sizeof(UiPathTextField));
     memset(pathtf, 0, sizeof(UiPathTextField));
     pathtf->obj = obj;
@@ -1117,8 +1127,9 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
             G_CALLBACK(ui_path_textfield_destroy),
             pathtf);
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, eventbox);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, eventbox, &layout);
     
     // hbox as parent for the GtkEntry and GtkButtonBox
     GtkWidget *hbox = ui_gtk_hbox_new(0);
@@ -1142,7 +1153,7 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
             G_CALLBACK(ui_path_textfield_key_press),
             pathtf);
     
-    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
     if (var) {
         UiString* value = (UiString*)var->value;
         value->obj = pathtf;
index 0409be93bf18715a30576aa342902671f3751b3a..64b221ab7307bd8f529f55cda6c81442ffd37e5c 100644 (file)
@@ -128,15 +128,9 @@ static void set_toolbutton_icon(GtkToolItem *item, const char *icon_name) {
 
 void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj) {
     GtkToolItem *button;
-    if(item->args.stockid) {
-#ifdef UI_GTK2
-        button = gtk_tool_button_new_from_stock(item->args.stockid);
-#else
-        // TODO: gtk3 stock
-        button = gtk_tool_button_new(NULL, item->args.label);
-#endif
-    } else {
-        button = gtk_tool_button_new(NULL, item->args.label);
+    button = gtk_tool_button_new(NULL, item->args.label);
+    if(item->args.tooltip) {
+        gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip);
     }
     
     gtk_tool_item_set_homogeneous(button, FALSE);
@@ -176,21 +170,16 @@ void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj) {
 
 void add_toolitem_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObject *obj) {
     GtkToolItem *button;
-    if(item->args.stockid) {
-#ifdef UI_GTK2
-        button = gtk_toggle_tool_button_new_from_stock(item->args.stockid);
-#else
-        button = gtk_toggle_tool_button_new_from_stock(item->args.stockid); // TODO: gtk3 stock
-#endif
-    } else {
-        button = gtk_toggle_tool_button_new();
-        gtk_tool_item_set_homogeneous(button, FALSE);
-        if(item->args.label) {
-            gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), item->args.label);
-        }
-        if(item->args.icon) {
-            set_toolbutton_icon(button, item->args.icon);
-        }    
+    button = gtk_toggle_tool_button_new();
+    gtk_tool_item_set_homogeneous(button, FALSE);
+    if(item->args.label) {
+        gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), item->args.label);
+    }
+    if(item->args.icon) {
+        set_toolbutton_icon(button, item->args.icon);
+    }
+    if(item->args.tooltip) {
+        gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip);
     }
     ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups);
     
@@ -282,21 +271,15 @@ static void ui_toolbar_menubutton_destroy(GtkWidget *widget, UiToolbarMenuWidget
 
 void add_toolitem_menu_widget(GtkToolbar *tb, UiToolbarMenuItem *item, UiObject *obj) {
     GtkToolItem *button;
-    if(item->args.stockid) {
-#ifdef UI_GTK2
-        button = gtk_tool_button_new_from_stock(item->args.stockid);
-#else
-        // TODO: gtk3 stock
-        button = gtk_tool_button_new(NULL, item->args.label);
-#endif
-    } else {
-        button = gtk_tool_button_new(NULL, item->args.label);
-    }
+    button = gtk_tool_button_new(NULL, item->args.label);
     
     gtk_tool_item_set_homogeneous(button, FALSE);
     if(item->args.icon) {
         set_toolbutton_icon(button, item->args.icon);
     }
+    if(item->args.tooltip) {
+        gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip);
+    }
     gtk_tool_item_set_is_important(button, TRUE);
     
     gtk_toolbar_insert(tb, button, -1);
index d64246c14b70c89b13dcdee06482ec96821663dc..3596c5ef1166b6a5f26d67d9d74151335f4ae54c 100644 (file)
@@ -79,7 +79,6 @@ UIEXPORT void ui_init(const char *appname, int argc, char **argv) {
 #endif
     
     ui_css_init();
-    uic_docmgr_init();
     uic_menu_init();
     uic_toolbar_init();
     ui_image_init();
@@ -323,15 +322,15 @@ void ui_destroy_widget_var(GtkWidget *object, UiVar *var) {
     ui_destroy_boundvar(NULL, var);
 }
 
+// TODO: move to common
 void ui_destroy_boundvar(UiContext *ctx, UiVar *var) {
+    uic_save_var(var);
     uic_unbind_var(var);
     
+    // UI_VAR_SPECIAL: anonymous value variable, that is not registered
+    //                 in ctx->vars
     if(var->type == UI_VAR_SPECIAL) {
         ui_free(var->from_ctx, var);
-    } else {
-        ui_free(var->from_ctx, var);
-        // TODO: free or unbound
-        //uic_remove_bound_var(ctx, var);
     }
 }
 
@@ -395,6 +394,9 @@ static const char *ui_gtk_css =
 "  margin-top: 4px;\n"
 "  margin-bottom: 10px;\n"
 "}\n"
+".ui-listbox-header-row {\n"
+"  font-weight: bold;\n"
+"}\n"
 ".ui-badge {\n"
 "  background-color: #e53935;\n"
 "  color: white;\n"
@@ -404,6 +406,14 @@ static const char *ui_gtk_css =
 "  margin-left: 4px;"
 "  margin-right: 4px;"
 "}\n"
+".ui-nopadding {"
+"  padding: 0;"
+"}\n"
+".ui-table-entry {"
+"  border: none;"
+"  box-shadow: none;"
+"  background: transparent;"
+"}\n"
 ;
 
 #elif GTK_MAJOR_VERSION == 3
@@ -437,6 +447,9 @@ static const char *ui_gtk_css =
 "  margin-top: 4px;\n"
 "  margin-bottom: 10px;\n"
 "}\n"
+".ui-listbox-header-row {\n"
+"  font-weight: bold;\n"
+"}\n"
 ".ui-badge {\n"
 "  background-color: #e53935;\n"
 "  color: white;\n"
@@ -446,6 +459,9 @@ static const char *ui_gtk_css =
 "  margin-left: 4px;"
 "  margin-right: 4px;"
 "}\n"
+".ui-nopadding {"
+"  padding: 0;"
+"}\n"
 ;
 #endif
 
index b66c695cd4abd84594ba4b4b1f73a4f9a4603943..7d9777295e63add73f67510d86d9b29219fb14e0 100644 (file)
@@ -74,6 +74,7 @@ extern "C" {
 #define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon)
 #define LISTBOX_REMOVE(listbox, row) gtk_list_box_remove(GTK_LIST_BOX(listbox), row)
 #define LISTBOX_ROW_SET_CHILD(row, child) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), child)
+#define LISTBOX_ROW_REMOVE_CHILD(row) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), NULL)
 #define PANED_SET_CHILD1(paned, child) gtk_paned_set_start_child(GTK_PANED(paned), child)
 #define PANED_SET_CHILD2(paned, child) gtk_paned_set_end_child(GTK_PANED(paned), child)
 #else
@@ -96,6 +97,7 @@ extern "C" {
 #define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON)
 #define LISTBOX_REMOVE(listbox, row) gtk_container_remove(GTK_CONTAINER(listbox), row)
 #define LISTBOX_ROW_SET_CHILD(row, child) gtk_container_add(GTK_CONTAINER(row), child)
+#define LISTBOX_ROW_REMOVE_CHILD(row) gtk_container_remove(GTK_CONTAINER(row), gtk_bin_get_child(GTK_BIN(row)))
 #define PANED_SET_CHILD1(paned, child) gtk_paned_pack1(GTK_PANED(paned), child, TRUE, TRUE)
 #define PANED_SET_CHILD2(paned, child) gtk_paned_pack2(GTK_PANED(paned), child, TRUE, TRUE)
 #endif
index b9b93c7374dc070909397456c2bd00d13ab6d485..f8f57185a091be6a607b389c0bbba4f567742266 100644 (file)
 #ifdef UI_WEBVIEW
 
 UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     GtkWidget *webview = webkit_web_view_new();
     
     ui_set_name_and_style(webview, args->name, args->style_class);
     
-    UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_GENERIC);
+    UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC);
     if(var) {
         WebViewData *data = malloc(sizeof(WebViewData));
         memset(data, 0, sizeof(WebViewData));
@@ -60,8 +58,9 @@ UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) {
     }
     
     ui_set_widget_groups(obj->ctx, webview, args->groups);
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, webview);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, webview, &layout);
     
     return webview;
 }
@@ -97,13 +96,13 @@ int ui_webview_set(UiGeneric *g, void *value, const char *type) {
     }
     
     ui_webview_enable_javascript(g, data->javascript);
-    webkit_web_view_set_zoom_level(data->webview, data->zoom);
+    ui_webview_set_zoom(g, data->zoom);
     
     return 0;
 }
 
 void ui_webview_load_url(UiGeneric *g, const char *url) {
-    WebViewData data = { .uri = (char*)url, .type = WEBVIEW_CONTENT_URL };
+    WebViewData data = { .uri = (char*)url, .type = WEBVIEW_CONTENT_URL, .javascript = TRUE, .zoom = 1 };
     g->set(g, &data, UI_WEBVIEW_OBJECT_TYPE);
 }
 
@@ -129,6 +128,8 @@ void ui_webview_load_content(
     data.mimetype = (char*)mimetype;
     data.encoding = (char*)encoding;
     data.type = WEBVIEW_CONTENT_CONTENT;
+    data.javascript = FALSE;
+    data.zoom = 1;
     g->set(g, &data, UI_WEBVIEW_OBJECT_TYPE);
 }
 
index 36f446505d93daba204ba3f5e14d8932dcdfd4c4..06ef1b2406395fd91e7c168aca136329c48b2dd2 100644 (file)
 #include "../common/object.h"
 
 UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
-    UiObject* current = uic_current_obj(obj);
-    
     UIWIDGET widget = create_widget(obj, args, userdata);
     
-    UI_APPLY_LAYOUT2(current, args);
-    current->container->add(current->container, widget);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
     
     return widget;
 }
 
 UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) {
-    UiObject* current = uic_current_obj(obj);
     GtkWidget *widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
     ui_set_name_and_style(widget, args->name, args->style_class);
-    UI_APPLY_LAYOUT1(current, (*args));
-    current->container->add(current->container, widget);
+    UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ct->add(ct, widget, &layout);
     return widget;
 }
 
index d0cabe30794a815e9d7b923aec8ced423596727e..44a3fe239621d573a661074ce8491bca5806e719 100644 (file)
@@ -49,6 +49,9 @@ static int nwindows = 0;
 static int window_default_width = 650;
 static int window_default_height = 550;
 
+static int splitview_window_default_pos = -1;
+static UiBool splitview_window_use_prop = TRUE;
+
 static gboolean ui_window_destroy(void *data)  {
     UiObject *obj = data;
     uic_object_destroy(obj);
@@ -77,6 +80,36 @@ void ui_exit_event(GtkWidget *widget, gpointer data) {
 }
 
 static gboolean ui_window_close_request(UiObject *obj) {
+    if(obj->widget) {
+        void *appwindow = g_object_get_data(G_OBJECT(obj->widget), "ui.appwindow");
+        if(appwindow) {
+            int width = 0;
+            int height = 0;
+#if GTK_CHECK_VERSION(4, 10, 0)
+            graphene_rect_t bounds;
+            if(gtk_widget_compute_bounds(obj->widget, obj->widget, &bounds)) {
+                width = bounds.size.width;
+                height = bounds.size.height;
+            }
+#elif GTK_CHECK_VERSION(4, 0, 0)
+            GtkAllocation alloc;
+            gtk_widget_get_allocation(GTK_WIDGET(obj->widget), &alloc);
+            width = alloc.width;
+            height = alloc.height;
+#else
+            gtk_window_get_size(GTK_WINDOW(obj->widget), &width, &height);
+#endif
+            if(width > 0 && height > 0) {
+                char width_str[32];
+                char height_str[32];
+                snprintf(width_str, 32, "%d", width);
+                snprintf(height_str, 32, "%d", height);
+                ui_set_property("ui.window.width", width_str);
+                ui_set_property("ui.window.height", height_str);
+            }
+        }
+    }
+    
     uic_context_prepare_close(obj->ctx);
     obj->ref--;
     if(obj->ref > 0) {
@@ -101,7 +134,14 @@ static gboolean close_request(GtkWidget* self, GdkEvent* event, UiObject *obj) {
 }
 #endif
 
-static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool simple) {
+static void save_window_splitview_pos(GtkWidget *widget, void *unused) {
+    int pos = gtk_paned_get_position(GTK_PANED(widget));
+    char buf[32];
+    snprintf(buf, 32, "%d", pos);
+    ui_set_property("ui.window.splitview.pos", buf);
+}
+
+static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool splitview, UiBool simple) {
     UiObject *obj = uic_object_new_toplevel();
    
 #ifdef UI_LIBADWAITA
@@ -122,20 +162,29 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side
         gtk_window_set_title(GTK_WINDOW(obj->widget), title);
     }
     
-    const char *width = ui_get_property("ui.window.width");
-    const char *height = ui_get_property("ui.window.height");
-    if(width && height) {
-        gtk_window_set_default_size(
-                GTK_WINDOW(obj->widget),
-                atoi(width),
-                atoi(height));
-    } else {
-        gtk_window_set_default_size(
-                GTK_WINDOW(obj->widget),
-                window_default_width + sidebar*250,
-                window_default_height);
+    if(!simple) {
+        g_object_set_data(G_OBJECT(obj->widget), "ui.appwindow", obj);
     }
     
+    int window_width = window_default_width;
+    int window_height = window_default_height;
+    if(!simple) {
+        const char *width = ui_get_property("ui.window.width");
+        const char *height = ui_get_property("ui.window.height");
+        if(width && height) {
+            int w = atoi(width);
+            int h = atoi(height);
+            if(w > 0 && h > 0) {
+                window_width = w;
+                window_height = h;
+            }
+        }
+    }
+    gtk_window_set_default_size(
+                GTK_WINDOW(obj->widget),
+                window_width,
+                window_height);
+    
     obj->destroy = ui_window_widget_destroy;
     g_signal_connect(
             obj->widget,
@@ -161,50 +210,95 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side
     GtkWidget *toolbar_view = adw_toolbar_view_new();
     adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(toolbar_view), vbox);
     
-    GtkWidget *content_box = ui_gtk_vbox_new(0);
-    BOX_ADD_EXPAND(GTK_BOX(vbox), content_box);
+    GtkWidget *headerbar_sidebar = NULL;
+    GtkWidget *headerbar_main = adw_header_bar_new();
+    GtkWidget *headerbar_right = NULL;
+    
+    GtkWidget *content = toolbar_view;
+    if(splitview) {
+        content = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+        g_signal_connect(
+                content,
+                "destroy",
+                G_CALLBACK(save_window_splitview_pos),
+                NULL);
+        
+        const char *splitview_pos_str = ui_get_property("ui.window.splitview.pos");
+        int pos = splitview_window_default_pos;
+        if(pos < 0) {
+            pos = window_width / 2;
+        }
+        if(splitview_pos_str && splitview_window_use_prop) {
+            int splitview_pos = atoi(splitview_pos_str);
+            if(splitview_pos > 0) {
+                pos = splitview_pos;
+            }
+        }
+        gtk_paned_set_position(GTK_PANED(content), pos);
+        
+        GtkWidget *right_panel = adw_toolbar_view_new();
+        GtkWidget *right_vbox = ui_gtk_vbox_new(0);
+        adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(right_panel), right_vbox);
+        
+        headerbar_right = adw_header_bar_new();
+        adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_right), FALSE);
+        adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(right_panel), headerbar_right);
+        
+        adw_header_bar_set_show_end_title_buttons(ADW_HEADER_BAR(headerbar_main), FALSE);
+        
+        gtk_paned_set_start_child(GTK_PANED(content), toolbar_view);
+        gtk_paned_set_end_child(GTK_PANED(content), right_panel);
+        
+        g_object_set_data(G_OBJECT(obj->widget), "ui_window_splitview", content);
+        g_object_set_data(G_OBJECT(obj->widget), "ui_left_panel", vbox);
+        g_object_set_data(G_OBJECT(obj->widget), "ui_right_panel", right_vbox);
+    }
+    
+    GtkWidget *content_box = vbox;
     
-    GtkWidget *sidebar_headerbar = NULL; 
     if(sidebar) {
         GtkWidget *splitview = adw_overlay_split_view_new();
         adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), splitview);
         
         GtkWidget *sidebar_toolbar_view = adw_toolbar_view_new();
         adw_overlay_split_view_set_sidebar(ADW_OVERLAY_SPLIT_VIEW(splitview), sidebar_toolbar_view);
-        sidebar_headerbar = adw_header_bar_new();
-        adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), sidebar_headerbar);
+        headerbar_sidebar = adw_header_bar_new();
+        adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+        adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), headerbar_sidebar);
         
-        adw_overlay_split_view_set_content(ADW_OVERLAY_SPLIT_VIEW(splitview), toolbar_view);
+        adw_overlay_split_view_set_content(ADW_OVERLAY_SPLIT_VIEW(splitview), content);
         
         g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_toolbar_view);
     } else {
-        adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), toolbar_view);
+        adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), content);
     }
     
-    GtkWidget *headerbar = adw_header_bar_new();
-    
     const char *show_title = ui_get_property("ui.gtk.window.showtitle");
     if(show_title) {
         if(!strcmp(show_title, "main") && sidebar) {
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(sidebar_headerbar), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
         } else if(!strcmp(show_title, "sidebar")) {
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), TRUE);
         } else if(!strcmp(show_title, "false")) {
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(sidebar_headerbar), FALSE);
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
         } else {
             fprintf(stderr, "Unknown value '%s' for property ui.gtk.window.showtitle\n", show_title);
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(sidebar_headerbar), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
         }
     } else {
-        adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar), FALSE);
+        adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
+        if(sidebar) {
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), TRUE);
+        }
     }
     
-    adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar);
-    g_object_set_data(G_OBJECT(obj->widget), "ui_headerbar", headerbar);
+    adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar_main);
+    g_object_set_data(G_OBJECT(obj->widget), "ui_headerbar", headerbar_main);
     
     if(!simple) {
-        ui_fill_headerbar(obj, headerbar);
+        ui_fill_headerbar(obj, headerbar_sidebar, headerbar_main, headerbar_right);
     }
 #elif GTK_MAJOR_VERSION >= 4
     GtkWidget *content_box = ui_gtk_vbox_new(0);
@@ -278,7 +372,8 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side
     gtk_container_add(GTK_CONTAINER(frame), content_box);
     obj->container = ui_box_container(obj, content_box);
     */
-    obj->container = ui_box_container(obj, content_box, UI_CONTAINER_VBOX);
+    UiContainerX *container = ui_box_container(obj, content_box, UI_CONTAINER_VBOX);
+    uic_object_push_container(obj, container);
     
     nwindows++;
     return obj;
@@ -286,15 +381,19 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side
 
 
 UiObject* ui_window(const char *title, void *window_data) {
-    return create_window(title, window_data, FALSE, FALSE);
+    return create_window(title, window_data, FALSE, FALSE, FALSE);
 }
 
 UiObject *ui_sidebar_window(const char *title, void *window_data) {
-    return create_window(title, window_data, TRUE, FALSE);
+    return create_window(title, window_data, TRUE, FALSE, FALSE);
+}
+
+UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar) {
+    return create_window(title, NULL, sidebar, TRUE, FALSE);
 }
 
 UiObject* ui_simple_window(const char *title, void *window_data) {
-    return create_window(title, window_data, FALSE, TRUE);
+    return create_window(title, window_data, FALSE, FALSE, TRUE);
 }
 
 void ui_window_size(UiObject *obj, int width, int height) {
@@ -304,6 +403,38 @@ void ui_window_size(UiObject *obj, int width, int height) {
                 height);
 }
 
+void ui_window_default_size(int width, int height) {
+    window_default_width = width;
+    window_default_height = height;
+}
+
+void ui_splitview_window_set_pos(UiObject *obj, int pos) {
+    GtkWidget *splitview = g_object_get_data(G_OBJECT(obj->widget), "ui_window_splitview");
+    if(splitview) {
+        gtk_paned_set_position(GTK_PANED(splitview), pos);
+    } else {
+        fprintf(stderr, "Error: window has no splitview\n");
+    }
+}
+
+int ui_splitview_window_get_pos(UiObject *obj) {
+    GtkWidget *splitview = g_object_get_data(G_OBJECT(obj->widget), "ui_window_splitview");
+    if(splitview) {
+        return gtk_paned_get_position(GTK_PANED(splitview));
+    } else {
+        fprintf(stderr, "Error: window has no splitview\n");
+    }
+    return 0;
+}
+
+void ui_splitview_window_set_default_pos(int pos) {
+    splitview_window_default_pos = pos;
+}
+
+void ui_splitview_window_use_property(UiBool enable) {
+    splitview_window_use_prop = enable;
+}
+
 #ifdef UI_LIBADWAITA
 
 static void dialog_response(AdwAlertDialog *self, gchar *response, UiEventData *data) {
@@ -693,18 +824,6 @@ static void ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action, unsign
                 G_CALLBACK(ui_destroy_userdata),
                 event);
     
-    
-    UiEvent evt;
-    evt.obj = obj;
-    evt.document = evt.obj->ctx->document;
-    evt.window = evt.obj->window;
-    evt.intval = 0;
-    
-    UiFileList flist;
-    flist.files = NULL;
-    flist.nfiles = 0;
-    evt.eventdata = &flist;
-    
     gtk_widget_show(dialog);
 }
 #endif
@@ -814,7 +933,8 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
 #endif
     
     GtkWidget *content_vbox = ui_gtk_vbox_new(0);
-    obj->container = ui_box_container(obj, content_vbox, UI_CONTAINER_VBOX);
+    UiContainerX *container = ui_box_container(obj, content_vbox, UI_CONTAINER_VBOX);
+    uic_object_push_container(obj, container);
     if(args->lbutton1 || args->lbutton2 || args->rbutton3 || args->rbutton4) {
 #if GTK_CHECK_VERSION(3, 10, 0)
         if(args->titlebar_buttons != UI_OFF) {
@@ -825,7 +945,7 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
             }
             
             if(args->lbutton1) {
-                GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, args->onclick, args->onclickdata, 1, args->default_button == 1);
+                GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 1, args->default_button == 1);
                 gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button);
                 if(args->default_button == 1) {
                     WIDGET_ADD_CSS_CLASS(button, "suggested-action");
@@ -833,7 +953,7 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
                 }
             }
             if(args->lbutton2) {
-                GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, args->onclick, args->onclickdata, 2, args->default_button == 2);
+                GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 2, args->default_button == 2);
                 gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button);
                 if(args->default_button == 2) {
                     WIDGET_ADD_CSS_CLASS(button, "suggested-action");
@@ -842,7 +962,7 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
             }
             
             if(args->rbutton4) {
-                GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, args->onclick, args->onclickdata, 4, args->default_button == 4);
+                GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 4, args->default_button == 4);
                 gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button);
                 if(args->default_button == 4) {
                     WIDGET_ADD_CSS_CLASS(button, "suggested-action");
@@ -850,7 +970,7 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
                 }
             }
             if(args->rbutton3) {
-                GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, args->onclick, args->onclickdata, 3, args->default_button == 3);
+                GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 3, args->default_button == 3);
                 gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button);
                 if(args->default_button == 3) {
                     WIDGET_ADD_CSS_CLASS(button, "suggested-action");
@@ -867,11 +987,11 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
         GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
         
         GtkWidget *grid = ui_create_grid_widget(10, 10);
-        GtkWidget *widget = ui_box_set_margin(grid, 16);
+        GtkWidget *widget = ui_gtk_set_margin(grid, 16, 0, 0, 0, 0);
         gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); 
         
         if(args->lbutton1) {
-            GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, args->onclick, args->onclickdata, 1, args->default_button == 1);
+            GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 1, args->default_button == 1);
             gtk_grid_attach(GTK_GRID(grid), button, 0, 0, 1, 1);
             if(args->default_button == 1) {
                 WIDGET_ADD_CSS_CLASS(button, "suggested-action");
@@ -879,7 +999,7 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
             }
         }
         if(args->lbutton2) {
-            GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, args->onclick, args->onclickdata, 2, args->default_button == 2);
+            GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 2, args->default_button == 2);
             gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1);
             if(args->default_button == 2) {
                 WIDGET_ADD_CSS_CLASS(button, "suggested-action");
@@ -890,7 +1010,7 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
         gtk_widget_set_hexpand(space, TRUE);
         gtk_grid_attach(GTK_GRID(grid), space, 2, 0, 1, 1);
         if(args->rbutton3) {
-            GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, args->onclick, args->onclickdata, 3, args->default_button == 3);
+            GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 3, args->default_button == 3);
             gtk_grid_attach(GTK_GRID(grid), button, 3, 0, 1, 1);
             if(args->default_button == 3) {
                 WIDGET_ADD_CSS_CLASS(button, "suggested-action");
@@ -898,7 +1018,7 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
             }
         }
         if(args->rbutton4) {
-            GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, args->onclick, args->onclickdata, 4, args->default_button == 4);
+            GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 4, args->default_button == 4);
             gtk_grid_attach(GTK_GRID(grid), button, 4, 0, 1, 1);
             if(args->default_button == 4) {
                 WIDGET_ADD_CSS_CLASS(button, "suggested-action");
index b1abbe0377bc13a14f7508669abd4b8493d2f7b8..a286f8a5408007d3ca89684884556747eea84579 100644 (file)
@@ -299,31 +299,32 @@ GridClassRec gridClassRec = {
 WidgetClass gridClass = (WidgetClass)&gridClassRec;
 
 
-void grid_class_initialize(Widget request, Widget new, ArgList args, Cardinal *num_args) {
+void grid_class_initialize(void) {
     
 }
 void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args) {
-    MyWidget mn = (MyWidget)new;
+    Grid mn = (Grid)new;
     
     mn->mywidget.max_col = 0;
     mn->mywidget.max_row = 0;
     
 }
-void grid_realize(MyWidget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) {
-    XtMakeResizeRequest((Widget)w, 400, 400, NULL, NULL);
+void grid_realize(Widget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) {
+    Grid grid = (Grid)w;
+    XtMakeResizeRequest(w, 400, 400, NULL, NULL);
     (coreClassRec.core_class.realize)((Widget)w, valueMask, attributes); 
-    grid_place_children(w);
+    grid_place_children(grid);
 }
 
 
-void grid_destroy(MyWidget widget) {
+void grid_destroy(Grid widget) {
     
 }
-void grid_resize(MyWidget widget) {
+void grid_resize(Grid widget) {
     grid_place_children(widget);
 }
 
-void grid_expose(MyWidget widget, XEvent *event, Region region) {
+void grid_expose(Grid widget, XEvent *event, Region region) {
     
 }
 
@@ -336,11 +337,11 @@ Boolean grid_acceptfocus(Widget w, Time *t) {
     
 }
 
-void grid_getfocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) {
+void grid_getfocus(Widget myw, XEvent *event, String *params, Cardinal *nparam) {
     
 }
 
-void grid_loosefocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) {
+void grid_loosefocus(Widget myw, XEvent *event, String *params, Cardinal *nparam) {
     
 }
 
@@ -358,7 +359,7 @@ XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, X
         widget->core.height = request->height;
         constraints->grid.pref_height = request->height;
     }
-    grid_place_children((MyWidget)XtParent(widget));
+    grid_place_children((Grid)XtParent(widget));
     return XtGeometryYes;
 }
 
@@ -368,7 +369,7 @@ void GridChangeManaged(Widget widget) {
 
 Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
     GridConstraintRec *constraints = neww->core.constraints;
-    MyWidget grid = (MyWidget)XtParent(neww);
+    Grid grid = (Grid)XtParent(neww);
     if(constraints->grid.x > grid->mywidget.max_col) {
         grid->mywidget.max_col = constraints->grid.x;
     }
@@ -387,7 +388,7 @@ void grid_constraint_init(
 {
     GridConstraintRec *constraints = neww->core.constraints;
     
-    MyWidget grid = (MyWidget)XtParent(neww);
+    Grid grid = (Grid)XtParent(neww);
     if(constraints->grid.x > grid->mywidget.max_col) {
         grid->mywidget.max_col = constraints->grid.x;
     }
@@ -398,7 +399,7 @@ void grid_constraint_init(
     constraints->grid.pref_height = neww->core.height;
 }
 
-void grid_place_children(MyWidget w) {
+void grid_place_children(Grid w) {
     int ncols = w->mywidget.max_col+1;
     int nrows = w->mywidget.max_row+1;
     GridDef *cols = calloc(ncols, sizeof(GridDef));
index 18621633c02b1906aff94a65c6e8cabc52dde6fd..f135cbdcfb4e73ed8f5c62ab427c08805afbf6e8 100644 (file)
@@ -125,23 +125,23 @@ typedef struct GridConstraintRec {
     GridContraintPart grid;
 } GridConstraintRec;
 
-typedef GridRec* MyWidget;
+typedef GridRec* Grid;
 
 extern WidgetClass gridClass;
 
-void grid_class_initialize();
-void grid_initialize();
-void grid_realize();
-void grid_destroy();
-void grid_resize();
-void grid_expose();
-Boolean grid_set_values();
+void grid_class_initialize(void);
+void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args);
+void grid_realize(Widget w,XtValueMask *valueMask,XSetWindowAttributes *attributes);
+void grid_destroy(Grid widget);
+void grid_resize(Grid widget);
+void grid_expose(Grid widget, XEvent *event, Region region);
+Boolean grid_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args);
 Boolean grid_acceptfocus(Widget , Time*);
 
-void grid_place_children(MyWidget w);
+void grid_place_children(Grid w);
 
-void grid_getfocus();
-void grid_loosefocus();
+void grid_getfocus(Widget myw, XEvent *event, String *params, Cardinal *nparam);
+void grid_loosefocus(Widget myw, XEvent *event, String *params, Cardinal *nparam);
 
 void grid_constraint_init(
     Widget     request,
index b421a8aa8e5bfac4b8b374c0a4099ecf3819b0c9..d2528c4c1541c196e4f8e59de05629ef7d56db08 100644 (file)
@@ -46,9 +46,9 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
        
     XmString label = NULL;
     if(args->label) {
@@ -59,7 +59,7 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
     char *name = args->name ? (char*)args->name : "button";
     Widget button = XmCreatePushButton(parent, name, xargs, n);
     XtManageChild(button);
-    ctn->add(ctn, button);
+    ui_container_add(ctn, button);
     
     ui_set_widget_groups(obj->ctx, button, args->groups);
     
@@ -101,9 +101,9 @@ UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     XtSetArg(xargs[n], XmNfillOnSelect, True); n++;
     XtSetArg(xargs[n], XmNindicatorOn, False); n++;
     
@@ -116,7 +116,7 @@ UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args) {
     char *name = args->name ? (char*)args->name : "togglebutton";
     Widget button = XmCreateToggleButton(parent, name, xargs, n);
     XtManageChild(button);
-    ctn->add(ctn, button);
+    ui_container_add(ctn, button);
     
     ui_set_widget_groups(obj->ctx, button, args->groups);
     
@@ -131,9 +131,9 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     
     XmString label = NULL;
     if(args->label) {
@@ -144,7 +144,7 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
     char *name = args->name ? (char*)args->name : "button";
     Widget button = XmCreateToggleButton(parent, name, xargs, n);
     XtManageChild(button);
-    ctn->add(ctn, button);
+    ui_container_add(ctn, button);
     
     ui_set_widget_groups(obj->ctx, button, args->groups);
     
@@ -350,9 +350,9 @@ UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     XtSetArg(xargs[n], XmNindicatorType, XmONE_OF_MANY_ROUND); n++;
     XmString label = NULL;
     if(args->label) {
@@ -363,7 +363,7 @@ UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) {
     char *name = args->name ? (char*)args->name : "button";
     Widget button = XmCreateToggleButton(parent, name, xargs, n);
     XtManageChild(button);
-    ctn->add(ctn, button);
+    ui_container_add(ctn, button);
     
     ui_set_widget_groups(obj->ctx, button, args->groups);
     
index 13377abf64211db2960c2a40ab153561ee4bd573..444bedbc19d6fade55fd6d3f456cffe80d4eca83 100644 (file)
@@ -33,6 +33,7 @@
 #include "container.h"
 #include "../common/context.h"
 #include "../common/object.h"
+#include "../common/container.h"
 
 #include <cx/array_list.h>
 
 
 
 
+Widget ui_container_prepare(UiContainerPrivate *container, UiLayout *layout, Arg *args, int *n) {
+    if(layout->margin != 0) {
+        layout->margin_left = layout->margin;
+        layout->margin_right = layout->margin;
+        layout->margin_top = layout->margin;
+        layout->margin_bottom = layout->margin;
+    }
+    return container->prepare(container, layout, args, n);
+}
+
+void ui_container_add(UiContainerPrivate *container, Widget widget) {
+    container->add(container, widget);
+}
+
 
 /* ---------------------------- Box Container ---------------------------- */
 
 static UIWIDGET box_create(UiObject *obj, UiContainerArgs *args, UiBoxOrientation orientation) { 
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     Arg xargs[16];
     int n = 0;
@@ -56,7 +71,7 @@ static UIWIDGET box_create(UiObject *obj, UiContainerArgs *args, UiBoxOrientatio
         //XtSetArg(xargs[n], gridColumnSpacing, args->spacing); n++;
     }
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     Widget grid = XtCreateManagedWidget(args->name ? args->name : "boxcontainer", gridClass, parent, xargs, n);
     ctn->add(ctn, grid);
     
@@ -86,43 +101,42 @@ UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orie
     return (UiContainerX*)ctn;
 }
 
-static Widget ui_box_container_prepare(UiBoxContainer *box, Arg *args, int *n) {
+static Widget ui_box_container_prepare(UiBoxContainer *box, UiLayout *layout, Arg *args, int *n) {
     int a = *n;
     box->n++;
     return box->container.widget;
 }
 
-Widget ui_vbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n) {
+Widget ui_vbox_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) {
     UiBoxContainer *box = (UiBoxContainer*)ctn;
     int a = *n;
     XtSetArg(args[a], gridRow, box->n); a++;
-    if(box->container.layout.fill == UI_ON) {
+    if(layout->fill) {
         XtSetArg(args[a], gridVExpand, TRUE); a++;
         XtSetArg(args[a], gridVFill, TRUE); a++;
     }
     XtSetArg(args[a], gridHExpand, TRUE); a++;
     XtSetArg(args[a], gridHFill, TRUE); a++;
     *n = a;
-    return ui_box_container_prepare(box, args, n);
+    return ui_box_container_prepare(box, layout, args, n);
 }
 
-Widget ui_hbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n) {
+Widget ui_hbox_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) {
     UiBoxContainer *box = (UiBoxContainer*)ctn;
     int a = *n;
     XtSetArg(args[a], gridColumn, box->n); a++;
-    if(box->container.layout.fill == UI_ON) {
+    if(layout->fill) {
         XtSetArg(args[a], gridHExpand, TRUE); a++;
         XtSetArg(args[a], gridHFill, TRUE); a++;
     }
     XtSetArg(args[a], gridVExpand, TRUE); a++;
     XtSetArg(args[a], gridVFill, TRUE); a++;
     *n = a;
-    return ui_box_container_prepare(box, args, n);
+    return ui_box_container_prepare(box, layout, args, n);
 }
 
 void ui_box_container_add(UiContainerPrivate *ctn, Widget widget) {
-    ui_reset_layout(ctn->layout);
-    
+        
 }
 
 
@@ -134,14 +148,14 @@ UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     XtSetArg(xargs[n], gridMargin, args->margin); n++;
     XtSetArg(xargs[n], gridColumnSpacing, args->columnspacing); n++;
     XtSetArg(xargs[n], gridRowSpacing, args->rowspacing); n++;
     Widget grid = XtCreateManagedWidget(args->name ? args->name : "gridcontainer", gridClass, parent, xargs, n);
-    ctn->add(ctn, grid);
+    ui_container_add(ctn, grid);
     
     UiContainerX *container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill);
     uic_object_push_container(obj, container);
@@ -171,9 +185,9 @@ UiContainerX* ui_grid_container(
     return (UiContainerX*)ctn;
 }
 
-Widget ui_grid_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n) {
+Widget ui_grid_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) {
     UiGridContainer *grid = (UiGridContainer*)ctn;
-    if(ctn->layout.newline) {
+    if(ctn->container.newline) {
         grid->y++;
         grid->x = 0;
     }
@@ -181,61 +195,25 @@ Widget ui_grid_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n) {
     int a = *n;
     XtSetArg(args[a], gridColumn, grid->x); a++;
     XtSetArg(args[a], gridRow, grid->y); a++;
-    if(ctn->layout.colspan > 0) {
-        XtSetArg(args[a], gridColspan, ctn->layout.colspan); a++;
-    }
-    if(ctn->layout.rowspan > 0) {
-        XtSetArg(args[a], gridRowspan, ctn->layout.rowspan); a++;
-    }
-    
-    int hexpand = FALSE;
-    int vexpand = FALSE;
-    int hfill = FALSE;
-    int vfill = FALSE;
-    if(!ctn->layout.override_defaults) {
-        if(grid->def_hexpand) {
-            hexpand = TRUE;
-            hfill = TRUE;
-        } else if(grid->def_hfill) {
-            hfill = TRUE;
-        }
-        if(grid->def_vexpand) {
-            vexpand = TRUE;
-            vfill = TRUE;
-        } else if(grid->def_vfill) {
-            vfill = TRUE;
-        }
-    }
-    
-    if(ctn->layout.hexpand) {
-        hexpand = TRUE;
-        hfill = TRUE;
-    } else if(ctn->layout.hfill) {
-        hfill = TRUE;
-    }
-    if(ctn->layout.vexpand) {
-        vexpand = TRUE;
-        vfill = TRUE;
-    } else if(ctn->layout.vfill) {
-        vfill = TRUE;
+    if(layout->colspan > 0) {
+        XtSetArg(args[a], gridColspan, layout->colspan); a++;
     }
-    if(ctn->layout.fill == UI_ON) {
-        hexpand = TRUE;
-        vexpand = TRUE;
-        hfill = TRUE;
-        vfill = TRUE;
+    if(layout->rowspan > 0) {
+        XtSetArg(args[a], gridRowspan, layout->rowspan); a++;
     }
     
-    if(hfill) {
+    uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill);
+    
+    if(layout->hfill) {
         XtSetArg(args[a], gridHFill, TRUE); a++;
     }
-    if(vfill) {
+    if(layout->vfill) {
         XtSetArg(args[a], gridVFill, TRUE); a++;
     }
-    if(hexpand) {
+    if(layout->hexpand) {
         XtSetArg(args[a], gridHExpand, TRUE); a++;
     }
-    if(vexpand) {
+    if(layout->vexpand) {
         XtSetArg(args[a], gridVExpand, TRUE); a++;
     }
     
@@ -246,7 +224,7 @@ Widget ui_grid_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n) {
 void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget) {
     UiGridContainer *grid = (UiGridContainer*)ctn;
     grid->x++;
-    ui_reset_layout(ctn->layout);
+    grid->container.container.newline = FALSE;
 }
 
 
@@ -313,7 +291,7 @@ UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     // create widgets
     // form
@@ -323,9 +301,10 @@ UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args) {
     memset(tabview, 0, sizeof(UiMotifTabView));
     char *name = args->name ? (char*)args->name : "tabview";
     XtSetArg(xargs[n], XmNuserData, tabview); n++;
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     Widget form = XmCreateForm(parent, name, xargs, n);
     XtManageChild(form);
+    ui_container_add(ctn, parent);
     
     n = 0;
     XtSetArg(xargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
@@ -569,7 +548,7 @@ void ui_motif_tabview_change_tab(UiMotifTabView *tabview, UiTab *tab) {
     XtManageChild(tab->child);
 }
 
-Widget ui_tabview_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n) {
+Widget ui_tabview_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) {
     UiTabViewContainer *ct = (UiTabViewContainer*)ctn;
     UiMotifTabView *tabview = ct->tabview;
     int a = *n;
@@ -583,12 +562,12 @@ Widget ui_tabview_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n)
 }
 
 void ui_tabview_container_add(UiContainerPrivate *ctn, Widget widget) {
-    ui_reset_layout(ctn->layout);
+    
 }
 
 /* -------------------- ScrolledWindow -------------------- */
 
-Widget ui_scrolledwindow_prepare(UiContainerPrivate *ctn, Arg *args, int *n) {
+Widget ui_scrolledwindow_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) {
     return ctn->widget;
 }
 
@@ -607,16 +586,16 @@ static UiContainerX* ui_scrolledwindow_container(UiObject *obj, Widget scrolledw
 
 UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     Arg xargs[16];
     int n = 0;
     
     XtSetArg(xargs[n], XmNscrollingPolicy, XmAUTOMATIC); n++;
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     Widget scrolledwindow = XmCreateScrolledWindow(parent, "scrolledwindow", xargs, n);
-    ctn->add(ctn, scrolledwindow);
+    ui_container_add(ctn, scrolledwindow);
     
     UiContainerX *container = ui_scrolledwindow_container(obj, scrolledwindow);
     uic_object_push_container(obj, container);
@@ -640,15 +619,3 @@ int ui_container_finish(UiObject *obj) {
     return 1;
 }
 
-
-/*
- * -------------------- Layout Functions --------------------
- * 
- * functions for setting layout attributes for the current container
- *
- */
-
-void ui_newline(UiObject *obj) {
-    UiContainerPrivate *ct = ui_obj_container(obj);
-    ct->layout.newline = TRUE;
-}
index 4f36550e4d3c59cdc797fdd096051b2d6e97e21d..9dd73cc806e0adcbc0893f400d03d92df1ffd6cd 100644 (file)
@@ -62,21 +62,6 @@ typedef enum UiContainerType UiContainerType;
 
 #define ui_obj_container(obj) (UiContainerPrivate*)obj->container_end
     
-typedef struct UiLayout UiLayout;
-
-struct UiLayout {
-    UiBool       fill;
-    UiBool       newline;
-    char         *label;
-    UiBool       hexpand;
-    UiBool       vexpand;
-    UiBool       hfill;
-    UiBool       vfill;
-    UiBool       override_defaults;
-    int          width;
-    int          colspan;
-    int          rowspan;
-};
 
 enum UiBoxOrientation {
     UI_BOX_VERTICAL = 0,
@@ -88,11 +73,10 @@ typedef struct UiContainerPrivate UiContainerPrivate;
 
 struct UiContainerPrivate {
     UiContainerX    container;
-    Widget          (*prepare)(UiContainerPrivate*, Arg *, int*);
+    Widget          (*prepare)(UiContainerPrivate*, UiLayout *layout, Arg *, int*);
     void            (*add)(UiContainerPrivate*, Widget);
     Widget          widget;
     UiContainerType type;
-    UiLayout        layout;
 };
 
 typedef struct UiBoxContainer {
@@ -143,6 +127,9 @@ typedef struct UiTabViewContainer {
     UiMotifTabView *tabview;
 } UiTabViewContainer;
 
+Widget ui_container_prepare(UiContainerPrivate *container, UiLayout *layout, Arg *args, int *n);
+void ui_container_add(UiContainerPrivate *container, Widget widget);
+
 void ui_motif_tabview_select(UiMotifTabView *tabview, int tab);
 void ui_motif_tabview_add_tab(UiMotifTabView *tabview, int index, const char *name, Widget child);
 void ui_motif_tabview_remove(UiMotifTabView *tabview, int index);
@@ -150,12 +137,12 @@ void ui_motif_tabview_change_tab(UiMotifTabView *tabview, UiTab *tab);
 int64_t ui_tabview_get(UiInteger *i);
 void ui_tabview_set(UiInteger *i, int64_t value);
 
-Widget ui_tabview_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n);
+Widget ui_tabview_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n);
 void ui_tabview_container_add(UiContainerPrivate *ctn, Widget widget);
 
 UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orientation);
-Widget ui_vbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n);
-Widget ui_hbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n);
+Widget ui_vbox_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n);
+Widget ui_hbox_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n);
 void ui_box_container_add(UiContainerPrivate *ctn, Widget widget);
 
 
@@ -166,7 +153,7 @@ UiContainerX* ui_grid_container(
         UiBool def_vexpand,
         UiBool def_hfill,
         UiBool def_vfill);
-Widget ui_grid_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n);
+Widget ui_grid_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n);
 void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget);
 
 #ifdef __cplusplus
index 37d5da332ff3274bba21222a4538ab725d99fba1..749c9e3eff9aebf3ad19c2fef8d66a30db1952ca 100644 (file)
@@ -41,9 +41,9 @@ static UIWIDGET label_create(UiObject *obj, UiLabelArgs *args, int align) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     
     XtSetArg(xargs[n], XmNalignment, align); n++;
     XmString label = NULL;
@@ -55,7 +55,7 @@ static UIWIDGET label_create(UiObject *obj, UiLabelArgs *args, int align) {
     char *name = args->name ? (char*)args->name : "label";
     Widget w = XmCreateLabel(parent, name, xargs, n);
     XtManageChild(w);
-    ctn->add(ctn, w);
+    ui_container_add(ctn, w);
       
     XmStringFree(label);
     return w;
@@ -108,12 +108,13 @@ UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     
     char *name = args->name ? (char*)args->name : "progressbar";
     Widget frame = XmCreateFrame(parent, name, xargs, n);
+    ui_container_add(ctn, frame);
     
     // create a button and get some informations about the height, shadow, highlight, ....
     // we want the frame to have the same dimensions as a normal button
@@ -191,9 +192,9 @@ UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs *args
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
      
     XmString label = XmStringCreateSimple("");
     XtSetArg(xargs[n], XmNlabelString, label); n++;
@@ -203,7 +204,7 @@ UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs *args
     char *name = args->name ? (char*)args->name : "progresss_spinner";
     Widget w = XmCreateLabel(parent, name, xargs, n);
     XtManageChild(w);
-    ctn->add(ctn, w);
+    ui_container_add(ctn, w);
     
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
     if(var) {
index ffb915bd6f58425de2167e5d9c48c5313651fce0..0bb1cbe143bbc1063eb4122e7242a62420fe107a 100644 (file)
@@ -55,7 +55,7 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     if(args->multiselection) {
         XtSetArg(xargs[n], XmNselectionPolicy, XmEXTENDED_SELECT); n++;
@@ -64,9 +64,10 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
     }
     
     char *name = args->name ? (char*)args->name : "listview";
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     Widget widget = XmCreateScrolledList(parent, name, xargs, n);
     XtManageChild(widget);
+    ui_container_add(ctn, widget);
     
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
     
@@ -264,12 +265,13 @@ UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     char *name = args->name ? (char*)args->name : "dropdown";
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     Widget widget = XmCreateDropDownList(parent, name, xargs, n);
     XtManageChild(widget);
+    ui_container_add(ctn, widget);
     
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
     
index c75d64af38ed7945adfec75cc06a7cf3e7fa28ff..c2038a84cf1a27297d694703a17df7dc72cc7717 100644 (file)
@@ -232,8 +232,7 @@ void add_menuitem_list_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj)
     ls->userdata = il->userdata;
     ls->addseparator = il->addseparator;
     
-    //ls->var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
-    ls->var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
+    ls->var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
     if(ls->var) {
         UiList *list = ls->var->value;
         list->update = ui_menulist_update;
index ae9bbb01be492cd904b911b5b238f934307190ad..5c5db1f39215680a5a693257ce23d592d5806ced 100644 (file)
@@ -45,13 +45,14 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
     XtSetArg(xargs[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     char *name = args->name ? (char*)args->name : "textarea";
     XtSetArg(xargs[n], XmNwidth, 100); n++;
     Widget widget = XmCreateScrolledText(parent, name, xargs, n);
     XtManageChild(widget);
+    ui_container_add(ctn, widget);
     
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_TEXT);
     
@@ -396,12 +397,13 @@ static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs *args, int frame
     }
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     char *name = args->name ? (char*)args->name : "textfield";
     Widget textfield = XmCreateTextField(parent, name, xargs, n);
     XtManageChild(textfield);
+    ui_container_add(ctn, textfield);
     
     ui_set_widget_groups(obj->ctx, textfield, args->groups);
     
@@ -971,9 +973,9 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     // TODO: name
     
 
@@ -987,7 +989,7 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
     
     
     XtManageChild(pathbar->widget);
-    ctn->add(ctn, pathbar->widget);
+    ui_container_add(ctn, pathbar->widget);
     
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
     if (var) {
index b6ffc845af40e903b902b8fd43cb3eefd77972f3..c2a1f2abd4506062cd604e72502c9205b73d2e17 100644 (file)
@@ -100,7 +100,6 @@ void ui_init(const char *appname, int argc, char **argv) {
     
     display =  XtOpenDisplay(app, NULL, appname, appname, NULL, 0, &argc, argv);
     
-    uic_docmgr_init();
     uic_menu_init();
     uic_toolbar_init();
     uic_load_app_properties();
index 18e2a8ebb30862d746173a059e421ab670e1a64b..0911a755a81967f909cdad3d2de6e937119e2f5b 100644 (file)
@@ -42,12 +42,12 @@ UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widge
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     Widget widget = create_widget(obj, args, userdata, parent, xargs, n);
     XtManageChild(widget);
-    ctn->add(ctn, widget);
+    ui_container_add(ctn, widget);
     
     return widget;
 }
@@ -58,13 +58,13 @@ UIEXPORT UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) {
     int n = 0;
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     char *name = args->name ? (char*)args->name : "separator";
-    Widget parent = ctn->prepare(ctn, xargs, &n);
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     Widget widget = XmCreateSeparator(parent, name, xargs, n);
     XtManageChild(widget);
-    ctn->add(ctn, widget);
+    ui_container_add(ctn, widget);
     
     return widget;
 }
index 39d4fc532db2389881761bef9ed6b51ddb8b1a9e..b1a7b0824ac6cc2a7140522995f94e5f098e3424 100644 (file)
@@ -33,13 +33,12 @@ UI_QT_LIB = ../build/ui/qt/
 $(QT_MAKEFILE): qt/$(QT_PRO_FILE)
        $(QMAKE) -o - $< > $(QT_MAKEFILE)
 
-$(UI_LIB): $(QT_MAKEFILE) $(OBJ) FORCE
+$(UI_LIB): $(QT_MAKEFILE) $(OBJ) $(UI_LIB) FORCE
        $(MAKE) -f $(QT_MAKEFILE)
        $(AR) $(ARFLAGS) $(UI_LIB) $(OBJ)
 
-$(UI_SHLIB): $(OBJ)
-       $(MAKE) -f $(QT_MAKEFILE)
-       $(CXX) -o $(UI_SHLIB) $(LDFLAGS) $(SHLIB_LDFLAGS) $(TK_LDFLAGS) $(OBJ) -L../build/lib -lucx
-
+$(UI_SHLIB): $(QT_MAKEFILE) $(OBJ) $(UI_LIB_SH) $(UI_LIB) FORCE
+       $(CXX) -o $(UI_SHLIB) $(LDFLAGS) $(SHLIB_LDFLAGS) $(TK_LDFLAGS) $(OBJ) ../build/ui/qt/*.o -L../build/lib -lucx
+       
 FORCE:
 
index 6036cd6dc63215658f6502cb30777e778f2fd5a2..db00f6fc9f3c8ebf9d42de62f934c164490b9a30 100644 (file)
@@ -32,7 +32,6 @@
 
 UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
     
     QString str = QString::fromUtf8(args->label);
     QPushButton *button = new QPushButton(str);
@@ -43,7 +42,8 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
         button->connect(button, SIGNAL(destroyed()), event, SLOT(destroy()));
     }
     
-    ctn->add(button);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(button, layout);
     
     return button;
 }
@@ -69,7 +69,6 @@ static void togglebutton_event(UiEvent *event, UiEventWrapper *wrapper) {
 
 UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
     
     QString str = QString::fromUtf8(args->label);
     QPushButton *button = new QPushButton(str);
@@ -98,7 +97,8 @@ UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args) {
         i->set = ui_togglebutton_set;
     }
     
-    ctn->add(button);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(button, layout);
     
     return button;
 }
@@ -129,7 +129,6 @@ static void checkbox_event(UiEvent *event, UiEventWrapper *wrapper) {
 
 UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
     
     QString str = QString::fromUtf8(args->label);
     QCheckBox *checkbox = new QCheckBox(str);
@@ -157,7 +156,8 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
         i->set = ui_checkbox_set;
     }
     
-    ctn->add(checkbox);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(checkbox, layout);
     
     return checkbox;
 }
@@ -188,7 +188,6 @@ static void radiobutton_event(UiEvent *event, UiEventWrapper *wrapper) {
 
 UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
     
     QString str = QString::fromUtf8(args->label);
     QRadioButton *button = new QRadioButton(str);
@@ -218,7 +217,8 @@ UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) {
     button->connect(button, SIGNAL(clicked()), event, SLOT(slot()));
     button->connect(button, SIGNAL(destroyed()), event, SLOT(destroy()));
     
-    ctn->add(button);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(button, layout);
     
     return button;
 }
index 1313ab6f1b61301c2a19028f9084bb55d2e15a12..245ba19252b6d8edf7473f8764496f9d05ee3757 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include "container.h"
 #include "../common/object.h"
+#include "../common/container.h"
 
 #include <cx/mempool.h>
 
@@ -41,34 +42,55 @@ static void delete_container(UiContainerPrivate *ct) {
     delete ct;
 }
 
-void ui_container_add(UiObject *obj, UiContainerPrivate *ct) {
+void ui_obj_add_container(UiObject *obj, UiContainerPrivate *ct) {
     UiContainerX *container = (UiContainerX*)ui_malloc(obj->ctx, sizeof(UiContainerX));
     container->close = 0;
     container->container = ct;
     container->prev = NULL;
     container->next = NULL;
+    ct->container = container;
     cxMempoolRegister(obj->ctx->mp, ct, (cx_destructor_func)delete_container);
     uic_object_push_container(obj, container);
 }
 
+/* ------------------------ margin helper ------------------------ */
+
+QWidget* ui_widget_set_margin(QWidget *w, int left, int right, int top, int bottom) {
+    if((left | right | top | bottom) == 0) {
+        return w;
+    }
+    QWidget* wrapper = new QWidget;
+    QVBoxLayout* inner = new QVBoxLayout(wrapper);
+    inner->setContentsMargins(left, top, right, bottom);
+    inner->addWidget(w);
+    
+    // TODO: handle widget visibility changes
+    
+    return wrapper;
+}
+
 /* -------------------- UiBoxContainer -------------------- */
 
 UiBoxContainer::UiBoxContainer(QBoxLayout* box) {
     this->box = box;
+    this->direction = box->direction();
     box->setContentsMargins(QMargins(0,0,0,0));
     box->setSpacing(0);
-    
-    ui_reset_layout(layout);
 }
 
-void UiBoxContainer::add(QWidget* widget) {
+void UiBoxContainer::add(QWidget* widget, UiLayout& layout) {
     bool fill = layout.fill;
     if(hasStretchedWidget && fill) {
         fill = false;
         fprintf(stderr, "UiError: container has 2 filled widgets");
     }
     
+    uic_layout_setup_margin(&layout);
+    widget = ui_widget_set_margin(widget, layout.margin_left, layout.margin_right, layout.margin_top, layout.margin_bottom);
     box->addWidget(widget);
+    if(direction == Qt::LeftToRight) {
+        box->setAlignment(widget, Qt::AlignTop);
+    }
     
     if(!hasStretchedWidget) {
         QSpacerItem *newspace = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
@@ -80,20 +102,19 @@ void UiBoxContainer::add(QWidget* widget) {
     if(fill) {
         hasStretchedWidget = true;
     }
-    ui_reset_layout(layout);
     current = widget;
 }
 
 UIWIDGET ui_box(UiObject *obj, UiContainerArgs *args, QBoxLayout::Direction dir) {
     UiContainerPrivate *ctn = (UiContainerPrivate*)ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     QWidget *widget = new QWidget();
     QBoxLayout *box = new QBoxLayout(dir);
     widget->setLayout(box);
-    ctn->add(widget);
+    ctn->add(widget, layout);
     
-    ui_container_add(obj, new UiBoxContainer(box));
+    ui_obj_add_container(obj, new UiBoxContainer(box));
     
     return widget;
 }
@@ -128,77 +149,44 @@ UiGridContainer::UiGridContainer(
     grid->setContentsMargins(QMargins(margin, margin, margin, margin));
     grid->setHorizontalSpacing(columnspacing);
     grid->setVerticalSpacing(rowspacing);
-    ui_reset_layout(layout);
 }
 
-void UiGridContainer::add(QWidget* widget) {
-    if(layout.newline) {
+void UiGridContainer::add(QWidget* widget, UiLayout& layout) {
+    if(container->newline) {
         x = 0;
         y++;
+        container->newline = false;
     }
     
-    bool fill = layout.fill;
-    bool hexpand = false;
-    bool vexpand = false;
-    bool hfill = false;
-    bool vfill = false;
-    if(!layout.override_defaults) {
-        if(def_hexpand) {
-            hexpand = true;
-            hfill = true;
-        } else if(def_hfill) {
-            hfill = true;
-        }
-        if(def_vexpand) {
-            vexpand = true;
-            vfill = true;
-        } else if(def_vfill) {
-            vfill = true;
-        }
-    }
+    uic_layout_setup_expand_fill(&layout, def_hexpand, def_vexpand, def_hfill, def_vfill);
+    uic_layout_setup_margin(&layout);
     
     if(layout.hexpand) {
-        hexpand = true;
-        //hfill = true;
-    } else if(layout.hfill) {
-        hfill = true;
-    }
-    if(layout.vexpand) {
-        vexpand = true;
-        //vfill = true;
-    } else if(layout.vfill) {
-        vfill = true;
-    }
-    if(fill) {
-        hfill = true;
-        vfill = true;
-    }
-    
-    if(hexpand) {
         col_expanding = true;
     }
-    if(vexpand) {
+    if(layout.vexpand) {
         row_expanding = true;
     }
     
-    if(hexpand) {
+    if(layout.hexpand) {
         grid->setColumnStretch(x, 1);
     }
-    if(vexpand) {
+    if(layout.vexpand) {
         grid->setRowStretch(y, 1);
     }
     
     Qt::Alignment alignment = 0;
-    if(!hfill) {
+    if(!layout.hfill) {
         alignment = Qt::AlignLeft;
     }
-    if(!vfill) {
+    if(!layout.vfill) {
         alignment = Qt::AlignTop;
     }
     
     int colspan = layout.colspan > 0 ? layout.colspan : 1;
     int rowspan = layout.rowspan > 0 ? layout.rowspan : 1;
     
+    widget = ui_widget_set_margin(widget, layout.margin_left, layout.margin_right, layout.margin_top, layout.margin_bottom);
     grid->addWidget(widget, y, x, rowspan, colspan, alignment);
     
     if(x > max_x) {
@@ -210,7 +198,6 @@ void UiGridContainer::add(QWidget* widget) {
     
     x += colspan;
     
-    ui_reset_layout(layout);
     current = widget;
 }
 
@@ -231,14 +218,14 @@ void UiGridContainer::end() {
 
 UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
     UiContainerPrivate *ctn = (UiContainerPrivate*)ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
     
     QWidget *widget = new QWidget();
     QGridLayout *grid = new QGridLayout();
     widget->setLayout(grid);
-    ctn->add(widget);
+    ctn->add(widget, layout);
     
-    ui_container_add(obj, new UiGridContainer(
+    ui_obj_add_container(obj, new UiGridContainer(
             grid,
             args->margin,
             args->columnspacing,
@@ -267,7 +254,7 @@ UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
     widget->setLayout(box);
     dock->setWidget(widget);
     
-    ui_container_add(obj, new UiBoxContainer(box));
+    ui_obj_add_container(obj, new UiBoxContainer(box));
     
     return dock;
 }
@@ -288,15 +275,3 @@ int ui_container_finish(UiObject *obj) {
     return 1;
 }
 
-
-/*
- * -------------------- Layout Functions --------------------
- * 
- * functions for setting layout attributes for the current container
- *
- */
-
-void ui_newline(UiObject *obj) {
-    UiContainerPrivate *ct = ui_obj_container(obj);
-    ct->layout.newline = TRUE;
-}
index 0d166cd80c2f2ad053eece0dd9dec7622d409999..867fc4c57ef1f086a600ad1e9d4a53126d75fbd3 100644 (file)
 #include <QStackedWidget>
 #include <QSplitter>
 
-#define UI_APPLY_LAYOUT(layout, args) \
-    layout.fill = args->fill; \
-    layout.hexpand = args->hexpand; \
-    layout.vexpand = args->vexpand; \
-    layout.hfill = args->hfill; \
-    layout.vfill = args->vfill; \
-    layout.override_defaults = args->override_defaults; \
-    layout.colspan = args->colspan; \
-    layout.rowspan = args->rowspan
-
-#define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout))
-#define ui_lb2bool(b) ((b) == UI_LAYOUT_TRUE ? TRUE : FALSE)
-#define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE)
-
 #define ui_obj_container(obj) (UiContainerPrivate*)((UiContainerX*)obj->container_end)->container
 
-typedef enum UiLayoutBool {
-    UI_LAYOUT_UNDEFINED = 0,
-    UI_LAYOUT_TRUE,
-    UI_LAYOUT_FALSE,
-} UiLayoutBool;
-
-typedef struct UiLayout UiLayout;
-struct UiLayout {
-    UiBool       fill;
-    UiBool       newline;
-    char         *label;
-    UiBool       hexpand;
-    UiBool       vexpand;
-    UiBool       hfill;
-    UiBool       vfill;
-    UiBool       override_defaults;
-    int          width;
-    int          colspan;
-    int          rowspan;
-};
-
 struct UiContainerPrivate {
-    UiLayout layout; 
     UIWIDGET current;
+    UiContainerX *container;
 
-    virtual void add(QWidget *widget) = 0;
+    virtual void add(QWidget *widget, UiLayout& layout) = 0;
     virtual void end() {}
 };
 
 class UiBoxContainer : public UiContainerPrivate {
 public:
-    QBoxLayout  *box;
-    bool        hasStretchedWidget = false;
-    QSpacerItem *space;
+    QBoxLayout            *box;
+    bool                  hasStretchedWidget = false;
+    QSpacerItem           *space;
+    QBoxLayout::Direction direction;
     
     UiBoxContainer(QBoxLayout *box);
     
-    virtual void add(QWidget *widget);
+    virtual void add(QWidget *widget, UiLayout& layout);
 };
 
 class UiGridContainer : public UiContainerPrivate {
@@ -120,11 +86,13 @@ public:
             bool def_hfill,
             bool def_vfill);
     
-    virtual void add(QWidget *widget);
+    virtual void add(QWidget *widget, UiLayout& layout);
     virtual void end();
 };
 
-void ui_container_add(UiObject *obj, UiContainerPrivate *ct);
+void ui_obj_add_container(UiObject *obj, UiContainerPrivate *ct);
+
+QWidget* ui_widget_set_margin(QWidget *w, int left, int right, int top, int bottom);
 
 
 #endif /* CONTAINER_H */
index af60ff7b194b85125510d561b4e8c8baf892f7dd..210f48f2aee159f945bfb193ab634af1e44288c4 100644 (file)
 
 
 
-UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
+    
+    double min = args->min;
+    double max = args->max != 0 ? args->max : 100000;
     
     bool use_double = false;
     UiVar *var = NULL;
+    UiVarType vartype = UI_VAR_SPECIAL;
     if(args->varname) {
         var = uic_get_var(obj->ctx, args->varname);
+        vartype = var->type;
         if(var->type == UI_VAR_DOUBLE) {
             use_double = true;
         } else if(var->type == UI_VAR_RANGE) {
@@ -57,11 +61,14 @@ UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
     if(!var) {
         if(args->intvalue) {
             var = uic_widget_var(obj->ctx, obj->ctx, args->intvalue, NULL, UI_VAR_INTEGER);
+            vartype = UI_VAR_INTEGER;
         } else if(args->doublevalue) {
             var = uic_widget_var(obj->ctx, obj->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE);
+            vartype = UI_VAR_DOUBLE;
             use_double = true;
         } else if(args->rangevalue) {
             var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, NULL, UI_VAR_RANGE);
+            vartype = UI_VAR_RANGE;
             use_double = true;
         } else {
             if(args->digits > 0) {
@@ -70,6 +77,12 @@ UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
         }
     }
     
+    if(vartype == UI_VAR_RANGE) {
+        UiRange *r = (UiRange*)var->value;
+        min = r->min;
+        max = r->max;
+    }
+    
     QAbstractSpinBox *widget = nullptr;
     if(use_double) {
         QDoubleSpinBox *spinbox = new QDoubleSpinBox();
@@ -77,17 +90,21 @@ UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
         if(args->step != 0) {
             spinbox->setSingleStep(args->step);
         }
+        spinbox->setMinimum(min);
+        spinbox->setMaximum(max);
         widget = spinbox;
     } else {
         QSpinBox *spinbox = new QSpinBox();
         if(args->step != 0) {
             spinbox->setSingleStep(args->step);
         }
+        spinbox->setMinimum((int)min);
+        spinbox->setMaximum((int)max);
         widget = spinbox;
     }
     
     if(var) {
-        if(var->type == UI_VAR_INTEGER) {
+        if(vartype == UI_VAR_INTEGER) {
             UiInteger *value = (UiInteger*)var->value;
             value->obj = widget;
             if(value->value != 0) {
@@ -96,7 +113,7 @@ UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
             }
             value->get = ui_spinbox_int_get;
             value->set = ui_spinbox_int_set;
-        } else if(var->type == UI_VAR_DOUBLE) {
+        } else if(vartype == UI_VAR_DOUBLE) {
             UiDouble *value = (UiDouble*)var->value;
             value->obj = widget;
             if(value->value != 0) {
@@ -105,7 +122,7 @@ UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
             }
             value->get = ui_spinbox_double_get;
             value->set = ui_spinbox_double_set;
-        } else if(var->type == UI_VAR_RANGE) {
+        } else if(vartype == UI_VAR_RANGE) {
             UiRange *value = (UiRange*)var->value;
             value->obj = widget;
             QDoubleSpinBox *spinbox = (QDoubleSpinBox*)widget;
@@ -125,8 +142,8 @@ UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
         }
     }
     
-    
-    ctn->add(widget);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(widget, layout);
     return widget;
 }
 
index 0101b6a45c9eaa3ddd9243919a6636ec2db82b84..bddc1d40e53b9a6bdf5b5da0da68e41eb7c5da53 100644 (file)
@@ -34,7 +34,6 @@
 
 UIWIDGET ui_label_create(UiObject* obj, UiLabelArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
     
     QString str = QString::fromUtf8(args->label);
     QLabel *widget = new QLabel(str);
@@ -47,7 +46,8 @@ UIWIDGET ui_label_create(UiObject* obj, UiLabelArgs *args) {
     }
     widget->setAlignment(align);
     
-    ctn->add(widget);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(widget, layout);
     
     return widget;
 }
index 6ce56e16aa7f0fa67d6ce94d88f25fcc68c22666..94d34269f78d180aee0fd185d3987d154f608052 100644 (file)
@@ -48,7 +48,6 @@ static void* null_getvalue(UiList *list, void *elm, int row, int col, void *user
 
 UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
     
     QListView *view = new QListView();
     ui_getvaluefunc2 getvalue = nullptr;
@@ -87,15 +86,14 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
             model,
             SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
     
-    
-    ctn->add(view);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(view, layout);
     
     return view;
 }
 
 UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
     
     QTreeView *view = new QTreeView();
     view->setItemsExpandable(false);
@@ -138,7 +136,8 @@ UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) {
             SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
     
     
-    ctn->add(view);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(view, layout);
     
     return view;
 }
index 31c3fa8a855d12ffa77726b9a9f7988ee9a81058..32871a295abb38cbe14052f4cb30421c0e86f2d5 100644 (file)
@@ -59,10 +59,10 @@ static QTextDocument* get_or_create_doc(UiText *value) {
 
 UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
     
     QTextEdit *textarea = new QTextEdit();
-    ctn->add(textarea);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(textarea, layout);
     
     QTextDocument *document = nullptr;
     
@@ -212,10 +212,10 @@ void ui_textarea_remove(UiText *text, int begin, int end) {
 
 static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs *args, bool password, bool frameless) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
     
     QLineEdit *textfield = new QLineEdit();
-    ctn->add(textfield);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(textfield, layout);
     
     if(password) {
         textfield->setEchoMode(QLineEdit::Password);
index 2dd98b812335c287b491a16001c0c708860fda63..c1fa90af7faf2b4a5fcf64bd401aa86b46822c82 100644 (file)
@@ -62,7 +62,6 @@ void ui_init(const char *appname, int argc, char **argv) {
     app_argv = argv;
     application = new QApplication(app_argc, app_argv);
     
-    uic_docmgr_init();
     uic_menu_init();
     uic_toolbar_init();
     
index 2a3cbd29cc39be9847c4b9143266ddbd892936df..0032a0e7774b4f73c7a5a55a040b687dada62926 100644 (file)
@@ -34,8 +34,8 @@
 UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
     UIWIDGET widget = create_widget(obj, args, userdata);
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
-    ctn->add(widget);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(widget, layout);
     return widget;
 }
 
@@ -45,13 +45,20 @@ UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) {
     separator->setFrameShadow(QFrame::Sunken);
     
     UiContainerPrivate *ctn = ui_obj_container(obj);
-    UI_APPLY_LAYOUT(ctn->layout, args);
-    
-    ctn->add(separator);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(separator, layout);
     
     return separator;
 }
 
+void ui_set_enabled(UIWIDGET widget, int enabled) {
+    widget->setEnabled(enabled);
+}
+
+void ui_set_visible(UIWIDGET widget, int visible) {
+    widget->setVisible(visible);
+}
+
 void ui_widget_set_size(UIWIDGET w, int width, int height) {
     w->resize(width >= 0 ? width : w->width(), height >= 0 ? height : w->height());
 }
index bdc8b4e1613f51db8c71a29092808efa6143bd6c..38570d5655511e7b8a879bd104bb91a474fa5ff6 100644 (file)
@@ -62,7 +62,7 @@ static UiObject* create_window(const char *title, void *window_data, bool simple
     QWidget *boxWidget = new QWidget();
     boxWidget->setLayout(box);
     window->setCentralWidget(boxWidget);
-    ui_container_add(obj, new UiBoxContainer(box));
+    ui_obj_add_container(obj, new UiBoxContainer(box));
     if(sidebar) {
         QDockWidget *dock = new QDockWidget();
         window->addDockWidget(Qt::LeftDockWidgetArea, dock);
index fff9383b0af59bdb877aea8d2fb4a672a271a994..693f7309fed15e57102ce35046091eee4d5c131f 100644 (file)
@@ -48,19 +48,24 @@ typedef struct UiButtonArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
     const char *style_class;
 
-    const charlabel;
-    const char* stockid;
-    const char* icon;
+    const char *label;
+    const char *icon;
+    const char *tooltip;
     UiLabelType labeltype;
     ui_callback onclick;
-    voidonclickdata;
+    void *onclickdata;
     
-    const intgroups;
+    const int *groups;
 } UiButtonArgs;
 
 typedef struct UiToggleArgs {
@@ -70,22 +75,27 @@ typedef struct UiToggleArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
     const char *style_class;
     
-    const charlabel;
-    const char* stockid;
-    const char* icon;
+    const char *label;
+    const char *icon;
+    const char *tooltip;
     UiLabelType labeltype;
-    UiIntegervalue;
-    const charvarname;
+    UiInteger *value;
+    const char *varname;
     ui_callback onchange;
-    voidonchangedata;
+    void *onchangedata;
     int enable_group;
     
-    const intgroups;
+    const int *groups;
 } UiToggleArgs;
 
 typedef struct UiLinkButtonArgs {
@@ -95,6 +105,11 @@ typedef struct UiLinkButtonArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
@@ -109,7 +124,7 @@ typedef struct UiLinkButtonArgs {
     UiBool nofollow;
     UiLinkType type;
     
-    const intgroups;
+    const int *groups;
 } UiLinkButtonArgs;
  
 #define ui_button(obj, ...) ui_button_create(obj, &(UiButtonArgs){ __VA_ARGS__ } )
index 75c78b002d14411795982e963ee221e8b9c56593..3355850cbb0ea9b3a1679ac667abd30b5c956b37 100644 (file)
@@ -65,12 +65,16 @@ typedef struct UiContainerArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
     const char *style_class;
 
-    int margin;
     int spacing;
     int columnspacing;
     int rowspacing;
@@ -87,14 +91,19 @@ typedef struct UiFrameArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
     const char *style_class;
 
     UiSubContainerType subcontainer;
-
-    int margin;
+    
+    int padding;
     int spacing;
     int columnspacing;
     int rowspacing;
@@ -110,6 +119,11 @@ typedef struct UiTabViewArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
@@ -124,13 +138,10 @@ typedef struct UiTabViewArgs {
     UiInteger *value;
     const char* varname;
 
-    int margin;
+    int padding;
     int spacing;
     int columnspacing;
     int rowspacing;
-
-    const char* label;
-    UiBool isexpanded;
 } UiTabViewArgs;
 
 typedef struct UiHeaderbarArgs {
@@ -140,6 +151,11 @@ typedef struct UiHeaderbarArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
@@ -156,6 +172,10 @@ typedef struct UiSidebarArgs {
     const char *name;
     const char *style_class;
     int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int spacing;
 } UiSidebarArgs;
 
@@ -166,17 +186,22 @@ typedef struct UiSplitPaneArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
     const char *style_class;
 
-    int margin;
     int spacing;
     int columnspacing;
     int rowspacing;
     
     int initial_position;
+    const char *position_property;
     UiInteger *value;
     const char* varname;
     int max_panes;
@@ -189,12 +214,16 @@ typedef struct UiItemListContainerArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
     const char *style_class;
     
-    int margin;
     int spacing;
     
     int sub_margin;
@@ -227,9 +256,29 @@ typedef struct UiItemListContainerArgs {
     UiSubContainerType subcontainer;
 } UiItemListContainerArgs;
 
+
+typedef struct UiLayout UiLayout;
+struct UiLayout {
+    UiBool       fill;
+    char         *label;
+    UiBool       hexpand;
+    UiBool       vexpand;
+    UiBool       hfill;
+    UiBool       vfill;
+    UiBool       override_defaults;
+    int          margin;
+    int          margin_left;
+    int          margin_right;
+    int          margin_top;
+    int          margin_bottom;
+    int          colspan;
+    int          rowspan;
+};
+
 struct UiContainerX {
     void *container;
-    int close;
+    UiBool close;
+    UiBool newline;
     UiContainerX *prev;
     UiContainerX *next;
 };
@@ -246,6 +295,8 @@ struct UiContainerX {
 #define ui_tabview(obj, ...) for(ui_tabview_create(obj, &(UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_headerbar(obj, ...) for(ui_headerbar_create(obj, &(UiHeaderbarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_sidebar(obj, ...) for(ui_sidebar_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_left_panel(obj, ...) for(ui_left_panel_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_right_panel(ob, ...) for(ui_right_panel_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 
 #define ui_vbox0(obj) for(ui_vbox_create(obj, &(UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_hbox0(obj) for(ui_hbox_create(obj, &(UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
@@ -256,6 +307,9 @@ struct UiContainerX {
 #define ui_tabview0(obj) for(ui_tabview_create(obj, &(UiTabViewArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_headerbar0(obj) for(ui_headerbar_create(obj, &(UiHeaderbarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_sidebar0(obj) for(ui_sidebar_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_left_panel0(obj) for(ui_left_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_right_panel0(obj) for(ui_right_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+
 
 #define ui_vbox_w(obj, w, ...) for(w = ui_vbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_hbox_w(obj, w, ...) for(w = ui_hbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
@@ -301,6 +355,8 @@ UIEXPORT void ui_headerbar_center_create(UiObject *obj);
 UIEXPORT void ui_headerbar_end_create(UiObject *obj);
 
 UIEXPORT UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args);
+UIEXPORT UIWIDGET ui_left_panel_create(UiObject *obj, UiSidebarArgs *args);
+UIEXPORT UIWIDGET ui_right_panel_create(UiObject *obj, UiSidebarArgs *args);
 
 UIEXPORT UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args);
 
@@ -309,18 +365,6 @@ UIEXPORT UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args);
 
 UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBool visible);
 
-// box container layout functions
-UIEXPORT void ui_layout_fill(UiObject *obj, UiBool fill);
-// grid container layout functions
-UIEXPORT void ui_layout_hexpand(UiObject *obj, UiBool expand);
-UIEXPORT void ui_layout_vexpand(UiObject *obj, UiBool expand);
-UIEXPORT void ui_layout_hfill(UiObject *obj, UiBool fill);
-UIEXPORT void ui_layout_vfill(UiObject *obj, UiBool fill);
-UIEXPORT void ui_layout_override_defaults(UiObject *obj, UiBool d);
-UIEXPORT void ui_layout_width(UiObject *obj, int width);
-UIEXPORT void ui_layout_height(UiObject* obj, int width);
-UIEXPORT void ui_layout_colspan(UiObject *obj, int cols);
-UIEXPORT void ui_layout_rowspan(UiObject* obj, int rows);
 UIEXPORT void ui_newline(UiObject *obj);
 
 // TODO
@@ -334,7 +378,7 @@ UIEXPORT void ui_container_begin_close(UiObject *obj);
 UIEXPORT int ui_container_finish(UiObject *obj);
 
 #define UI_APPLY_LAYOUT1(obj, args) \
-    if(args.fill != UI_DEFAULT) ui_layout_fill(obj, args.fill == UI_ON ? 1 : 0 ); \
+    if(args.fill) ui_layout_fill(obj, 1); \
     if(args.hexpand) ui_layout_hexpand(obj, 1); \
     if(args.vexpand) ui_layout_vexpand(obj, 1); \
     if(args.hfill) ui_layout_hfill(obj, 1); \
@@ -345,7 +389,7 @@ UIEXPORT int ui_container_finish(UiObject *obj);
     /*force caller to add ';'*/(void)0
 
 #define UI_APPLY_LAYOUT2(obj, args) \
-    if(args->fill != UI_DEFAULT) ui_layout_fill(obj, args->fill == UI_ON ? 1 : 0 ); \
+    if(args->fill) ui_layout_fill(obj, 1); \
     if(args->hexpand) ui_layout_hexpand(obj, 1); \
     if(args->vexpand) ui_layout_vexpand(obj, 1); \
     if(args->hfill) ui_layout_hfill(obj, 1); \
@@ -355,7 +399,21 @@ UIEXPORT int ui_container_finish(UiObject *obj);
     if(args->rowspan > 0) ui_layout_rowspan(obj, args->rowspan); \
     /*force caller to add ';'*/(void)0
 
-
+#define UI_ARGS2LAYOUT(args) { \
+    .fill = args->fill, \
+    .hexpand = args->hexpand, \
+    .vexpand = args->vexpand, \
+    .hfill = args->hfill, \
+    .vfill = args->vfill, \
+    .override_defaults = args->override_defaults, \
+    .margin = args->margin, \
+    .margin_left = args->margin_left, \
+    .margin_right = args->margin_right, \
+    .margin_top = args->margin_top, \
+    .margin_bottom = args->margin_bottom, \
+    .colspan = args->colspan, \
+    .rowspan = args->rowspan }
+    
 #ifdef __cplusplus
 }
 #endif
index 3e0522241add5930c699005ff1b0b49cfe58948b..1ed1235ba3170ed93a88e6ef88c97e1a92268789 100644 (file)
@@ -56,6 +56,11 @@ typedef struct UiLabelArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
@@ -75,6 +80,11 @@ typedef struct UiProgressbarArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     int width;
@@ -94,6 +104,11 @@ typedef struct UiProgressbarSpinnerArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
index cb1b7f70633f0e67eb0df4c56d9073b897472afb..6dd6a0a5983572a16aa4810db9c6887c5d8b2dc2 100644 (file)
@@ -36,20 +36,28 @@ extern "C" {
 #endif
 
 
-typedef struct UiSpinnerArgs {
+typedef struct UiSpinBoxArgs {
     UiBool fill;
     UiBool hexpand;
     UiBool vexpand;
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
+    int width;
     const char *name;
     const char *style_class;
 
     double step;
     int digits;
+    double min;
+    double max;
     UiInteger *intvalue;
     UiDouble* doublevalue;
     UiRange *rangevalue;
@@ -58,13 +66,13 @@ typedef struct UiSpinnerArgs {
     void* onchangedata;
     
     const int *groups;
-} UiSpinnerArgs;
+} UiSpinBoxArgs;
 
 
     
-UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args);
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args);
 
-#define ui_spinner(obj, ...) ui_spinner_create(obj, &(UiSpinnerArgs){ __VA_ARGS__ } )
+#define ui_spinbox(obj, ...) ui_spinbox_create(obj, &(UiSpinBoxArgs){ __VA_ARGS__ } )
 
 void ui_spinner_setrange(UIWIDGET spinner, double min, double max);
 void ui_spinner_setdigits(UIWIDGET spinner, int digits);
index 6eb0daf3fa273ae20686595396f516dfc8bc22ee..e0063b974f0f03b152c0b646dda50ff5725eff65 100644 (file)
@@ -45,25 +45,53 @@ struct UiGraphics {
     int height;
 };
 
-UIWIDGET ui_drawingarea(UiObject *obj, ui_drawfunc f, void *userdata);
-void ui_drawingarea_mousehandler(UiObject *obj, UIWIDGET widget, ui_callback f, void *u);
-void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height);
-void ui_drawingarea_redraw(UIWIDGET drawingarea);
+typedef struct UiDrawingAreaArgs {
+    UiBool fill;
+    UiBool hexpand;
+    UiBool vexpand;
+    UiBool hfill;
+    UiBool vfill;
+    UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
+    int colspan;
+    int rowspan;
+    const char *name;
+    const char *style_class;
+    
+    int width;
+    int height;
+    ui_drawfunc draw;
+    void *drawdata;
+    ui_callback onclick;
+    void *onclickdata;
+    ui_callback onmotion;
+    void *onmotiondata;
+} UiDrawingAreaArgs;
+
+#define ui_drawingarea(obj, ...) ui_drawingarea_create(obj, &(UiDrawingAreaArgs) { __VA_ARGS__ } )
+
+UIEXPORT UIWIDGET ui_drawingarea_create(UiObject *obj, UiDrawingAreaArgs *args);
+UIEXPORT void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height);
+UIEXPORT void ui_drawingarea_redraw(UIWIDGET drawingarea);
 
 // text layout
-UiTextLayout* ui_text(UiGraphics *g);
-void ui_text_free(UiTextLayout *text);
-void ui_text_setstring(UiTextLayout *layout, char *str);
-void ui_text_setstringl(UiTextLayout *layout, char *str, int len);
-void ui_text_setfont(UiTextLayout *layout, char *font, int size);
-void ui_text_getsize(UiTextLayout *layout, int *width, int *height);
-void ui_text_setwidth(UiTextLayout *layout, int width);
+UIEXPORT UiTextLayout* ui_text(UiGraphics *g);
+UIEXPORT void ui_text_free(UiTextLayout *text);
+UIEXPORT void ui_text_setstring(UiTextLayout *layout, char *str);
+UIEXPORT void ui_text_setstringl(UiTextLayout *layout, char *str, int len);
+UIEXPORT void ui_text_setfont(UiTextLayout *layout, const char *font, int size);
+UIEXPORT void ui_text_getsize(UiTextLayout *layout, int *width, int *height);
+UIEXPORT void ui_text_setwidth(UiTextLayout *layout, int width);
 
 // drawing functions
-void ui_graphics_color(UiGraphics *g, int red, int green, int blue);
-void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2);
-void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, int fill);
-void ui_draw_text(UiGraphics *g, int x, int y, UiTextLayout *text);
+UIEXPORT void ui_graphics_color(UiGraphics *g, int red, int green, int blue);
+UIEXPORT void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2);
+UIEXPORT void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, UiBool fill);
+UIEXPORT void ui_draw_text(UiGraphics *g, int x, int y, UiTextLayout *text);
 
 #ifdef __cplusplus
 }
index 405b50946cdc4ddf77261375d410f80a82798fab..77d923fe49967317f353aafb84a41067399e4989 100644 (file)
@@ -51,6 +51,11 @@ typedef struct UiImageViewerArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
index 3f805b886e78278683a87c048d1d32d83d760d93..9dde9b4546bca50c1b7d60793d8b009b06dedd1e 100644 (file)
@@ -38,7 +38,6 @@ extern "C" {
 
 typedef struct UiMenuItemArgs {
        const char* label;
-       const char* stockid;
        const char* icon;
 
        ui_callback onclick;
@@ -49,7 +48,6 @@ typedef struct UiMenuItemArgs {
 
 typedef struct UiMenuToggleItemArgs {
        const char* label;
-       const char* stockid;
        const char* icon;
 
        const char* varname;
@@ -96,7 +94,8 @@ UIEXPORT void ui_menu_end(void); // TODO: private
 #define ui_contextmenu(builder) for(ui_contextmenu_builder(builder);ui_menu_is_open();ui_menu_close())
 
 UIEXPORT void ui_contextmenu_builder(UiMenuBuilder **out_builder);
-UIEXPORT void ui_menubuilder_free(UiMenuBuilder *builder);
+UIEXPORT void ui_menubuilder_ref(UiMenuBuilder *builder);
+UIEXPORT void ui_menubuilder_unref(UiMenuBuilder *builder);
 UIEXPORT UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, UIWIDGET widget);
 UIEXPORT void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y);
 
index 5c985d9a1532a6185f4fab4c019d6829c942e3c2..ffb10ddbb00242f288965b20649b15de5db7b3e6 100644 (file)
@@ -41,14 +41,14 @@ const char* ui_set_default_property(const char *name, const char *value);
 
 int ui_properties_store(void);
 
-void ui_locales_dir(char *path);
-void ui_pixmaps_dir(char *path);
+void ui_locales_dir(const char *path);
+void ui_pixmaps_dir(const char *path);
 
-void ui_load_lang(char *locale);
+void ui_load_lang(const char *locale);
 void ui_load_lang_def(char *locale, char *default_locale);
     
-char* uistr(char *name);
-char* uistr_n(char *name);
+char* uistr(const char *name);
+char* uistr_n(const char *name);
 
 #ifdef __cplusplus
 }
index 0daf925a8504fb768a2d75f567754052a83f01cb..ac5a2c1d3a16fb645fafbc08fea94e15f820e218 100644 (file)
@@ -42,9 +42,15 @@ typedef struct UiTextAreaArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     int width;
+    int height;
     const char *name;
     const char *style_class;
 
@@ -63,6 +69,11 @@ typedef struct UiTextFieldArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     int width;
@@ -97,6 +108,11 @@ typedef struct UiPathTextFieldArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
index 4b42af2a2e92c5d1c4fb309f9332c3aef1521f98..066cee26ae8058846a1e87bde1518fdb073aa539 100644 (file)
@@ -37,38 +37,43 @@ extern "C" {
 #endif
 
 typedef struct UiToolbarItemArgs {
-       const char* label;
-       const char* stockid;
-       const char* icon;
-
-       ui_callback onclick;
-       void* onclickdata;
-        
-        const int *groups;
+    const char *label;
+    const char *icon;
+    const char *tooltip;
+
+    ui_callback onclick;
+    void* onclickdata;
+
+    const int *groups;
 } UiToolbarItemArgs;
 
 typedef struct UiToolbarToggleItemArgs {
-       const char* label;
-       const char* stockid;
-       const char* icon;
-
-       const char* varname;
-       ui_callback onchange;
-       void* onchangedata;
-        
-        const int *groups;
+    const char *label;
+    const char *icon;
+    const char *tooltip;
+
+    const char *varname;
+    ui_callback onchange;
+    void *onchangedata;
+
+    const int *groups;
 } UiToolbarToggleItemArgs;
 
 typedef struct UiToolbarMenuArgs {
-       const char* label;
-       const char* stockid;
-       const char* icon;
+    const char *label;
+    const char *icon;
+    const char *tooltip;
 } UiToolbarMenuArgs;
 
 enum UiToolbarPos {
-       UI_TOOLBAR_LEFT = 0,
-       UI_TOOLBAR_CENTER,
-       UI_TOOLBAR_RIGHT
+    UI_TOOLBAR_LEFT = 0,
+    UI_TOOLBAR_CENTER,
+    UI_TOOLBAR_RIGHT,
+    UI_TOOLBAR_SIDEBAR_LEFT,
+    UI_TOOLBAR_SIDEBAR_RIGHT,
+    UI_TOOLBAR_RIGHTPANEL_LEFT,
+    UI_TOOLBAR_RIGHTPANEL_CENTER,
+    UI_TOOLBAR_RIGHTPANEL_RIGHT
 };
 
 #define ui_toolbar_item(name, ...) ui_toolbar_item_create(name, &(UiToolbarItemArgs){ __VA_ARGS__ } )
index d412156cafaea5c164520a71a5d6f0b0263647c1..6c2433014df2d25ae014a25007a645e4f0475dec 100644 (file)
@@ -88,13 +88,7 @@ typedef void* UIMENU;   // NSMenu*
 
 #elif UI_WIN32
 
-#include <Windows.h>
-
-#define UIEXPORT __declspec(dllexport)
-
-typedef struct W32Widget {
-    HWND hwnd;
-} W32Widget;
+#include "win32.h"
 
 #define UIWIDGET W32Widget*
 #define UIWINDOW void*
@@ -200,6 +194,9 @@ typedef struct UiFileList   UiFileList;
 
 typedef struct UiListSelection UiListSelection;
 
+typedef struct UiTextStyle  UiTextStyle;
+typedef struct UiColor      UiColor;
+
 /* begin opaque types */
 typedef struct UiContext     UiContext;
 typedef struct UiContainer   UiContainer;
@@ -251,6 +248,7 @@ typedef void(*ui_callback)(UiEvent*, void*); /* event, user data */
 
 typedef void*(*ui_getvaluefunc)(void *elm, int col);
 typedef void*(*ui_getvaluefunc2)(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult);
+typedef UiBool(*ui_getstylefunc)(UiList *list, void *elm, int row, int col, void *userdata, UiTextStyle *style);
 
 typedef int(*ui_threadfunc)(void*);
 
@@ -281,11 +279,6 @@ struct UiObject {
      */
     UiContext   *ctx;
     
-    /*
-     * container interface (deprecated)
-     */
-    UiContainer *container;
-    
     /*
      * container list
      * TODO: remove old UiContainer and rename UiContainerX to UiContainer
@@ -502,6 +495,23 @@ enum UiEventType {
     UI_EVENT_DATA_FILE_LIST
 };
 
+#define UI_COLOR(r, g, b) (UiColor){r, g, b}
+struct UiColor {
+    uint8_t red;
+    uint8_t green;
+    uint8_t blue;
+};
+
+#define UI_TEXT_STYLE_BOLD      1
+#define UI_TEXT_STYLE_ITALIC    2
+#define UI_TEXT_STYLE_UNDERLINE 4
+
+struct UiTextStyle {
+    uint32_t text_style;
+    UiColor  fg;
+    UiBool   fg_set;
+};
+
 
 UIEXPORT void ui_init(const char *appname, int argc, char **argv);
 UIEXPORT const char* ui_appname();
@@ -547,6 +557,8 @@ UIEXPORT void ui_attach_document(UiContext *ctx, void *document);
 UIEXPORT void ui_detach_document(UiContext *ctx, void *document);
 
 UIEXPORT void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...);
+UIEXPORT void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, int *groups, int ngroups);
+UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, int *states, int nstates);
 
 UIEXPORT void ui_set_group(UiContext *ctx, int group);
 UIEXPORT void ui_unset_group(UiContext *ctx, int group);
@@ -590,6 +602,13 @@ UIEXPORT void ui_string_set(UiString *s, const char *value);
 UIEXPORT char* ui_string_get(UiString *s);
 UIEXPORT void ui_text_set(UiText *s, const char* value);
 UIEXPORT char* ui_text_get(UiText *s);
+UIEXPORT void ui_range_set(UiRange *r, double value);
+UIEXPORT void ui_range_set_range(UiRange *r, double min, double max);
+UIEXPORT void ui_range_set_extent(UiRange *r, double extent);
+UIEXPORT double ui_range_get(UiRange *r);
+UIEXPORT double ui_range_get_min(UiRange *r);
+UIEXPORT double ui_range_get_max(UiRange *r);
+UIEXPORT double ui_range_get_extent(UiRange *r);
 UIEXPORT void ui_generic_set_image(UiGeneric *g, void *img);
 UIEXPORT void* ui_generic_get_image(UiGeneric *g);
 
@@ -636,11 +655,6 @@ UIEXPORT char* ui_clipboard_get();
 
 UIEXPORT void ui_add_image(char *imgname, char *filename); // TODO: remove?
 
-// general widget functions
-UIEXPORT void ui_set_enabled(UIWIDGET widget, int enabled);
-UIEXPORT void ui_set_show_all(UIWIDGET widget, int value);
-UIEXPORT void ui_set_visible(UIWIDGET widget, int visible);
-
 
 
 UIEXPORT void ui_listselection_free(UiListSelection selection);
@@ -651,7 +665,7 @@ UIEXPORT UiStr ui_str_free(char *str, void (*free)(void *v));
 
 
 UIEXPORT char* ui_getappdir(void);
-UIEXPORT char* ui_configfile(char *name);
+UIEXPORT char* ui_configfile(const char *name);
 
 UIEXPORT UiCondVar* ui_condvar_create(void);
 UIEXPORT void ui_condvar_wait(UiCondVar *var);
index 4a1e59014b6bbdf8f28b05e6574f7d1238fc5d01..33a903c06196df279a8d87a2c6005bc1700c06f0 100644 (file)
@@ -51,15 +51,33 @@ typedef enum UiModelType {
     UI_INTEGER,
     UI_ICON,
     UI_ICON_TEXT,
-    UI_ICON_TEXT_FREE
+    UI_ICON_TEXT_FREE,
+    UI_STRING_EDITABLE,
+    UI_BOOL_EDITABLE
 } UiModelType;
 
+typedef struct UiCellValue {
+    union {
+        const char *string;
+        int64_t i;
+        UiBool b;
+    };
+    UiModelType type;
+} UiCellValue;
+
+typedef UiBool (*ui_list_savefunc)(UiList *list, int row, int col, UiCellValue *value, void *userdata);
+
 struct UiModel {
     /*
      * number of columns
      */
     int columns;
     
+    /*
+     * current allocation size (internal)
+     */
+    int alloc;
+    
     /*
      * array of column types
      * array length is the number of columns
@@ -102,11 +120,18 @@ struct UiListArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
+    int width;
+    int height;
+
     const char *name;
     const char *style_class;
-
     UiList* list;
     const char* varname;
     UiModel* model;
@@ -115,6 +140,8 @@ struct UiListArgs {
     ui_getvaluefunc getvalue;
     ui_getvaluefunc2 getvalue2;
     void *getvalue2data;
+    ui_getstylefunc getstyle;
+    void *getstyledata;
     ui_callback onactivate;
     void* onactivatedata;
     ui_callback onselection;
@@ -127,6 +154,8 @@ struct UiListArgs {
     void* ondropdata;
     UiBool multiselection;
     UiMenuBuilder *contextmenu;
+    ui_list_savefunc onsave;
+    void *onsavedata;
     
     const int *groups;
 };
@@ -156,12 +185,13 @@ typedef struct UiSubListEventData {
  * will be passed to free
  */
 struct UiSubListItem {
-    char *icon;
-    char *label;
-    char *button_icon;
-    char *button_label;
-    char *badge;
-    void *eventdata;
+    char          *icon;
+    char          *label;
+    char          *button_icon;
+    char          *button_label;
+    UiMenuBuilder *button_menu;
+    char          *badge;
+    void          *eventdata;
 };
 
 struct UiSourceListArgs {
@@ -171,8 +201,15 @@ struct UiSourceListArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
+    int width;
+    int height;
     const char *name;
     const char *style_class;
     
@@ -218,6 +255,11 @@ struct UiSourceListArgs {
      */
     void *getvaluedata;
     
+    /*
+     * is a sublist header a selectable item
+     */
+    UiBool header_is_item;
+    
     /*
      * activated when a list item is selected
      */
@@ -247,6 +289,8 @@ struct UiSourceListArgs {
  * UiModel *model = ui_model(ctx, UI_STRING, "Column 1", UI_STRING, "Column 2", -1);
  */
 UIEXPORT UiModel* ui_model(UiContext *ctx, ...);
+UIEXPORT UiModel* ui_model_new(UiContext *ctx);
+UIEXPORT void ui_model_add_column(UiContext *ctx, UiModel *model, UiModelType type, const char *title, int width);
 UIEXPORT UiModel* ui_model_copy(UiContext *ctx, UiModel* model);
 UIEXPORT void ui_model_free(UiContext *ctx, UiModel *mi);
 
@@ -270,10 +314,23 @@ UIEXPORT void ui_sublist_item_set_icon(UiSubListItem *item, const char *icon);
 UIEXPORT void ui_sublist_item_set_label(UiSubListItem *item, const char *label);
 UIEXPORT void ui_sublist_item_set_button_icon(UiSubListItem *item, const char *button_icon);
 UIEXPORT void ui_sublist_item_set_button_label(UiSubListItem *item, const char *button_label);
+UIEXPORT void ui_sublist_item_set_button_menu(UiSubListItem *item, UiMenuBuilder *menu);
 UIEXPORT void ui_sublist_item_set_badge(UiSubListItem *item, const char *badge);
 UIEXPORT void ui_sublist_item_set_eventdata(UiSubListItem *item, void *eventdata);
 
 
+
+/*
+ * Only relevant for some language bindings
+ */
+typedef void(*ui_sourcelist_update_func)(void);
+
+/*
+ * The sourcelist update callback is called after any source list
+ * sublist update is completed
+ */
+UIEXPORT void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb);
+
 #ifdef __cplusplus
 }
 #endif
index 36f77f97a7d0a67491e4bd39c0bbe446f4668960..90d5268b719198e23348c1dcba1f57756b1122ca 100644 (file)
@@ -39,12 +39,17 @@ extern "C" {
 #define UI_WEBVIEW_OBJECT_TYPE "webview"
     
 typedef struct UiWebviewArgs {
-    UiTri fill;
+    UiBool fill;
     UiBool hexpand;
     UiBool vexpand;
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
index fee08d90e27aa50e99b1d07e0694b8d2b2ff1b17..aea5b8eb3e7967276106c25b30be9667f7de0fa0 100644 (file)
@@ -41,6 +41,11 @@ typedef struct UiWidgetArgs {
     UiBool hfill;
     UiBool vfill;
     UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
     int colspan;
     int rowspan;
     const char *name;
@@ -69,6 +74,9 @@ UIEXPORT UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args);
 
 #define ui_separator(obj, ...) ui_separator_create(obj, &(UiWidgetArgs){ __VA_ARGS__ } )
 
+UIEXPORT void ui_set_enabled(UIWIDGET widget, int enabled);
+UIEXPORT void ui_set_visible(UIWIDGET widget, int visible);
+
 UIEXPORT void ui_widget_set_size(UIWIDGET w, int width, int height);
 UIEXPORT void ui_widget_redraw(UIWIDGET w);
 
diff --git a/ui/ui/win32.h b/ui/ui/win32.h
new file mode 100644 (file)
index 0000000..54d0543
--- /dev/null
@@ -0,0 +1,71 @@
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2025 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above copyright\r
+ *      notice, this list of conditions and the following disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above copyright\r
+ *      notice, this list of conditions and the following disclaimer in the\r
+ *      documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#ifndef UI_WIN32_H\r
+#define UI_WIN32_H\r
+\r
+#include <Windows.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+#define UIEXPORT __declspec(dllexport)\r
+\r
+typedef struct W32WidgetClass W32WidgetClass;\r
+typedef struct W32Widget W32Widget;\r
+typedef struct W32Size W32Size;\r
+\r
+typedef void (*W32LayoutFunc)(void *, int, int);\r
+\r
+struct W32Size {\r
+    int width;\r
+    int height;\r
+};\r
+\r
+struct W32WidgetClass {\r
+    void (*eventproc)(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\r
+    void (*show)(W32Widget *widget, BOOLEAN show);\r
+    void (*enable)(W32Widget *widget, BOOLEAN enable);\r
+    W32Size (*get_preferred_size)(W32Widget *widget);\r
+    void (*destroy)(W32Widget *widget);\r
+};\r
+\r
+struct W32Widget {\r
+    W32WidgetClass *wclass;\r
+    HWND hwnd;\r
+    void *userdata;\r
+    void (*layout)(void *layout, int width, int height);\r
+    void *layoutmanager;\r
+};\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif /* UI_WIN32_H */\r
index 78703c2c616eed7157be58289bc1b61202c1a492..bbb6f59f9d690999200f5e4cab070e42df919085 100644 (file)
@@ -74,6 +74,7 @@ typedef struct UiDialogWindowArgs {
 
 UIEXPORT UiObject *ui_window(const char *title, void *window_data);
 UIEXPORT UiObject *ui_sidebar_window(const char *title, void *window_data);
+UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar);
 UIEXPORT UiObject *ui_simple_window(const char *title, void *window_data);
 UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args);
 
@@ -81,6 +82,12 @@ UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs
 #define ui_dialog_window0(parent) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ 0 });
 
 UIEXPORT void ui_window_size(UiObject *obj, int width, int height);
+UIEXPORT void ui_window_default_size(int width, int height);
+
+UIEXPORT void ui_splitview_window_set_pos(UiObject *obj, int pos);
+UIEXPORT int ui_splitview_window_get_pos(UiObject *obj);
+UIEXPORT void ui_splitview_window_set_default_pos(int pos);
+UIEXPORT void ui_splitview_window_use_property(UiBool enable);
 
 #define ui_dialog(parent, ...) ui_dialog_create(parent, &(UiDialogArgs){ __VA_ARGS__ } )
 
index 2cb2e17a1cc07c6be5ba223df802a29c3322570c..ed6520a63ba5102803729139f3bce78808b7870e 100644 (file)
  */\r
 \r
 #include "button.h"\r
+#include "widget.h"\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include <commctrl.h>\r
+\r
+static W32WidgetClass button_widget_class = {\r
+    .eventproc = ui_button_eventproc,\r
+    .enable = w32_widget_default_enable,\r
+    .show = w32_widget_default_show,\r
+    .get_preferred_size = ui_button_get_preferred_size,\r
+    .destroy  = w32_widget_default_destroy\r
+};\r
 \r
 UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) {\r
-    return NULL;\r
+    HINSTANCE hInstance = GetModuleHandle(NULL);\r
+    UiContainerPrivate *container = ui_obj_container(obj);\r
+    HWND parent = ui_container_get_parent(container);\r
+    UiLayout layout = UI_ARGS2LAYOUT(args);\r
+\r
+    HWND hwnd = CreateWindow(\r
+            "BUTTON",\r
+            args->label,\r
+            WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,\r
+            0, 0, 100, 30,\r
+            parent,\r
+            (HMENU)0,\r
+            hInstance,\r
+            NULL);\r
+    ui_win32_set_ui_font(hwnd);\r
+\r
+    W32Widget *widget = w32_widget_create(&button_widget_class, hwnd, sizeof(UiWidget));\r
+    ui_container_add(container, widget, &layout);\r
+\r
+    UiWidget *btn = (UiWidget*)widget;\r
+    btn->obj = obj;\r
+    btn->callback = args->onclick;\r
+    btn->callbackdata = args->onclickdata;\r
+\r
+    return widget;\r
+}\r
+\r
+W32Size ui_button_get_preferred_size(W32Widget *widget) {\r
+    W32Size size;\r
+    size.width = 100;\r
+    size.height = 30;\r
+    SIZE sz;\r
+    if (Button_GetIdealSize(widget->hwnd, &sz)) {\r
+        size.width = sz.cx;\r
+        size.height = sz.cy;\r
+    }\r
+    return size;\r
+}\r
+\r
+void ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {\r
+    UiWidget *w = (UiWidget*)widget;\r
+\r
+    UiEvent e;\r
+    e.obj = w->obj;\r
+    e.document = e.obj->ctx->document;\r
+    e.window = e.obj->window;\r
+    e.eventdata = NULL;\r
+    e.eventdatatype = 0;\r
+    e.intval = 0;\r
+    e.set = ui_get_setop();\r
+\r
+    if (w->callback) {\r
+        w->callback(&e, w->callbackdata);\r
+    }\r
 }\r
index 6a79f7b37211f4184659df9aaabfc6c054795cbc..8770e892a3f0e77ede10b91018d43e5fefa37f09 100644 (file)
@@ -32,4 +32,8 @@
 #include "../ui/button.h"\r
 #include "container.h"\r
 \r
+W32Size ui_button_get_preferred_size(W32Widget *widget);\r
+\r
+void ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\r
+\r
 #endif //BUTTON_H\r
index 251be4a422bbe62562b6c12396317a887ee84247..9ae6f2629da9f3cfa1ea1055266e4f42685eced4 100644 (file)
  */\r
 \r
 #include "container.h"\r
+#include "grid.h"\r
+\r
+#include "../common/context.h"\r
+#include "../common/container.h"\r
+#include "../motif/container.h"\r
+\r
+\r
+static W32WidgetClass grid_layout_widget_class = {\r
+    .eventproc = NULL,\r
+    .enable = NULL,\r
+    .show = w32_widget_default_show,\r
+    .get_preferred_size = ui_grid_layout_get_preferred_size,\r
+    .destroy  = w32_widget_default_destroy\r
+};\r
+\r
+UiContainerPrivate* ui_obj_container(UiObject *obj) {\r
+    return (UiContainerPrivate*)obj->container_end;\r
+}\r
+\r
+HWND ui_container_get_parent(UiContainerPrivate *ctn) {\r
+    return ctn->parent ? ctn->parent(ctn) : ctn->hwnd;\r
+}\r
+\r
+void ui_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout) {\r
+    UiLayout layout2 = *layout;\r
+    if (layout2.margin > 0) {\r
+        layout2.margin_left = layout2.margin;\r
+        layout2.margin_right = layout2.margin;\r
+        layout2.margin_top = layout2.margin;\r
+        layout2.margin_bottom = layout2.margin;\r
+    }\r
+    ctn->add(ctn, widget, &layout2);\r
+    ctn->container.newline = FALSE;\r
+}\r
+\r
+W32Size ui_grid_layout_get_preferred_size(W32Widget *widget) {\r
+    UiGridLayout *grid = widget->layoutmanager;\r
+    W32Size size;\r
+    size.width = grid->preferred_width;\r
+    size.height = grid->preferred_height;\r
+    return size;\r
+}\r
 \r
 \r
 /* ---------------------------- Box Container ---------------------------- */\r
@@ -45,4 +87,118 @@ UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args) {
     return box_create(obj, args, UI_BOX_HORIZONTAL);\r
 }\r
 \r
+UiContainerX* ui_box_container_create(UiObject *obj, HWND hwnd, UiBoxOrientation orientation, short spacing, GridEdgeInsets padding) {\r
+    UiBoxContainer *container = cxZalloc(obj->ctx->allocator, sizeof(UiBoxContainer));\r
+    container->container.hwnd = hwnd;\r
+    container->container.add = ui_box_container_add;\r
+    container->layout = ui_grid_layout_create(obj->ctx->allocator, spacing, spacing);\r
+    container->layout->padding = padding;\r
+    container->orientation = orientation;\r
+    return (UiContainerX*)container;\r
+}\r
+\r
+void ui_box_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout) {\r
+    UiBoxContainer *box = (UiBoxContainer*)ctn;\r
+    GridLayoutInfo gridLayout = (GridLayoutInfo) {\r
+        .margin = (GridEdgeInsets) { layout->margin_top, layout->margin_bottom, layout->margin_left, layout->margin_right },\r
+    };\r
+    if (box->orientation == UI_BOX_HORIZONTAL) {\r
+        gridLayout.vexpand = TRUE;\r
+        gridLayout.vfill = TRUE;\r
+        gridLayout.hexpand = layout->fill;\r
+        gridLayout.hfill = layout->fill;\r
+    } else {\r
+        gridLayout.hexpand = TRUE;\r
+        gridLayout.hfill = TRUE;\r
+        gridLayout.vexpand = layout->fill;\r
+        gridLayout.vfill = layout->fill;\r
+    }\r
+    ui_grid_add_widget(box->layout, box->x, box->y, widget, &gridLayout);\r
+    if (box->orientation == UI_BOX_HORIZONTAL) {\r
+        box->x++;\r
+    } else {\r
+        box->y++;\r
+    }\r
+}\r
+\r
+/* ---------------------------- Grid Container ---------------------------- */\r
+\r
+UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {\r
+    HINSTANCE hInstance = GetModuleHandle(NULL);\r
+    UiContainerPrivate *container = ui_obj_container(obj);\r
+    HWND parent = ui_container_get_parent(container);\r
+    UiLayout layout = UI_ARGS2LAYOUT(args);\r
+\r
+    HWND hwnd = CreateWindowEx(\r
+            0,\r
+            TEXT("STATIC"),\r
+            NULL,\r
+            WS_CHILD | WS_VISIBLE,\r
+            0, 0, 100, 100,\r
+            parent,\r
+            NULL,\r
+            hInstance,\r
+            NULL);\r
+\r
+    W32Widget *widget = w32_widget_new(&grid_layout_widget_class, hwnd);\r
+    ui_container_add(container, widget, &layout);\r
+\r
+    UiContainerX *gridContainer = ui_grid_container_create(obj, hwnd, args->columnspacing, args->rowspacing, INSETS_ZERO);\r
+    uic_object_push_container(obj, gridContainer);\r
+\r
+    UiGridLayoutContainer *grid = (UiGridLayoutContainer*)gridContainer;\r
+    widget->layout = (W32LayoutFunc)ui_grid_layout;\r
+    widget->layoutmanager = grid->layout;\r
+    grid->layout->preferred_width = 200;\r
+    grid->layout->preferred_height = 200;\r
+\r
+    return widget;\r
+}\r
+\r
+UiContainerX* ui_grid_container_create(UiObject *obj, HWND hwnd, short columnspacing, short rowspacing, GridEdgeInsets padding) {\r
+    UiGridLayoutContainer *container = cxZalloc(obj->ctx->allocator, sizeof(UiGridLayoutContainer));\r
+    container->container.hwnd = hwnd;\r
+    container->container.add = ui_grid_container_add;\r
+    container->layout = ui_grid_layout_create(obj->ctx->allocator, columnspacing, rowspacing);\r
+    container->layout->padding = padding;\r
+    return (UiContainerX*)container;\r
+}\r
+\r
+void ui_grid_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout) {\r
+    UiGridLayoutContainer *grid = (UiGridLayoutContainer*)ctn;\r
+    if (ctn->container.newline) {\r
+        grid->y++;\r
+        grid->x = 0;\r
+    }\r
+\r
+    uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill);\r
+    GridLayoutInfo gridLayout = (GridLayoutInfo) {\r
+        .margin = (GridEdgeInsets) { layout->margin_top, layout->margin_bottom, layout->margin_left, layout->margin_right },\r
+        .colspan = layout->colspan,\r
+        .rowspan = layout->rowspan,\r
+        .hexpand = layout->hexpand,\r
+        .vexpand = layout->vexpand,\r
+        .hfill = layout->hfill,\r
+        .vfill = layout->vfill,\r
+    };\r
+    ui_grid_add_widget(grid->layout, grid->x, grid->y, widget, &gridLayout);\r
+\r
+    grid->x++;\r
+}\r
+\r
+\r
+/* ---------------------------- Container Helper ---------------------------- */\r
+\r
+void ui_container_begin_close(UiObject *obj) {\r
+    UiContainerX *ct = obj->container_end;\r
+    ct->close = 1;\r
+}\r
 \r
+int ui_container_finish(UiObject *obj) {\r
+    UiContainerX *ct = obj->container_end;\r
+    if(ct->close) {\r
+        ui_end_new(obj);\r
+        return 0;\r
+    }\r
+    return 1;\r
+}
\ No newline at end of file
index f1d517e24ac6ef2fd1ad9ee8867e1ee8806e2037..8809ab222d3459359d0331b7a0918a51e57622d7 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
-* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
  *\r
  * Copyright 2025 Olaf Wintermann. All rights reserved.\r
  *\r
  */\r
 \r
 #ifndef CONTAINER_H\r
+#define CONTAINER_H\r
 \r
 #include "../ui/container.h"\r
+#include "toolkit.h"\r
+#include "grid.h"\r
 \r
-#define CONTAINER_H\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
 \r
-#define UI_APPLY_LAYOUT(layout, args) \\r
-    layout.fill = args->fill; \\r
-    layout.hexpand = args->hexpand; \\r
-    layout.vexpand = args->vexpand; \\r
-    layout.hfill = args->hfill; \\r
-    layout.vfill = args->vfill; \\r
-    layout.override_defaults = args->override_defaults; \\r
-    layout.colspan = args->colspan; \\r
-    layout.rowspan = args->rowspan\r
-\r
-typedef struct UiLayout UiLayout;\r
-\r
-struct UiLayout {\r
-    UiBool       fill;\r
-    UiBool       newline;\r
-    char         *label;\r
-    UiBool       hexpand;\r
-    UiBool       vexpand;\r
-    UiBool       hfill;\r
-    UiBool       vfill;\r
-    UiBool       override_defaults;\r
-    int          width;\r
-    int          colspan;\r
-    int          rowspan;\r
-};\r
+typedef struct UiContainerPrivate    UiContainerPrivate;\r
+typedef struct UiGridLayoutContainer UiGridLayoutContainer;\r
+typedef struct UiBoxContainer        UiBoxContainer;\r
 \r
 enum UiBoxOrientation {\r
     UI_BOX_VERTICAL = 0,\r
@@ -70,8 +53,6 @@ enum UiContainerType {
 };\r
 typedef enum UiContainerType UiContainerType;\r
 \r
-typedef struct UiContainerPrivate UiContainerPrivate;\r
-\r
 typedef struct UiRect {\r
     int x;\r
     int y;\r
@@ -82,10 +63,50 @@ typedef struct UiRect {
 \r
 struct UiContainerPrivate {\r
     UiContainerX    container;\r
-    void            (*prepare)(UiContainerPrivate*, UiRect*);\r
-    void            (*add)(UiContainerPrivate*, UiRect*, W32Widget*);\r
+    HWND            (*parent)(UiContainerPrivate*);\r
+    void            (*add)(UiContainerPrivate*, W32Widget*, UiLayout*);\r
     UiContainerType type;\r
-    UiLayout        layout;\r
+    HWND            hwnd;\r
 };\r
 \r
+struct UiBoxContainer {\r
+    UiContainerPrivate container;\r
+    UiGridLayout *layout;\r
+    UiBoxOrientation orientation;\r
+    int x;\r
+    int y;\r
+};\r
+\r
+struct UiGridLayoutContainer {\r
+    UiContainerPrivate container;\r
+    UiGridLayout *layout;\r
+    int x;\r
+    int y;\r
+    UiBool def_hexpand;\r
+    UiBool def_vexpand;\r
+    UiBool def_hfill;\r
+    UiBool def_vfill;\r
+};\r
+\r
+UiContainerPrivate* ui_obj_container(UiObject *obj);\r
+HWND ui_container_get_parent(UiContainerPrivate *ctn);\r
+void ui_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout);\r
+\r
+W32Size ui_grid_layout_get_preferred_size(W32Widget *widget);\r
+\r
+UiContainerX* ui_box_container_create(UiObject *obj, HWND hwnd, UiBoxOrientation orientation, short spacing, GridEdgeInsets padding);\r
+void ui_box_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout);\r
+\r
+UiContainerX* ui_grid_container_create(\r
+    UiObject *obj,\r
+    HWND hwnd,\r
+    short columnspacing,\r
+    short rowspacing,\r
+    GridEdgeInsets padding);\r
+void ui_grid_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
 #endif //CONTAINER_H\r
index b37986ffbec22e8f7ebdde04d3d6099f07a89cda..023a16be07a99b428ed45b9f808a87f3f0a0d6b2 100644 (file)
 #include "../../ucx/cx/array_list.h"\r
 #include "../common/context.h"\r
 \r
-UiGridLayout* ui_grid_container(UiObject *obj, HWND control, short padding, short columnspacing, short rowspacing) {\r
-    UiGridLayout *grid = cxZalloc(obj->ctx->allocator, sizeof(UiGridLayout));\r
-    grid->hwnd = control;\r
-    grid->widgets = cxArrayListCreate(obj->ctx->allocator, NULL, sizeof(GridElm), 32);\r
-    grid->padding = padding;\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+UiGridLayout* ui_grid_layout_create(const CxAllocator *a, short columnspacing, short rowspacing) {\r
+    UiGridLayout *grid = cxZalloc(a, sizeof(UiGridLayout));\r
+    grid->widgets = cxArrayListCreate(a, NULL, sizeof(GridElm), 32);\r
     grid->columnspacing = columnspacing;\r
     grid->rowspacing = rowspacing;\r
     return grid;\r
@@ -48,14 +49,256 @@ void ui_grid_add_widget(
     W32Widget *widget,\r
     GridLayoutInfo *layout)\r
 {\r
+    // add the widget\r
     GridElm elm;\r
     elm.widget = widget;\r
-    elm.x = x;\r
-    elm.y = y;\r
+    elm.gridx = x;\r
+    elm.gridy = y;\r
     elm.layout = *layout;\r
-    cxListAdd(grid->widgets, elm);\r
+    cxListAdd(grid->widgets, &elm);\r
+\r
+    // adjust max col/row count\r
+    if (x > grid->max_column) {\r
+        grid->max_column = x;\r
+    }\r
+    if (y > grid->max_row) {\r
+        grid->max_row = y;\r
+    }\r
 }\r
 \r
-void ui_grid_layout(UiGridLayout *grid) {\r
-    // TODO\r
+void ui_grid_layout(UiGridLayout *grid, int width, int height) {\r
+    if (width == 0 || height == 0) {\r
+        return;\r
+    }\r
+\r
+    int ncols = grid->max_column+1;\r
+    int nrows = grid->max_row+1;\r
+\r
+    GridDef *cols = calloc(ncols, sizeof(GridDef));\r
+    GridDef *rows = calloc(nrows, sizeof(GridDef));\r
+\r
+    int colspacing = grid->columnspacing;\r
+    int rowspacing = grid->rowspacing;\r
+\r
+    int span_max = 1;\r
+    for(int r=0;r<2;r++) {\r
+        CxIterator i = cxListIterator(grid->widgets);\r
+        cx_foreach(GridElm *, elm, i) {\r
+            int x = elm->gridx;\r
+            int y = elm->gridy;\r
+            GridDef *col = &cols[x];\r
+            GridDef *row = &rows[y];\r
+\r
+            W32Size size = w32_widget_get_preferred_size(elm->widget);\r
+            elm->layout.preferred_width = size.width;\r
+            elm->layout.preferred_height = size.height;\r
+\r
+            int elm_width = size.width + elm->layout.margin.left + elm->layout.margin.right;\r
+            if(elm_width > cols[elm->gridx].preferred_size && elm->layout.colspan <= 1 && span_max == 1) {\r
+                cols[elm->gridx].preferred_size = elm_width;\r
+            }\r
+            int elm_height = size.height + elm->layout.margin.top + elm->layout.margin.bottom;\r
+            if(elm_height > rows[elm->gridy].preferred_size && elm->layout.rowspan <= 1 && span_max == 1) {\r
+                rows[elm->gridy].preferred_size = elm_height;\r
+            }\r
+\r
+            if(elm->layout.rowspan > span_max || elm->layout.colspan > span_max) {\r
+                continue;\r
+            }\r
+\r
+            int end_col = x+elm->layout.colspan;\r
+            if(end_col > ncols) {\r
+                end_col = ncols;\r
+            }\r
+            int end_row = y+elm->layout.rowspan;\r
+            if(end_row > nrows) {\r
+                end_row = nrows;\r
+            }\r
+\r
+            // are all columns in the span > preferred_width?\r
+            if(elm->layout.colspan > 1) {\r
+                int span_width = 0;\r
+                GridDef *last_col = col;\r
+                for(int c=x;c<end_col;c++) {\r
+                    span_width += cols[c].size;\r
+                    last_col = &cols[c];\r
+                }\r
+                if(span_width < elm->layout.preferred_width) {\r
+                    last_col->size += elm->layout.preferred_width - span_width;\r
+                }\r
+            }\r
+\r
+            // are all rows in the span > preferred_height?\r
+            if(elm->layout.rowspan > 1) {\r
+                int span_height = 0;\r
+                GridDef *last_row = row;\r
+                for(int c=x;c<end_row;c++) {\r
+                    span_height += rows[c].size;\r
+                    last_row = &rows[c];\r
+                }\r
+                if(span_height < elm->layout.preferred_height) {\r
+                    last_row->size += elm->layout.preferred_height - span_height;\r
+                }\r
+            }\r
+\r
+            if(elm->layout.hexpand) {\r
+                if(elm->layout.colspan > 1) {\r
+                    // check if any column in the span is expanding\r
+                    // if not, make the last column expanding\r
+                    GridDef *last_col = col;\r
+                    for(int c=x;c<end_col;c++) {\r
+                        last_col = &cols[c];\r
+                        if(last_col->expand) {\r
+                            break;\r
+                        }\r
+                    }\r
+                    last_col->expand = TRUE;\r
+                } else {\r
+                    col->expand = TRUE;\r
+                }\r
+            }\r
+            if(elm->layout.vexpand) {\r
+                if(elm->layout.rowspan > 1) {\r
+                    // same as colspan\r
+                    GridDef *last_row = row;\r
+                    for(int c=x;c<nrows;c++) {\r
+                        last_row = &rows[c];\r
+                        if(last_row->expand) {\r
+                            break;\r
+                        }\r
+                    }\r
+                    last_row->expand = TRUE;\r
+                } else {\r
+                    row->expand = TRUE;\r
+                }\r
+            }\r
+        }\r
+        span_max = 50000; // not sure if this is unreasonable low or high\r
+    }\r
+\r
+    int col_ext = 0;\r
+    int row_ext = 0;\r
+\r
+    int preferred_width = 0;\r
+    int preferred_height = 0;\r
+    for(int j=0;j<ncols;j++) {\r
+        preferred_width += cols[j].preferred_size;\r
+        if(cols[j].expand) {\r
+            col_ext++;\r
+        }\r
+    }\r
+    for(int j=0;j<nrows;j++) {\r
+        preferred_height += rows[j].preferred_size;\r
+        if(rows[j].expand) {\r
+            row_ext++;\r
+        }\r
+    }\r
+    if(ncols > 0) {\r
+        preferred_width += (ncols-1) * colspacing;\r
+    }\r
+    if(nrows > 0) {\r
+        preferred_height += (nrows-1) * rowspacing;\r
+    }\r
+\r
+    grid->preferred_width = preferred_width;\r
+    grid->preferred_height = preferred_height;\r
+\r
+    int hremaining = width - preferred_width;\r
+    int vremaining = height - preferred_height;\r
+    int hext = col_ext > 0 ? hremaining/col_ext : 0;\r
+    int vext = row_ext > 0 ? vremaining/row_ext : 0;\r
+\r
+    for(int j=0;j<ncols;j++) {\r
+        GridDef *col = &cols[j];\r
+        if(col->expand) {\r
+            col->size = col->preferred_size + hext;\r
+        } else {\r
+            col->size = col->preferred_size;\r
+        }\r
+    }\r
+    for(int j=0;j<nrows;j++) {\r
+        GridDef *row = &rows[j];\r
+        if(row->expand) {\r
+            row->size = row->preferred_size + vext;\r
+        } else {\r
+            row->size = row->preferred_size;\r
+        }\r
+    }\r
+\r
+    int pos = 0;\r
+    for(int j=0;j<ncols;j++) {\r
+        cols[j].pos = pos;\r
+        pos += cols[j].size + colspacing;\r
+    }\r
+    pos = 0;\r
+    for(int j=0;j<nrows;j++) {\r
+        rows[j].pos = pos;\r
+        pos += rows[j].size + rowspacing;\r
+    }\r
+\r
+    CxIterator i = cxListIterator(grid->widgets);\r
+    cx_foreach(GridElm *, elm, i) {\r
+        GridDef *col = &cols[elm->gridx];\r
+        GridDef *row = &rows[elm->gridy];\r
+\r
+        int child_width = 0;\r
+        int child_height = 0;\r
+        int child_x = 0;\r
+        int child_y = 0;\r
+        if(elm->layout.hfill) {\r
+            if(elm->layout.colspan > 1) {\r
+                int cwidth = 0;\r
+                int end_col = elm->gridx + elm->layout.colspan;\r
+                if(end_col > ncols) {\r
+                    end_col = ncols;\r
+                }\r
+                int real_span = 0;\r
+                for(int c=elm->gridx;c<end_col;c++) {\r
+                    cwidth += cols[c].size;\r
+                    real_span++;\r
+                }\r
+                if(real_span > 0) {\r
+                    cwidth += (real_span-1) * colspacing;\r
+                }\r
+                child_width = cwidth;\r
+            } else {\r
+                child_width = col->size;\r
+            }\r
+            child_width -= elm->layout.margin.left + elm->layout.margin.right;\r
+        } else {\r
+            child_width = elm->layout.preferred_width;\r
+        }\r
+\r
+        if(elm->layout.vfill) {\r
+            if(elm->layout.rowspan > 1) {\r
+                int rheight = 0;\r
+                int end_row = elm->gridy + elm->layout.rowspan;\r
+                if(end_row > nrows) {\r
+                    end_row = nrows;\r
+                }\r
+                int real_span = 0;\r
+                for(int r=elm->gridy;r<end_row;r++) {\r
+                    rheight += rows[r].size;\r
+                    real_span++;\r
+                }\r
+                if(real_span > 0) {\r
+                    rheight += (real_span-1) * rowspacing;\r
+                }\r
+                child_height = rheight;\r
+            }\r
+            child_height = row->size - elm->layout.margin.top - elm->layout.margin.bottom;\r
+        } else {\r
+            child_height = elm->layout.preferred_height;\r
+        }\r
+\r
+        child_x = col->pos + elm->layout.margin.left;\r
+        child_y = row->pos + elm->layout.margin.top;\r
+        SetWindowPos(elm->widget->hwnd, NULL, child_x, child_y, child_width, child_height, SWP_NOZORDER);\r
+        if (elm->widget->layout) {\r
+            elm->widget->layout(elm->widget->layoutmanager, child_width, child_height);\r
+        }\r
+    }\r
+\r
+    free(cols);\r
+    free(rows);\r
 }\r
index 6cbb6b70a596dc00d95cea086a345aab44c191e4..a30450cc89514b1b7c9fc585f322bbb55afbdbf8 100644 (file)
 #ifndef GRID_H\r
 #define GRID_H\r
 \r
-#include "container.h"\r
+#include "../ui/win32.h"\r
 #include <stdbool.h>\r
 #include <cx/array_list.h>\r
 \r
-typedef struct GridElm {\r
-    W32Widget *widget;\r
-    short x;\r
-    short y;\r
-    GridLayoutInfo layout;\r
-} GridElm;\r
+#include "win32.h"\r
+\r
+#define INSETS_ZERO (GridEdgeInsets){0}\r
+typedef struct GridEdgeInsets {\r
+    short top;\r
+    short bottom;\r
+    short left;\r
+    short right;\r
+} GridEdgeInsets;\r
 \r
 typedef struct GridLayoutInfo {\r
-    short margin_left;\r
-    short margin_right;\r
-    short margin_top;\r
-    short margin_bottom;\r
+    GridEdgeInsets margin;\r
     short colspan;\r
     short rowspan;\r
-    short preferred_width;\r
-    short preferred_height;\r
+    int preferred_width;\r
+    int preferred_height;\r
     bool hexpand;\r
     bool vexpand;\r
     bool hfill;\r
     bool vfill;\r
 } GridLayoutInfo;\r
 \r
-typedef struct UiGridLayout {\r
-    HWND hwnd;\r
+typedef struct GridElm {\r
+    W32Widget *widget;\r
+    short gridx;\r
+    short gridy;\r
+    GridLayoutInfo layout;\r
+} GridElm;\r
 \r
-    short padding;\r
+typedef struct UiGridLayout {\r
+    GridEdgeInsets padding;\r
     short columnspacing;\r
     short rowspacing;\r
 \r
+    int preferred_width;\r
+    int preferred_height;\r
+\r
     /*\r
      * list element type: GridElm\r
      */\r
     CxList *widgets;\r
 \r
+    int max_column;\r
+    int max_row;\r
 } UiGridLayout;\r
 \r
-UiGridLayout* ui_grid_container(UiObject *obj, HWND control, short padding, short columnspacing, short rowspacing);\r
+typedef struct GridDef {\r
+    int size;\r
+    int pos;\r
+    int preferred_size;\r
+    BOOLEAN expand;\r
+} GridDef;\r
+\r
+UiGridLayout* ui_grid_layout_create(const CxAllocator *a, short columnspacing, short rowspacing);\r
 \r
 void ui_grid_add_widget(\r
     UiGridLayout *grid,\r
@@ -77,6 +94,6 @@ void ui_grid_add_widget(
     W32Widget *widget,\r
     GridLayoutInfo *layout);\r
 \r
-void ui_grid_layout(UiGridLayout *grid);\r
+void ui_grid_layout(UiGridLayout *grid, int width, int height);\r
 \r
 #endif //GRID_H\r
index 37e9366c908d17a5d0d1561fcee69ebbb7118771..1241582406b24e270dcc3f5808f616353cab6484 100644 (file)
@@ -30,10 +30,13 @@ WIN32_SRC_DIR = ui/win32/
 WIN32_OBJPRE = $(OBJ_DIR)$(WIN32_SRC_DIR)
 
 WIN32OBJ  = toolkit.obj
+WIN32OBJ += win32.obj
+WIN32OBJ += widget.obj
 WIN32OBJ += window.obj
 WIN32OBJ += image.obj
 WIN32OBJ += container.obj
 WIN32OBJ += button.obj
+WIN32OBJ += grid.obj
 
 TOOLKITOBJS += $(WIN32OBJ:%=$(WIN32_OBJPRE)%)
 TOOLKITSOURCE += $(WIN32OBJ:%.obj=win32/%.c)
index 63de9c7ae1f73de725d1f738e1a8a9c9994741e0..1f13f95e89e785e85ad6963e2f50958e0b17a4d1 100644 (file)
 #include "../common/document.h"
 #include "../common/properties.h"
 
+#include "../ui/widget.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <commctrl.h>
+
 static const char *application_name;
 
 static ui_callback   startup_func;
@@ -48,16 +52,36 @@ void                 *open_data;
 static ui_callback   exit_func;
 void                 *exit_data;
 
+static HFONT ui_font = NULL;
+
 void ui_init(const char *appname, int argc, char **argv) {
     application_name = appname;
 
     uic_init_global_context();
-    uic_docmgr_init();
     uic_menu_init();
     uic_toolbar_init();
     uic_load_app_properties();
 
     ui_window_init();
+
+    INITCOMMONCONTROLSEX icex = { sizeof(icex), ICC_WIN95_CLASSES };
+    InitCommonControlsEx(&icex);
+
+    SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
+
+    NONCLIENTMETRICS ncm = { sizeof(ncm) };
+    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE);
+    ui_font = CreateFontIndirect(&ncm.lfMessageFont);
+}
+
+HFONT ui_win32_get_font(void) {
+    return ui_font;
+}
+
+void ui_win32_set_ui_font(HWND control) {
+    if (ui_font) {
+        SendMessage(control, WM_SETFONT, (WPARAM)ui_font, TRUE);
+    }
 }
 
 const char* ui_appname() {
@@ -96,3 +120,7 @@ void ui_main() {
     }
     uic_store_app_properties();
 }
+
+void ui_show(UiObject *obj) {
+    ui_set_visible(obj->widget, TRUE);
+}
\ No newline at end of file
index 3b5071c137fbc080ebf67cbb1cf13bcd216c16da..f3be04ce117c38bb308414de26bb24d441339696 100644 (file)
 #include "../common/context.h"
 #include "../common/object.h"
 
+#include "win32.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/*
+ * widget struct that can be used for most primitive widgets,
+ * like buttons, checkboxes
+ */
+typedef struct UiWidget {
+    W32Widget widget;
+    UiObject *obj;
+    UiVar *var;
+    ui_callback callback;
+    void *callbackdata;
+    int64_t intvalue;
+} UiWidget;
+
+HFONT ui_win32_get_font(void);
+void ui_win32_set_ui_font(HWND control);
 
 
 #ifdef __cplusplus
diff --git a/ui/win32/widget.c b/ui/win32/widget.c
new file mode 100644 (file)
index 0000000..33f240b
--- /dev/null
@@ -0,0 +1,43 @@
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2025 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above copyright\r
+ *      notice, this list of conditions and the following disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above copyright\r
+ *      notice, this list of conditions and the following disclaimer in the\r
+ *      documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#include "widget.h"\r
+\r
+void ui_set_enabled(UIWIDGET widget, UiBool enable) {\r
+    W32Widget *w = (W32Widget *)widget;\r
+    if (w->wclass->enable) {\r
+        w->wclass->enable(w, enable);\r
+    }\r
+}\r
+\r
+void ui_set_visible(UIWIDGET widget, UiBool visible) {\r
+    W32Widget *w = (W32Widget *)widget;\r
+    if (w->wclass->show) {\r
+        w->wclass->show(w, visible);\r
+    }\r
+}
\ No newline at end of file
diff --git a/ui/win32/widget.h b/ui/win32/widget.h
new file mode 100644 (file)
index 0000000..90c2f22
--- /dev/null
@@ -0,0 +1,44 @@
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2025 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above copyright\r
+ *      notice, this list of conditions and the following disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above copyright\r
+ *      notice, this list of conditions and the following disclaimer in the\r
+ *      documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#ifndef WIDGET_H\r
+#define WIDGET_H\r
+\r
+#include "toolkit.h"\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif //WIDGET_H\r
diff --git a/ui/win32/win32.c b/ui/win32/win32.c
new file mode 100644 (file)
index 0000000..db707c1
--- /dev/null
@@ -0,0 +1,67 @@
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2025 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above copyright\r
+ *      notice, this list of conditions and the following disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above copyright\r
+ *      notice, this list of conditions and the following disclaimer in the\r
+ *      documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#include <stdlib.h>\r
+\r
+#include "win32.h"\r
+\r
+W32Widget* w32_widget_new(W32WidgetClass *wclass, HWND hwnd) {\r
+    return w32_widget_create(wclass, hwnd, sizeof(W32Widget));\r
+}\r
+\r
+void* w32_widget_create(W32WidgetClass *wclass, HWND hwnd, size_t obj_size) {\r
+    W32Widget *w = calloc(obj_size, 1);\r
+    w->wclass = wclass;\r
+    w->hwnd = hwnd;\r
+    SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)w);\r
+    return w;\r
+}\r
+\r
+W32Size w32_widget_get_preferred_size(W32Widget *w) {\r
+    if (w->wclass->get_preferred_size) {\r
+        return w->wclass->get_preferred_size(w);\r
+    }\r
+    return (W32Size){0,0};\r
+}\r
+\r
+void w32_widget_default_destroy(W32Widget *w) {\r
+    free(w);\r
+}\r
+\r
+void w32_widget_default_show(W32Widget *w, BOOLEAN show) {\r
+    ShowWindow(w->hwnd, show ? SW_SHOW : SW_HIDE);\r
+}\r
+\r
+void w32_widget_default_enable(W32Widget *w, BOOLEAN enable) {\r
+    // TODO\r
+}\r
+\r
+W32Size w32_widget_default_get_preferred_size(W32Widget *widget) {\r
+    return (W32Size){0,0};\r
+}\r
+\r
diff --git a/ui/win32/win32.h b/ui/win32/win32.h
new file mode 100644 (file)
index 0000000..c03e344
--- /dev/null
@@ -0,0 +1,61 @@
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2025 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above copyright\r
+ *      notice, this list of conditions and the following disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above copyright\r
+ *      notice, this list of conditions and the following disclaimer in the\r
+ *      documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#ifndef UI_TK_WIN32_H\r
+#define UI_TK_WIN32_H\r
+\r
+#include "../ui/win32.h"\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+/*\r
+ * Creates a standard W32Widget object for an HWND handle and stores the widget object as\r
+ * GWLP_USERDATA in the window.\r
+ */\r
+W32Widget* w32_widget_new(W32WidgetClass *wclass, HWND hwnd);\r
+\r
+/*\r
+ * Same as w32_widget_new, but uses obj_size for allocation, allowing to create objects\r
+ * derived from W32Widget.\r
+ */\r
+void* w32_widget_create(W32WidgetClass *wclass, HWND hwnd, size_t obj_size);\r
+\r
+W32Size w32_widget_get_preferred_size(W32Widget *w);\r
+\r
+void w32_widget_default_destroy(W32Widget *w);\r
+void w32_widget_default_show(W32Widget *w, BOOLEAN show);\r
+void w32_widget_default_enable(W32Widget *w, BOOLEAN enable);\r
+W32Size w32_widget_default_get_preferred_size(W32Widget *widget);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif //UI_TK_WIN32_H\r
index b121565e00c3ff187c0b472715c82fc143398299..98d587fbee03b8513c6bee664aa1ebc5a97bc5de 100644 (file)
@@ -27,7 +27,9 @@
  */
 
 #include "window.h"
-#include "Windows.h"
+#include <Windows.h>
+
+#include "container.h"
 
 #include "../common/object.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "win32.h"
+
+static W32WidgetClass w32_toplevel_widget_class = {
+       .eventproc = ui_window_widget_event,
+       .show = ui_window_widget_show,
+       .enable = NULL,
+       .get_preferred_size =  NULL,
+       .destroy =  w32_widget_default_destroy
+};
 
 static HINSTANCE hInstance;
 
 static const char *mainWindowClass = "UiMainWindow";
 
 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+       W32Widget *widget = (W32Widget*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+       if (widget && widget->wclass->eventproc) {
+               widget->wclass->eventproc(widget, hwnd, uMsg, wParam, lParam);
+       }
     switch(uMsg) {
-        case WM_DESTROY:
-            PostQuitMessage(0);
-            break;
-        default:
-            return DefWindowProc(hwnd, uMsg, wParam, lParam);
+        case WM_DESTROY: {
+               PostQuitMessage(0);
+               break;
+        }
+               case WM_COMMAND: {
+               HWND hwndCtrl = (HWND)lParam;
+               W32Widget *cmdWidget = (W32Widget*)GetWindowLongPtr(hwndCtrl, GWLP_USERDATA);
+               if (cmdWidget && cmdWidget->wclass->eventproc) {
+                       cmdWidget->wclass->eventproc(cmdWidget, hwnd, uMsg, wParam, lParam);
+               }
+        }
+               case WM_SIZE: {
+               int width  = LOWORD(lParam);
+               int height = HIWORD(lParam);
+               if (widget->layout) {
+                       widget->layout(widget->layoutmanager, width, height);
+               }
+               break;
+               }
+        default: return DefWindowProc(hwnd, uMsg, wParam, lParam);
     }
     return 0;
 }
@@ -60,7 +90,7 @@ void ui_window_init(void) {
     wc.hInstance = hInstance;
     wc.lpszClassName = mainWindowClass;
     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
-    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+    wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
 
     if(!RegisterClassExA(&wc)) {
         MessageBox(NULL, "RegisterClassEx failed", "Error", MB_ICONERROR);
@@ -71,12 +101,12 @@ void ui_window_init(void) {
 static UiObject* create_window(const char *title, void *window_data, bool simple) {
     UiObject *obj = uic_object_new_toplevel();
     obj->window = window_data;
-       
+
        HWND hwnd = CreateWindowExA(
                        0,
                        "UiMainWindow",
                        title,
-                       WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+                       WS_OVERLAPPEDWINDOW,
             CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        800,
@@ -85,10 +115,20 @@ static UiObject* create_window(const char *title, void *window_data, bool simple
                        NULL,
                        hInstance,
                        NULL);
-       
-       ShowWindow(hwnd, SW_SHOWNORMAL);
+
     UpdateWindow(hwnd);
-       
+
+       UiContainerX *container = ui_box_container_create(obj, hwnd, UI_BOX_VERTICAL, 0, INSETS_ZERO);
+       uic_object_push_container(obj, container);
+       UiBoxContainer *box = (UiBoxContainer*)container;
+
+       UiWindow *widget = w32_widget_create(&w32_toplevel_widget_class, hwnd, sizeof(UiWindow));
+       widget->obj = obj;
+       widget->widget.layout = (W32LayoutFunc)ui_grid_layout;
+       widget->widget.layoutmanager = box->layout;
+       obj->widget = (W32Widget*)widget;
+       obj->ref = 1;
+
        return obj;
 }
 
@@ -96,3 +136,11 @@ UiObject *ui_window(const char *title, void *window_data) {
        return create_window(title, window_data, FALSE);
 }
 
+
+void ui_window_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+       //UiWindow *window = (UiWindow*)widget;
+}
+
+void ui_window_widget_show(W32Widget *w, BOOLEAN show) {
+       ShowWindow(w->hwnd, show ? SW_SHOWNORMAL : SW_HIDE);
+}
index 305f40ada82c5def3cf562f0206c9d60615751b4..5d4c8f7fee97f35f905aa14288807fbe283a18a3 100644 (file)
 #include "../common/object.h"
 
 #include "toolkit.h"
-
+#include "container.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+typedef struct UiWindow {
+    W32Widget widget;
+    UiObject *obj;
+} UiWindow;
+
 void ui_window_init(void);
 
+void ui_window_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+void ui_window_widget_show(W32Widget *w, BOOLEAN show);
+
 #ifdef __cplusplus
 }
 #endif
index 08426ff900ab04078e432e0c0c6dd6b9ab1043eb..1844cc28671d1d7d570e7b8adf582ab07c4a0921 100644 (file)
@@ -165,7 +165,6 @@ void ui_init(const char* appname, int argc, char** argv) {
        //ui_appsdk_bootstrap();\r
 \r
        uic_init_global_context();\r
-       uic_docmgr_init();\r
         uic_menu_init();\r
        uic_toolbar_init();\r
        \r