From: Olaf Wintermann Date: Fri, 12 Dec 2025 09:37:38 +0000 (+0100) Subject: move navigation to the headerbar X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=mizunara.git move navigation to the headerbar --- diff --git a/mizunara/menu.c b/mizunara/menu.c index 461ac89..1014f84 100644 --- a/mizunara/menu.c +++ b/mizunara/menu.c @@ -52,9 +52,16 @@ static void menubar_init(void) { } static void toolbar_init(void) { + ui_toolbar_item("Back", .icon = UI_ICON_GO_BACK); + ui_toolbar_item("Forward", .icon = UI_ICON_GO_FORWARD); ui_toolbar_item("Home", .icon = UI_ICON_HOME, .onclick = action_go_home); - +#ifdef UI_LIBADWAITA + ui_toolbar_add_default("Back", UI_TOOLBAR_LEFT); + ui_toolbar_add_default("Forward", UI_TOOLBAR_LEFT); + ui_toolbar_add_default("Home", UI_TOOLBAR_RIGHT); +#else ui_toolbar_add_default("Home", UI_TOOLBAR_LEFT); +#endif } static void appmenu_init(void) { diff --git a/mizunara/window.c b/mizunara/window.c index d3edb43..412e575 100644 --- a/mizunara/window.c +++ b/mizunara/window.c @@ -55,9 +55,17 @@ void window_update_gridview( mz_update_files_view((MzFilesView*)win->files_gridview, sections, numsections); } +#ifdef UI_LIBADWAITA + +void libadwaita_headerbar_add() { + +} + +#endif + UiObject* window_create(const char *url) { // toplevel window object - UiObject *obj = ui_sidebar_window("Mizunara", NULL); + UiObject *obj = ui_sidebar_window("Mizunara"); ui_window_size(obj, 1000, 600); // create window data object @@ -89,11 +97,19 @@ UiObject* window_create(const char *url) { .fill = TRUE); } +#ifdef UI_LIBADWAITA + ui_headerbar(obj) { + ui_headerbar_center(obj) { + ui_path_textfield(obj, .varname = "path", .fill = TRUE, .onactivate = action_pathbar_activate); + } + } +#else ui_hbox(obj, .spacing = 4, .margin_left = 6, .margin_right = 8) { ui_button(obj, .style_class = "flat", .icon = UI_ICON_GO_BACK); ui_button(obj, .style_class = "flat", .icon = UI_ICON_GO_FORWARD); ui_path_textfield(obj, .varname = "path", .fill = TRUE, .onactivate = action_pathbar_activate); } +#endif window_create_browser_view(obj, wdata); ui_set(browser->view, 0); // select default view diff --git a/ui/common/app.c b/ui/common/app.c new file mode 100644 index 0000000..9674599 --- /dev/null +++ b/ui/common/app.c @@ -0,0 +1,71 @@ +/* + * 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 "app.h" + +static ui_callback startup_func; +static void *startup_data; +static ui_callback open_func; +void *open_data; +static ui_callback exit_func; +void *exit_data; + + +void ui_onstartup(ui_callback f, void *userdata) { + startup_func = f; + startup_data = userdata; +} + +void ui_onopen(ui_callback f, void *userdata) { + open_func = f; + open_data = userdata; +} + +void ui_onexit(ui_callback f, void *userdata) { + exit_func = f; + exit_data = userdata; +} + + +void uic_application_startup(UiEvent *event) { + if(startup_func) { + startup_func(event, startup_data); + } +} + +void uic_application_open(UiEvent *event) { + if(open_func) { + open_func(event, open_data); + } +} + +void uic_application_exit(UiEvent *event) { + if(exit_func) { + exit_func(event, exit_data); + } +} diff --git a/ui/common/app.h b/ui/common/app.h new file mode 100644 index 0000000..02356a9 --- /dev/null +++ b/ui/common/app.h @@ -0,0 +1,48 @@ +/* + * 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_APP_H +#define UIC_APP_H + +#include "../ui/toolkit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void uic_application_startup(UiEvent *event); +void uic_application_open(UiEvent *event); +void uic_application_exit(UiEvent *event); + + +#ifdef __cplusplus +} +#endif + +#endif /* UIC_APP_H */ + diff --git a/ui/common/args.c b/ui/common/args.c index 796919b..2ef732e 100644 --- a/ui/common/args.c +++ b/ui/common/args.c @@ -175,10 +175,10 @@ void ui_dialogwindow_args_free(UiDialogWindowArgs *args) { free((void*)args->lbutton2); free((void*)args->rbutton3); free((void*)args->rbutton4); - free((void*)args->lbutton1_groups); - free((void*)args->lbutton2_groups); - free((void*)args->rbutton3_groups); - free((void*)args->rbutton4_groups); + free((void*)args->lbutton1_states); + free((void*)args->lbutton2_states); + free((void*)args->rbutton3_states); + free((void*)args->rbutton4_states); free(args); } @@ -310,16 +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 *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_set_states(UiToolbarItemArgs *args, int *states, int numstates) { + args->states = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->states, states, numstates * sizeof(int)); + ((int*)args->states)[numstates] = -1; } void ui_toolbar_item_args_free(UiToolbarItemArgs *args) { free((void*)args->label); free((void*)args->icon); free((void*)args->tooltip); - free((void*)args->groups); + free((void*)args->states); free(args); } @@ -355,10 +355,10 @@ void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args, args->onchangedata = onchangedata; } -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_set_states(UiToolbarToggleItemArgs *args,int *states, int numstates) { + args->states = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->states, states, numstates * sizeof(int)); + ((int*)args->states)[numstates] = -1; } void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args) { @@ -366,7 +366,7 @@ void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args) { free((void*)args->icon); free((void*)args->tooltip); free((void*)args->varname); - free((void*)args->groups); + free((void*)args->states); free(args); } @@ -1383,10 +1383,10 @@ void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata){ args->onclickdata = onclickdata; } -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_set_states(UiButtonArgs *args, int *states, int numstates) { + args->states = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->states, states, numstates * sizeof(int)); + ((int*)args->states)[numstates] = -1; } void ui_button_args_free(UiButtonArgs *args) { @@ -1395,7 +1395,7 @@ void ui_button_args_free(UiButtonArgs *args) { free((void*)args->label); free((void*)args->icon); free((void*)args->tooltip); - free((void*)args->groups); + free((void*)args->states); free(args); } @@ -1502,14 +1502,14 @@ void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value) { args->value = value; } -void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group) { - args->enable_group = group; +void ui_toggle_args_set_enablestate(UiToggleArgs *args, int state) { + args->enable_state = state; } -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_set_states(UiToggleArgs *args, int *states, int numstates) { + args->states = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->states, states, numstates * sizeof(int)); + ((int*)args->states)[numstates] = -1; } void ui_toggle_args_free(UiToggleArgs *args) { @@ -1519,7 +1519,7 @@ void ui_toggle_args_free(UiToggleArgs *args) { free((void*)args->icon); free((void*)args->tooltip); free((void*)args->varname); - free((void*)args->groups); + free((void*)args->states); free(args); } @@ -1633,10 +1633,10 @@ void ui_linkbutton_args_set_value(UiLinkButtonArgs *args, UiString *value) { args->value = value; } -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_set_states(UiLinkButtonArgs *args, int *states, int numstates) { + args->states = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->states, states, numstates * sizeof(int)); + ((int*)args->states)[numstates] = -1; } void ui_linkbutton_args_free(UiLinkButtonArgs *args) { @@ -1645,7 +1645,7 @@ void ui_linkbutton_args_free(UiLinkButtonArgs *args) { free((void*)args->label); free((void*)args->uri); free((void*)args->varname); - free((void*)args->groups); + free((void*)args->states); free(args); } @@ -1815,10 +1815,10 @@ void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder) args->contextmenu = menubuilder; } -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_set_states(UiListArgs *args, int *states, int numstates) { + args->states = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->states, states, numstates * sizeof(int)); + ((int*)args->states)[numstates] = -1; } void ui_list_args_free(UiListArgs *args) { @@ -1831,7 +1831,7 @@ void ui_list_args_free(UiListArgs *args) { } free(args->static_elements); } - free((void*)args->groups); + free((void*)args->states); free(args); } @@ -1971,7 +1971,7 @@ void ui_sourcelist_args_free(UiSourceListArgs *args) { free((void*)args->style_class); free((void*)args->varname); free((void*)args->sublists); - free((void*)args->groups); + free((void*)args->states); free(args); } @@ -2071,17 +2071,17 @@ void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value) { args->value = value; } -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_set_states(UiTextAreaArgs *args, int *states, int numstates) { + args->states = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->states, states, numstates * sizeof(int)); + ((int*)args->states)[numstates] = -1; } void ui_textarea_args_free(UiTextAreaArgs *args) { free((void*)args->name); free((void*)args->style_class); free((void*)args->varname); - free((void*)args->groups); + free((void*)args->states); free(args); } @@ -2191,17 +2191,17 @@ void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value) { args->value = value; } -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_set_states(UiTextFieldArgs *args, int *states, int numstates) { + args->states = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->states, states, numstates * sizeof(int)); + ((int*)args->states)[numstates] = -1; } void ui_textfield_args_free(UiTextFieldArgs *args) { free((void*)args->name); free((void*)args->style_class); free((void*)args->varname); - free((void*)args->groups); + free((void*)args->states); free(args); } @@ -2314,17 +2314,17 @@ 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_set_states(UiSpinBoxArgs *args, int *states, int numstates) { + args->states = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->states, states, numstates * sizeof(int)); + ((int*)args->states)[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((void*)args->states); free(args); } @@ -2414,17 +2414,17 @@ void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value) { args->value = value; } -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_set_states(UiWebviewArgs *args, int *states, int numstates) { + args->states = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->states, states, numstates * sizeof(int)); + ((int*)args->states)[numstates] = -1; } void ui_webview_args_free(UiWebviewArgs *args) { free((void*)args->name); free((void*)args->style_class); free((void*)args->varname); - free((void*)args->groups); + free((void*)args->states); free(args); } diff --git a/ui/common/args.h b/ui/common/args.h index e533193..41d33f6 100644 --- a/ui/common/args.h +++ b/ui/common/args.h @@ -36,7 +36,7 @@ #include "../ui/entry.h" #include "../ui/menu.h" #include "../ui/toolbar.h" -#include "../ui/tree.h" +#include "../ui/list.h" #include "../ui/text.h" #include "../ui/webview.h" #include "../ui/widget.h" @@ -107,7 +107,7 @@ UIEXPORT void ui_toolbar_item_args_set_icon(UiToolbarItemArgs *args, const char 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 *states, int numstates); +UIEXPORT void ui_toolbar_item_args_set_states(UiToolbarItemArgs *args, int *states, int numstates); UIEXPORT void ui_toolbar_item_args_free(UiToolbarItemArgs *args); UIEXPORT UiToolbarToggleItemArgs* ui_toolbar_toggleitem_args_new(void); @@ -117,7 +117,7 @@ UIEXPORT void ui_toolbar_toggleitem_args_set_tooltip(UiToolbarToggleItemArgs *ar 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 *states, int numstates); +UIEXPORT void ui_toolbar_toggleitem_args_set_states(UiToolbarToggleItemArgs *args, int *states, int numstates); UIEXPORT void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args); UIEXPORT UiToolbarMenuArgs* ui_toolbar_menu_args_new(void); @@ -137,7 +137,7 @@ 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_margin_bottom(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); @@ -346,7 +346,7 @@ 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 *states, int numstates); +UIEXPORT void ui_button_args_set_states(UiButtonArgs *args, int *states, int numstates); UIEXPORT void ui_button_args_free(UiButtonArgs *args); UIEXPORT UiToggleArgs* ui_toggle_args_new(void); @@ -373,8 +373,8 @@ UIEXPORT void ui_toggle_args_set_onchange(UiToggleArgs *args, ui_callback callba 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 *states, int numstates); +UIEXPORT void ui_toggle_args_set_enablestate(UiToggleArgs *args, int state); +UIEXPORT void ui_toggle_args_set_states(UiToggleArgs *args, int *states, int numstates); UIEXPORT void ui_toggle_args_free(UiToggleArgs *args); UIEXPORT UiLinkButtonArgs* ui_linkbutton_args_new(void); @@ -401,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 *states, int numstates); +UIEXPORT void ui_linkbutton_args_set_states(UiLinkButtonArgs *args, int *states, int numstates); UIEXPORT void ui_linkbutton_args_free(UiLinkButtonArgs *args); UIEXPORT UiListArgs* ui_list_args_new(void); @@ -443,7 +443,7 @@ 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 *states, int numstates); +UIEXPORT void ui_list_args_set_states(UiListArgs *args, int *states, int numstates); UIEXPORT void ui_list_args_free(UiListArgs *args); UIEXPORT UiSourceListArgs* ui_sourcelist_args_new(void); @@ -495,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 *states, int numstates); +UIEXPORT void ui_textarea_args_set_states(UiTextAreaArgs *args, int *states, int numstates); UIEXPORT void ui_textarea_args_free(UiTextAreaArgs *args); UIEXPORT UiTextFieldArgs* ui_textfield_args_new(void); @@ -520,7 +520,7 @@ 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 *states, int numstates); +UIEXPORT void ui_textfield_args_set_states(UiTextFieldArgs *args, int *states, int numstates); UIEXPORT void ui_textfield_args_free(UiTextFieldArgs *args); UIEXPORT UiSpinBoxArgs* ui_spinbox_args_new(void); @@ -549,7 +549,7 @@ UIEXPORT void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varna 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_set_states(UiSpinBoxArgs *args, int *states, int numstates); UIEXPORT void ui_spinbox_args_free(UiSpinBoxArgs *args); UIEXPORT UiWebviewArgs* ui_webview_args_new(void); @@ -570,7 +570,7 @@ 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 *states, int numstates); +UIEXPORT void ui_webview_args_set_states(UiWebviewArgs *args, int *states, int numstates); UIEXPORT void ui_webview_args_free(UiWebviewArgs *args); #ifdef __cplusplus diff --git a/ui/common/context.c b/ui/common/context.c index 73911a5..bd1d855 100644 --- a/ui/common/context.c +++ b/ui/common/context.c @@ -64,8 +64,8 @@ UiContext* uic_context(UiObject *toplevel, CxMempool *mp) { ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16); ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, CX_STORE_POINTERS); - ctx->group_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiGroupWidget)); - ctx->groups = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32); + ctx->state_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiStateWidget)); + ctx->states = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32); ctx->attach_document = uic_context_attach_document; ctx->detach_document2 = uic_context_detach_document; @@ -92,11 +92,17 @@ void uic_context_add_destructor(UiContext *ctx, cx_destructor_func func, void *d } void uic_context_prepare_close(UiContext *ctx) { - cxListClear(ctx->groups); - cxListClear(ctx->group_widgets); + cxListClear(ctx->states); + cxListClear(ctx->state_widgets); } void uic_context_attach_document(UiContext *ctx, void *document) { + if(ctx->single_document_mode) { + if(ctx->document) { + uic_context_detach_document(ctx, ctx->document); + } + } + cxListAdd(ctx->documents, document); ctx->document = document; @@ -114,7 +120,7 @@ void uic_context_attach_document(UiContext *ctx, void *document) { UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key); if(docvar) { // bind var to document var - uic_copy_binding(var, docvar, TRUE); + uic_copy_var_binding(var, docvar, TRUE); cxIteratorFlagRemoval(i); } } @@ -124,16 +130,19 @@ void uic_context_attach_document(UiContext *ctx, void *document) { } static void uic_context_unbind_vars(UiContext *ctx) { + ui_onchange_events_enable(FALSE); CxMapIterator mi = cxMapIterator(ctx->vars); cx_foreach(CxMapEntry*, entry, mi) { + printf("detach %.*s\n", (int)entry->key->len, (char*)entry->key->data); UiVar *var = entry->value; // var->from && var->from_ctx && var->from_ctx != ctx uic_save_var(var); if(var->from) { - uic_copy_binding(var, var->from, FALSE); + uic_copy_var_binding(var, var->from, FALSE); cxMapPut(var->from->from_ctx->vars, *entry->key, var->from); var->from = NULL; } + uic_unbind_var(var); } if(ctx->documents) { @@ -143,6 +152,8 @@ static void uic_context_unbind_vars(UiContext *ctx) { uic_context_unbind_vars(subctx); } } + + ui_onchange_events_enable(TRUE); } void uic_context_detach_document(UiContext *ctx, void *document) { @@ -197,6 +208,14 @@ UiVar* uic_get_var(UiContext *ctx, const char *name) { return ctx_getvar(ctx, key); } +UiVar* uic_get_var_t(UiContext *ctx,const char *name, UiVarType type) { + UiVar *var = uic_get_var(ctx, name); + if(var && var->type == type) { + return var; + } + return NULL; +} + UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { UiVar *var = uic_get_var(ctx, name); if(var) { @@ -213,8 +232,9 @@ UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { var->original_value = NULL; var->from = NULL; var->from_ctx = ctx; - - cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var); + var->bound = FALSE; + + cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var); // TODO: use another destructor that cleans the value (UiString free, UiText destroy, ...) cxMapPut(ctx->vars, name, var); @@ -266,6 +286,40 @@ void* uic_create_value(UiContext *ctx, UiVarType type) { return val; } +// destroys a value, that was created by uic_create_value +void uic_destroy_value(UiContext *ctx, UiVarType type, void *value) { + switch(type) { + default: { + ui_free(ctx, value); + break; + } + case UI_VAR_SPECIAL: break; + case UI_VAR_STRING: { + UiString *s = value; + if(s->value.free) { + s->value.free(s->value.ptr); + } + ui_free(ctx, value); + } + case UI_VAR_TEXT: { + UiText *t = value; + if(t->value.free) { + t->value.free(t->value.ptr); + t->value.free = NULL; + t->value.ptr = NULL; + } + if(t->destroy) { + t->destroy(t); + } + ui_free(ctx, value); + } + case UI_VAR_LIST: { + ui_list_free(ctx, value); + break; + } + } +} + UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type) { if (value) { @@ -278,7 +332,7 @@ UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, cons } -void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { +void uic_copy_var_binding(UiVar *from, UiVar *to, UiBool copytodoc) { // check type if(from->type != to->type) { fprintf(stderr, "UI Error: var has incompatible type.\n"); @@ -301,32 +355,36 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { } } - ui_setop_enable(TRUE); + uic_copy_value_binding(from->type, fromvalue, tovalue); +} + +void uic_copy_value_binding(UiVarType type, void *from, void *to) { + ui_setop_enable(TRUE); // copy binding - // we don't copy the observer, because the from var has never one - switch(from->type) { + // we don't copy the observer, because the from value never has oberservers + switch(type) { default: fprintf(stderr, "uic_copy_binding: wtf!\n"); break; case UI_VAR_SPECIAL: break; case UI_VAR_INTEGER: { - UiInteger *f = fromvalue; - UiInteger *t = tovalue; + UiInteger *f = from; + UiInteger *t = to; if(!f->obj) break; uic_int_copy(f, t); t->set(t, t->value); break; } case UI_VAR_DOUBLE: { - UiDouble *f = fromvalue; - UiDouble *t = tovalue; + UiDouble *f = from; + UiDouble *t = to; if(!f->obj) break; uic_double_copy(f, t); t->set(t, t->value); break; } case UI_VAR_STRING: { - UiString *f = fromvalue; - UiString *t = tovalue; + UiString *f = from; + UiString *t = to; if(!f->obj) break; uic_string_copy(f, t); char *tvalue = t->value.ptr ? t->value.ptr : ""; @@ -335,23 +393,23 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { break; } case UI_VAR_TEXT: { - UiText *f = fromvalue; - UiText *t = tovalue; + UiText *f = from; + UiText *t = to; if(!f->obj) break; uic_text_copy(f, t); t->restore(t); break; } case UI_VAR_LIST: { - UiList *f = fromvalue; - UiList *t = tovalue; + UiList *f = from; + UiList *t = to; uic_list_copy(f, t); ui_list_update(t); break; } case UI_VAR_RANGE: { - UiRange *f = fromvalue; - UiRange *t = tovalue; + UiRange *f = from; + UiRange *t = to; if(!f->obj) break; uic_range_copy(f, t); t->setextent(t, t->extent); @@ -360,8 +418,8 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { break; } case UI_VAR_GENERIC: { - UiGeneric *f = fromvalue; - UiGeneric *t = tovalue; + UiGeneric *f = from; + UiGeneric *t = to; if(!f->obj) break; uic_generic_copy(f, t); t->set(t, t->value, t->type); @@ -398,59 +456,56 @@ void uic_unbind_var(UiVar *var) { } } +const char *uic_type2str(UiVarType type) { + switch(type) { + default: return ""; + case UI_VAR_INTEGER: return "int"; + case UI_VAR_DOUBLE: return "double"; + case UI_VAR_STRING: return "string"; + case UI_VAR_TEXT: return "text"; + case UI_VAR_LIST: return "list"; + case UI_VAR_RANGE: return "range"; + case UI_VAR_GENERIC: return "generic"; + } +} + void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value) { - // TODO: do we need/want this? Why adding vars to a context after - // widgets reference these? Workarounds: - // 1. add vars to ctx before creating ui - // 2. create ui, create new document with vars, attach doc - // also it would be possible to create a function, that scans unbound vars - // and connects them to available vars - /* - UiContext *rootctx = uic_root_context(ctx); - UiVar *b = NULL; - if(rootctx->bound) { - // some widgets are already bound to some vars - b = ucx_map_cstr_get(rootctx->bound, name); - if(b) { - // a widget is bound to a var with this name - // if ctx is the root context we can remove the var from bound - // because set_doc or detach can't fuck things up - if(ctx == rootctx) { - ucx_map_cstr_remove(ctx->bound, name); - // TODO: free stuff - } + UiVar *var = cxMapGet(ctx->vars, name); + if(!var) { + // create new var and add it to the context var map + var = ui_malloc(ctx, sizeof(UiVar)); + cxMapPut(ctx->vars, name, var); + } else { + // override var with new value + if(var->type != type) { + fprintf(stderr, "Error: var %s type mismatch: %s - %s\n", name, uic_type2str(var->type), uic_type2str(type)); + return; + } + if(var->bound) { + fprintf(stderr, "Error: var %s already bound\n", name); + return; } + UiInteger *prev_value = var->value; + uic_copy_value_binding(type, prev_value, value); + uic_destroy_value(var->from_ctx, var->type, var->value); } - */ - // create new var and add it to doc's vars - UiVar *var = ui_malloc(ctx, sizeof(UiVar)); var->type = type; var->value = value; var->from = NULL; var->from_ctx = ctx; - size_t oldcount = cxMapSize(ctx->vars); - cxMapPut(ctx->vars, name, var); - if(cxMapSize(ctx->vars) != oldcount + 1) { - fprintf(stderr, "UiError: var '%s' already exists\n", name); - } - - // TODO: remove? - // a widget is already bound to a var with this name - // copy the binding (like uic_context_set_document) - /* - if(b) { - uic_copy_binding(b, var, TRUE); - } - */ + var->bound = TRUE; } -void uic_remove_bound_var(UiContext *ctx, UiVar *var) { - // TODO -} +// public API +void* ui_context_get_document(UiContext *ctx) { + return ctx->document; +} -// public API +void ui_context_single_attachment_mode(UiContext *ctx, UiBool enable) { + ctx->single_document_mode = enable; +} void ui_attach_document(UiContext *ctx, void *document) { uic_context_attach_document(ctx, document); @@ -478,48 +533,48 @@ UiContext* ui_context_parent(UiContext *ctx) { } -void ui_set_group(UiContext *ctx, int group) { - if(!cxListIndexValid(ctx->groups, cxListFind(ctx->groups, &group))) { - cxListAdd(ctx->groups, &group); +void ui_set_state(UiContext *ctx, int state) { + if(!cxListIndexValid(ctx->states, cxListFind(ctx->states, &state))) { + cxListAdd(ctx->states, &state); } // enable/disable group widgets - uic_check_group_widgets(ctx); + uic_check_state_widgets(ctx); } -void ui_unset_group(UiContext *ctx, int group) { - int i = cxListFind(ctx->groups, &group); +void ui_unset_state(UiContext *ctx, int state) { + int i = cxListFind(ctx->states, &state); if(i != -1) { - cxListRemove(ctx->groups, i); + cxListRemove(ctx->states, i); } // enable/disable group widgets - uic_check_group_widgets(ctx); + uic_check_state_widgets(ctx); } -int* ui_active_groups(UiContext *ctx, int *ngroups) { - *ngroups = cxListSize(ctx->groups); - return cxListAt(ctx->groups, 0); +int* ui_active_states(UiContext *ctx, int *nstates) { + *nstates = cxListSize(ctx->states); + return cxListAt(ctx->states, 0); } -void uic_check_group_widgets(UiContext *ctx) { +void uic_check_state_widgets(UiContext *ctx) { int ngroups = 0; - int *groups = ui_active_groups(ctx, &ngroups); + int *groups = ui_active_states(ctx, &ngroups); - CxIterator i = cxListIterator(ctx->group_widgets); - cx_foreach(UiGroupWidget *, gw, i) { - char *check = calloc(1, gw->numgroups); + CxIterator i = cxListIterator(ctx->state_widgets); + cx_foreach(UiStateWidget *, gw, i) { + char *check = calloc(1, gw->numstates); for(int i=0;inumgroups;k++) { - if(groups[i] == gw->groups[k]) { + for(int k=0;knumstates;k++) { + if(groups[i] == gw->states[k]) { check[k] = 1; } } } int enable = 1; - for(int i=0;inumgroups;i++) { + for(int i=0;inumstates;i++) { if(check[i] == 0) { enable = 0; break; @@ -530,70 +585,70 @@ void uic_check_group_widgets(UiContext *ctx) { } } -void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) { +void ui_widget_set_states(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); + // get states + CxList *states = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); va_list ap; va_start(ap, enable); - int group; - while((group = va_arg(ap, int)) != -1) { - cxListAdd(groups, &group); + int state; + while((state = va_arg(ap, int)) != -1) { + cxListAdd(states, &state); } va_end(ap); - uic_add_group_widget(ctx, widget, enable, groups); + uic_add_state_widget(ctx, widget, enable, states); - cxListFree(groups); + cxListFree(states); } -void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups) { +void ui_widget_set_states2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *states, int nstates) { if(enable == NULL) { enable = (ui_enablefunc)ui_set_enabled; } - CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), ngroups); - for(int i=0;i= 0;i++) { } + for(i=0;states[i] >= 0;i++) { } return i; } -void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups) { - uic_add_group_widget_i(ctx, widget, enable, cxListAt(groups, 0), cxListSize(groups)); +void uic_add_state_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *states) { + uic_add_state_widget_i(ctx, widget, enable, cxListAt(states, 0), cxListSize(states)); } -void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups) { +void uic_add_state_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *states, size_t numstates) { const CxAllocator *a = ctx->allocator; - UiGroupWidget gw; + UiStateWidget gw; gw.widget = widget; gw.enable = enable; - gw.numgroups = numgroups; - gw.groups = cxCalloc(a, numgroups, sizeof(int)); + gw.numstates = numstates; + gw.states = cxCalloc(a, numstates, sizeof(int)); - // copy groups - if(groups) { - memcpy(gw.groups, groups, gw.numgroups * sizeof(int)); + // copy states + if(states) { + memcpy(gw.states, states, gw.numstates * sizeof(int)); } - cxListAdd(ctx->group_widgets, &gw); + cxListAdd(ctx->state_widgets, &gw); } -void uic_remove_group_widget(UiContext *ctx, void *widget) { - (void)cxListFindRemove(ctx->group_widgets, widget); +void uic_remove_state_widget(UiContext *ctx, void *widget) { + (void)cxListFindRemove(ctx->state_widgets, widget); } UIEXPORT void *ui_allocator(UiContext *ctx) { @@ -638,3 +693,33 @@ void ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr) { void ui_set_destructor(void *mem, ui_destructor_func destr) { cxMempoolSetDestructor(mem, (cx_destructor_func)destr); } + +UiInteger* ui_get_int_var(UiContext *ctx, const char *name) { + UiVar *var = uic_get_var_t(ctx, name, UI_VAR_INTEGER); + return var ? var->value : NULL; +} + +UiDouble* ui_get_double_var(UiContext *ctx, const char *name) { + UiVar *var = uic_get_var_t(ctx, name, UI_VAR_DOUBLE); + return var ? var->value : NULL; +} + +UiString* ui_get_string_var(UiContext *ctx, const char *name) { + UiVar *var = uic_get_var_t(ctx, name, UI_VAR_STRING); + return var ? var->value : NULL; +} + +UiText* ui_get_text_var(UiContext *ctx, const char *name) { + UiVar *var = uic_get_var_t(ctx, name, UI_VAR_TEXT); + return var ? var->value : NULL; +} + +UiRange* ui_get_range_var(UiContext *ctx, const char *name) { + UiVar *var = uic_get_var_t(ctx, name, UI_VAR_RANGE); + return var ? var->value : NULL; +} + +UiGeneric* ui_get_generic_var(UiContext *ctx, const char *name) { + UiVar *var = uic_get_var_t(ctx, name, UI_VAR_GENERIC); + return var ? var->value : NULL; +} diff --git a/ui/common/context.h b/ui/common/context.h index f517090..5efcc2e 100644 --- a/ui/common/context.h +++ b/ui/common/context.h @@ -43,7 +43,7 @@ extern "C" { typedef struct UiVar UiVar; typedef struct UiListPtr UiListPtr; typedef struct UiListVar UiListVar; -typedef struct UiGroupWidget UiGroupWidget; +typedef struct UiStateWidget UiStateWidget; typedef struct UiDestroyHandler UiDestroyHandler; typedef enum UiVarType { @@ -69,8 +69,8 @@ struct UiContext { CxMap *vars; - CxList *groups; // int list - CxList *group_widgets; // UiGroupWidget list + CxList *states; // int list + CxList *state_widgets; // UiGroupWidget list void (*attach_document)(UiContext *ctx, void *document); void (*detach_document2)(UiContext *ctx, void *document); @@ -84,8 +84,10 @@ struct UiContext { GtkAccelGroup *accel_group; #endif #endif - - + + // allow only one document to be attached + // attaching a document will automatically detach the current document + UiBool single_document_mode; ui_callback close_callback; void *close_data; @@ -97,13 +99,14 @@ struct UiVar { UiVarType type; UiVar *from; UiContext *from_ctx; + UiBool bound; }; -struct UiGroupWidget { +struct UiStateWidget { void *widget; ui_enablefunc enable; - int *groups; - int numgroups; + int *states; + int numstates; }; struct UiDestroyHandler { @@ -127,25 +130,27 @@ void uic_context_detach_context(UiContext *ctx, UiContext *doc_ctx); // TODO void uic_context_detach_all(UiContext *ctx); UiVar* uic_get_var(UiContext *ctx, const char *name); +UiVar* uic_get_var_t(UiContext *ctx, const char *name, UiVarType type); 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); +void uic_destroy_value(UiContext *ctx, UiVarType type, void *value); 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_copy_var_binding(UiVar *from, UiVar *to, UiBool copytodoc); +void uic_copy_value_binding(UiVarType type, void *from, void *to); void uic_save_var(UiVar *var); void uic_unbind_var(UiVar *var); +const char *uic_type2str(UiVarType type); void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value); -void uic_remove_bound_var(UiContext *ctx, UiVar *var); - -size_t uic_group_array_size(const int *groups); -void uic_check_group_widgets(UiContext *ctx); -void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups); -void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups); -void uic_remove_group_widget(UiContext *ctx, void *widget); +size_t uic_state_array_size(const int *states); +void uic_check_state_widgets(UiContext *ctx); +void uic_add_state_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *states); +void uic_add_state_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *states, size_t numstates); +void uic_remove_state_widget(UiContext *ctx, void *widget); #ifdef __cplusplus } diff --git a/ui/common/menu.c b/ui/common/menu.c index b0cfc46..64c49c1 100644 --- a/ui/common/menu.c +++ b/ui/common/menu.c @@ -89,20 +89,20 @@ static char* nl_strdup(const char* s) { return s ? strdup(s) : NULL; } -int* uic_copy_groups(const int* groups, size_t *ngroups) { - *ngroups = 0; - if (!groups) { +int* uic_copy_states(const int* states, size_t *nstates) { + *nstates = 0; + if (!states) { return NULL; } size_t n; - for (n = 0; groups[n] > -1; n++) { } + for (n = 0; states[n] > -1; n++) { } - if (ngroups > 0) { + if (nstates > 0) { int* newarray = calloc(n+1, sizeof(int)); - memcpy(newarray, groups, n * sizeof(int)); + memcpy(newarray, states, n * sizeof(int)); newarray[n] = -1; - *ngroups = n; + *nstates = n; return newarray; } return NULL; @@ -152,7 +152,7 @@ void ui_menuitem_create(UiMenuItemArgs *args) { item->icon = nl_strdup(args->icon); item->userdata = args->onclickdata; item->callback = args->onclick; - item->groups = uic_copy_groups(args->groups, &item->ngroups); + item->states = uic_copy_states(args->states, &item->nstates); add_item((UiMenuItemI*)item); } @@ -179,7 +179,7 @@ void ui_menu_toggleitem_create(UiMenuToggleItemArgs *args) { item->varname = nl_strdup(args->varname); item->userdata = args->onchangedata; item->callback = args->onchange; - item->groups = uic_copy_groups(args->groups, &item->ngroups); + item->states = uic_copy_states(args->nstates, &item->nstates); add_item((UiMenuItemI*)item); } @@ -196,7 +196,7 @@ void ui_menu_radioitem_create(UiMenuToggleItemArgs *args) { item->varname = nl_strdup(args->varname); item->userdata = args->onchangedata; item->callback = args->onchange; - item->groups = uic_copy_groups(args->groups, &item->ngroups); + item->states = uic_copy_states(args->nstates, &item->nstates); add_item((UiMenuItemI*)item); } @@ -296,14 +296,14 @@ static void free_menuitem(UiMenuItemI *item) { } case UI_MENU_ITEM: { UiMenuItem *i = (UiMenuItem*)item; - free(i->groups); + free(i->states); free(i->label); free(i->icon); break; } case UI_MENU_CHECK_ITEM: { UiMenuCheckItem *i = (UiMenuCheckItem*)item; - free(i->groups); + free(i->states); free(i->label); free(i->icon); free(i->varname); @@ -311,7 +311,7 @@ static void free_menuitem(UiMenuItemI *item) { } case UI_MENU_RADIO_ITEM: { UiMenuRadioItem *i = (UiMenuRadioItem*)item; - free(i->groups); + free(i->states); free(i->label); free(i->icon); free(i->varname); diff --git a/ui/common/menu.h b/ui/common/menu.h index f44f6b4..9aea578 100644 --- a/ui/common/menu.h +++ b/ui/common/menu.h @@ -79,8 +79,8 @@ struct UiMenuItem { char *label; char *icon; void *userdata; - int *groups; - size_t ngroups; + int *states; + size_t nstates; }; struct UiMenuCheckItem { @@ -90,8 +90,8 @@ struct UiMenuCheckItem { char *varname; ui_callback callback; void *userdata; - int *groups; - size_t ngroups; + int *states; + size_t nstates; }; struct UiMenuRadioItem { @@ -101,8 +101,8 @@ struct UiMenuRadioItem { char *varname; ui_callback callback; void *userdata; - int *groups; - size_t ngroups; + int *states; + size_t nstates; }; struct UiMenuItemList { @@ -129,7 +129,7 @@ UiMenu* uic_get_menu_list(void); void uic_add_menu_to_stack(UiMenu* menu); -int* uic_copy_groups(const int* groups, size_t *ngroups); +int* uic_copy_states(const int* states, size_t *nstates); void uic_set_tmp_eventdata(void *eventdata, int type); void* uic_get_tmp_eventdata(void); diff --git a/ui/common/message.c b/ui/common/message.c new file mode 100644 index 0000000..30ef304 --- /dev/null +++ b/ui/common/message.c @@ -0,0 +1,214 @@ +/* + * 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 _WIN32 + +#include +#include +#include + +#include "message.h" + +int uic_message_send_(UiMessageHandler *handler, cxstring msg) { + return handler->send(handler, msg); +} + +UiMessageHandler* uic_simple_msg_handler(int in, int out, msg_received_callback callback) { + UiSimpleMessageHandler *handler = malloc(sizeof(UiSimpleMessageHandler)); + handler->handler.start = uic_simple_msg_handler_start; + handler->handler.stop = uic_simple_msg_handler_stop; + handler->handler.send = uic_simple_msg_handler_send; + handler->handler.callback = callback; + handler->in = in; + handler->out = out; + handler->outbuf = cxBufferCreate(NULL, 4096, NULL, CX_BUFFER_FREE_CONTENTS | CX_BUFFER_AUTO_EXTEND); + handler->stop = 0; + pthread_mutex_init(&handler->queue_lock, NULL); + pthread_mutex_init(&handler->avlbl_lock, NULL); + pthread_cond_init(&handler->available, NULL); + return (UiMessageHandler*)handler; +} + +int uic_simple_msg_handler_start(UiMessageHandler *handler) { + UiSimpleMessageHandler *sh = (UiSimpleMessageHandler*)handler; + if(pthread_create(&sh->in_thread, NULL, uic_simple_msg_handler_in_thread, sh)) { + return 1; + } + if(pthread_create(&sh->out_thread, NULL, uic_simple_msg_handler_out_thread, sh)) { + return 1; + } + return 0; +} + +int uic_simple_msg_handler_stop(UiMessageHandler *handler) { + UiSimpleMessageHandler *sh = (UiSimpleMessageHandler*)handler; + pthread_mutex_lock(&sh->queue_lock); + sh->stop = 0; + pthread_cond_signal(&sh->available); + pthread_mutex_unlock(&sh->queue_lock); + close(sh->in); + sh->in = -1; + + pthread_join(sh->in_thread, NULL); + pthread_join(sh->out_thread, NULL); + + return 0; +} + +int uic_simple_msg_handler_send(UiMessageHandler *handler, cxstring msg) { + UiSimpleMessageHandler *sh = (UiSimpleMessageHandler*)handler; + pthread_mutex_lock(&sh->queue_lock); + char header[32]; + snprintf(header, 32, "%zu\n", msg.length); + cxBufferPutString(sh->outbuf, header); + cxBufferWrite(msg.ptr, 1, msg.length, sh->outbuf); + pthread_cond_signal(&sh->available); + pthread_mutex_unlock(&sh->queue_lock); + return 0; +} + +#define HEADERBUF_SIZE 64 + +void* uic_simple_msg_handler_in_thread(void *data) { + UiSimpleMessageHandler *handler = data; + + char *msg = NULL; + size_t msg_size = 0; + size_t msg_pos = 0; // currently received message length + + char headerbuf[HEADERBUF_SIZE]; + size_t headerpos = 0; + + char buf[2048]; + ssize_t r; + while((r = read(handler->in, buf, 2024)) > 0) { + char *buffer = buf; + size_t available = r; + + while(available > 0) { + if(msg) { + // read message + size_t need = msg_size - msg_pos; + size_t cplen = r > need ? need : available; + memcpy(msg+msg_pos, buffer, cplen); + buffer += cplen; + available -= cplen; + msg_pos += cplen; + if(msg_pos == msg_size) { + // message complete + //fprintf(stderr, "send: %.*s\n", (int)msg_size, msg); + if(handler->handler.callback) { + handler->handler.callback(cx_strn(msg, msg_size)); + } + free(msg); + msg = NULL; + msg_size = 0; + msg_pos = 0; + } + } else { + size_t header_max = HEADERBUF_SIZE - headerpos - 1; + if(header_max > available) { + header_max = available; + } + // search for line break + int i; + int header_complete = 0; + for(i=0;i= HEADERBUF_SIZE) { + fprintf(stderr, "Error: message header too big\n"); + exit(-1); + } + } + } + + + } + perror("error"); + fprintf(stderr, "stop simple_msg_handler_in_thread\n"); + + return NULL; +} + +void* uic_simple_msg_handler_out_thread(void *data) { + UiSimpleMessageHandler *handler = data; + CxBuffer *buffer = handler->outbuf; + + pthread_mutex_lock(&handler->queue_lock); + + for(;;) { + if(buffer->pos == 0) { + pthread_cond_wait(&handler->available, &handler->queue_lock); + continue; + } else { + size_t n = buffer->pos; + size_t pos = 0; + while(n > 0) { + ssize_t w = write(handler->out, buffer->space + pos, n); + if(w <= 0) { + fprintf(stderr, "Error: output error\n"); + break; + } + n -= w; + pos += w; + } + if(n > 0) { + break; // error + } + buffer->pos = 0; + } + } + + pthread_mutex_unlock(&handler->queue_lock); + + return NULL; +} + +#endif diff --git a/ui/common/message.h b/ui/common/message.h new file mode 100644 index 0000000..83cb135 --- /dev/null +++ b/ui/common/message.h @@ -0,0 +1,91 @@ +/* + * 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_MESSAGE_H +#define UIC_MESSAGE_H + + +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct UiMessageHandler UiMessageHandler; + +typedef void(*msg_received_callback)(cxstring msg); + +struct UiMessageHandler { + int (*start)(UiMessageHandler *handler); + int (*stop)(UiMessageHandler *handler); + int (*send)(UiMessageHandler *handler, cxstring msg); + + msg_received_callback callback; +}; + +typedef struct UiSimpleMessageHandler { + UiMessageHandler handler; + int in; + int out; +#ifndef _WIN32 + pthread_t in_thread; + pthread_t out_thread; + pthread_mutex_t queue_lock; + pthread_mutex_t avlbl_lock; + pthread_cond_t available; +#endif + CxBuffer *outbuf; + int stop; +} UiSimpleMessageHandler; + +int uic_message_send_(UiMessageHandler *handler, cxstring msg); +#define uic_message_send(handler, msg) uic_message_send_(handler, cx_strcast(msg)) + +UiMessageHandler* uic_simple_msg_handler(int in, int out, msg_received_callback callback); +int uic_simple_msg_handler_start(UiMessageHandler *handler); +int uic_simple_msg_handler_stop(UiMessageHandler *handler); +int uic_simple_msg_handler_send(UiMessageHandler *handler, cxstring msg); + +void* uic_simple_msg_handler_in_thread(void *data); +void* uic_simple_msg_handler_out_thread(void *data); + + +#ifdef __cplusplus +} +#endif + +#endif /* UIC_MESSAGE_H */ + diff --git a/ui/common/object.c b/ui/common/object.c index b041bc9..9b0a8b7 100644 --- a/ui/common/object.c +++ b/ui/common/object.c @@ -32,6 +32,7 @@ #include "context.h" #include +#include #include "../ui/container.h" @@ -108,7 +109,7 @@ void uic_object_destroy(UiObject *obj) { UiObject* uic_object_new_toplevel(void) { fflush(stdout); CxMempool *mp = cxMempoolCreateSimple(256); - UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); + UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObjectPrivate)); fflush(stdout); obj->ctx = uic_context(obj, mp); obj->ctx->parent = ui_global_context(); @@ -119,7 +120,7 @@ UiObject* uic_object_new_toplevel(void) { } UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget) { - UiObject *newobj = cxCalloc(ctx->allocator, 1, sizeof(UiObject)); + UiObject *newobj = cxCalloc(ctx->allocator, 1, sizeof(UiObjectPrivate)); newobj->ctx = ctx; newobj->widget = widget; uic_object_created(newobj); @@ -170,3 +171,22 @@ void uic_object_remove_second_last_container(UiObject *toplevel) { fprintf(stderr, "Error: uic_object_remove_second_last_container expected at least 2 containers\n"); } } + +// public API + +void ui_object_set(UiObject *obj, const char *key, void *data) { + UiObjectPrivate *p = (UiObjectPrivate*)obj; + if(!p->ext) { + p->ext = cxHashMapCreate(obj->ctx->mp->allocator, CX_STORE_POINTERS, 4); + } + if(data) { + cxMapPut(p->ext, key, data); + } else { + cxMapRemove(p->ext, key); + } +} + +void* ui_object_get(UiObject *obj, const char *key) { + UiObjectPrivate *p = (UiObjectPrivate*)obj; + return p->ext ? cxMapGet(p->ext, key) : NULL; +} diff --git a/ui/common/object.h b/ui/common/object.h index e6dd3c3..4327875 100644 --- a/ui/common/object.h +++ b/ui/common/object.h @@ -30,10 +30,16 @@ #define UIC_OBJECT_H #include "../ui/toolkit.h" +#include #ifdef __cplusplus extern "C" { #endif + +typedef struct UiObjectPrivate { + UiObject obj; + CxMap *ext; +} UiObjectPrivate; typedef void (*ui_object_callback)(UiObject *obj, void *userdata); diff --git a/ui/common/objs.mk b/ui/common/objs.mk index 4d401b5..3b49d85 100644 --- a/ui/common/objs.mk +++ b/ui/common/objs.mk @@ -34,6 +34,7 @@ COMMON_OBJ += document$(OBJ_EXT) COMMON_OBJ += object$(OBJ_EXT) COMMON_OBJ += container$(OBJ_EXT) COMMON_OBJ += types$(OBJ_EXT) +COMMON_OBJ += app$(OBJ_EXT) COMMON_OBJ += properties$(OBJ_EXT) COMMON_OBJ += menu$(OBJ_EXT) COMMON_OBJ += toolbar$(OBJ_EXT) @@ -42,6 +43,8 @@ COMMON_OBJ += threadpool$(OBJ_EXT) COMMON_OBJ += condvar$(OBJ_EXT) COMMON_OBJ += args$(OBJ_EXT) COMMON_OBJ += wrapper$(OBJ_EXT) +COMMON_OBJ += utils$(OBJ_EXT) +COMMON_OBJ += message$(OBJ_EXT) TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)uic_%) TOOLKITSOURCE += $(COMMON_OBJ:%$(OBJ_EXT)=common/%.c) diff --git a/ui/common/properties.c b/ui/common/properties.c index 28676fb..33ffaea 100644 --- a/ui/common/properties.c +++ b/ui/common/properties.c @@ -54,6 +54,8 @@ static CxMap *language; static char *locales_dir; static char *pixmaps_dir; +static UiBool use_xdg_config_home = TRUE; + #endif @@ -73,6 +75,8 @@ char* ui_getappdir(void) { #define UI_ENV_HOME "USERPROFILE" #endif +#define UI_XDG_CONFIG_HOME_VAR "XDG_CONFIG_HOME" + char* ui_configfile(const char *name) { const char *appname = ui_appname(); if(!appname) { @@ -102,8 +106,23 @@ char* ui_configfile(const char *name) { // on Windows the app dir is $USERPROFILE/AppData/Local/$APPNAME/ cxBufferPutString(&buf, "AppData\\Local\\"); #else - // app dir is $HOME/.$APPNAME/ - cxBufferPut(&buf, '.'); + if(use_xdg_config_home) { + // app dir is $HOME/.config/$APPNAME/ + char *xdghome = getenv(UI_XDG_CONFIG_HOME_VAR); + size_t xdghomelen = xdghome ? strlen(xdghome) : 0; + if(xdghome && xdghomelen > 0) { + cxBufferSeek(&buf, 0, SEEK_SET); + cxBufferPutString(&buf, xdghome); + if(xdghome[xdghomelen-1] != '/') { + cxBufferPut(&buf, '/'); + } + } else { + cxBufferPutString(&buf, ".config/"); + } + } else { + cxBufferPut(&buf, '.'); + } + #endif cxBufferPutString(&buf, appname); cxBufferPut(&buf, UI_PATH_SEPARATOR); @@ -139,9 +158,37 @@ void uic_load_app_properties() { if(!dir) { return; } + size_t len = strlen(dir); + if(len < 2) { + return; + } if(ui_mkdir(dir)) { + if(errno == ENOENT) { + char *parent = strdup(dir); + for(int i=len-2;i>=0;i--) { + if(parent[i] == '/') { + parent[i] = 0; + break; + } + } + // try creating the parent + int err = ui_mkdir(parent); + if(err) { + fprintf(stderr, "Error: Cannot create directory %s: %s\n", strerror(errno)); + free(parent); + free(dir); + return; + } + free(parent); + + // try dir again + if(!ui_mkdir(dir)) { + errno = EEXIST; // success + } + } + if(errno != EEXIST) { - fprintf(stderr, "Ui Error: Cannot create directory %s\n", dir); + fprintf(stderr, "Error: Cannot create directory %s: %s\n", dir, strerror(errno)); free(dir); return; } diff --git a/ui/common/threadpool.c b/ui/common/threadpool.c index 7988eab..3b4c096 100644 --- a/ui/common/threadpool.c +++ b/ui/common/threadpool.c @@ -40,24 +40,20 @@ static threadpool_job kill_job; UiThreadpool* threadpool_new(int min, int max) { UiThreadpool *pool = malloc(sizeof(UiThreadpool)); - pool->queue = NULL; - pool->queue_len = 0; + pool->queue = ui_queue_create(); pool->num_idle = 0; pool->min_threads = min; pool->max_threads = max; - pthread_mutex_init(&pool->queue_lock, NULL); - pthread_mutex_init(&pool->avlbl_lock, NULL); - pthread_cond_init(&pool->available, NULL); - return pool; } int threadpool_start(UiThreadpool *pool) { + pool->nthreads = pool->min_threads; + pool->threads = calloc(pool->max_threads, sizeof(pthread_t)); /* create pool threads */ - for(int i=0;imin_threads;i++) { - pthread_t t; - if (pthread_create(&t, NULL, threadpool_func, pool) != 0) { + for(int i=0;inthreads;i++) { + if (pthread_create(&pool->threads[i], NULL, threadpool_func, pool) != 0) { fprintf(stderr, "uic: threadpool_start: pthread_create failed: %s", strerror(errno)); return 1; } @@ -65,6 +61,16 @@ int threadpool_start(UiThreadpool *pool) { return 0; } +int threadpool_join(UiThreadpool *pool) { + int err = 0; + for(int i=0;inthreads;i++) { + if(pthread_join(pool->threads[i], NULL)) { + err = 1; + } + } + return err; +} + void* threadpool_func(void *data) { UiThreadpool *pool = (UiThreadpool*)data; @@ -82,71 +88,19 @@ void* threadpool_func(void *data) { } threadpool_job* threadpool_get_job(UiThreadpool *pool) { - pthread_mutex_lock(&pool->queue_lock); - - threadpool_job *job = NULL; - pool->num_idle++; - while(job == NULL) { - if(pool->queue_len == 0) { - pthread_cond_wait(&pool->available, &pool->queue_lock); - continue; - } else { - pool_queue_t *q = pool->queue; - job = q->job; - pool->queue = q->next; - pool->queue_len--; - free(q); - } - } - pool->num_idle--; - - pthread_mutex_unlock(&pool->queue_lock); + threadpool_job *job = ui_queue_get_wait(pool->queue); return job; } -void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data) { - // TODO: handle errors - +void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data) { threadpool_job *job = malloc(sizeof(threadpool_job)); job->callback = func; job->data = data; - - pthread_mutex_lock(&pool->queue_lock); - threadpool_enqueue_job(pool, job); - - int create_thread = 0; - int destroy_thread = 0; - int diff = pool->queue_len - pool->num_idle; - - //if(pool->queue_len == 1) { - pthread_cond_signal(&pool->available); - //} - - pthread_mutex_unlock(&pool->queue_lock); -} - -void threadpool_enqueue_job(UiThreadpool *pool, threadpool_job *job) { - pool_queue_t *q = malloc(sizeof(pool_queue_t)); - q->job = job; - q->next = NULL; - - if(pool->queue == NULL) { - pool->queue = q; - } else { - pool_queue_t *last_elem = pool->queue; - while(last_elem->next != NULL) { - last_elem = last_elem->next; - } - last_elem->next = q; - } - pool->queue_len++; + ui_queue_put(pool->queue, job); } - - - UiThreadpool* ui_threadpool_create(int nthreads) { UiThreadpool *pool = threadpool_new(nthreads, nthreads); threadpool_start(pool); // TODO: check return value @@ -191,6 +145,71 @@ void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void threadpool_run(pool, ui_threadpool_job_func, job); } +/* --------------------------------- Queue --------------------------------- */ + +UiQueue* ui_queue_create(void) { + UiQueue *queue = calloc(1, sizeof(UiQueue)); + pthread_mutex_init(&queue->lock, NULL); + pthread_mutex_init(&queue->avlbl_lock, NULL); + pthread_cond_init(&queue->available, NULL); + return queue; +} + +void ui_queue_free(UiQueue *queue) { + // The queue must be empty, we could free UiQueueElm, + // but not the payload data + pthread_mutex_destroy(&queue->lock); + pthread_mutex_destroy(&queue->avlbl_lock); + pthread_cond_destroy(&queue->available); + free(queue); +} + +void ui_queue_put(UiQueue *queue, void *data) { + // create queue element + UiQueueElm *elm = malloc(sizeof(UiQueueElm)); + elm->data = data; + elm->next = NULL; + + pthread_mutex_lock(&queue->lock); + + // put queue element at the end of the linked list + if(queue->elements) { + UiQueueElm *end = queue->elements; + while(end->next) { + end = end->next; + } + end->next = elm; + } else { + queue->elements = elm; + } + queue->length++; + + // signal new available data + pthread_cond_signal(&queue->available); + + pthread_mutex_unlock(&queue->lock); +} + +void* ui_queue_get_wait(UiQueue *queue) { + pthread_mutex_lock(&queue->lock); + + void *data = NULL; + while(data == NULL) { + if(queue->length == 0) { + pthread_cond_wait(&queue->available, &queue->lock); + continue; + } else { + UiQueueElm *q = queue->elements; + data = q->data; + queue->elements = q->next; + queue->length--; + free(q); + } + } + + pthread_mutex_unlock(&queue->lock); + return data; +} #endif diff --git a/ui/common/threadpool.h b/ui/common/threadpool.h index d167062..4a997f6 100644 --- a/ui/common/threadpool.h +++ b/ui/common/threadpool.h @@ -39,6 +39,8 @@ extern "C" { #endif +typedef struct UiQueueElm UiQueueElm; +typedef struct UiQueue UiQueue; typedef struct UiJob { UiObject *obj; @@ -48,20 +50,30 @@ typedef struct UiJob { void *finish_data; } UiJob; +struct UiQueueElm { + void *data; + UiQueueElm *next; +}; + +struct UiQueue { + UiQueueElm *elements; + size_t length; + pthread_mutex_t lock; + pthread_mutex_t avlbl_lock; + pthread_cond_t available; +}; typedef struct _threadpool_job threadpool_job; typedef void*(*job_callback_f)(void *data); typedef struct _pool_queue pool_queue_t; struct UiThreadpool { - pthread_mutex_t queue_lock; - pthread_mutex_t avlbl_lock; - pthread_cond_t available; - pool_queue_t *queue; - uint32_t queue_len; - uint32_t num_idle; - int min_threads; - int max_threads; + UiQueue *queue; + uint32_t num_idle; + int min_threads; + int max_threads; + pthread_t *threads; + int nthreads; }; struct _threadpool_job { @@ -76,10 +88,15 @@ struct _pool_queue { UiThreadpool* threadpool_new(int min, int max); int threadpool_start(UiThreadpool *pool); +int threadpool_join(UiThreadpool *pool); void* threadpool_func(void *data); threadpool_job* threadpool_get_job(UiThreadpool *pool); void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data); -void threadpool_enqueue_job(UiThreadpool *pool, threadpool_job *job); + +UiQueue* ui_queue_create(void); +void ui_queue_free(UiQueue *queue); +void ui_queue_put(UiQueue *queue, void *data); +void* ui_queue_get_wait(UiQueue *queue); #ifdef __cplusplus } diff --git a/ui/common/toolbar.c b/ui/common/toolbar.c index f237290..eed1246 100644 --- a/ui/common/toolbar.c +++ b/ui/common/toolbar.c @@ -57,15 +57,15 @@ static UiToolbarItemArgs itemargs_copy(UiToolbarItemArgs *args, size_t *ngroups, newargs.tooltip = nl_strdup(args->tooltip); newargs.onclick = args->onclick; newargs.onclickdata = args->onclickdata; - newargs.groups = uic_copy_groups(args->groups, ngroups); - newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates); + newargs.states = uic_copy_states(args->states, ngroups); + newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates); return newargs; } void ui_toolbar_item_create(const char* name, UiToolbarItemArgs *args) { UiToolbarItem* item = malloc(sizeof(UiToolbarItem)); item->item.type = UI_TOOLBAR_ITEM; - item->args = itemargs_copy(args, &item->ngroups, &item->nvstates); + item->args = itemargs_copy(args, &item->nstates, &item->nvstates); cxMapPut(toolbar_items, name, item); } @@ -78,15 +78,15 @@ static UiToolbarToggleItemArgs toggleitemargs_copy(UiToolbarToggleItemArgs *args newargs.varname = nl_strdup(args->varname); newargs.onchange = args->onchange; newargs.onchangedata = args->onchangedata; - newargs.groups = uic_copy_groups(args->groups, ngroups); - newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates); + newargs.states = uic_copy_states(args->states, ngroups); + newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates); return newargs; } void ui_toolbar_toggleitem_create(const char* name, UiToolbarToggleItemArgs *args) { UiToolbarToggleItem* item = malloc(sizeof(UiToolbarToggleItem)); item->item.type = UI_TOOLBAR_TOGGLEITEM; - item->args = toggleitemargs_copy(args, &item->ngroups, &item->nvstates); + item->args = toggleitemargs_copy(args, &item->nstates, &item->nvstates); cxMapPut(toolbar_items, name, item); } @@ -95,7 +95,7 @@ static UiToolbarMenuArgs menuargs_copy(UiToolbarMenuArgs *args, size_t *nvstates newargs.label = nl_strdup(args->label); newargs.icon = nl_strdup(args->icon); newargs.tooltip = nl_strdup(args->tooltip); - newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates); + newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates); return newargs; } diff --git a/ui/common/toolbar.h b/ui/common/toolbar.h index 1ac1d55..9ae60f0 100644 --- a/ui/common/toolbar.h +++ b/ui/common/toolbar.h @@ -62,14 +62,14 @@ struct UiToolbarItemI { struct UiToolbarItem { UiToolbarItemI item; UiToolbarItemArgs args; - size_t ngroups; + size_t nstates; size_t nvstates; }; struct UiToolbarToggleItem { UiToolbarItemI item; UiToolbarToggleItemArgs args; - size_t ngroups; + size_t nstates; size_t nvstates; }; diff --git a/ui/common/types.c b/ui/common/types.c index 57ad68e..e8680b7 100644 --- a/ui/common/types.c +++ b/ui/common/types.c @@ -33,13 +33,15 @@ #include #include -#include "../ui/tree.h" +#include "../ui/list.h" #include "types.h" #include "context.h" #include "../ui/image.h" static ui_list_init_func default_list_init; static void *default_list_init_userdata; +static ui_list_destroy_func default_list_destroy; +static void *default_list_destroy_userdata; UiObserver* ui_observer_new(ui_callback f, void *data) { UiObserver *observer = malloc(sizeof(UiObserver)); @@ -105,6 +107,11 @@ void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused) { list->count = ui_list_count; } +void uic_ucx_list_destroy(UiContext *ctx, UiList *list, void *unused) { + cxListFree(list->data); + ui_free(ctx, list); +} + UiList* ui_list_new(UiContext *ctx, const char *name) { return ui_list_new2(ctx, name, default_list_init ? default_list_init : uic_ucx_list_init, default_list_init_userdata); } @@ -121,9 +128,13 @@ UiList* ui_list_new2(UiContext *ctx, const char *name, ui_list_init_func listini return list; } -void ui_list_free(UiList *list) { - cxListFree(list->data); - free(list); +void ui_list_free(UiContext *ctx, UiList *list) { + if(!default_list_destroy) { + uic_ucx_list_destroy(ctx, list, NULL); + } else { + default_list_destroy(ctx, list, default_list_destroy_userdata); + } + } void* ui_list_first(UiList *list) { @@ -201,6 +212,7 @@ typedef struct { UiModel* ui_model(UiContext *ctx, ...) { UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel)); + info->ctx = ctx; va_list ap; va_start(ap, ctx); @@ -240,8 +252,18 @@ UiModel* ui_model(UiContext *ctx, ...) { #define UI_MODEL_DEFAULT_ALLOC_SIZE 16 + +static void model_notify_observer(UiModel *model, int insert, int delete) { + UiModelChangeObserver *obs = model->observer; + while(obs) { + obs->update(model, obs->userdata, insert, delete); + obs = obs->next; + } +} + UiModel* ui_model_new(UiContext *ctx) { UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel)); + info->ctx = ctx; 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*)); @@ -249,16 +271,21 @@ UiModel* ui_model_new(UiContext *ctx) { return info; } -void ui_model_add_column(UiContext *ctx, UiModel *model, UiModelType type, const char *title, int width) { +void ui_model_add_column(UiModel *model, UiModelType type, const char *title, int width) { + UiContext *ctx = model->ctx; 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; + int index = model->columns; + model->types[index] = type; + model->titles[index] = ui_strdup(ctx, title); + model->columnsize[index] = width; + + model_notify_observer(model, index, -1); + model->columns++; } @@ -266,6 +293,7 @@ UiModel* ui_model_copy(UiContext *ctx, UiModel* model) { const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator; UiModel* newmodel = cxMalloc(a, sizeof(UiModel)); + newmodel->ctx = ctx; *newmodel = *model; newmodel->types = cxCalloc(a, model->columns, sizeof(UiModelType)); @@ -281,11 +309,67 @@ UiModel* ui_model_copy(UiContext *ctx, UiModel* model) { return newmodel; } -void ui_model_free(UiContext *ctx, UiModel *mi) { - const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator; +void ui_model_ref(UiModel *model) { + model->ref++; +} + +void ui_model_unref(UiModel *model) { + if(--model->ref == 0) { + ui_model_free(model); + } +} + +void ui_model_add_observer(UiModel *model, ui_model_update_func update, void *data) { + UiModelChangeObserver *observer = ui_malloc(model->ctx, sizeof(UiModelChangeObserver)); + observer->update = update; + observer->userdata = data; + observer->next = NULL; + + if(model->observer) { + UiModelChangeObserver *last = model->observer; + while(last->next) { + last = last->next; + } + last->next = observer; + } else { + model->observer = observer; + } +} + +void ui_model_remove_observer(UiModel *model, void *data) { + if(model->observer) { + UiModelChangeObserver *obs = model->observer; + UiModelChangeObserver *prev = NULL; + while(obs) { + if(obs->userdata == data) { + // remove + if(prev) { + prev->next = obs->next; + } else { + model->observer = obs->next; + } + // free + ui_free(model->ctx, obs); + break; + } + prev = obs; + obs = obs->next; + } + } +} + +void ui_model_free(UiModel *mi) { + UiContext *ctx = mi->ctx; + const CxAllocator* a = ctx->allocator; for(int i=0;icolumns;i++) { ui_free(ctx, mi->titles[i]); } + UiModelChangeObserver *obs = mi->observer; + while(obs) { + UiModelChangeObserver *n = obs->next; + cxFree(a, obs); + obs = n; + } cxFree(a, mi->types); cxFree(a, mi->titles); cxFree(a, mi->columnsize); @@ -724,9 +808,7 @@ UIEXPORT void ui_list_setselection(UiList *list, int index) { } UIEXPORT void ui_listselection_free(UiListSelection selection) { - if (selection.rows) { - free(selection.rows); - } + free(selection.rows); } UIEXPORT UiStr ui_str(char *cstr) { @@ -787,6 +869,7 @@ void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObser } static int ui_set_op = 0; +static int ui_onchange_events_enabled = TRUE; void ui_setop_enable(int set) { ui_set_op = set; @@ -796,6 +879,14 @@ int ui_get_setop(void) { return ui_set_op; } +void ui_onchange_events_enable(UiBool enable) { + ui_onchange_events_enabled = enable; +} + +UiBool ui_onchange_events_is_enabled(void) { + return ui_onchange_events_enabled; +} + /* ---------------- List initializers and wrapper functions ---------------- */ void ui_global_list_initializer(ui_list_init_func func, void *userdata) { @@ -803,6 +894,11 @@ void ui_global_list_initializer(ui_list_init_func func, void *userdata) { default_list_init_userdata = userdata; } +void ui_global_list_destructor(ui_list_destroy_func func, void *userdata) { + default_list_destroy = func; + default_list_destroy_userdata = userdata; +} + void ui_list_class_set_first(UiList *list, void*(*first)(UiList *list)) { list->first = first; } diff --git a/ui/common/types.h b/ui/common/types.h index 46d6c2e..2f4a524 100644 --- a/ui/common/types.h +++ b/ui/common/types.h @@ -36,6 +36,7 @@ extern "C" { #endif void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused); +void uic_ucx_list_destroy(UiContext *ctx, UiList *list, void *unused); void uic_int_copy(UiInteger *from, UiInteger *to); void uic_double_copy(UiDouble *from, UiDouble *to); diff --git a/ui/common/utils.c b/ui/common/utils.c new file mode 100644 index 0000000..8d3b5dd --- /dev/null +++ b/ui/common/utils.c @@ -0,0 +1,151 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 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 "utils.h" +#include "properties.h" + +#include +#include + +#include +#include + +UiPathElm* ui_default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { + cxstring *pathelms; + size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); + + if (nelm == 0) { + *ret_nelm = 0; + return NULL; + } + + UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); + size_t n = nelm; + int j = 0; + for (int i = 0; i < nelm; i++) { + cxstring c = pathelms[i]; + if (c.length == 0) { + if (i == 0) { + c.length = 1; + } + else { + n--; + continue; + } + } + + cxmutstr m = cx_strdup(c); + elms[j].name = m.ptr; + elms[j].name_len = m.length; + + size_t elm_path_len = c.ptr + c.length - full_path; + cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); + elms[j].path = elm_path.ptr; + elms[j].path_len = elm_path.length; + + j++; + } + *ret_nelm = n; + + return elms; +} + +void ui_get_window_default_width(int *width, int *height) { + const char *width_str = ui_get_property("ui.window.width"); + const char *height_str = ui_get_property("ui.window.height"); + if(width_str && height_str) { + int w = atoi(width_str); + int h = atoi(height_str); + if(w > 0 && h > 0) { + *width = w; + *height = h; + } + } +} + +// from UCX json.c +static cxmutstr escape_string(cxstring str, bool escape_slash) { + // note: this function produces the string without enclosing quotes + // the reason is that we don't want to allocate memory just for that + CxBuffer buf = {0}; + + bool all_printable = true; + for (size_t i = 0; i < str.length; i++) { + unsigned char c = str.ptr[i]; + bool escape = c < 0x20 || c == '\\' || c == '"' + || (escape_slash && c == '/'); + + if (all_printable && escape) { + size_t capa = str.length + 32; + char *space = cxMallocDefault(capa); + if (space == NULL) return cx_mutstrn(NULL, 0); + cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND); + cxBufferWrite(str.ptr, 1, i, &buf); + all_printable = false; + } + if (escape) { + cxBufferPut(&buf, '\\'); + if (c == '\"') { + cxBufferPut(&buf, '\"'); + } else if (c == '\n') { + cxBufferPut(&buf, 'n'); + } else if (c == '\t') { + cxBufferPut(&buf, 't'); + } else if (c == '\r') { + cxBufferPut(&buf, 'r'); + } else if (c == '\\') { + cxBufferPut(&buf, '\\'); + } else if (c == '/') { + cxBufferPut(&buf, '/'); + } else if (c == '\f') { + cxBufferPut(&buf, 'f'); + } else if (c == '\b') { + cxBufferPut(&buf, 'b'); + } else { + char code[6]; + snprintf(code, sizeof(code), "u%04x", (unsigned int) c); + cxBufferPutString(&buf, code); + } + } else if (!all_printable) { + cxBufferPut(&buf, c); + } + } + cxmutstr ret; + if (all_printable) { + // don't copy the string when we don't need to escape anything + ret = cx_mutstrn((char*)str.ptr, str.length); + } else { + ret = cx_mutstrn(buf.space, buf.size); + } + cxBufferDestroy(&buf); + return ret; +} + +cxmutstr ui_escape_string(cxstring str) { + return escape_string(str, FALSE); +} diff --git a/ui/common/utils.h b/ui/common/utils.h new file mode 100644 index 0000000..94f3e89 --- /dev/null +++ b/ui/common/utils.h @@ -0,0 +1,56 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 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_UTILS_H +#define UIC_UTILS_H + +#include "../ui/toolkit.h" +#include "../ui/text.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +UiPathElm* ui_default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data); + +/* + * overrides width/height with values from + * ui.window.width and ui.window.height properties if available + */ +void ui_get_window_default_width(int *width, int *height); + +cxmutstr ui_escape_string(cxstring str); + + +#ifdef __cplusplus +} +#endif + +#endif /* UIC_UTILS_H */ + diff --git a/ui/common/wrapper.c b/ui/common/wrapper.c index 50ecf40..575dd29 100644 --- a/ui/common/wrapper.c +++ b/ui/common/wrapper.c @@ -125,6 +125,11 @@ void ui_srclist_remove(UiList *list, int index) { cxListRemove(cxlist, index); } +void ui_srclist_swap(UiList *list, int i1, int i2) { + CxList *cxlist = list->data; + cxListSwap(cxlist, i1, i2); +} + void ui_srclist_clear(UiList *list) { CxList *cxlist = list->data; cxListClear(cxlist); diff --git a/ui/common/wrapper.h b/ui/common/wrapper.h index 88fd8c8..37f2f38 100644 --- a/ui/common/wrapper.h +++ b/ui/common/wrapper.h @@ -30,7 +30,7 @@ #define UIC_WRAPPER_H #include "../ui/toolkit.h" -#include "../ui/tree.h" +#include "../ui/list.h" #ifdef __cplusplus extern "C" { @@ -56,6 +56,7 @@ UIEXPORT UiList* ui_srclist_new(UiContext *ctx, const char *name); UIEXPORT void ui_srclist_add(UiList *list, UiSubList *item); UIEXPORT void ui_srclist_insert(UiList *list, int index, UiSubList *item); UIEXPORT void ui_srclist_remove(UiList *list, int index); +UIEXPORT void ui_srclist_swap(UiList *list, int i1, int i2); UIEXPORT void ui_srclist_clear(UiList *list); UIEXPORT int ui_srclist_size(UiList *list); UIEXPORT void ui_srclist_generate_sublist_num_data(UiList *list); diff --git a/ui/gtk/button.c b/ui/gtk/button.c index 6db5141..cf911cf 100644 --- a/ui/gtk/button.c +++ b/ui/gtk/button.c @@ -106,7 +106,7 @@ GtkWidget* ui_create_button( UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) { 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_set_widget_states(obj->ctx, button, args->states); UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; UiLayout layout = UI_ARGS2LAYOUT(args); ct->add(ct, button, &layout); @@ -174,9 +174,9 @@ static void ui_toggled_callback(GtkToggleButton *widget, UiEventData *event) { static void ui_togglebutton_enable_state_callback(GtkToggleButton *widget, UiEventData *event) { if(gtk_toggle_button_get_active(widget)) { - ui_set_group(event->obj->ctx, event->value); + ui_set_state(event->obj->ctx, event->value); } else { - ui_unset_group(event->obj->ctx, event->value); + ui_unset_state(event->obj->ctx, event->value); } } @@ -308,9 +308,9 @@ static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleAr args->value, args->onchange, args->onchangedata, - args->enable_group); + args->enable_state); ui_set_name_and_style(widget, args->name, args->style_class); - ui_set_widget_groups(obj->ctx, widget, args->groups); + ui_set_widget_states(obj->ctx, widget, args->states); UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; UiLayout layout = UI_ARGS2LAYOUT(args); @@ -351,9 +351,9 @@ static void ui_checkbox_callback(GtkCheckButton *widget, UiEventData *event) { static void ui_checkbox_enable_state(GtkCheckButton *widget, UiEventData *event) { if(gtk_check_button_get_active(widget)) { - ui_set_group(event->obj->ctx, event->value); + ui_set_state(event->obj->ctx, event->value); } else { - ui_unset_group(event->obj->ctx, event->value); + ui_unset_state(event->obj->ctx, event->value); } } @@ -370,10 +370,10 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) { args->onchange, args->onchangedata, (ui_toggled_func)ui_checkbox_enable_state, - args->enable_group); + args->enable_state); ui_set_name_and_style(widget, args->name, args->style_class); - ui_set_widget_groups(obj->ctx, widget, args->groups); + ui_set_widget_states(obj->ctx, widget, args->states); UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; UiLayout layout = UI_ARGS2LAYOUT(args); @@ -419,7 +419,7 @@ static void switch_changed( UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) { 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); + ui_set_widget_states(obj->ctx, widget, args->states); UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); if(var) { @@ -541,7 +541,7 @@ UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) { GtkWidget *rbutton = RADIOBUTTON_NEW(rg, args->label); ui_set_name_and_style(rbutton, args->name, args->style_class); - ui_set_widget_groups(obj->ctx, rbutton, args->groups); + ui_set_widget_states(obj->ctx, rbutton, args->states); if(rgroup) { #if GTK_MAJOR_VERSION >= 4 if(rg) { @@ -895,7 +895,7 @@ 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_set_widget_states(obj->ctx, button, args->states); UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; UiLayout layout = UI_ARGS2LAYOUT(args); ct->add(ct, button, &layout); diff --git a/ui/gtk/container.c b/ui/gtk/container.c index 9ca07f9..a8df7e8 100644 --- a/ui/gtk/container.c +++ b/ui/gtk/container.c @@ -112,6 +112,21 @@ GtkWidget* ui_subcontainer_create( return add; } +/* -------------------- Custom Container -------------------- */ + +void ui_custom_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) { + UiCustomContainer *custom = (UiCustomContainer*)ct; + custom->add(custom->obj, ct->widget, widget, custom->userdata); +} + +void ui_custom_container_create(UiObject *obj, UIWIDGET widget, ui_addwidget_func add_child, void *userdata) { + UiCustomContainer *container = cxZalloc(obj->ctx->allocator, sizeof(UiCustomContainer)); + container->container.add = ui_custom_container_add; + container->container.widget = widget; + container->add = add_child; + container->userdata = userdata; + uic_object_push_container(obj, (UiContainerX*)container); +} /* -------------------- Box Container -------------------- */ UiContainerX* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) { @@ -985,6 +1000,8 @@ void ui_headerbar_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLay GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); hb->centerbox = box; UI_HEADERBAR_SET_TITLE_WIDGET(ct->widget, box); + UI_HEADERBAR_SHOW_TITLE_WIDGET(ct->widget, TRUE); + UI_HEADERBAR_SETTINGS(ct->widget); } BOX_ADD(hb->centerbox, widget); } @@ -1140,6 +1157,7 @@ UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) { UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation, int max, int init) { UiSplitPane *ct = malloc(sizeof(UiSplitPane)); + memset(ct, 0, sizeof(UiSplitPane)); ct->current_pane = pane; ct->orientation = orientation; ct->max = max; diff --git a/ui/gtk/container.h b/ui/gtk/container.h index 2f2510c..f224aad 100644 --- a/ui/gtk/container.h +++ b/ui/gtk/container.h @@ -61,6 +61,13 @@ struct UiContainerPrivate { int close; }; +typedef struct UiCustomContainer { + UiContainerPrivate container; + UiObject *obj; + ui_addwidget_func add; + void *userdata; +} UiCustomContainer; + typedef struct UiBoxContainer { UiContainerPrivate container; UiSubContainerType type; diff --git a/ui/gtk/entry.c b/ui/gtk/entry.c index b411191..9e61e4e 100644 --- a/ui/gtk/entry.c +++ b/ui/gtk/entry.c @@ -79,7 +79,7 @@ UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) { #endif 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); + ui_set_widget_states(obj->ctx, spin, args->states); if(args->width > 0) { gtk_widget_set_size_request(spin, args->width, -1); diff --git a/ui/gtk/graphics.c b/ui/gtk/graphics.c index 33fd186..3ae1025 100644 --- a/ui/gtk/graphics.c +++ b/ui/gtk/graphics.c @@ -107,14 +107,14 @@ UIWIDGET ui_drawingarea_create(UiObject *obj, UiDrawingAreaArgs *args) { widget, "draw", G_CALLBACK(draw_callback), - NULL); + drawingarea); #endif g_signal_connect( - widget, - "destroy", - G_CALLBACK(destroy_drawingarea), - drawingarea); + widget, + "destroy", + G_CALLBACK(destroy_drawingarea), + drawingarea); return widget; } diff --git a/ui/gtk/headerbar.c b/ui/gtk/headerbar.c index 0a9bd3e..ef1d9c7 100644 --- a/ui/gtk/headerbar.c +++ b/ui/gtk/headerbar.c @@ -164,7 +164,7 @@ void ui_add_headerbar_item( enum UiToolbarPos pos) { 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); + ui_set_widget_states(obj->ctx, button, item->args.states); ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states); WIDGET_ADD_CSS_CLASS(button, "flat"); headerbar_add(headerbar, box, button, pos); @@ -178,7 +178,7 @@ void ui_add_headerbar_toggleitem( enum UiToolbarPos pos) { GtkWidget *button = gtk_toggle_button_new(); - ui_set_widget_groups(obj->ctx, button, item->args.groups); + ui_set_widget_states(obj->ctx, button, item->args.states); ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states); WIDGET_ADD_CSS_CLASS(button, "flat"); 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); diff --git a/ui/gtk/headerbar.h b/ui/gtk/headerbar.h index 93028ea..392eae7 100644 --- a/ui/gtk/headerbar.h +++ b/ui/gtk/headerbar.h @@ -46,15 +46,20 @@ extern "C" { #define UI_HEADERBAR_PACK_START(h, w) adw_header_bar_pack_start(ADW_HEADER_BAR(h), w) #define UI_HEADERBAR_PACK_END(h, w) adw_header_bar_pack_end(ADW_HEADER_BAR(h), w) #define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) adw_header_bar_set_title_widget(ADW_HEADER_BAR(h), w) +#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b) adw_header_bar_set_show_title(ADW_HEADER_BAR(h), b) +#define UI_HEADERBAR_SETTINGS(h) adw_header_bar_set_centering_policy(ADW_HEADER_BAR(h), ADW_CENTERING_POLICY_LOOSE) #else #define UI_HEADERBAR GtkHeaderBar* #define UI_HEADERBAR_CAST(h) GTK_HEADER_BAR(h) #define UI_HEADERBAR_PACK_START(h, w) gtk_header_bar_pack_start(GTK_HEADER_BAR(h), w) #define UI_HEADERBAR_PACK_END(h, w) gtk_header_bar_pack_end(GTK_HEADER_BAR(h), w) +#define UI_HEADERBAR_SETTINGS(h) #if GTK_MAJOR_VERSION >= 4 #define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_title_widget(GTK_HEADER_BAR(h), w) +#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b) gtk_header_bar_set_show_title(GTK_HEADER_BAR(h), b) #else #define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_custom_title(GTK_HEADER_BAR(h), w) +#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b) gtk_header_bar_set_show_title(GTK_HEADER_BAR(h), b) #endif #endif diff --git a/ui/gtk/image.c b/ui/gtk/image.c index e60dbdd..8181126 100644 --- a/ui/gtk/image.c +++ b/ui/gtk/image.c @@ -30,6 +30,7 @@ #include "container.h" #include "menu.h" +#include "widget.h" #include "../common/context.h" #include "../common/object.h" @@ -67,8 +68,14 @@ UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs *args) { GtkWidget *drawingarea = gtk_drawing_area_new(); GtkWidget *toplevel; GtkWidget *widget = drawingarea; - - gtk_widget_set_size_request(drawingarea, 100, 100); + + int width = args->width; + int height = args->height; + if(width == 0 && height == 0) { + width = 100; + height = 100; + } + ui_widget_size_request(drawingarea, width, height); #if GTK_MAJOR_VERSION < 4 GtkWidget *eventbox = gtk_event_box_new(); diff --git a/ui/gtk/list.c b/ui/gtk/list.c index f793b2c..7a08ffc 100644 --- a/ui/gtk/list.c +++ b/ui/gtk/list.c @@ -80,6 +80,7 @@ static UiListView* create_listview(UiObject *obj, UiListArgs *args) { memset(tableview, 0, sizeof(UiListView)); tableview->obj = obj; tableview->model = args->model; + tableview->multiselection = args->multiselection; tableview->onactivate = args->onactivate; tableview->onactivatedata = args->onactivatedata; tableview->onselection = args->onselection; @@ -98,6 +99,11 @@ static UiListView* create_listview(UiObject *obj, UiListArgs *args) { tableview->onsave = args->onsave; tableview->onsavedata = args->onsavedata; +#if GTK_CHECK_VERSION(4, 0, 0) + tableview->coldata.listview = tableview; + tableview->coldata.column = 0; +#endif + if(args->getvalue2) { tableview->getvalue = args->getvalue2; tableview->getvaluedata = args->getvalue2data; @@ -200,7 +206,7 @@ static void cell_entry_activate( static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { UiColData *col = userdata; UiModel *model = col->listview->model; - UiModelType type = model->types[col->model_column]; + UiModelType type = model->types[col->column]; if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) { GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); GtkWidget *image = gtk_image_new(); @@ -281,16 +287,17 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g UiColData *col = userdata; UiList *list = col->listview->var ? col->listview->var->value : NULL; UiListView *listview = col->listview; + int datacolumn = listview->columns[col->column]; ObjWrapper *obj = gtk_list_item_get_item(item); UiModel *model = col->listview->model; - UiModelType type = model->types[col->model_column]; + UiModelType type = model->types[col->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) { + if(row->items[col->column] == NULL) { row->bound++; } } else { @@ -298,10 +305,10 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g cxMapPut(listview->bound_rows, row_key, row); row->bound = 1; } - row->items[col->model_column] = item; + row->items[col->column] = item; UiBool freevalue = FALSE; - void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue); + void *data = listview->getvalue(list, obj->data, obj->i, datacolumn, listview->getvaluedata, &freevalue); GtkWidget *child = gtk_list_item_get_child(item); PangoAttrList *attributes = NULL; @@ -319,7 +326,7 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g } } - int style_col = col->data_column; + int style_col = datacolumn; 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 } @@ -363,7 +370,7 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g } case UI_ICON_TEXT_FREE: { - void *data2 = listview->getvalue(list, obj->data, obj->i, col->data_column+1, listview->getvaluedata, &freevalue); + void *data2 = listview->getvalue(list, obj->data, obj->i, datacolumn+1, listview->getvaluedata, &freevalue); if(type == UI_ICON_TEXT_FREE) { freevalue = TRUE; } @@ -387,7 +394,7 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g if(entry) { entry->listview = col->listview; entry->row = obj->i; - entry->col = col->data_column; + entry->col = datacolumn; entry->previous_value = strdup(data); } ENTRY_SET_TEXT(child, data); @@ -405,13 +412,13 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g } } -static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) { +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->items[col->column] = NULL; row->bound--; if(row->bound == 0) { cxMapRemove(listview->bound_rows, row_key); @@ -457,17 +464,16 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { } listview->numcolumns = 1; - listview->columns = malloc(sizeof(UiColData)); - listview->columns->listview = listview; - listview->columns->data_column = 0; - listview->columns->model_column = 0; + listview->columns = malloc(sizeof(int)); + listview->columns[0] = 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); + g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), &listview->coldata); + g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), &listview->coldata); + g_signal_connect(factory, "unbind", G_CALLBACK(column_factory_unbind), &listview->coldata); GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection); GtkWidget *view = gtk_list_view_new(GTK_SELECTION_MODEL(selection_model), factory); @@ -540,7 +546,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { return scroll_area; } -UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { +UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) { // 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); @@ -554,17 +560,15 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { } listview->numcolumns = 1; - listview->columns = malloc(sizeof(UiColData)); - listview->columns->listview = listview; - listview->columns->data_column = 0; - listview->columns->model_column = 0; + listview->columns = malloc(sizeof(int)); + listview->columns[0] = 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); + g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), &listview->coldata); + g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), &listview->coldata); GtkWidget *view = gtk_drop_down_new(G_LIST_MODEL(ls), NULL); gtk_drop_down_set_factory(GTK_DROP_DOWN(view), factory); @@ -591,8 +595,8 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { list->obj = listview; list->update = ui_listview_update2; - list->getselection = ui_combobox_getselection; - list->setselection = ui_combobox_setselection; + list->getselection = ui_dropdown_getselection; + list->setselection = ui_dropdown_setselection; ui_update_liststore(ls, list); } else if (args->static_elements && args->static_nelm > 0) { @@ -619,10 +623,34 @@ void ui_listview_select(UIWIDGET listview, int index) { gtk_selection_model_select_item(model, index, TRUE); } -void ui_combobox_select(UIWIDGET dropdown, int index) { +void ui_dropdown_select(UIWIDGET dropdown, int index) { gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), index); } +static void add_column(UiListView *tableview, int index) { + UiModel *model = tableview->model; + + UiColData *col = malloc(sizeof(UiColData)); + col->listview = tableview; + col->column = index; + + GtkListItemFactory *factory = gtk_signal_list_item_factory_new(); + g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), col); + g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), col); + g_object_set_data_full(G_OBJECT(factory), "coldata", col, (GDestroyNotify)free); + + GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[index], factory); + gtk_column_view_column_set_resizable(column, true); + gtk_column_view_insert_column(GTK_COLUMN_VIEW(tableview->widget), index, column); + + int size = model->columnsize[index]; + if(size > 0) { + gtk_column_view_column_set_fixed_width(column, size); + } else if(size < 0) { + gtk_column_view_column_set_expand(column, TRUE); + } +} + UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { GListStore *ls = g_list_store_new(G_TYPE_OBJECT); //g_list_store_append(ls, v1); @@ -650,9 +678,13 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { // create columns from UiModel UiModel *model = args->model; - int columns = model ? model->columns : 0; + int columns = 0; + if(model) { + columns = model->columns; + ui_model_add_observer(model, ui_listview_update_model, tableview); + } - tableview->columns = calloc(columns, sizeof(UiColData)); + tableview->columns = calloc(columns, sizeof(int)); tableview->numcolumns = columns; tableview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128); @@ -660,30 +692,14 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { int addi = 0; for(int i=0;icolumns[i].listview = tableview; - tableview->columns[i].model_column = i; - tableview->columns[i].data_column = i+addi; + tableview->columns[i] = i+addi; if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) { // icon+text has 2 data columns addi++; } - GtkListItemFactory *factory = gtk_signal_list_item_factory_new(); - UiColData *col = &tableview->columns[i]; - g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), col); - g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), col); - - GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[i], factory); - gtk_column_view_column_set_resizable(column, true); - gtk_column_view_append_column(GTK_COLUMN_VIEW(view), column); - - int size = model->columnsize[i]; - if(size > 0) { - gtk_column_view_column_set_fixed_width(column, size); - } else if(size < 0) { - gtk_column_view_column_set_expand(column, TRUE); - } + add_column(tableview, i); } // bind listview to list @@ -734,6 +750,49 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { return scroll_area; } +void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index) { + UiListView *listview = userdata; + if(insert_index >= listview->numcolumns) { + listview->numcolumns = insert_index+1; + listview->columns = realloc(listview->columns, listview->numcolumns * sizeof(UiColData)); + } + + gtk_column_view_set_model(GTK_COLUMN_VIEW(listview->widget), NULL); + cxMapClear(listview->bound_rows); + + if(insert_index) { + int prev = 0; + if(insert_index > 0) { + prev = listview->columns[insert_index-1]; + } + listview->columns[insert_index] = prev+1; + add_column(listview, insert_index); + + if(insert_index+1 < listview->numcolumns) { + // the data index of trailing columns must be adjusted + UiModelType type = model->types[insert_index]; + int add = 1; + if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) { + add++; + } + for(int i=insert_index+1;inumcolumns;i++) { + listview->columns[i] += add; + } + } + } // TODO: delete_index + + GListStore *ls = g_list_store_new(G_TYPE_OBJECT); + GtkSelectionModel *selection_model = create_selection_model(listview, ls, listview->multiselection); + gtk_column_view_set_model(GTK_COLUMN_VIEW(listview->widget), selection_model); + listview->selectionmodel = selection_model; + listview->liststore = ls; + + if(listview->var) { + UiList *list = listview->var->value; + ui_list_update(list); + } +} + static UiListSelection selectionmodel_get_selection(GtkSelectionModel *model) { UiListSelection sel = { 0, NULL }; GtkBitset *bitset = gtk_selection_model_get_selection(model); @@ -860,7 +919,9 @@ void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm) void ui_listview_update2(UiList *list, int i) { UiListView *view = list->obj; + view->current_row = -1; if(i < 0) { + cxMapClear(view->bound_rows); ui_update_liststore(view->liststore, list); } else { void *value = list->get(list, i); @@ -873,9 +934,12 @@ void ui_listview_update2(UiList *list, int i) { CxHashKey row_key = cx_hash_key(&i, sizeof(int)); UiRowItems *row = cxMapGet(view->bound_rows, row_key); if(row) { + UiColData coldata; + coldata.listview = view; for(int c=0;cnumcolumns;c++) { if(row->items[c] != NULL) { - column_factory_bind(NULL, row->items[c], &view->columns[c]); + coldata.column = c; + column_factory_bind(NULL, row->items[c], &coldata); } } } @@ -915,7 +979,7 @@ void ui_listview_setselection2(UiList *list, UiListSelection selection) { ui_setop_enable(FALSE); } -UiListSelection ui_combobox_getselection(UiList *list) { +UiListSelection ui_dropdown_getselection(UiList *list) { UiListView *view = list->obj; guint selection = gtk_drop_down_get_selected(GTK_DROP_DOWN(view->widget)); UiListSelection sel = { 0, NULL }; @@ -927,7 +991,7 @@ UiListSelection ui_combobox_getselection(UiList *list) { return sel; } -void ui_combobox_setselection(UiList *list, UiListSelection selection) { +void ui_dropdown_setselection(UiList *list, UiListSelection selection) { ui_setop_enable(TRUE); UiListView *view = list->obj; if(selection.count > 0) { @@ -1156,7 +1220,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { // create treeview GtkWidget *view = gtk_tree_view_new(); ui_set_name_and_style(view, args->name, args->style_class); - ui_set_widget_groups(obj->ctx, view, args->groups); + ui_set_widget_states(obj->ctx, view, args->states); GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); @@ -1272,7 +1336,7 @@ void ui_listview_select(UIWIDGET listview, int index) { //g_object_unref(path); } -void ui_combobox_select(UIWIDGET dropdown, int index) { +void ui_dropdown_select(UIWIDGET dropdown, int index) { gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index); } @@ -1514,16 +1578,16 @@ void ui_listview_setselection(UiList *list, UiListSelection selection) { -/* --------------------------- ComboBox --------------------------- */ +/* --------------------------- Dropdown --------------------------- */ -UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { +UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) { 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_set_widget_states(obj->ctx, combobox, args->states); UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; UiLayout layout = UI_ARGS2LAYOUT(args); ct->add(ct, combobox, &layout); @@ -1544,8 +1608,8 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { if(var) { listview->var = var; list->update = ui_combobox_modelupdate; - list->getselection = ui_combobox_getselection; - list->setselection = ui_combobox_setselection; + list->getselection = ui_dropdown_getselection; + list->setselection = ui_dropdown_setselection; list->obj = listview; list->update(list, -1); } else if(args->static_nelm > 0) { @@ -1622,7 +1686,7 @@ void ui_combobox_modelupdate(UiList *list, int i) { g_object_unref(store); } -UiListSelection ui_combobox_getselection(UiList *list) { +UiListSelection ui_dropdown_getselection(UiList *list) { UiListView *combobox = list->obj; UiListSelection ret; ret.rows = malloc(sizeof(int*)); @@ -1631,7 +1695,7 @@ UiListSelection ui_combobox_getselection(UiList *list) { return ret; } -void ui_combobox_setselection(UiList *list, UiListSelection selection) { +void ui_dropdown_setselection(UiList *list, UiListSelection selection) { ui_setop_enable(TRUE); UiListView *combobox = list->obj; if(selection.count > 0) { @@ -2045,6 +2109,10 @@ void ui_listview_destroy(GtkWidget *w, UiListView *v) { if(v->var) { ui_destroy_boundvar(v->obj->ctx, v->var); } + if(v->model) { + ui_model_remove_observer(v->model, v); + ui_model_unref(v->model); + } if(v->elements) { for(int i=0;inelm;i++) { free(v->elements[i]); @@ -2160,6 +2228,8 @@ static void add_sublist(UiListBox *uilistbox, CxList *sublists, UiSubList *subli UiList *list = uisublist.var->value; list->obj = sublist_ptr; list->update = ui_listbox_list_update; + list->getselection = ui_listbox_list_getselection; + list->setselection = ui_listbox_list_setselection; } } @@ -2181,7 +2251,7 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox); ui_set_name_and_style(listbox, args->name, args->style_class); - ui_set_widget_groups(obj->ctx, listbox, args->groups); + ui_set_widget_states(obj->ctx, listbox, args->states); UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; UiLayout layout = UI_ARGS2LAYOUT(args); ct->add(ct, scroll_area, &layout); @@ -2223,6 +2293,8 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { UiList *list = var->value; list->obj = uilistbox; list->update = ui_listbox_dynamic_update; + list->getselection = ui_listbox_dynamic_getselection; + list->setselection = ui_listbox_dynamic_setselection; ui_listbox_dynamic_update(list, -1); } @@ -2294,6 +2366,32 @@ void ui_listbox_dynamic_update(UiList *list, int x) { ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists)); } +void ui_listbox_dynamic_setselection(UiList *list, UiListSelection sel) { + UiListBox *uilistbox = list->obj; + gtk_list_box_unselect_all(uilistbox->listbox); + if(sel.count > 0) { + int index = sel.rows[0]; + if(index >= 0) { + GtkListBoxRow *row = gtk_list_box_get_row_at_index(uilistbox->listbox, index); + if(row) { + gtk_list_box_select_row(uilistbox->listbox, row); + } + } + } +} + +UiListSelection ui_listbox_dynamic_getselection(UiList *list) { + UiListSelection sel = { 0, NULL }; + UiListBox *uilistbox = list->obj; + GtkListBoxRow *row = gtk_list_box_get_selected_row(uilistbox->listbox); + if(row) { + sel.count = 1; + sel.rows = malloc(sizeof(int)); + sel.rows[0] = gtk_list_box_row_get_index(row); + } + return sel; +} + void ui_listbox_update(UiListBox *listbox, int from, int to) { CxIterator i = cxListIterator(listbox->sublists); size_t pos = 0; @@ -2659,6 +2757,39 @@ void ui_listbox_list_update(UiList *list, int i) { ui_sourcelist_update_finished(); } +void ui_listbox_list_setselection(UiList *list, UiListSelection sel) { + UiListBoxSubList *sublist = list->obj; + UiListBox *uilistbox = sublist->listbox; + gtk_list_box_unselect_all(uilistbox->listbox); + if(sel.count > 0) { + int index = sel.rows[0]; + if(index >= 0 && index < sublist->numitems) { + int global_index = sublist->startpos + index; + GtkListBoxRow *row = gtk_list_box_get_row_at_index(uilistbox->listbox, global_index); + if(row) { + gtk_list_box_select_row(uilistbox->listbox, row); + } + } + } +} + +UiListSelection ui_listbox_list_getselection(UiList *list) { + UiListSelection sel = { 0, NULL }; + UiListBoxSubList *sublist = list->obj; + UiListBox *uilistbox = sublist->listbox; + GtkListBoxRow *row = gtk_list_box_get_selected_row(uilistbox->listbox); + if(row) { + int index = gtk_list_box_row_get_index(row); + size_t startpos = sublist->startpos; + if(index >= startpos && index < startpos+sublist->numitems) { + sel.count = 1; + sel.rows = malloc(sizeof(int)); + sel.rows[0] = index - startpos; + } + } + return sel; +} + void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata"); if(!data) { diff --git a/ui/gtk/list.h b/ui/gtk/list.h index 1aaf89b..ef828d4 100644 --- a/ui/gtk/list.h +++ b/ui/gtk/list.h @@ -26,10 +26,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TREE_H -#define TREE_H +#ifndef LIST_H +#define LIST_H -#include "../ui/tree.h" +#include "../ui/list.h" #include "toolkit.h" #include @@ -38,6 +38,7 @@ extern "C" { #endif +typedef struct UiListView UiListView; typedef struct UiColData UiColData; #if GTK_CHECK_VERSION(4, 10, 0) @@ -47,11 +48,17 @@ typedef struct UiRowItems { } UiRowItems; #endif -typedef struct UiListView { +struct UiColData { + UiListView *listview; + int column; +}; + +struct UiListView { UiObject *obj; GtkWidget *widget; UiVar *var; UiModel *model; + UiBool multiselection; ui_getvaluefunc2 getvalue; void *getvaluedata; ui_getstylefunc getstyle; @@ -65,8 +72,9 @@ typedef struct UiListView { CxMap *bound_rows; GListStore *liststore; GtkSelectionModel *selectionmodel; - UiColData *columns; + int *columns; int numcolumns; + UiColData coldata; PangoAttrList *current_row_attributes; #else int style_offset; @@ -85,12 +93,6 @@ typedef struct UiListView { void *onsavedata; UiListSelection selection; -} UiListView; - -struct UiColData { - UiListView *listview; - int model_column; - int data_column; }; typedef struct UiTreeEventData { @@ -136,6 +138,7 @@ struct UiListBox { void ui_update_liststore(GListStore *liststore, UiList *list); void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm); +void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index); void ui_listview_update2(UiList *list, int i); UiListSelection ui_listview_getselection2(UiList *list); void ui_listview_setselection2(UiList *list, UiListSelection selection); @@ -155,6 +158,7 @@ UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks GtkWidget* ui_get_tree_widget(UIWIDGET widget); +void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index); void ui_listview_update(UiList *list, int i); UiListSelection ui_listview_getselection(UiList *list); void ui_listview_setselection(UiList *list, UiListSelection selection); @@ -186,13 +190,18 @@ UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, ui_callback f, void *udata); void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e); void ui_combobox_modelupdate(UiList *list, int i); -UiListSelection ui_combobox_getselection(UiList *list); -void ui_combobox_setselection(UiList *list, UiListSelection selection); +UiListSelection ui_dropdown_getselection(UiList *list); +void ui_dropdown_setselection(UiList *list, UiListSelection selection); void ui_listbox_dynamic_update(UiList *list, int i); +void ui_listbox_dynamic_setselection(UiList *list, UiListSelection sel); +UiListSelection ui_listbox_dynamic_getselection(UiList *list); + void ui_listbox_update(UiListBox *listbox, int from, int to); void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index); void ui_listbox_list_update(UiList *list, int i); +void ui_listbox_list_setselection(UiList *list, UiListSelection sel); +UiListSelection ui_listbox_list_getselection(UiList *list); void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data); @@ -200,5 +209,5 @@ void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user } #endif -#endif /* TREE_H */ +#endif /* LIST_H */ diff --git a/ui/gtk/menu.c b/ui/gtk/menu.c index 74597ae..44d32ca 100644 --- a/ui/gtk/menu.c +++ b/ui/gtk/menu.c @@ -129,11 +129,11 @@ void add_menuitem_widget(GtkWidget *parent, int index, UiMenuItemI *item, UiObje gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget); - if(i->groups) { - CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups); - cxListAddArray(groups, i->groups, i->ngroups); - uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); - cxListFree(groups); + if(i->states) { + CxList *states = cxArrayListCreateSimple(sizeof(int), i->nstates); + cxListAddArray(states, i->states, i->nstates); + uic_add_state_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, states); + cxListFree(states); } } @@ -462,10 +462,10 @@ void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject 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); - cxListAddArray(groups, i->groups, i->ngroups); - uic_add_group_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups); + if(i->states) { + CxList *groups = cxArrayListCreateSimple(sizeof(int), i->nstates); + cxListAddArray(groups, i->states, i->nstates); + uic_add_state_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups); cxListFree(groups); } diff --git a/ui/gtk/text.c b/ui/gtk/text.c index 0859642..cb52e4a 100644 --- a/ui/gtk/text.c +++ b/ui/gtk/text.c @@ -32,6 +32,7 @@ #include "text.h" #include "container.h" +#include "widget.h" #include @@ -53,9 +54,9 @@ static void selection_handler( int sel = gtk_text_buffer_get_selection_bounds (buf, &begin, &end); if(sel != textview->last_selection_state) { if(sel) { - ui_set_group(textview->ctx, UI_GROUP_SELECTION); + ui_set_state(textview->ctx, UI_GROUP_SELECTION); } else { - ui_unset_group(textview->ctx, UI_GROUP_SELECTION); + ui_unset_state(textview->ctx, UI_GROUP_SELECTION); } } textview->last_selection_state = sel; @@ -112,7 +113,7 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) { GtkWidget *text_area = gtk_text_view_new(); ui_set_name_and_style(text_area, args->name, args->style_class); - ui_set_widget_groups(obj->ctx, text_area, args->groups); + ui_set_widget_states(obj->ctx, text_area, args->states); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_area), GTK_WRAP_WORD_CHAR); g_signal_connect( @@ -144,17 +145,7 @@ 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); - } + ui_widget_size_request(scroll_area, args->width, args->height); // font and padding //PangoFontDescription *font; @@ -335,6 +326,10 @@ void ui_textarea_realize_event(GtkWidget *widget, gpointer data) { void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea) { + if(!ui_onchange_events_is_enabled()) { + return; + } + UiText *value = textarea->var->value; UiEvent e; @@ -608,7 +603,7 @@ void ui_text_redo(UiText *value) { static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool password, UiTextFieldArgs *args) { GtkWidget *textfield = gtk_entry_new(); ui_set_name_and_style(textfield, args->name, args->style_class); - ui_set_widget_groups(obj->ctx, textfield, args->groups); + ui_set_widget_states(obj->ctx, textfield, args->states); UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); @@ -698,14 +693,18 @@ void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield) { } void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) { - UiString *value = textfield->var->value; + if(!ui_onchange_events_is_enabled()) { + return; + } + + UiString *value = textfield->var ? textfield->var->value : NULL; UiEvent e; e.obj = textfield->obj; e.window = e.obj->window; e.document = textfield->obj->ctx->document; e.eventdata = value; - e.eventdatatype = UI_EVENT_DATA_TEXT_VALUE; + e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0; e.intval = 0; e.set = ui_get_setop(); @@ -720,12 +719,14 @@ void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) { void ui_textfield_activate(GtkEntry* self, UiTextField *textfield) { if(textfield->onactivate) { + UiString *value = textfield->var ? textfield->var->value : NULL; + UiEvent e; e.obj = textfield->obj; e.window = e.obj->window; e.document = textfield->obj->ctx->document; - e.eventdata = NULL; - e.eventdatatype = 0; + e.eventdata = value; + e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0; e.intval = 0; e.set = ui_get_setop(); textfield->onactivate(&e, textfield->onactivatedata); @@ -956,6 +957,10 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { pathtf->stack = gtk_stack_new(); gtk_widget_set_name(pathtf->stack, "path-textfield-box"); + if(args->width > 0) { + gtk_widget_set_size_request(pathtf->stack, args->width, -1); + } + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; UiLayout layout = UI_ARGS2LAYOUT(args); ct->add(ct, pathtf->stack, &layout); @@ -1127,6 +1132,10 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { G_CALLBACK(ui_path_textfield_destroy), pathtf); + if(args->width > 0) { + gtk_widget_set_size_request(eventbox, args->width, -1); + } + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; UiLayout layout = UI_ARGS2LAYOUT(args); ct->add(ct, eventbox, &layout); diff --git a/ui/gtk/toolbar.c b/ui/gtk/toolbar.c index 64b221a..0e195ff 100644 --- a/ui/gtk/toolbar.c +++ b/ui/gtk/toolbar.c @@ -139,7 +139,7 @@ void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj) { } gtk_tool_item_set_is_important(button, TRUE); - ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups); + ui_set_widget_nstates(obj->ctx, GTK_WIDGET(button), item->args.states, item->nstates); if(item->args.onclick) { UiEventData *event = cxMalloc( @@ -181,7 +181,7 @@ void add_toolitem_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObj 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); + ui_set_widget_nstates(obj->ctx, GTK_WIDGET(button), item->args.states, item->nstates); UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, item->args.varname, UI_VAR_INTEGER); if(var) { @@ -394,7 +394,7 @@ void add_headerbar_item_widget(GtkHeaderBar *hb, UiToolbarItem *item, UiObject * if(item->args.icon) { ui_button_set_icon_name(button, item->args.icon); } - ui_set_widget_groups(obj->ctx, button, item->args.groups); + ui_set_widget_states(obj->ctx, button, item->args.states); gtk_header_bar_pack_start(hb, button); diff --git a/ui/gtk/toolkit.c b/ui/gtk/toolkit.c index 87d1ef1..e0a30c2 100644 --- a/ui/gtk/toolkit.c +++ b/ui/gtk/toolkit.c @@ -39,6 +39,7 @@ #include "../common/menu.h" #include "../common/toolbar.h" #include "../common/threadpool.h" +#include "../common/app.h" #include #include @@ -51,13 +52,6 @@ UI_APPLICATION app; static const char *application_name; -static ui_callback startup_func; -static void *startup_data; -static ui_callback open_func; -void *open_data; -static ui_callback exit_func; -void *exit_data; - static ui_callback appclose_fnc; static void *appclose_udata; @@ -95,30 +89,13 @@ const char* ui_appname() { return application_name; } -void ui_onstartup(ui_callback f, void *userdata) { - startup_func = f; - startup_data = userdata; -} - -void ui_onopen(ui_callback f, void *userdata) { - open_func = f; - open_data = userdata; -} - -void ui_onexit(ui_callback f, void *userdata) { - exit_func = f; - exit_data = userdata; -} - void ui_app_exit_on_shutdown(UiBool exitapp) { exit_on_shutdown = exitapp; } #ifdef UI_APPLICATION static void app_startup(GtkApplication* app, gpointer userdata) { - if(startup_func) { - startup_func(NULL, startup_data); - } + uic_application_startup(NULL); } static void app_activate(GtkApplication* app, gpointer userdata) { @@ -126,9 +103,7 @@ static void app_activate(GtkApplication* app, gpointer userdata) { } static void app_shutdown(GtkApplication *app, gpointer userdata) { - if(exit_func) { - exit_func(NULL, exit_data); - } + uic_application_exit(NULL); ui_app_save_settings(); } @@ -148,13 +123,9 @@ void ui_main() { free(appid.ptr); #else - if(startup_func) { - startup_func(NULL, startup_data); - } + uic_application_startup(NULL); gtk_main(); - if(exit_func) { - exit_func(NULL, exit_data); - } + uic_application_exit(NULL); ui_app_save_settings(); #endif if(exit_on_shutdown) { @@ -164,7 +135,7 @@ void ui_main() { #ifndef UI_GTK2 void ui_app_quit() { - g_application_quit(G_APPLICATION(app)); + g_application_quit(G_APPLICATION(app)); // TODO: fix, does not work } GtkApplication* ui_get_application() { @@ -175,7 +146,7 @@ GtkApplication* ui_get_application() { void ui_show(UiObject *obj) { gboolean visible = gtk_widget_is_visible(obj->widget); - uic_check_group_widgets(obj->ctx); + uic_check_state_widgets(obj->ctx); #if GTK_MAJOR_VERSION >= 4 gtk_window_present(GTK_WINDOW(obj->widget)); #elif GTK_MAJOR_VERSION <= 3 @@ -263,14 +234,14 @@ void ui_set_show_all(UIWIDGET widget, int value) { #endif } -void ui_set_visible(UIWIDGET widget, int visible) { +void ui_set_visible(UIWIDGET widget, UiBool visible) { #if GTK_MAJOR_VERSION >= 4 gtk_widget_set_visible(widget, visible); #else if(visible) { - gtk_widget_set_no_show_all(widget, FALSE); gtk_widget_show_all(widget); } else { + gtk_widget_set_no_show_all(widget, FALSE); gtk_widget_hide(widget); } #endif @@ -346,6 +317,11 @@ UiObject *ui_get_active_window() { #if GTK_MAJOR_VERSION == 4 static const char *ui_gtk_css = +".ui-entry-box {\n" +" background-color: alpha(currentColor, 0.1);" +" border-radius: 6px;" +" padding: 7px;" +"}\n" "#path-textfield-box {\n" " background-color: alpha(currentColor, 0.1);" " border-radius: 6px;" @@ -529,17 +505,17 @@ void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *styl } } -void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups) { - if(!groups) { +void ui_set_widget_states(UiContext *ctx, GtkWidget *widget, const int *states) { + if(!states) { return; } - size_t ngroups = uic_group_array_size(groups); - ui_set_widget_ngroups(ctx, widget, groups, ngroups); + size_t nstates = uic_state_array_size(states); + ui_set_widget_nstates(ctx, widget, states, nstates); } -void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups) { - if(ngroups > 0) { - uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups); +void ui_set_widget_nstates(UiContext *ctx, GtkWidget *widget, const int *states, size_t nstates) { + if(nstates > 0) { + uic_add_state_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, states, nstates); ui_set_enabled(widget, FALSE); } } @@ -548,14 +524,14 @@ void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const in if(!states) { return; } - size_t nstates = uic_group_array_size(states); + size_t nstates = uic_state_array_size(states); ui_set_widget_nvisibility_states(ctx, widget, states, nstates); } void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups) { if(ngroups > 0) { - uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_visible, states, ngroups); + uic_add_state_widget_i(ctx, widget, (ui_enablefunc)ui_set_visible, states, ngroups); ui_set_visible(widget, FALSE); } } diff --git a/ui/gtk/toolkit.h b/ui/gtk/toolkit.h index cfa25bb..8d1ee57 100644 --- a/ui/gtk/toolkit.h +++ b/ui/gtk/toolkit.h @@ -178,8 +178,8 @@ GtkApplication* ui_get_application(); int ui_get_scalefactor(); void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *style); -void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups); -void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups); +void ui_set_widget_states(UiContext *ctx, GtkWidget *widget, const int *states); +void ui_set_widget_nstates(UiContext *ctx, GtkWidget *widget, const int *states, size_t nstates); void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const int *states); void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups); diff --git a/ui/gtk/webview.c b/ui/gtk/webview.c index f8f5718..6a1f319 100644 --- a/ui/gtk/webview.c +++ b/ui/gtk/webview.c @@ -30,6 +30,7 @@ #include "container.h" #include "webview.h" +#include "widget.h" #ifdef UI_WEBVIEW @@ -38,6 +39,8 @@ UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) { ui_set_name_and_style(webview, args->name, args->style_class); + ui_widget_size_request(webview, args->width, args->height); + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC); if(var) { WebViewData *data = malloc(sizeof(WebViewData)); @@ -57,7 +60,7 @@ UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) { } } - ui_set_widget_groups(obj->ctx, webview, args->groups); + ui_set_widget_states(obj->ctx, webview, args->states); UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; UiLayout layout = UI_ARGS2LAYOUT(args); ct->add(ct, webview, &layout); diff --git a/ui/gtk/widget.c b/ui/gtk/widget.c index 06ef1b2..95cbb12 100644 --- a/ui/gtk/widget.c +++ b/ui/gtk/widget.c @@ -31,7 +31,20 @@ #include "../common/object.h" -UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) { +void ui_widget_size_request(UIWIDGET w, int width, int height) { + if(width > 0 || height > 0) { + if(width == 0) { + width = -1; + } + if(height == 0) { + height = -1; + } + gtk_widget_set_size_request(w, width, height); + } +} + + +UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) { UIWIDGET widget = create_widget(obj, args, userdata); UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; diff --git a/ui/gtk/widget.h b/ui/gtk/widget.h index 57c7314..7ee6ed4 100644 --- a/ui/gtk/widget.h +++ b/ui/gtk/widget.h @@ -35,7 +35,12 @@ extern "C" { #endif - +/* + * Sets a widget width/height. + * + * If wdith or height is 0, the dimension is not changed + */ +void ui_widget_size_request(UIWIDGET w, int width, int height); #ifdef __cplusplus diff --git a/ui/gtk/window.c b/ui/gtk/window.c index f90997f..2081de2 100644 --- a/ui/gtk/window.c +++ b/ui/gtk/window.c @@ -35,6 +35,7 @@ #include "../common/context.h" #include "../common/menu.h" #include "../common/toolbar.h" +#include "../common/utils.h" #include @@ -141,7 +142,7 @@ static void save_window_splitview_pos(GtkWidget *widget, void *unused) { ui_set_property("ui.window.splitview.pos", buf); } -static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool splitview, UiBool simple) { +static UiObject* create_window(const char *title, UiBool sidebar, UiBool splitview, UiBool simple) { UiObject *obj = uic_object_new_toplevel(); #ifdef UI_LIBADWAITA @@ -152,8 +153,6 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side obj->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); #endif - obj->window = window_data; - #if GTK_CHECK_VERSION(4, 0, 0) obj->ctx->action_map = G_ACTION_MAP(obj->widget); #endif @@ -169,16 +168,7 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side 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; - } - } + ui_get_window_default_width(&window_width, &window_height); } gtk_window_set_default_size( GTK_WINDOW(obj->widget), @@ -205,6 +195,21 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side obj); #endif + int splitview_pos = 0; + if(splitview) { + const char *splitview_pos_str = ui_get_property("ui.window.splitview.pos"); + splitview_pos= splitview_window_default_pos; + if(splitview_pos < 0) { + splitview_pos = window_width / 2; + } + if(splitview_pos_str && splitview_window_use_prop) { + int sv_pos = atoi(splitview_pos_str); + if(sv_pos > 0) { + splitview_pos = sv_pos; + } + } + } + GtkWidget *vbox = ui_gtk_vbox_new(0); #ifdef UI_LIBADWAITA GtkWidget *toolbar_view = adw_toolbar_view_new(); @@ -223,18 +228,7 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side 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); + gtk_paned_set_position(GTK_PANED(content), splitview_pos); GtkWidget *right_panel = adw_toolbar_view_new(); GtkWidget *right_vbox = ui_gtk_vbox_new(0); @@ -345,14 +339,32 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side GtkWidget *content_box = ui_gtk_vbox_new(0); WINDOW_SET_CONTENT(obj->widget, vbox); - if(sidebar) { + if(sidebar || splitview) { GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); - GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0); - gtk_paned_add1(GTK_PANED(paned), sidebar_vbox); - gtk_paned_add2(GTK_PANED(paned), content_box); + if(sidebar) { + GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0); + gtk_paned_add1(GTK_PANED(paned), sidebar_vbox); + g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox); + gtk_paned_set_position(GTK_PANED(paned), 200); + } + + if(splitview) { + GtkWidget *content_paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); + gtk_paned_set_position(GTK_PANED(content_paned), splitview_pos); + gtk_paned_add2(GTK_PANED(paned), content_paned); + + GtkWidget *right_content_box = ui_gtk_vbox_new(0); + gtk_paned_add1(GTK_PANED(content_paned), content_box); + gtk_paned_add2(GTK_PANED(content_paned), right_content_box); + + g_object_set_data(G_OBJECT(obj->widget), "ui_window_splitview", content_paned); + g_object_set_data(G_OBJECT(obj->widget), "ui_left_panel", content_box); + g_object_set_data(G_OBJECT(obj->widget), "ui_right_panel", right_content_box); + } else { + gtk_paned_add2(GTK_PANED(paned), content_box); + } + BOX_ADD_EXPAND(GTK_BOX(vbox), paned); - g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox); - gtk_paned_set_position (GTK_PANED(paned), 200); } else { BOX_ADD_EXPAND(GTK_BOX(vbox), content_box); } @@ -380,20 +392,20 @@ 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, FALSE); +UiObject* ui_window(const char *title) { + return create_window(title, FALSE, FALSE, FALSE); } -UiObject *ui_sidebar_window(const char *title, void *window_data) { - return create_window(title, window_data, TRUE, FALSE, FALSE); +UiObject *ui_sidebar_window(const char *title) { + return create_window(title, TRUE, FALSE, FALSE); } -UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar) { - return create_window(title, NULL, sidebar, TRUE, FALSE); +UiObject *ui_splitview_window(const char *title, UiBool sidebar) { + return create_window(title, sidebar, TRUE, FALSE); } -UiObject* ui_simple_window(const char *title, void *window_data) { - return create_window(title, window_data, FALSE, FALSE, TRUE); +UiObject* ui_simple_window(const char *title) { + return create_window(title, FALSE, FALSE, TRUE); } void ui_window_size(UiObject *obj, int width, int height) { diff --git a/ui/ui/button.h b/ui/ui/button.h index 693f730..55367f1 100644 --- a/ui/ui/button.h +++ b/ui/ui/button.h @@ -65,7 +65,7 @@ typedef struct UiButtonArgs { ui_callback onclick; void *onclickdata; - const int *groups; + const int *states; } UiButtonArgs; typedef struct UiToggleArgs { @@ -93,9 +93,9 @@ typedef struct UiToggleArgs { const char *varname; ui_callback onchange; void *onchangedata; - int enable_group; + int enable_state; - const int *groups; + const int *states; } UiToggleArgs; typedef struct UiLinkButtonArgs { @@ -124,7 +124,7 @@ typedef struct UiLinkButtonArgs { UiBool nofollow; UiLinkType type; - const int *groups; + const int *states; } UiLinkButtonArgs; #define ui_button(obj, ...) ui_button_create(obj, &(UiButtonArgs){ __VA_ARGS__ } ) diff --git a/ui/ui/container.h b/ui/ui/container.h index 3355850..dc2aa29 100644 --- a/ui/ui/container.h +++ b/ui/ui/container.h @@ -98,6 +98,8 @@ typedef struct UiFrameArgs { int margin_bottom; int colspan; int rowspan; + int width; + int height; const char *name; const char *style_class; @@ -310,13 +312,14 @@ struct UiContainerX { #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)) #define ui_grid_w(obj, w, ...) for(w = ui_grid_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_tabview_w(obj, w, ...) for(w = ui_tabview_create(obj, &(UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_scrolledwindow_w(obj, w, ...) for(w = ui_scrolledwindow_create(obj, &(UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_custom_container(ob, widget, addfunc, data) for(ui_custom_container_create(obj, widget, addfunc, data);ui_container_finish(obj);ui_container_begin_close(obj)) + #define ui_hsplitpane(obj, ...) for(ui_hsplitpane_create(obj, &(UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_vsplitpane(obj, ...) for(ui_vsplitpane_create(obj, &(UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_hsplitpane0(obj) for(ui_hsplitpane_create(obj, &(UiSplitPaneArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) @@ -367,6 +370,9 @@ UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBo UIEXPORT void ui_newline(UiObject *obj); +typedef void(*ui_addwidget_func)(UiObject *obj, UIWIDGET parent, UIWIDGET child, void *userdata); +UIEXPORT void ui_custom_container_create(UiObject *obj, UIWIDGET widget, ui_addwidget_func add_child, void *userdata); + // TODO UIEXPORT UiTabbedPane* ui_tabbed_document_view(UiObject *obj); UIEXPORT UiObject* ui_document_tab(UiTabbedPane *view); diff --git a/ui/ui/entry.h b/ui/ui/entry.h index 6dd6a0a..9ee26d3 100644 --- a/ui/ui/entry.h +++ b/ui/ui/entry.h @@ -65,7 +65,7 @@ typedef struct UiSpinBoxArgs { ui_callback onchange; void* onchangedata; - const int *groups; + const int *states; } UiSpinBoxArgs; diff --git a/ui/ui/image.h b/ui/ui/image.h index 77d923f..0730f75 100644 --- a/ui/ui/image.h +++ b/ui/ui/image.h @@ -58,6 +58,8 @@ typedef struct UiImageViewerArgs { int margin_bottom; int colspan; int rowspan; + int width; + int height; const char *name; const char *style_class; diff --git a/ui/ui/tree.h b/ui/ui/list.h similarity index 85% rename from ui/ui/tree.h rename to ui/ui/list.h index 33a903c..b4d0e0a 100644 --- a/ui/ui/tree.h +++ b/ui/ui/list.h @@ -67,7 +67,18 @@ typedef struct UiCellValue { typedef UiBool (*ui_list_savefunc)(UiList *list, int row, int col, UiCellValue *value, void *userdata); +typedef void (*ui_model_update_func)(UiModel *model, void *userdata, int insert_index, int delete_index); + +typedef struct UiModelChangeObserver UiModelChangeObserver; +struct UiModelChangeObserver { + ui_model_update_func update; + void *userdata; + UiModelChangeObserver *next; +}; + struct UiModel { + UiContext *ctx; + /* * number of columns */ @@ -94,6 +105,17 @@ struct UiModel { * array of column size hints */ int *columnsize; + + /* + * Model change observers, that will be called when + * columns are added or removed + */ + UiModelChangeObserver *observer; + + /* + * reference counter + */ + int ref; }; struct UiListCallbacks { @@ -132,9 +154,9 @@ struct UiListArgs { const char *name; const char *style_class; - UiList* list; + UiList *list; const char* varname; - UiModel* model; + UiModel *model; char **static_elements; size_t static_nelm; ui_getvaluefunc getvalue; @@ -143,21 +165,21 @@ struct UiListArgs { ui_getstylefunc getstyle; void *getstyledata; ui_callback onactivate; - void* onactivatedata; + void *onactivatedata; ui_callback onselection; - void* onselectiondata; + void *onselectiondata; ui_callback ondragstart; - void* ondragstartdata; + void *ondragstartdata; ui_callback ondragcomplete; - void* ondragcompletedata; + void *ondragcompletedata; ui_callback ondrop; - void* ondropdata; + void *ondropdata; UiBool multiselection; UiMenuBuilder *contextmenu; ui_list_savefunc onsave; void *onsavedata; - const int *groups; + const int *states; }; typedef void (*ui_sublist_getvalue_func)(UiList *list, void *sublist_userdata, void *rowdata, int index, UiSubListItem *item, void *userdata); @@ -213,7 +235,7 @@ struct UiSourceListArgs { const char *name; const char *style_class; - const int *groups; + const int *states; /* * static list of sublists @@ -290,23 +312,27 @@ struct UiSourceListArgs { */ 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 void ui_model_add_column(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); +UIEXPORT void ui_model_ref(UiModel *model); +UIEXPORT void ui_model_unref(UiModel *model); +UIEXPORT void ui_model_add_observer(UiModel *model, ui_model_update_func update, void *data); +UIEXPORT void ui_model_remove_observer(UiModel *model, void *data); +UIEXPORT void ui_model_free(UiModel *mi); #define ui_listview(obj, ...) ui_listview_create(obj, &(UiListArgs) { __VA_ARGS__ } ) #define ui_table(obj, ...) ui_table_create(obj, &(UiListArgs) { __VA_ARGS__ } ) -#define ui_combobox(obj, ...) ui_combobox_create(obj, &(UiListArgs) { __VA_ARGS__ } ) +#define ui_dropdown(obj, ...) ui_dropdown_create(obj, &(UiListArgs) { __VA_ARGS__ } ) #define ui_breadcrumbbar(obj, ...) ui_breadcrumbbar_create(obj, &(UiListArgs) { __VA_ARGS__ } ) #define ui_sourcelist(obj, ...) ui_sourcelist_create(obj, &(UiSourceListArgs) { __VA_ARGS__ } ) -UIEXPORT UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args); -UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args); -UIEXPORT UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args); -UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject* obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject *obj, UiListArgs *args); UIEXPORT void ui_listview_select(UIWIDGET listview, int index); -UIEXPORT void ui_combobox_select(UIWIDGET dropdown, int index); +UIEXPORT void ui_dropdown_select(UIWIDGET dropdown, int index); UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args); diff --git a/ui/ui/menu.h b/ui/ui/menu.h index 9dde9b4..43cca7e 100644 --- a/ui/ui/menu.h +++ b/ui/ui/menu.h @@ -43,7 +43,7 @@ typedef struct UiMenuItemArgs { ui_callback onclick; void* onclickdata; - const int* groups; + const int* states; } UiMenuItemArgs; typedef struct UiMenuToggleItemArgs { @@ -54,7 +54,7 @@ typedef struct UiMenuToggleItemArgs { ui_callback onchange; void* onchangedata; - const int* groups; + const int* nstates; } UiMenuToggleItemArgs; typedef struct UiMenuItemListArgs { diff --git a/ui/ui/stock.h b/ui/ui/stock.h deleted file mode 100644 index ab2d13d..0000000 --- a/ui/ui/stock.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 UI_STOCK_H -#define UI_STOCK_H - -#include "toolkit.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// motif stock ids -#if UI_MOTIF || UI_COCOA || UI_QT4 || UI_QT5 - -#define UI_STOCK_NEW "ui.stock.New" -#define UI_STOCK_OPEN "ui.stock.Open" -#define UI_STOCK_SAVE "ui.stock.Save" -#define UI_STOCK_SAVE_AS "ui.stock.SaveAs" -#define UI_STOCK_REVERT_TO_SAVED "ui.stock.RevertToSaved" -#define UI_STOCK_GO_BACK "ui.stock.GoBack" -#define UI_STOCK_GO_FORWARD "ui.stock.GoForward" -#define UI_STOCK_ADD "ui.stock.Add" -#define UI_STOCK_CLOSE "ui.stock.Close" - -#define UI_STOCK_UNDO "ui.stock.Undo" -#define UI_STOCK_REDO "ui.stock.Redo" -#define UI_STOCK_CUT "ui.stock.Cut" -#define UI_STOCK_COPY "ui.stock.Copy" -#define UI_STOCK_PASTE "ui.stock.Paste" -#define UI_STOCK_DELETE "ui.stock.Delete" - -#endif - -#if UI_GTK2 || UI_GTK3 - -#define UI_STOCK_NEW GTK_STOCK_NEW -#define UI_STOCK_OPEN GTK_STOCK_OPEN -#define UI_STOCK_SAVE GTK_STOCK_SAVE -#define UI_STOCK_SAVE_AS GTK_STOCK_SAVE_AS -#define UI_STOCK_REVERT_TO_SAVED GTK_STOCK_REVERT_TO_SAVED -#define UI_STOCK_UNDO GTK_STOCK_UNDO -#define UI_STOCK_REDO GTK_STOCK_REDO -#define UI_STOCK_GO_BACK GTK_STOCK_GO_BACK -#define UI_STOCK_GO_FORWARD GTK_STOCK_GO_FORWARD -#define UI_STOCK_ADD GTK_STOCK_ADD -#define UI_STOCK_CLOSE GTK_STOCK_CLOSE - -#define UI_STOCK_UNDO GTK_STOCK_UNDO -#define UI_STOCK_REDO GTK_STOCK_REDO -#define UI_STOCK_CUT GTK_STOCK_CUT -#define UI_STOCK_COPY GTK_STOCK_COPY -#define UI_STOCK_PASTE GTK_STOCK_PASTE -#define UI_STOCK_DELETE GTK_STOCK_DELETE - -#endif - - -#ifdef __cplusplus -} -#endif - -#endif /* UI_STOCK_H */ - diff --git a/ui/ui/text.h b/ui/ui/text.h index ac5a2c1..c102f0b 100644 --- a/ui/ui/text.h +++ b/ui/ui/text.h @@ -59,7 +59,7 @@ typedef struct UiTextAreaArgs { ui_callback onchange; void *onchangedata; - const int *groups; + const int *states; } UiTextAreaArgs; typedef struct UiTextFieldArgs { @@ -87,7 +87,7 @@ typedef struct UiTextFieldArgs { ui_callback onactivate; void *onactivatedata; - const int *groups; + const int *states; } UiTextFieldArgs; typedef struct UiPathElmRet { @@ -115,6 +115,7 @@ typedef struct UiPathTextFieldArgs { int margin_bottom; int colspan; int rowspan; + int width; const char *name; const char *style_class; diff --git a/ui/ui/toolbar.h b/ui/ui/toolbar.h index 26a79e6..2e230df 100644 --- a/ui/ui/toolbar.h +++ b/ui/ui/toolbar.h @@ -44,7 +44,7 @@ typedef struct UiToolbarItemArgs { ui_callback onclick; void* onclickdata; - const int *groups; + const int *states; const int *visibility_states; } UiToolbarItemArgs; @@ -57,7 +57,7 @@ typedef struct UiToolbarToggleItemArgs { ui_callback onchange; void *onchangedata; - const int *groups; + const int *states; const int *visibility_states; } UiToolbarToggleItemArgs; diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index 83cd78a..628e3e8 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -146,6 +146,14 @@ public: #endif +#elif UI_SERVER + +typedef struct UiWidget UiWidget; + +#define UIWIDGET UiWidget* +#define UIWINDOW UiWidget* +#define UIMENU void* + #endif #ifndef TRUE @@ -417,6 +425,7 @@ struct UiGeneric { }; typedef void (*ui_list_init_func)(UiContext *ctx, UiList *list, void *userdata); +typedef void (*ui_list_destroy_func)(UiContext *ctx, UiList *list, void *userdata); /* * abstract list @@ -541,6 +550,7 @@ UIEXPORT int ui_app_save_settings(void); UIEXPORT void ui_app_exit_on_shutdown(UiBool exitapp); UIEXPORT void ui_main(void); +UIEXPORT void ui_app_quit(void); UIEXPORT void ui_show(UiObject *obj); UIEXPORT void ui_close(UiObject *obj); @@ -557,16 +567,18 @@ UIEXPORT void* ui_get_subdocument(void *document); // deprecated UIEXPORT UiContext* ui_document_context(void *doc); +UIEXPORT void* ui_context_get_document(UiContext *ctx); +UIEXPORT void ui_context_single_attachment_mode(UiContext *ctx, UiBool enable); 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, const int *groups, int ngroups); +UIEXPORT void ui_widget_set_states(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...); +UIEXPORT void ui_widget_set_states2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *states, int nstates); UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates); -UIEXPORT void ui_set_group(UiContext *ctx, int group); -UIEXPORT void ui_unset_group(UiContext *ctx, int group); -UIEXPORT int* ui_active_groups(UiContext *ctx, int *ngroups); +UIEXPORT void ui_set_state(UiContext *ctx, int state); +UIEXPORT void ui_unset_state(UiContext *ctx, int state); +UIEXPORT int* ui_active_states(UiContext *ctx, int *nstates); UIEXPORT void* ui_allocator(UiContext *ctx); UIEXPORT void* ui_cx_mempool(UiContext *ctx); @@ -625,6 +637,13 @@ UIEXPORT double ui_var_get_double(UiContext *ctx, const char *name); UIEXPORT void ui_var_set_string(UiContext *ctx, const char *name, char *value); UIEXPORT char* ui_var_get_string(UiContext *ctx, const char *name); +UIEXPORT UiInteger* ui_get_int_var(UiContext *ctx, const char *name); +UIEXPORT UiDouble* ui_get_double_var(UiContext *ctx, const char *name); +UIEXPORT UiString* ui_get_string_var(UiContext *ctx, const char *name); +UIEXPORT UiText* ui_get_text_var(UiContext *ctx, const char *name); +UIEXPORT UiRange* ui_get_range_var(UiContext *ctx, const char *name); +UIEXPORT UiGeneric* ui_get_generic_var(UiContext *ctx, const char *name); + UIEXPORT UiObserver* ui_observer_new(ui_callback f, void *data); UIEXPORT UiObserver* ui_obsvlist_add(UiObserver *list, UiObserver *observer); UIEXPORT UiObserver* ui_add_observer(UiObserver *list, ui_callback f, void *data); @@ -635,7 +654,7 @@ UIEXPORT void ui_notify_evt(UiObserver *observer, UiEvent *event); UIEXPORT UiList* ui_list_new(UiContext *ctx, const char *name); UIEXPORT UiList* ui_list_new2(UiContext *ctx, const char *name, ui_list_init_func init, void *userdata); -UIEXPORT void ui_list_free(UiList *list); +UIEXPORT void ui_list_free(UiContext *ctx, UiList *list); UIEXPORT void* ui_list_first(UiList *list); UIEXPORT void* ui_list_next(UiList *list); UIEXPORT void* ui_list_get(UiList *list, int i); @@ -680,9 +699,12 @@ UIEXPORT void ui_condvar_destroy(UiCondVar *var); UIEXPORT void ui_setop_enable(int set); UIEXPORT int ui_get_setop(void); +UIEXPORT void ui_onchange_events_enable(UiBool enable); +UIEXPORT UiBool ui_onchange_events_is_enabled(void); UIEXPORT void ui_global_list_initializer(ui_list_init_func func, void *userdata); +UIEXPORT void ui_global_list_destructor(ui_list_destroy_func func, void *userdata); UIEXPORT void ui_list_class_set_first(UiList *list, void*(*first)(UiList *list)); UIEXPORT void ui_list_class_set_next(UiList *list, void*(*next)(UiList *list)); UIEXPORT void ui_list_class_set_get(UiList *list, void*(*get)(UiList *list, int i)); @@ -690,6 +712,9 @@ UIEXPORT void ui_list_class_set_count(UiList *list, int(*count)(UiList *list)); UIEXPORT void ui_list_class_set_data(UiList *list, void *data); UIEXPORT void ui_list_class_set_iter(UiList *list, void *iter); +UIEXPORT void ui_object_set(UiObject *obj, const char *key, void *data); +UIEXPORT void* ui_object_get(UiObject *obj, const char *key); + #ifdef __cplusplus } #endif diff --git a/ui/ui/ui.h b/ui/ui/ui.h index 57c8025..472ca74 100644 --- a/ui/ui/ui.h +++ b/ui/ui/ui.h @@ -35,11 +35,10 @@ #include "menu.h" #include "toolbar.h" #include "window.h" -#include "stock.h" #include "button.h" #include "text.h" #include "properties.h" -#include "tree.h" +#include "list.h" #include "graphics.h" #include "entry.h" #include "range.h" diff --git a/ui/ui/webview.h b/ui/ui/webview.h index 9636165..d5a243d 100644 --- a/ui/ui/webview.h +++ b/ui/ui/webview.h @@ -60,13 +60,15 @@ typedef struct UiWebviewArgs { int margin_bottom; int colspan; int rowspan; + int width; + int height; const char *name; const char *style_class; UiGeneric *value; const char *varname; - const int* groups; + const int* states; } UiWebviewArgs; #define ui_webview(obj, ...) ui_webview_create(obj, &(UiWebviewArgs){ __VA_ARGS__ } ) diff --git a/ui/ui/widget.h b/ui/ui/widget.h index aea5b8e..40503ba 100644 --- a/ui/ui/widget.h +++ b/ui/ui/widget.h @@ -64,6 +64,8 @@ typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata); #elif defined(UI_WIN32) typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata); +#elif defined(UI_SERVER) +typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata); #endif UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args); diff --git a/ui/ui/win32.h b/ui/ui/win32.h index 54d0543..1137265 100644 --- a/ui/ui/win32.h +++ b/ui/ui/win32.h @@ -49,7 +49,7 @@ struct W32Size { }; struct W32WidgetClass { - void (*eventproc)(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + int (*eventproc)(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void (*show)(W32Widget *widget, BOOLEAN show); void (*enable)(W32Widget *widget, BOOLEAN enable); W32Size (*get_preferred_size)(W32Widget *widget); diff --git a/ui/ui/window.h b/ui/ui/window.h index 83e85a3..d100016 100644 --- a/ui/ui/window.h +++ b/ui/ui/window.h @@ -61,10 +61,10 @@ typedef struct UiDialogWindowArgs { const char *lbutton2; const char *rbutton3; const char *rbutton4; - const int *lbutton1_groups; - const int *lbutton2_groups; - const int *rbutton3_groups; - const int *rbutton4_groups; + const int *lbutton1_states; + const int *lbutton2_states; + const int *rbutton3_states; + const int *rbutton4_states; int default_button; int width; int height; @@ -72,10 +72,10 @@ typedef struct UiDialogWindowArgs { void *onclickdata; } 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_window(const char *title); +UIEXPORT UiObject *ui_sidebar_window(const char *title); UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar); -UIEXPORT UiObject *ui_simple_window(const char *title, void *window_data); +UIEXPORT UiObject *ui_simple_window(const char *title); UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args); #define ui_dialog_window(parent, ...) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ __VA_ARGS__ }); @@ -83,6 +83,9 @@ UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs UIEXPORT void ui_window_size(UiObject *obj, int width, int height); UIEXPORT void ui_window_default_size(int width, int height); +UIEXPORT void ui_window_menubar_set_visible(UiObject *obj, UiBool visible); + +UIEXPORT void ui_window_fullscreen(UiObject *obj, UiBool fullscreen); UIEXPORT void ui_splitview_window_set_pos(UiObject *obj, int pos); UIEXPORT int ui_splitview_window_get_pos(UiObject *obj);