Sun, 29 Sep 2024 13:32:51 +0200
update toolkit: gtk4 improvements
--- a/configure Mon Jun 17 21:20:58 2024 +0200 +++ b/configure Sun Sep 29 13:32:51 2024 +0200 @@ -72,7 +72,7 @@ Options: --debug add extra compile flags for debug builds --release add extra compile flags for release builds - --toolkit=(gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|motif) + --toolkit=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|motif) __EOF__ } @@ -508,6 +508,32 @@ dep_checked_openssl=1 return 0 } +dependency_error_libadwaita() +{ + print_check_msg "$dep_checked_libadwaita" "checking for libadwaita... " + # dependency libadwaita + while true + do + if [ -z "$PKG_CONFIG" ]; then + break + fi + if test_pkg_config "libadwaita-1" "" "" "" ; then + TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags libadwaita-1`" + TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs libadwaita-1`" + else + break + fi + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK4 -DUI_LIBADWAITA" + TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread" + print_check_msg "$dep_checked_libadwaita" "yes\n" + dep_checked_libadwaita=1 + return 1 + done + + print_check_msg "$dep_checked_libadwaita" "no\n" + dep_checked_libadwaita=1 + return 0 +} dependency_error_motif() { print_check_msg "$dep_checked_motif" "checking for motif... " @@ -751,6 +777,21 @@ # # OPTION VALUES # +checkopt_toolkit_libadwaita() +{ + VERR=0 + if dependency_error_libadwaita ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ +TOOLKIT = gtk +GTKOBJ = draw_cairo.o +__EOF__ + return 0 +} checkopt_toolkit_gtk4() { VERR=0 @@ -978,6 +1019,14 @@ echo "checking option toolkit = $OPT_TOOLKIT" if false; then false + elif [ "$OPT_TOOLKIT" = "libadwaita" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_libadwaita ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi elif [ "$OPT_TOOLKIT" = "gtk4" ]; then echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options if checkopt_toolkit_gtk4 ; then
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/cc.mk Sun Sep 29 13:32:51 2024 +0200 @@ -0,0 +1,14 @@ +# +# cc toolchain config +# + +CFLAGS = +CXXFLAGS = +DEBUG_CC_FLAGS = -g +DEBUG_CXX_FLAGS = -g +RELEASE_CC_FLAGS = -O3 -DNDEBUG +RELEASE_CXX_FLAGS = -O3 -DNDEBUG +LDFLAGS = + +SHLIB_CFLAGS = -fPIC +SHLIB_LDFLAGS = -shared \ No newline at end of file
--- a/make/clang.mk Mon Jun 17 21:20:58 2024 +0200 +++ b/make/clang.mk Sun Sep 29 13:32:51 2024 +0200 @@ -2,8 +2,13 @@ # clang toolchain config # -CFLAGS = -LDFLAGS = +CFLAGS = +CXXFLAGS = +DEBUG_CC_FLAGS = -g +DEBUG_CXX_FLAGS = -g +RELEASE_CC_FLAGS = -O3 -DNDEBUG +RELEASE_CXX_FLAGS = -O3 -DNDEBUG +LDFLAGS = SHLIB_CFLAGS = -fPIC SHLIB_LDFLAGS = -shared
--- a/make/gcc.mk Mon Jun 17 21:20:58 2024 +0200 +++ b/make/gcc.mk Sun Sep 29 13:32:51 2024 +0200 @@ -2,8 +2,13 @@ # gcc toolchain config # -CFLAGS = -LDFLAGS = +CFLAGS = +CXXFLAGS = +DEBUG_CC_FLAGS = -g +DEBUG_CXX_FLAGS = -g +RELEASE_CC_FLAGS = -O3 -DNDEBUG +RELEASE_CXX_FLAGS = -O3 -DNDEBUG +LDFLAGS = SHLIB_CFLAGS = -fPIC SHLIB_LDFLAGS = -shared
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/osx.mk Sun Sep 29 13:32:51 2024 +0200 @@ -0,0 +1,43 @@ +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2011 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. +# + +CC = gcc +LD = gcc +AR = ar +RM = rm + +CFLAGS += -std=gnu99 -g -I/usr/include/libxml2 +LDFLAGS += -lxml2 -lz -lpthread -licucore -lm +ARFLAGS = -r +RMFLAGS = -f + +OBJ_EXT = o +LIB_EXT = a +APP_EXT = + +PACKAGE_SCRIPT = package_osx.sh
--- a/make/project.xml Mon Jun 17 21:20:58 2024 +0200 +++ b/make/project.xml Sun Sep 29 13:32:51 2024 +0200 @@ -45,7 +45,11 @@ <pkgconfig>openssl</pkgconfig> </dependency> - + <dependency name="libadwaita"> + <pkgconfig>libadwaita-1</pkgconfig> + <cflags>-DUI_GTK4 -DUI_LIBADWAITA</cflags> + <ldflags>-lpthread</ldflags> + </dependency> <dependency name="gtk4"> <pkgconfig>gtk+-4.0</pkgconfig> <cflags>-DUI_GTK3</cflags> @@ -123,6 +127,11 @@ <target name="tk"> <option arg="toolkit"> + <value str="libadwaita"> + <dependencies>libadwaita</dependencies> + <make>TOOLKIT = gtk</make> + <make>GTKOBJ = draw_cairo.o</make> + </value> <value str="gtk4"> <dependencies>gtk4</dependencies> <make>TOOLKIT = gtk</make>
--- a/make/suncc.mk Mon Jun 17 21:20:58 2024 +0200 +++ b/make/suncc.mk Sun Sep 29 13:32:51 2024 +0200 @@ -2,8 +2,13 @@ # suncc toolchain # -CFLAGS = -LDFLAGS = +CFLAGS = +CXXFLAGS = +DEBUG_CC_FLAGS = -g +DEBUG_CXX_FLAGS = -g +RELEASE_CC_FLAGS = -O3 -DNDEBUG +RELEASE_CXX_FLAGS = -O3 -DNDEBUG +LDFLAGS = SHLIB_CFLAGS = -Kpic SHLIB_LDFLAGS = -G
--- a/ui/common/context.h Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/common/context.h Sun Sep 29 13:32:51 2024 +0200 @@ -77,9 +77,15 @@ char *title; -#if UI_GTK2 || UI_GTK3 +#ifdef UI_GTK +#if GTK_CHECK_VERSION(4, 0, 0) + GActionMap *action_map; +#elif UI_GTK2 || UI_GTK3 GtkAccelGroup *accel_group; +#endif #endif + + ui_callback close_callback; void *close_data;
--- a/ui/common/menu.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/common/menu.c Sun Sep 29 13:32:51 2024 +0200 @@ -38,6 +38,8 @@ static UiMenu *menus_end; static CxList *current; +static int menu_item_counter = 0; + static void add_menu(UiMenu *menu) { cx_linked_list_add( (void**)&menus_begin, @@ -57,6 +59,10 @@ item); } +static void mitem_set_id(UiMenuItemI *item) { + snprintf(item->id, 8, "%x", menu_item_counter++); +} + static char* nl_strdup(const char* s) { return s ? strdup(s) : NULL; } @@ -86,6 +92,7 @@ // create menu UiMenu *menu = malloc(sizeof(UiMenu)); + mitem_set_id(&menu->item); menu->item.prev = NULL; menu->item.next = NULL; menu->item.type = UI_MENU; @@ -118,6 +125,7 @@ } UiMenuItem* item = malloc(sizeof(UiMenuItem)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_ITEM; @@ -138,6 +146,7 @@ } UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); + item->id[0] = 0; item->prev = NULL; item->next = NULL; item->type = UI_MENU_SEPARATOR; @@ -151,6 +160,7 @@ } UiMenuCheckItem *item = malloc(sizeof(UiMenuCheckItem)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_CHECK_ITEM; @@ -172,6 +182,7 @@ } UiMenuCheckItem* item = malloc(sizeof(UiMenuCheckItem)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_CHECK_ITEM; @@ -193,6 +204,7 @@ } UiMenuItemList*item = malloc(sizeof(UiMenuItemList)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_ITEM_LIST; @@ -210,6 +222,7 @@ } UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_CHECKITEM_LIST; @@ -226,6 +239,7 @@ } UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_RADIOITEM_LIST;
--- a/ui/common/menu.h Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/common/menu.h Sun Sep 29 13:32:51 2024 +0200 @@ -61,6 +61,7 @@ UiMenuItemI *prev; UiMenuItemI *next; UiMenuItemType type; + char id[8]; }; struct UiMenu {
--- a/ui/gtk/button.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/button.c Sun Sep 29 13:32:51 2024 +0200 @@ -54,20 +54,21 @@ #endif } -UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs args) { - UiObject* current = uic_current_obj(obj); - GtkWidget *button = gtk_button_new(); - if(args.label) { - gtk_button_set_label(GTK_BUTTON(button), args.label); - } - ui_button_set_icon_name(button, args.icon); - +GtkWidget* ui_create_button( + UiObject *obj, + const char *label, + const char *icon, + ui_callback onclick, + void *userdata) +{ + GtkWidget *button = gtk_button_new_with_label(label); + ui_button_set_icon_name(button, icon); - if(args.onclick) { + if(onclick) { UiEventData *event = malloc(sizeof(UiEventData)); event->obj = obj; - event->userdata = args.onclickdata; - event->callback = args.onclick; + event->userdata = userdata; + event->callback = onclick; event->value = 0; event->customdata = NULL; @@ -83,9 +84,14 @@ event); } + return button; +} + +UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs args) { + UiObject* current = uic_current_obj(obj); + GtkWidget *button = ui_create_button(obj, args.label, args.icon, args.onclick, args.onclickdata); UI_APPLY_LAYOUT1(current, args); current->container->add(current->container, button, FALSE); - return button; } @@ -112,32 +118,73 @@ gtk_toggle_button_set_active(button, value != 0 ? TRUE : FALSE); } -void ui_toggled_obs(GtkToggleToolButton *widget, UiVarEventData *event) { +void ui_toggled_obs(void *widget, UiVarEventData *event) { + UiInteger *i = event->var->value; UiEvent e; e.obj = event->obj; e.window = event->obj->window; e.document = event->obj->ctx->document; e.eventdata = event->var->value; - e.intval = gtk_toggle_tool_button_get_active(widget); + e.intval = i->get(i); - UiInteger *i = event->var->value; ui_notify_evt(i->observers, &e); } -static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleArgs args) { - UiObject* current = uic_current_obj(obj); +static void ui_toggled_callback(GtkToggleButton *widget, UiEventData *event) { + UiEvent e; + e.obj = event->obj; + e.window = event->obj->window; + e.document = event->obj->ctx->document; + e.eventdata = NULL; + e.intval = gtk_toggle_button_get_active(widget); + event->callback(&e, event->userdata); +} + +void ui_setup_togglebutton( + UiObject *obj, + GtkWidget *togglebutton, + const char *label, + const char *icon, + const char *varname, + UiInteger *value, + ui_callback onchange, + void *onchangedata) +{ + if(label) { + gtk_button_set_label(GTK_BUTTON(togglebutton), label); + } + ui_button_set_icon_name(togglebutton, icon); - if(args.label) { - gtk_button_set_label(GTK_BUTTON(widget), args.label); - } - ui_button_set_icon_name(widget, args.icon); - - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER); + ui_bind_togglebutton( + obj, + togglebutton, + ui_toggle_button_get, + ui_toggle_button_set, + varname, + value, + (ui_toggled_func)ui_toggled_callback, + onchange, + onchangedata); +} + +void ui_bind_togglebutton( + UiObject *obj, + GtkWidget *widget, + int64_t (*getfunc)(UiInteger*), + void (*setfunc)(UiInteger*, int64_t), + const char *varname, + UiInteger *value, + void (*toggled_callback)(void*, void*), + ui_callback onchange, + void *onchangedata) +{ + UiObject* current = uic_current_obj(obj); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, value, varname, UI_VAR_INTEGER); if (var) { UiInteger* value = (UiInteger*)var->value; value->obj = widget; - value->get = ui_toggle_button_get; - value->set = ui_toggle_button_set; + value->get = getfunc; + value->set = setfunc; UiVarEventData *event = malloc(sizeof(UiVarEventData)); event->obj = obj; @@ -148,7 +195,7 @@ g_signal_connect( widget, - "clicked", + "toggled", G_CALLBACK(ui_toggled_obs), event); g_signal_connect( @@ -158,6 +205,32 @@ event); } + if(onchange) { + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = onchangedata; + event->callback = onchange; + event->value = 0; + event->customdata = NULL; + + g_signal_connect( + widget, + "toggled", + G_CALLBACK(toggled_callback), + event); + g_signal_connect( + widget, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + } +} + +static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleArgs args) { + UiObject* current = uic_current_obj(obj); + + ui_setup_togglebutton(current, widget, args.label, args.icon, args.varname, args.value, args.onchange, args.onchangedata); + UI_APPLY_LAYOUT1(current, args); current->container->add(current->container, widget, FALSE); @@ -168,9 +241,56 @@ return togglebutton_create(obj, gtk_toggle_button_new(), args); } +#if GTK_MAJOR_VERSION >= 4 + +int64_t ui_check_button_get(UiInteger *integer) { + GtkCheckButton *button = integer->obj; + integer->value = (int)gtk_check_button_get_active(button); + return integer->value; +} + +void ui_check_button_set(UiInteger *integer, int64_t value) { + GtkCheckButton *button = integer->obj; + integer->value = value; + gtk_check_button_set_active(button, value != 0 ? TRUE : FALSE); +} + +static void ui_checkbox_callback(GtkCheckButton *widget, UiEventData *event) { + UiEvent e; + e.obj = event->obj; + e.window = event->obj->window; + e.document = event->obj->ctx->document; + e.eventdata = NULL; + e.intval = gtk_check_button_get_active(widget); + event->callback(&e, event->userdata); +} + +UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) { + UiObject* current = uic_current_obj(obj); + + GtkWidget *widget = gtk_check_button_new_with_label(args.label); + ui_bind_togglebutton( + obj, + widget, + ui_check_button_get, + ui_check_button_set, + args.varname, + args.value, + (ui_toggled_func)ui_checkbox_callback, + args.onchange, + args.onchangedata); + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, widget, FALSE); + + return widget; +} + +#else UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) { return togglebutton_create(obj, gtk_check_button_new(), args); } +#endif UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) { #ifdef UI_GTK3 @@ -180,10 +300,27 @@ #endif } - +#if GTK_MAJOR_VERSION >= 4 +#define RADIOBUTTON_NEW(group, label) gtk_check_button_new_with_label(label) +#define RADIOBUTTON_SET_GROUP(button, group) +#define RADIOBUTTON_GET_GROUP(button) GTK_CHECK_BUTTON(button) +#define RADIOBUTTON_GET_ACTIVE(button) gtk_check_button_get_active(GTK_CHECK_BUTTON(button)) +#else +#define RADIOBUTTON_NEW(group, label) gtk_radio_button_new_with_label(group, label) +#define RADIOBUTTON_SET_GROUP(button, group) /* noop */ +#define RADIOBUTTON_GET_GROUP(button) gtk_radio_button_get_group(GTK_RADIO_BUTTON(button)) +#define RADIOBUTTON_GET_ACTIVE(button) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) +#endif - - +static void radiobutton_toggled(void *widget, UiEventData *event) { + UiEvent e; + e.obj = event->obj; + e.window = event->obj->window; + e.document = event->obj->ctx->document; + e.eventdata = NULL; + e.intval = RADIOBUTTON_GET_ACTIVE(widget); + event->callback(&e, event->userdata); +} UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs args) { UiObject* current = uic_current_obj(obj); @@ -191,12 +328,7 @@ GSList *rg = NULL; UiInteger *rgroup; - UiVar* var = NULL; - if (args.value) { - var = uic_create_value_var(current->ctx, args.value); - } else if (args.varname) { - var = uic_create_var(obj->ctx, args.varname, UI_VAR_INTEGER); - } + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER); UiBool first = FALSE; if(var) { @@ -207,10 +339,18 @@ } } - GtkWidget *rbutton = gtk_radio_button_new_with_label(rg, args.label ? args.label : ""); - rg = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton)); - + GtkWidget *rbutton = RADIOBUTTON_NEW(rg, args.label); if(rgroup) { +#if GTK_MAJOR_VERSION >= 4 + if(rg) { + gtk_check_button_set_group(GTK_CHECK_BUTTON(rbutton), rg->data); + } + rg = g_slist_prepend(rg, rbutton); +#else + gtk_radio_button_set_group(GTK_RADIO_BUTTON(rbutton), rg); + rg = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton)); +#endif + rgroup->obj = rg; rgroup->get = ui_radiobutton_get; rgroup->set = ui_radiobutton_set; @@ -226,7 +366,7 @@ g_signal_connect( rbutton, - "clicked", + "toggled", G_CALLBACK(ui_radio_obs), event); if(first) { @@ -238,13 +378,33 @@ } } + if(args.onchange) { + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = args.onchangedata; + event->callback = args.onchange; + event->value = 0; + event->customdata = NULL; + + g_signal_connect( + rbutton, + "toggled", + G_CALLBACK(radiobutton_toggled), + event); + g_signal_connect( + rbutton, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + } + UiContainer *ct = uic_get_current_container(obj); ct->add(ct, rbutton, FALSE); return rbutton; } -void ui_radio_obs(GtkToggleToolButton *widget, UiVarEventData *event) { +void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event) { UiInteger *i = event->var->value; UiEvent e; @@ -257,6 +417,41 @@ ui_notify_evt(i->observers, &e); } +#if GTK_MAJOR_VERSION >= 4 +int64_t ui_radiobutton_get(UiInteger *value) { + int selection = 0; + GSList *ls = value->obj; + int i = 0; + guint len = g_slist_length(ls); + while(ls) { + if(gtk_check_button_get_active(GTK_CHECK_BUTTON(ls->data))) { + selection = len - i - 1; + break; + } + ls = ls->next; + i++; + } + + value->value = selection; + return selection; +} + +void ui_radiobutton_set(UiInteger *value, int64_t i) { + GSList *ls = value->obj; + int s = g_slist_length(ls) - 1 - i; + int j = 0; + while(ls) { + if(j == s) { + gtk_check_button_set_active(GTK_CHECK_BUTTON(ls->data), TRUE); + break; + } + ls = ls->next; + j++; + } + + value->value = i; +} +#else int64_t ui_radiobutton_get(UiInteger *value) { int selection = 0; GSList *ls = value->obj; @@ -290,4 +485,4 @@ value->value = i; } - +#endif
--- a/ui/gtk/button.h Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/button.h Sun Sep 29 13:32:51 2024 +0200 @@ -36,20 +36,50 @@ #ifdef __cplusplus extern "C" { #endif + +void ui_button_set_icon_name(GtkWidget *button, const char *icon_name); -void ui_button_set_icon_name(GtkWidget *button, const char *icon_name); +typedef void (*ui_toggled_func)(void*, void*); + +GtkWidget* ui_create_button( + UiObject *obj, + const char *label, + const char *icon, + ui_callback onclick, + void *userdata); + +void ui_setup_togglebutton( + UiObject *obj, + GtkWidget *togglebutton, + const char *label, + const char *icon, + const char *varname, + UiInteger *value, + ui_callback onchange, + void *onchangedata); + +void ui_bind_togglebutton( + UiObject *obj, + GtkWidget *widget, + int64_t (*getfunc)(UiInteger*), + void (*setfunc)(UiInteger*, int64_t), + const char *varname, + UiInteger *value, + void (*toggled_callback)(void*, void*), + ui_callback onchange, + void *onchangedata); // event wrapper void ui_button_clicked(GtkWidget *widget, UiEventData *event); -void ui_toggled_obs(GtkToggleToolButton *widget, UiVarEventData *event); +void ui_toggled_obs(void *widget, UiVarEventData *event); UIWIDGET ui_checkbox_var(UiObject *obj, char *label, UiVar *var); UIWIDGET ui_radiobutton_var(UiObject *obj, char *label, UiVar *var); -void ui_radio_obs(GtkToggleToolButton *widget, UiVarEventData *event); +void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event); int64_t ui_radiobutton_get(UiInteger *value); void ui_radiobutton_set(UiInteger *value, int64_t i);
--- a/ui/gtk/container.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/container.c Sun Sep 29 13:32:51 2024 +0200 @@ -52,7 +52,7 @@ } GtkWidget* ui_gtk_vbox_new(int spacing) { -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 return gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing); #else return gtk_vbox_new(FALSE, spacing); @@ -60,7 +60,7 @@ } GtkWidget* ui_gtk_hbox_new(int spacing) { -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 return gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing); #else return gtk_hbox_new(FALSE, spacing); @@ -70,13 +70,14 @@ /* -------------------- Box Container -------------------- */ -UiContainer* ui_box_container(UiObject *obj, GtkWidget *box) { +UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) { UiBoxContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiBoxContainer)); ct->container.widget = box; ct->container.add = ui_box_container_add; + ct->type = type; return (UiContainer*)ct; } @@ -95,7 +96,22 @@ } UiBool expand = fill; +#if GTK_MAJOR_VERSION >= 4 + gtk_box_append(GTK_BOX(ct->widget), widget); + GtkAlign align = expand ? GTK_ALIGN_FILL : GTK_ALIGN_START; + if(bc->type == UI_CONTAINER_VBOX) { + gtk_widget_set_valign(widget, align); + gtk_widget_set_vexpand(widget, expand); + gtk_widget_set_hexpand(widget, TRUE); + } else if(bc->type == UI_CONTAINER_HBOX) { + gtk_widget_set_halign(widget, align); + gtk_widget_set_hexpand(widget, expand); + gtk_widget_set_vexpand(widget, TRUE); + } + +#else gtk_box_pack_start(GTK_BOX(ct->widget), widget, expand, fill, 0); +#endif ui_reset_layout(ct->layout); ct->current = widget; @@ -108,14 +124,12 @@ sizeof(UiGridContainer)); ct->container.widget = grid; ct->container.add = ui_grid_container_add; -#ifdef UI_GTK2 - ct->width = 0; - ct->height = 1; -#endif + UI_GTK_V2(ct->width = 0); + UI_GTK_V2(ct->height = 1); return (UiContainer*)ct; } -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 void ui_grid_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { UiGridContainer *grid = (UiGridContainer*)ct; @@ -134,12 +148,8 @@ vexpand = ct->layout.vexpand; } - if(hexpand) { - gtk_widget_set_hexpand(widget, TRUE); - } - if(vexpand) { - gtk_widget_set_vexpand(widget, TRUE); - } + gtk_widget_set_hexpand(widget, hexpand); + gtk_widget_set_vexpand(widget, vexpand); int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1; int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1; @@ -202,11 +212,7 @@ void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { // TODO: check if the widget implements GtkScrollable -#ifdef UI_GTK3 - gtk_container_add(GTK_CONTAINER(ct->widget), widget); -#else - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ct->widget), widget); -#endif + SCROLLEDWINDOW_SET_CHILD(ct->widget, widget); ui_reset_layout(ct->layout); ct->current = widget; } @@ -235,8 +241,8 @@ static GtkWidget* box_set_margin(GtkWidget *box, int margin) { GtkWidget *ret = box; -#ifdef UI_GTK3 -#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 12 +#if GTK_MAJOR_VERSION >= 3 +#if GTK_MAJOR_VERSION * 1000 + GTK_MINOR_VERSION >= 3012 gtk_widget_set_margin_start(box, margin); gtk_widget_set_margin_end(box, margin); #else @@ -264,7 +270,7 @@ ct->add(ct, widget, TRUE); UiObject *newobj = uic_object_new(obj, box); - newobj->container = ui_box_container(obj, box); + newobj->container = ui_box_container(obj, box, type); uic_obj_add(obj, newobj); return widget; @@ -278,43 +284,26 @@ return ui_box_create(obj, args, UI_CONTAINER_HBOX); } - +static GtkWidget* create_grid(int colspacing, int rowspacing) { +#if GTK_MAJOR_VERSION >= 3 + GtkWidget *grid = gtk_grid_new(); + gtk_grid_set_column_spacing(GTK_GRID(grid), colspacing); + gtk_grid_set_row_spacing(GTK_GRID(grid), rowspacing); +#else + GtkWidget *grid = gtk_table_new(1, 1, FALSE); + gtk_table_set_col_spacings(GTK_TABLE(grid), colspacing); + gtk_table_set_row_spacings(GTK_TABLE(grid), rowspacing); +#endif + return grid; +} UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args) { UiObject* current = uic_current_obj(obj); UI_APPLY_LAYOUT1(current, args); GtkWidget *widget; -#ifdef UI_GTK3 - GtkWidget *grid = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(grid), args.columnspacing); - gtk_grid_set_row_spacing(GTK_GRID(grid), args.rowspacing); -#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 12 - gtk_widget_set_margin_start(grid, args.margin); - gtk_widget_set_margin_end(grid, args.margin); -#else - gtk_widget_set_margin_left(grid, args.margin); - gtk_widget_set_margin_right(grid, args.margin); -#endif - gtk_widget_set_margin_top(grid, args.margin); - gtk_widget_set_margin_bottom(grid, args.margin); - - widget = grid; -#elif defined(UI_GTK2) - GtkWidget *grid = gtk_table_new(1, 1, FALSE); - - gtk_table_set_col_spacings(GTK_TABLE(grid), columnspacing); - gtk_table_set_row_spacings(GTK_TABLE(grid), rowspacing); - - if(margin > 0) { - GtkWidget *a = gtk_alignment_new(0.5, 0.5, 1, 1); - gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin, margin, margin, margin); - gtk_container_add(GTK_CONTAINER(a), grid); - widget = a; - } else { - widget = grid; - } -#endif + GtkWidget *grid = create_grid(args.columnspacing, args.rowspacing); + widget = box_set_margin(grid, args.margin); current->container->add(current->container, widget, TRUE); UiObject *newobj = uic_object_new(obj, grid); @@ -329,7 +318,7 @@ UiObject* current = uic_current_obj(obj); UI_APPLY_LAYOUT1(current, args); - GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL); + GtkWidget *sw = SCROLLEDWINDOW_NEW(); UiObject *newobj = uic_object_new(obj, sw); newobj->container = ui_scrolledwindow_container(obj, sw); uic_obj_add(obj, newobj); @@ -345,7 +334,7 @@ /* -------------------- Splitpane -------------------- */ static GtkWidget* create_paned(UiOrientation orientation) { -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 switch(orientation) { case UI_HORIZONTAL: return gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); case UI_VERTICAL: return gtk_paned_new(GTK_ORIENTATION_VERTICAL);
--- a/ui/gtk/container.h Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/container.h Sun Sep 29 13:32:51 2024 +0200 @@ -78,6 +78,7 @@ typedef struct UiBoxContainer { UiContainer container; + UiSubContainerType type; UiBool has_fill; } UiBoxContainer; @@ -111,7 +112,7 @@ UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, UiSubContainerType type); -UiContainer* ui_box_container(UiObject *obj, GtkWidget *box); +UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type); void ui_box_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); UiContainer* ui_grid_container(UiObject *obj, GtkWidget *grid);
--- a/ui/gtk/display.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/display.c Sun Sep 29 13:32:51 2024 +0200 @@ -36,7 +36,7 @@ #include "../ui/display.h" static void set_alignment(GtkWidget *widget, float xalign, float yalign) { -#if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16 +#if GTK_MAJOR_VERSION >= 4 || (GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16) gtk_label_set_xalign(GTK_LABEL(widget), xalign); gtk_label_set_yalign(GTK_LABEL(widget), yalign); #else @@ -106,7 +106,7 @@ } UIWIDGET ui_separator_deprecated(UiObject *obj) { -#if UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 GtkWidget *widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); #else GtkWidget *widget = gtk_hseparator_new();
--- a/ui/gtk/draw_cairo.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/draw_cairo.c Sun Sep 29 13:32:51 2024 +0200 @@ -33,17 +33,19 @@ #include "draw_cairo.h" -#if UI_GTK3 || UI_GTK4 -gboolean ui_drawingarea_expose(GtkWidget *w, cairo_t *cr, void *data) { + +#if GTK_MAJOR_VERSION >= 3 +static void ui_drawingarea_draw( + GtkDrawingArea *area, + cairo_t *cr, + int width, + int height, + gpointer data) +{ UiCairoGraphics g; -#ifdef UI_GTK4 - g.g.width = gtk_widget_get_width(w); - g.g.height = gtk_widget_get_height(w); -#else - g.g.width = gtk_widget_get_allocated_width(w); - g.g.height = gtk_widget_get_allocated_height(w); -#endif - g.widget = w; + g.g.width = width; + g.g.height = height; + g.widget = GTK_WIDGET(area); g.cr = cr; UiDrawEvent *event = data; @@ -53,7 +55,14 @@ ev.document = event->obj->ctx->document; event->callback(&ev, &g.g, event->userdata); - +} +#endif + +#if UI_GTK3 +gboolean ui_drawingarea_expose(GtkWidget *w, cairo_t *cr, void *data) { + int width = gtk_widget_get_allocated_width(w); + int height = gtk_widget_get_allocated_height(w); + ui_drawingarea_draw(GTK_DRAWING_AREA(w), cr, width, height, data); return FALSE; } #endif @@ -80,7 +89,9 @@ // function from graphics.h void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event) { -#if UI_GTK3 || UI_GTK4 +#if GTK_MAJOR_VERSION >= 4 + gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(widget), ui_drawingarea_draw, event, NULL); +#elif GTK_MAJOR_VERSION == 3 g_signal_connect(G_OBJECT(widget), "draw", G_CALLBACK(ui_drawingarea_expose),
--- a/ui/gtk/graphics.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/graphics.c Sun Sep 29 13:32:51 2024 +0200 @@ -51,6 +51,7 @@ } +#if GTK_MAJOR_VERSION <= 3 static gboolean widget_button_pressed( GtkWidget *widget, GdkEvent *event, @@ -82,14 +83,18 @@ } return TRUE; } +#endif void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height) { -#ifdef UI_GTK3 - *width = gtk_widget_get_allocated_width(drawingarea); - *height = gtk_widget_get_allocated_height(drawingarea); +#if GTK_MAJOR_VERSION >= 4 + *width = gtk_widget_get_width(drawingarea); + *height = gtk_widget_get_height(drawingarea); +#elif GTK_MAJOR_VERSION == 3 + *width = gtk_widget_get_allocated_width(drawingarea); + *height = gtk_widget_get_allocated_height(drawingarea); #else - *width = drawingarea->allocation.width; - *height = drawingarea->allocation.height; + *width = drawingarea->allocation.width; + *height = drawingarea->allocation.height; #endif } @@ -98,6 +103,9 @@ } void ui_drawingarea_mousehandler(UiObject *obj, UIWIDGET widget, ui_callback f, void *u) { +#if GTK_MAJOR_VERSION >= 4 + // TODO +#else gtk_widget_set_events(widget, GDK_BUTTON_PRESS_MASK); if(f) { UiEventData *event = malloc(sizeof(UiEventData)); @@ -114,6 +122,7 @@ } else { // TODO: warning } +#endif }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/gtk/headerbar.c Sun Sep 29 13:32:51 2024 +0200 @@ -0,0 +1,172 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "headerbar.h" + +#include "button.h" +#include "menu.h" + +#if GTK_MAJOR_VERSION >= 3 + +#ifdef UI_LIBADWAITA +#define UI_HEADERBAR AdwHeaderBar* +#define UI_HEADERBAR_CAST(h) ADW_HEADER_BAR(h) +#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) +#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) +#if GTK_MAJOR_VERSION >= 4 +#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_title_widget(GTK_HEADER_BAR(h), w) +#else +#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_custom_title(GTK_HEADER_BAR(h), w) +#endif +#endif + +void ui_fill_headerbar(UiObject *obj, GtkWidget *headerbar) { + CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); + CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); + CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); + + ui_headerbar_add_items(obj, headerbar, left_defaults, UI_TOOLBAR_LEFT); + ui_headerbar_add_items(obj, headerbar, center_defaults, UI_TOOLBAR_CENTER); + ui_headerbar_add_items(obj, headerbar, right_defaults, UI_TOOLBAR_RIGHT); +} + +static void create_item(UiObject *obj, GtkWidget *headerbar, GtkWidget *box, UiToolbarItemI *i, enum UiToolbarPos pos) { + switch(i->type) { + case UI_TOOLBAR_ITEM: { + ui_add_headerbar_item(headerbar, box, (UiToolbarItem*)i, obj, pos); + break; + } + case UI_TOOLBAR_TOGGLEITEM: { + ui_add_headerbar_toggleitem(headerbar, box, (UiToolbarToggleItem*)i, obj, pos); + break; + } + case UI_TOOLBAR_MENU: { + ui_add_headerbar_menu(headerbar, box, (UiToolbarMenuItem*)i, obj, pos); + break; + } + default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type); + } +} + +static void headerbar_add(GtkWidget *headerbar, GtkWidget *box, GtkWidget *item, enum UiToolbarPos pos) { + switch(pos) { + case UI_TOOLBAR_LEFT: { + UI_HEADERBAR_PACK_START(headerbar, item); + break; + } + case UI_TOOLBAR_CENTER: { + +#if GTK_MAJOR_VERSION >= 4 + gtk_box_append(GTK_BOX(box), item); +#else + gtk_box_pack_start(GTK_BOX(box), item, 0, 0, 0); +#endif + break; + } + case UI_TOOLBAR_RIGHT: { + UI_HEADERBAR_PACK_END(headerbar, item); + break; + } + } +} + +void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos) { + GtkWidget *box = NULL; + + if(pos == UI_TOOLBAR_CENTER && cxListSize(items) > 0) { + box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + UI_HEADERBAR_SET_TITLE_WIDGET(headerbar, box); + } + + CxIterator i = pos == UI_TOOLBAR_RIGHT ? cxListBackwardsIterator(items) : cxListIterator(items); + cx_foreach(char*, def, i) { + UiToolbarItemI* item = uic_toolbar_get_item(def); + if (!item) { + fprintf(stderr, "unknown toolbar item: %s\n", def); + continue; + } + create_item(obj, headerbar, box, item, pos); + } +} + +void ui_add_headerbar_item( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarItem *item, + UiObject *obj, + enum UiToolbarPos pos) +{ + GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.onclick, item->args.onclickdata); + WIDGET_ADD_CSS_CLASS(button, "flat"); + headerbar_add(headerbar, box, button, pos); +} + +void ui_add_headerbar_toggleitem( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarToggleItem *item, + UiObject *obj, + enum UiToolbarPos pos) +{ + GtkWidget *button = gtk_toggle_button_new(); + WIDGET_ADD_CSS_CLASS(button, "flat"); + ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.varname, NULL, item->args.onchange, item->args.onchangedata); + headerbar_add(headerbar, box, button, pos); +} + +void ui_add_headerbar_menu( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarMenuItem *item, + UiObject *obj, + enum UiToolbarPos pos) +{ + GtkWidget *menubutton = gtk_menu_button_new(); + + if(item->args.label) { + gtk_menu_button_set_label(GTK_MENU_BUTTON(menubutton), item->args.label); + } + if(item->args.icon) { + gtk_menu_button_set_icon_name(GTK_MENU_BUTTON(menubutton), item->args.icon); + } + + GMenu *menu = g_menu_new(); + ui_gmenu_add_menu_items(menu, 0, &item->menu, obj); + + gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(menubutton), G_MENU_MODEL(menu)); + + headerbar_add(headerbar, box, menubutton, pos); +} + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/gtk/headerbar.h Sun Sep 29 13:32:51 2024 +0200 @@ -0,0 +1,75 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HEADERBAR_H +#define HEADERBAR_H + +#include "toolkit.h" +#include "../ui/toolbar.h" +#include "../common/toolbar.h" +#include <cx/list.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#if GTK_MAJOR_VERSION >= 3 + +void ui_fill_headerbar(UiObject *obj, GtkWidget *headerbar); + +void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos); + +void ui_add_headerbar_item( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarItem *item, + UiObject *obj, + enum UiToolbarPos pos); + +void ui_add_headerbar_toggleitem( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarToggleItem *item, + UiObject *obj, + enum UiToolbarPos pos); + +void ui_add_headerbar_menu( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarMenuItem *item, + UiObject *obj, + enum UiToolbarPos pos); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HEADERBAR_H */ +
--- a/ui/gtk/image.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/image.c Sun Sep 29 13:32:51 2024 +0200 @@ -39,10 +39,16 @@ static GtkIconTheme *icon_theme; +#if GTK_MAJOR_VERSION >= 4 +#define ICONTHEME_GET_DEFAULT() gtk_icon_theme_get_for_display(gdk_display_get_default()) +#else +#define ICONTHEME_GET_DEFAULT() gtk_icon_theme_get_default() +#endif + void ui_image_init(void) { image_map = cxHashMapCreateSimple(CX_STORE_POINTERS); - icon_theme = gtk_icon_theme_get_default(); + icon_theme = ICONTHEME_GET_DEFAULT(); } // **** deprecated functions **** @@ -62,7 +68,9 @@ // **** deprecated2**** static UiIcon* get_icon(const char *name, int size, int scale) { -#ifdef UI_SUPPORTS_SCALE +#if GTK_MAJOR_VERSION >= 4 + GtkIconPaintable *info = gtk_icon_theme_lookup_icon(icon_theme, name, NULL, size, scale, GTK_TEXT_DIR_LTR, GTK_ICON_LOOKUP_FORCE_REGULAR); +#elif defined(UI_SUPPORTS_SCALE) GtkIconInfo *info = gtk_icon_theme_lookup_icon_for_scale(icon_theme, name, size, scale, 0); #else GtkIconInfo *info = gtk_icon_theme_lookup_icon(icon_theme, name, size, 0); @@ -116,6 +124,16 @@ return get_icon(name, size, 1); } +#if GTK_MAJOR_VERSION >= 4 +GdkPixbuf* ui_icon_pixbuf(UiIcon *icon) { + if(!icon->pixbuf) { + GFile *file = gtk_icon_paintable_get_file(icon->info); + GError *error = NULL; + icon->pixbuf = gdk_pixbuf_new_from_file(g_file_get_path(file), &error); + } + return icon->pixbuf; +} +#else GdkPixbuf* ui_icon_pixbuf(UiIcon *icon) { if(!icon->pixbuf) { GError *error = NULL; @@ -123,7 +141,9 @@ } return icon->pixbuf; } +#endif +/* UiImage* ui_icon_image(UiIcon *icon) { GError *error = NULL; GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); @@ -134,6 +154,7 @@ } return NULL; } +*/ /* UiImage* ui_image(const char *filename) {
--- a/ui/gtk/image.h Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/image.h Sun Sep 29 13:32:51 2024 +0200 @@ -41,7 +41,11 @@ struct UiIcon { +#if GTK_MAJOR_VERSION >= 4 + GtkIconPaintable *info; +#else GtkIconInfo *info; +#endif GdkPixbuf *pixbuf; };
--- a/ui/gtk/menu.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/menu.c Sun Sep 29 13:32:51 2024 +0200 @@ -42,8 +42,7 @@ #include <cx/linked_list.h> #include <cx/array_list.h> -#if UI_GTK2 || UI_GTK3 - +#if GTK_MAJOR_VERSION <= 3 static ui_menu_add_f createMenuItem[] = { /* UI_MENU */ add_menu_widget, @@ -501,4 +500,194 @@ } } -#endif /* UI_GTK2 || UI_GTK3 */ +#endif /* GTK_MAJOR_VERSION <= 3 */ + + + +#if GTK_MAJOR_VERSION >= 4 + + + +static ui_gmenu_add_f createMenuItem[] = { + /* UI_MENU */ ui_gmenu_add_menu, + /* UI_MENU_ITEM */ ui_gmenu_add_menuitem, + /* UI_MENU_CHECK_ITEM */ ui_gmenu_add_checkitem, + /* UI_MENU_RADIO_ITEM */ ui_gmenu_add_radioitem, + /* UI_MENU_ITEM_LIST */ ui_gmenu_add_menuitem_list, + /* UI_MENU_CHECKITEM_LIST */ ui_gmenu_add_menuitem_list, + /* UI_MENU_RADIOITEM_LIST */ ui_gmenu_add_menuitem_list, + /* UI_MENU_SEPARATOR */ ui_gmenu_add_menuseparator +}; + +void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj) { + UiMenuItemI *it = menu->items_begin; + int index = 0; + while(it) { + createMenuItem[it->type](parent, index, it, obj); + it = it->next; + index++; + } +} + +void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) { + UiMenu *mi = (UiMenu*)item; + GMenu *menu = g_menu_new(); + ui_gmenu_add_menu_items(menu, 0, mi, obj); + g_menu_append_submenu(parent, mi->label, G_MENU_MODEL(menu)); +} + +void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) { + UiMenuItem *i = (UiMenuItem*)item; + + GSimpleAction *action = g_simple_action_new(item->id, NULL); + g_action_map_add_action(obj->ctx->action_map, G_ACTION(action)); + + if(i->callback != NULL) { + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = i->userdata; + event->callback = i->callback; + event->value = 0; + event->customdata = NULL; + + g_signal_connect( + action, + "activate", + G_CALLBACK(ui_activate_event_wrapper), + event); + g_signal_connect( + obj->widget, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + } + + char action_name[32]; + snprintf(action_name, 32, "win.%s", item->id); + g_menu_append(parent, i->label, action_name); +} + +void ui_gmenu_add_menuseparator(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + +} + +void ui_gmenu_add_checkitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + UiMenuCheckItem *checkitem = (UiMenuCheckItem*)item; + + // TODO +} + +void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + +} + +void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + UiMenuItemList *il = (UiMenuItemList*)item; + + const CxAllocator *a = obj->ctx->allocator; + + UiActiveGMenuItemList *ls = cxMalloc( + a, + sizeof(UiActiveGMenuItemList)); + + ls->object = obj; + ls->menu = p; + ls->index = index; + ls->oldcount = 0; + ls->getvalue = il->getvalue; + + UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); + ls->var = var; + UiList *list = var->value; + + ls->callback = il->callback; + ls->userdata = il->userdata; + + list->observers = ui_add_observer( + list->observers, + (ui_callback)ui_update_gmenu_item_list, + ls); + + GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i")); + g_action_map_add_action(obj->ctx->action_map, G_ACTION(action)); + snprintf(ls->action, 32, "win.%s", item->id); + + + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = il->userdata; + event->callback = il->callback; + event->customdata = var; + event->value = 0; + + g_signal_connect( + action, + "activate", + G_CALLBACK(ui_menu_list_item_activate_event_wrapper), + event); + g_signal_connect( + obj->widget, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + + ui_update_gmenu_item_list(NULL, ls); +} + +void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) { + int intval = event->value; + if(parameter && g_variant_is_of_type(parameter, G_VARIANT_TYPE_INT32)) { + intval = g_variant_get_int32(parameter); + } + + UiEvent evt; + evt.obj = event->obj; + evt.window = event->obj->window; + evt.document = event->obj->ctx->document; + evt.eventdata = event->customdata; + evt.intval = intval; + event->callback(&evt, event->userdata); +} + +void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) { + int index = g_variant_get_int32(parameter); + UiVar *var = event->customdata; + UiList *list = var->value; + + UiEvent evt; + evt.obj = event->obj; + evt.window = event->obj->window; + evt.document = event->obj->ctx->document; + evt.eventdata = ui_list_get(list, index); + evt.intval = index; + event->callback(&evt, event->userdata); + +} + +void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list) { + // remove old items + for(int i=0;i<list->oldcount;i++) { + g_menu_remove(list->menu, list->index); + } + UiList *ls = list->var->value; + + // add list items + ui_getvaluefunc getvalue = list->getvalue; + int i = 0; + void* elm = ui_list_first(ls); + while(elm) { + char *label = (char*) (getvalue ? getvalue(elm, 0) : elm); + + GMenuItem *item = g_menu_item_new(label, NULL); + GVariant *v = g_variant_new("i", i); + g_menu_item_set_action_and_target_value(item, list->action, v); + g_menu_insert_item(list->menu, list->index+i, item); + + elm = ui_list_next(ls); + i++; + } + + list->oldcount = i; +} + +#endif
--- a/ui/gtk/menu.h Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/menu.h Sun Sep 29 13:32:51 2024 +0200 @@ -34,10 +34,12 @@ #include <cx/list.h> #include "toolkit.h" + #ifdef __cplusplus extern "C" { #endif +#if GTK_MAJOR_VERSION <= 3 typedef struct UiActiveMenuItemList UiActiveMenuItemList; @@ -73,6 +75,41 @@ int64_t ui_checkitem_get(UiInteger *i); void ui_checkitem_set(UiInteger *i, int64_t value); +#endif /* GTK_MAJOR_VERSION <= 3 */ + +#if GTK_MAJOR_VERSION >= 4 + +typedef void(*ui_gmenu_add_f)(GMenu *, int, UiMenuItemI*, UiObject*); + +typedef struct UiActiveGMenuItemList UiActiveGMenuItemList; +struct UiActiveGMenuItemList { + UiObject *object; + GMenu *menu; + char action[32]; + int index; + int oldcount; + UiVar *var; + ui_getvaluefunc getvalue; + ui_callback callback; + void *userdata; +}; + +void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj); + +void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_menuseparator(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_checkitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); + +void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event); +void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event); +void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list); + +#endif + + #ifdef __cplusplus } #endif
--- a/ui/gtk/objs.mk Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/objs.mk Sun Sep 29 13:32:51 2024 +0200 @@ -44,6 +44,7 @@ GTKOBJ += range.o GTKOBJ += entry.o GTKOBJ += dnd.o +GTKOBJ += headerbar.o TOOLKITOBJS += $(GTKOBJ:%=$(GTK_OBJPRE)%) TOOLKITSOURCE += $(GTKOBJ:%.o=gtk/%.c)
--- a/ui/gtk/range.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/range.c Sun Sep 29 13:32:51 2024 +0200 @@ -36,7 +36,7 @@ static UIWIDGET ui_scrollbar(UiObject *obj, UiOrientation orientation, UiRange *range, ui_callback f, void *userdata) { -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 GtkWidget *scrollbar = gtk_scrollbar_new(orientation == UI_HORIZONTAL ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, NULL); #else GtkWidget *scrollbar; @@ -124,7 +124,7 @@ #else gtk_adjustment_set_page_size(a, extent); #endif -#if !(GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 18) +#if GTK_MAJOR_VERSION * 100 + GTK_MIMOR_VERSION < 318 gtk_adjustment_changed(a); #endif range->extent = extent;
--- a/ui/gtk/text.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/text.c Sun Sep 29 13:32:51 2024 +0200 @@ -82,17 +82,17 @@ G_CALLBACK(ui_textarea_destroy), uitext); - GtkWidget *scroll_area = gtk_scrolled_window_new (NULL, NULL); + GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scroll_area), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS - gtk_container_add(GTK_CONTAINER(scroll_area), text_area); + SCROLLEDWINDOW_SET_CHILD(scroll_area, text_area); // font and padding PangoFontDescription *font; font = pango_font_description_from_string("Monospace"); - gtk_widget_modify_font(text_area, font); + //gtk_widget_modify_font(text_area, font); // TODO pango_font_description_free(font); gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_area), 2); @@ -180,7 +180,7 @@ } UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea) { - return gtk_bin_get_child(GTK_BIN(textarea)); + return SCROLLEDWINDOW_GET_CHILD(textarea); } char* ui_textarea_get(UiText *text) { @@ -557,7 +557,10 @@ uitext); if(args.width > 0) { + // TODO: gtk4 +#if GTK_MAJOR_VERSION <= 3 gtk_entry_set_width_chars(GTK_ENTRY(textfield), args.width); +#endif } if(frameless) { // TODO: gtk2legacy workaroud @@ -573,7 +576,7 @@ if(var) { UiString *value = var->value; if(value->value.ptr) { - gtk_entry_set_text(GTK_ENTRY(textfield), value->value.ptr); + ENTRY_SET_TEXT(textfield, value->value.ptr); value->value.free(value->value.ptr); value->value.ptr = NULL; value->value.free = NULL; @@ -631,13 +634,13 @@ if(str->value.ptr) { str->value.free(str->value.ptr); } - str->value.ptr = g_strdup(gtk_entry_get_text(str->obj)); + str->value.ptr = g_strdup(ENTRY_GET_TEXT(str->obj)); str->value.free = (ui_freefunc)g_free; return str->value.ptr; } void ui_textfield_set(UiString *str, const char *value) { - gtk_entry_set_text(str->obj, value); + ENTRY_SET_TEXT(str->obj, value); if(str->value.ptr) { str->value.free(str->value.ptr); str->value.ptr = NULL; @@ -688,17 +691,6 @@ return elms; } -static gboolean path_textfield_btn_pressed(GtkWidget *widget, GdkEventButton *event, UiPathTextField *pathtf) { - gtk_box_pack_start(GTK_BOX(pathtf->hbox), pathtf->entry, TRUE, TRUE, 0); - gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox); - pathtf->buttonbox = NULL; - - gtk_widget_show(pathtf->entry); - gtk_widget_grab_focus(pathtf->entry); - - return TRUE; -} - static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) { for(int i=0;i<nelm;i++) { free(elms[i].name); @@ -708,13 +700,77 @@ } static void ui_path_textfield_destroy(GtkWidget *object, UiPathTextField *pathtf) { - free(pathtf->current_path); + free(pathtf->hbox); g_object_unref(pathtf->entry); free(pathtf); } +void ui_path_button_clicked(GtkWidget *widget, UiEventData *event) { + UiPathElm *elm = event->customdata; + cxmutstr path = cx_strdup(cx_strn(elm->path, elm->path_len)); + UiEvent evt; + evt.obj = event->obj; + evt.window = evt.obj->window; + evt.document = evt.obj->ctx->document; + evt.eventdata = elm->path; + evt.intval = event->value; + event->callback(&evt, event->userdata); + free(path.ptr); +} + +int ui_pathtextfield_update(UiPathTextField* pathtf, const char *full_path) { + size_t full_path_len = strlen(full_path); + if(full_path_len == 0) { + return 1; + } + + size_t nelm = 0; + UiPathElm* path_elm = pathtf->getpathelm(full_path, full_path_len, &nelm, pathtf->getpathelmdata); + if (!path_elm) { + return 1; + } + + free(pathtf->current_path); + pathtf->current_path = strdup(full_path); + + ui_pathelm_destroy(pathtf->current_pathelms, pathtf->current_nelm); + pathtf->current_pathelms = path_elm; + pathtf->current_nelm = nelm; + + return ui_pathtextfield_update_widget(pathtf); +} + +static GtkWidget* ui_path_elm_button(UiPathTextField *pathtf, UiPathElm *elm, int i) { + cxmutstr name = cx_strdup(cx_strn(elm->name, elm->name_len)); + GtkWidget *button = gtk_button_new_with_label(name.ptr); + free(name.ptr); + + if(pathtf->onactivate) { + UiEventData *eventdata = malloc(sizeof(UiEventData)); + eventdata->callback = pathtf->onactivate; + eventdata->userdata = pathtf->onactivatedata; + eventdata->obj = pathtf->obj; + eventdata->customdata = elm; + eventdata->value = i; + + g_signal_connect( + button, + "clicked", + G_CALLBACK(ui_path_button_clicked), + eventdata); + + g_signal_connect( + button, + "destroy", + G_CALLBACK(ui_destroy_userdata), + eventdata); + } + + return button; +} + static void ui_path_textfield_activate(GtkWidget *entry, UiPathTextField *pathtf) { - const gchar *text = gtk_entry_get_text(GTK_ENTRY(pathtf->entry)); + const gchar *text = ENTRY_GET_TEXT(pathtf->entry); if(strlen(text) == 0) { return; } @@ -736,6 +792,168 @@ } } +#if GTK_MAJOR_VERSION >= 4 + +static void pathbar_show_hbox(GtkWidget *widget, UiPathTextField *pathtf) { + if(pathtf->current_path) { + gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->hbox); + ENTRY_SET_TEXT(pathtf->entry, pathtf->current_path); + } +} + +static gboolean ui_path_textfield_key_controller( + GtkEventControllerKey* self, + guint keyval, + guint keycode, + GdkModifierType state, + UiPathTextField *pathtf) +{ + if(keyval == GDK_KEY_Escape) { + pathbar_show_hbox(NULL, pathtf); + } + return FALSE; +} + +UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) { + UiObject* current = uic_current_obj(obj); + + UiPathTextField *pathtf = malloc(sizeof(UiPathTextField)); + memset(pathtf, 0, sizeof(UiPathTextField)); + pathtf->obj = obj; + pathtf->getpathelm = args.getpathelm; + pathtf->getpathelmdata = args.getpathelmdata; + pathtf->onactivate = args.onactivate; + pathtf->onactivatedata = args.onactivatedata; + pathtf->ondragcomplete = args.ondragcomplete; + pathtf->ondragcompletedata = args.ondragcompletedata; + pathtf->ondragstart = args.ondragstart; + pathtf->ondragstartdata = args.ondragstartdata; + pathtf->ondrop = args.ondrop; + pathtf->ondropdata = args.ondropsdata; + + if(!pathtf->getpathelm) { + pathtf->getpathelm = default_pathelm_func; + pathtf->getpathelmdata = NULL; + } + + pathtf->stack = gtk_stack_new(); + gtk_widget_set_name(pathtf->stack, "path-textfield-box"); + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, pathtf->stack, FALSE); + + pathtf->entry_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + pathtf->entry = gtk_entry_new(); + gtk_box_append(GTK_BOX(pathtf->entry_box), pathtf->entry); + gtk_widget_set_hexpand(pathtf->entry, TRUE); + + GtkWidget *cancel_button = gtk_button_new_from_icon_name("window-close-symbolic"); + gtk_widget_add_css_class(cancel_button, "flat"); + gtk_widget_add_css_class(cancel_button, "pathbar-extra-button"); + gtk_box_append(GTK_BOX(pathtf->entry_box), cancel_button); + g_signal_connect( + cancel_button, + "clicked", + G_CALLBACK(pathbar_show_hbox), + pathtf); + + gtk_stack_add_child(GTK_STACK(pathtf->stack), pathtf->entry_box); + g_object_ref(pathtf->entry); // for compatibility with older pathbar version + g_signal_connect( + pathtf->entry, + "activate", + G_CALLBACK(ui_path_textfield_activate), + pathtf); + + GtkEventController *entry_cancel = gtk_event_controller_key_new(); + gtk_widget_add_controller(pathtf->entry, entry_cancel); + g_signal_connect(entry_cancel, "key-pressed", G_CALLBACK(ui_path_textfield_key_controller), pathtf); + + gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->entry_box); + + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); + if (var) { + UiString* value = (UiString*)var->value; + value->obj = pathtf; + value->get = ui_path_textfield_get; + value->set = ui_path_textfield_set; + + if(value->value.ptr) { + char *str = strdup(value->value.ptr); + ui_string_set(value, str); + free(str); + } + } + + return pathtf->stack; +} + +static void pathbar_pressed( + GtkGestureClick* self, + gint n_press, + gdouble x, + gdouble y, + UiPathTextField *pathtf) +{ + gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->entry_box); + gtk_widget_grab_focus(pathtf->entry); +} + +int ui_pathtextfield_update_widget(UiPathTextField* pathtf) { + // recreate button hbox + if(pathtf->hbox) { + gtk_stack_remove(GTK_STACK(pathtf->stack), pathtf->hbox); + } + pathtf->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_set_homogeneous(GTK_BOX(pathtf->hbox), FALSE); + gtk_stack_add_child(GTK_STACK(pathtf->stack), pathtf->hbox); + gtk_widget_set_name(pathtf->hbox, "pathbar"); + + // add buttons for path elements + for (int i=0;i<pathtf->current_nelm;i++) { + UiPathElm *elm = &pathtf->current_pathelms[i]; + + GtkWidget *button = ui_path_elm_button(pathtf, elm, i); + gtk_widget_add_css_class(button, "flat"); + + gtk_box_append(GTK_BOX(pathtf->hbox), button); + + if(i+1 < pathtf->current_nelm && cx_strcmp(cx_strn(elm->name, elm->name_len), CX_STR("/"))) { + gtk_box_append(GTK_BOX(pathtf->hbox), gtk_label_new("/")); + } + } + gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->hbox); + + // create a widget for receiving button press events + GtkWidget *event_area = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + GtkGesture *handler = gtk_gesture_click_new(); + gtk_widget_add_controller(event_area, GTK_EVENT_CONTROLLER(handler)); + g_signal_connect( + handler, + "pressed", + G_CALLBACK(pathbar_pressed), + pathtf); + gtk_widget_set_hexpand(event_area, TRUE); + gtk_widget_set_vexpand(event_area, TRUE); + gtk_box_append(GTK_BOX(pathtf->hbox), event_area); + + return 0; +} + +#else + +static gboolean path_textfield_btn_pressed(GtkWidget *widget, GdkEventButton *event, UiPathTextField *pathtf) { + gtk_box_pack_start(GTK_BOX(pathtf->hbox), pathtf->entry, TRUE, TRUE, 0); + gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox); + pathtf->buttonbox = NULL; + + gtk_widget_show(pathtf->entry); + gtk_widget_grab_focus(pathtf->entry); + + return TRUE; +} + static gboolean ui_path_textfield_key_press(GtkWidget *self, GdkEventKey *event, UiPathTextField *pathtf) { if (event->keyval == GDK_KEY_Escape) { // reset GtkEntry value @@ -833,38 +1051,7 @@ return hbox; } -void ui_path_button_clicked(GtkWidget *widget, UiEventData *event) { - UiPathElm *elm = event->customdata; - cxmutstr path = cx_strdup(cx_strn(elm->path, elm->path_len)); - UiEvent evt; - evt.obj = event->obj; - evt.window = evt.obj->window; - evt.document = evt.obj->ctx->document; - evt.eventdata = elm->path; - evt.intval = event->value; - event->callback(&evt, event->userdata); - free(path.ptr); -} - -int ui_pathtextfield_update(UiPathTextField* pathtf, const char *full_path) { - size_t full_path_len = strlen(full_path); - if(full_path_len == 0) { - return 1; - } - - size_t nelm = 0; - UiPathElm* path_elm = pathtf->getpathelm(full_path, full_path_len, &nelm, pathtf->getpathelmdata); - if (!path_elm) { - return 1; - } - - free(pathtf->current_path); - pathtf->current_path = strdup(full_path); - - ui_pathelm_destroy(pathtf->current_pathelms, pathtf->current_nelm); - pathtf->current_pathelms = path_elm; - pathtf->current_nelm = nelm; - +int ui_pathtextfield_update_widget(UiPathTextField* pathtf) { GtkWidget *buttonbox = create_path_button_box(); // switch from entry to buttonbox or remove current buttonbox @@ -876,34 +1063,9 @@ gtk_box_pack_start(GTK_BOX(pathtf->hbox), buttonbox, FALSE, FALSE, 0); pathtf->buttonbox = buttonbox; - for (int i=0;i<nelm;i++) { - UiPathElm *elm = &path_elm[i]; - - cxmutstr name = cx_strdup(cx_strn(elm->name, elm->name_len)); - GtkWidget *button = gtk_button_new_with_label(name.ptr); - free(name.ptr); - - if(pathtf->onactivate) { - UiEventData *eventdata = malloc(sizeof(UiEventData)); - eventdata->callback = pathtf->onactivate; - eventdata->userdata = pathtf->onactivatedata; - eventdata->obj = pathtf->obj; - eventdata->customdata = elm; - eventdata->value = i; - - g_signal_connect( - button, - "clicked", - G_CALLBACK(ui_path_button_clicked), - eventdata); - - g_signal_connect( - button, - "destroy", - G_CALLBACK(ui_destroy_userdata), - eventdata); - } - + for (int i=0;i<pathtf->current_nelm;i++) { + UiPathElm *elm = &pathtf->current_pathelms[i]; + GtkWidget *button = ui_path_elm_button(pathtf, elm, i); gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 0); } @@ -912,19 +1074,21 @@ return 0; } +#endif + char* ui_path_textfield_get(UiString *str) { if(str->value.ptr) { str->value.free(str->value.ptr); } UiPathTextField *tf = str->obj; - str->value.ptr = g_strdup(gtk_entry_get_text(GTK_ENTRY(tf->entry))); + str->value.ptr = g_strdup(ENTRY_GET_TEXT(tf->entry)); str->value.free = (ui_freefunc)g_free; return str->value.ptr; } void ui_path_textfield_set(UiString *str, const char *value) { UiPathTextField *tf = str->obj; - gtk_entry_set_text(GTK_ENTRY(tf->entry), value); + ENTRY_SET_TEXT(tf->entry, value); ui_pathtextfield_update(tf, value); if(str->value.ptr) { str->value.free(str->value.ptr);
--- a/ui/gtk/text.h Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/text.h Sun Sep 29 13:32:51 2024 +0200 @@ -75,9 +75,13 @@ typedef struct UiPathTextField { UiObject *obj; + GtkWidget *stack; GtkWidget *hbox; + GtkWidget *entry_box; GtkWidget *entry; +#if GTK_MAJOR_VERSION == 3 GtkWidget *buttonbox; +#endif char *current_path; UiPathElm *current_pathelms; @@ -136,6 +140,7 @@ void ui_textfield_set(UiString *str, const char *value); int ui_pathtextfield_update(UiPathTextField* pathtf, const char *full_path); +int ui_pathtextfield_update_widget(UiPathTextField* pathtf); char* ui_path_textfield_get(UiString *str); void ui_path_textfield_set(UiString *str, const char *value);
--- a/ui/gtk/toolbar.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/toolbar.c Sun Sep 29 13:32:51 2024 +0200 @@ -361,9 +361,9 @@ CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); - ui_headerbar_add_items(obj, headerbar, items, left_defaults); - ui_headerbar_add_items(obj, headerbar, items, center_defaults); - ui_headerbar_add_items(obj, headerbar, items, right_defaults); + ui_toolbar_headerbar_add_items(obj, headerbar, items, left_defaults); + ui_toolbar_headerbar_add_items(obj, headerbar, items, center_defaults); + ui_toolbar_headerbar_add_items(obj, headerbar, items, right_defaults); return headerbar; } @@ -388,7 +388,7 @@ } -void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxMap *items, CxList *defaults) { +void ui_toolbar_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxMap *items, CxList *defaults) { // add pre-configured items CxIterator i = cxListIterator(defaults); cx_foreach(char*, def, i) {
--- a/ui/gtk/toolbar.h Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/toolbar.h Sun Sep 29 13:32:51 2024 +0200 @@ -127,7 +127,7 @@ GtkWidget* ui_create_headerbar(UiObject *obj); -void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxMap *items, CxList *defaults); +void ui_toolbar_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxMap *items, CxList *defaults); void add_headerbar_item_widget(GtkHeaderBar *hb, UiToolbarItem *item, UiObject *obj); void add_headerbar_item_toggle_widget(GtkHeaderBar *hb, UiToolbarToggleItem *item, UiObject *obj);
--- a/ui/gtk/toolkit.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/toolkit.c Sun Sep 29 13:32:51 2024 +0200 @@ -46,8 +46,8 @@ #include <pthread.h> -#ifndef UI_GTK2 -static GtkApplication *app; +#ifdef UI_APPLICATION +UI_APPLICATION app; #endif static const char *application_name; @@ -67,22 +67,24 @@ static int scale_factor = 1; UIEXPORT void ui_init(const char *appname, int argc, char **argv) { + application_name = appname; uic_init_global_context(); +#if GTK_MAJOR_VERSION >= 4 + gtk_init(); +#else gtk_init(&argc, &argv); - application_name = appname; +#endif ui_css_init(); - uic_docmgr_init(); - uic_toolbar_init(); - ui_image_init(); - uic_load_app_properties(); -#ifdef UI_SUPPORTS_SCALE +#if GTK_MAJOR_VERSION >= 4 + scale_factor = 1; // TODO +#elif defined(UI_SUPPORTS_SCALE) scale_factor = gdk_monitor_get_scale_factor( gdk_display_get_primary_monitor(gdk_display_get_default())); #endif @@ -121,14 +123,11 @@ #endif void ui_main() { -#ifndef UI_GTK2 +#ifdef UI_APPLICATION cxmutstr appid = cx_asprintf( "ui.%s", application_name ? application_name : "application1"); - - app = gtk_application_new( - appid.ptr, - G_APPLICATION_FLAGS_NONE); + app = UI_APPLICATION_NEW(appid.ptr); g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_application_run(G_APPLICATION (app), 0, NULL); @@ -153,17 +152,22 @@ } GtkApplication* ui_get_application() { - return app; + return GTK_APPLICATION(app); } #endif void ui_show(UiObject *obj) { uic_check_group_widgets(obj->ctx); +#if GTK_MAJOR_VERSION >= 4 + gtk_window_present(GTK_WINDOW(obj->widget)); +#elif GTK_MAJOR_VERSION <= 3 gtk_widget_show_all(obj->widget); +#endif } void ui_close(UiObject *obj) { - gtk_widget_destroy(obj->widget); + // TODO + //gtk_widget_destroy(obj->widget); } @@ -226,24 +230,38 @@ } void ui_set_show_all(UIWIDGET widget, int value) { + // TODO: gtk4 +#if GTK_MAJOR_VERSION <= 3 gtk_widget_set_no_show_all(widget, !value); +#endif } void ui_set_visible(UIWIDGET widget, int visible) { + // TODO: gtk4 +#if GTK_MAJOR_VERSION <= 3 if(visible) { gtk_widget_set_no_show_all(widget, FALSE); gtk_widget_show_all(widget); } else { gtk_widget_hide(widget); } +#endif } void ui_clipboard_set(char *str) { +#if GTK_MAJOR_VERSION >= 4 + // TODO: gtk4: needs widget +#else GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text(cb, str, strlen(str)); +#endif } char* ui_clipboard_get() { +#if GTK_MAJOR_VERSION >= 4 + // TODO: gtk4: needs widget + return NULL; +#else GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); char *str = gtk_clipboard_wait_for_text(cb); if(str) { @@ -253,6 +271,7 @@ } else { return NULL; } +#endif } int ui_get_scalefactor() { @@ -295,12 +314,44 @@ static GtkCssProvider* ui_gtk_css_provider; +#if GTK_MAJOR_VERSION == 4 +static const char *ui_gtk_css = +"#path-textfield-box {\n" +" background-color: alpha(currentColor, 0.1);" +" border-radius: 6px;" +" padding: 0px;" +"}\n" +".pathbar-extra-button {\n" +" border-top-right-radius: 6px;" +" border-bottom-right-radius: 6px;" +" border-top-left-radius: 0px;" +" border-bottom-left-radius: 0px;" +"}\n" +"#pathbar button {\n" +" margin: 3px;" +" border-radius: 4px;" +" padding-top: 0px;" +" padding-bottom: 0px;" +" padding-left: 8px;" +" padding-right: 8px;" +"}\n" +"#path-textfield-box entry {\n" +" background-color: #00000000;" +" border-top-left-radius: 6px;" +" border-bottom-left-radius: 6px;" +" border-top-right-radius: 0px;" +" border-bottom-right-radius: 0px;" +"}" +; + +#elif GTK_MAJOR_VERSION == 3 static const char *ui_gtk_css = "#path-textfield-box {" " background-color: @theme_base_color;" " border-radius: 5px;" " padding: 0px;" "}"; +#endif void ui_css_init(void) { ui_gtk_css_provider = gtk_css_provider_new(); @@ -325,7 +376,7 @@ #endif /* GTK_MINOR_VERSION < 12 */ GdkDisplay *display = gdk_display_get_default(); - gtk_style_context_add_provider_for_display(display, GTK_STYLE_PROVIDER(ui_gtk_css_provider), GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_provider_for_display(display, GTK_STYLE_PROVIDER(ui_gtk_css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); #endif /* UI_GTK4 */ }
--- a/ui/gtk/toolkit.h Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/toolkit.h Sun Sep 29 13:32:51 2024 +0200 @@ -39,6 +39,71 @@ #pragma clang diagnostic ignored "-Wdeprecated-declarations" + +#if GLIB_MAJOR_VERSION * 1000 + GLIB_MINOR_VERSION > 74 +#define UI_G_APPLICATION_FLAGS G_APPLICATION_DEFAULT_FLAGS +#else +#define UI_G_APPLICATION_FLAGS G_APPLICATION_FLAGS_NONE +#endif + +#ifdef UI_LIBADWAITA +#define UI_APPLICATION AdwApplication* +#define UI_APPLICATION_NEW(id) adw_application_new(id, UI_G_APPLICATION_FLAGS) +#elif GTK_MAJOR_VERSION >= 3 +#define UI_APPLICATION GtkApplication* +#define UI_APPLICATION_NEW(id) gtk_application_new(id, UI_G_APPLICATION_FLAGS) +#endif + +#if GTK_MAJOR_VERSION >= 4 +#define WINDOW_SHOW(window) gtk_window_present(GTK_WINDOW(window)) +#define WINDOW_DESTROY(window) gtk_window_destroy(GTK_WINDOW(window)) +#define WINDOW_SET_CONTENT(window, child) gtk_window_set_child(GTK_WINDOW(window), child) +#define BOX_ADD(box, child) gtk_box_append(GTK_BOX(box), child) +#define ENTRY_SET_TEXT(entry, text) gtk_editable_set_text(GTK_EDITABLE(entry), text) +#define ENTRY_GET_TEXT(entry) gtk_editable_get_text(GTK_EDITABLE(entry)) +#define SCROLLEDWINDOW_NEW() gtk_scrolled_window_new() +#define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), child) +#define SCROLLEDWINDOW_GET_CHILD(sw) gtk_scrolled_window_get_child(GTK_SCROLLED_WINDOW(sw)) +#define WIDGET_ADD_CSS_CLASS(w, cssclass) gtk_widget_add_css_class(w, cssclass) +#else +#define WINDOW_SHOW(window) gtk_widget_show_all(window) +#define WINDOW_DESTROY(window) gtk_widget_destroy(window) +#define WINDOW_SET_CONTENT(window, child) gtk_container_add(GTK_CONTAINER(window), child) +#define BOX_ADD(box, child) gtk_box_pack_end(GTK_BOX(box), child, TRUE, TRUE, 0) +#define ENTRY_SET_TEXT(entry, text) gtk_entry_set_text(GTK_ENTRY(entry), text) +#define ENTRY_GET_TEXT(entry) gtk_entry_get_text(GTK_ENTRY(entry)) +#define SCROLLEDWINDOW_NEW() gtk_scrolled_window_new(NULL, NULL) +#define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_container_add(GTK_CONTAINER(sw), child) +#define SCROLLEDWINDOW_GET_CHILD(sw) gtk_bin_get_child(GTK_BIN(sw)) +#define WIDGET_ADD_CSS_CLASS(w, cssclass) gtk_style_context_add_class(gtk_widget_get_style_context(w), cssclass) +#endif + +#ifdef UI_GTK2 +#undef SCROLLEDWINDOW_SET_CHILD +#define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), child) +#endif + +#if GTK_MAJOR_VERSION >= 4 +#define UI_GTK_SINCE_V4(st) st +#define UI_GTK_SINCE_V3(st) +#define UI_GTK_V2(st) +#define UI_GTK_V3(st) +#define UI_GTK_V4(st) st +#elif GTK_MAJOR_VERSION >= 3 +#define UI_GTK_SINCE_V4(st) st +#define UI_GTK_SINCE_V3(st) st +#define UI_GTK_V2(st) +#define UI_GTK_V3(st) st +#define UI_GTK_V4(st) +#else +#define UI_GTK_SINCE_V4(st) +#define UI_GTK_SINCE_V3(st) +#define UI_GTK_V2(st) st +#define UI_GTK_V3(st) +#define UI_GTK_V4(st) +#endif + + typedef struct UiEventData { UiObject *obj; ui_callback callback; @@ -64,7 +129,7 @@ typedef enum UiOrientation UiOrientation; enum UiOrientation { UI_HORIZONTAL = 0, UI_VERTICAL }; -#ifndef UI_GTK2 +#ifdef UI_APPLICATION void ui_app_quit(); GtkApplication* ui_get_application(); #endif
--- a/ui/gtk/tree.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/tree.c Sun Sep 29 13:32:51 2024 +0200 @@ -95,6 +95,9 @@ case UI_ICON: { g_value_init(&value, G_TYPE_OBJECT); UiIcon *icon = data; +#if GTK_MAJOR_VERSION >= 4 + g_value_set_object(&value, icon->info); // TODO: does this work? +#else if(!icon->pixbuf && icon->info) { GError *error = NULL; GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); @@ -104,14 +107,19 @@ if(icon->pixbuf) { g_value_set_object(&value, icon->pixbuf); } - - +#endif break; } case UI_ICON_TEXT: case UI_ICON_TEXT_FREE: { + UiIcon *icon = data; +#if GTK_MAJOR_VERSION >= 4 + GValue iconvalue = G_VALUE_INIT; + g_value_init(&iconvalue, G_TYPE_OBJECT); + g_value_set_object(&iconvalue, ui_icon_pixbuf(icon)); + gtk_list_store_set_value(store, &iter, c, &iconvalue); +#else GValue pixbufvalue = G_VALUE_INIT; - UiIcon *icon = data; if(!icon->pixbuf && icon->info) { GError *error = NULL; GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); @@ -120,6 +128,7 @@ g_value_init(&pixbufvalue, G_TYPE_OBJECT); g_value_set_object(&pixbufvalue, icon->pixbuf); gtk_list_store_set_value(store, &iter, c, &pixbufvalue); +#endif c++; char *str = model->getvalue(elm, c); @@ -205,12 +214,12 @@ } // add widget to the current container - GtkWidget *scroll_area = gtk_scrolled_window_new(NULL, NULL); + GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scroll_area), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS - gtk_container_add(GTK_CONTAINER(scroll_area), view); + SCROLLEDWINDOW_SET_CHILD(scroll_area, view); UI_APPLY_LAYOUT1(current, args); current->container->add(current->container, scroll_area, FALSE); @@ -222,6 +231,7 @@ return scroll_area; } +/* static void drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer udata) { printf("drag begin\n"); @@ -236,13 +246,16 @@ printf("drag end\n"); } +*/ +/* static GtkTargetEntry targetentries[] = { { "STRING", 0, 0 }, { "text/plain", 0, 1 }, { "text/uri-list", 0, 2 }, }; +*/ UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { UiObject* current = uic_current_obj(obj); @@ -356,12 +369,12 @@ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); // add widget to the current container - GtkWidget *scroll_area = gtk_scrolled_window_new(NULL, NULL); + GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scroll_area), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS - gtk_container_add(GTK_CONTAINER(scroll_area), view); + SCROLLEDWINDOW_SET_CHILD(scroll_area, view); UI_APPLY_LAYOUT1(current, args); current->container->add(current->container, scroll_area, FALSE); @@ -375,11 +388,7 @@ GtkWidget* ui_get_tree_widget(UIWIDGET widget) { - GList *c = gtk_container_get_children(GTK_CONTAINER(widget)); - if(c) { - return c->data; - } - return NULL; + return SCROLLEDWINDOW_GET_CHILD(widget); } static char** targets2array(char *target0, va_list ap, int *nelm) { @@ -402,6 +411,7 @@ return targets; } +/* static GtkTargetEntry* targetstr2gtktargets(char **str, int nelm) { GtkTargetEntry *targets = calloc(nelm, sizeof(GtkTargetEntry)); for(int i=0;i<nelm;i++) { @@ -409,6 +419,7 @@ } return targets; } +*/ void ui_table_dragsource(UIWIDGET tablewidget, int actions, char *target0, ...) { va_list ap;
--- a/ui/gtk/window.c Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/gtk/window.c Sun Sep 29 13:32:51 2024 +0200 @@ -41,6 +41,7 @@ #include "menu.h" #include "toolbar.h" #include "container.h" +#include "headerbar.h" static int nwindows = 0; @@ -72,8 +73,10 @@ static UiObject* create_window(const char *title, void *window_data, UiBool simple) { CxMempool *mp = cxBasicMempoolCreate(256); UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); - -#ifndef UI_GTK2 + +#ifdef UI_LIBADWAITA + obj->widget = adw_application_window_new(ui_get_application()); +#elif !defined(UI_GTK2) obj->widget = gtk_application_window_new(ui_get_application()); #else obj->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -83,6 +86,10 @@ obj->ctx = uic_context(obj, mp); obj->window = window_data; +#if GTK_CHECK_VERSION(4, 0, 0) + obj->ctx->action_map = G_ACTION_MAP(obj->widget); +#endif + if(title != NULL) { gtk_window_set_title(GTK_WINDOW(obj->widget), title); } @@ -108,6 +115,18 @@ obj); GtkWidget *vbox = ui_gtk_vbox_new(0); +#ifdef UI_LIBADWAITA + GtkWidget *toolbar_view = adw_toolbar_view_new(); + adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), toolbar_view); + adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(toolbar_view), vbox); + + GtkWidget *headerbar = adw_header_bar_new(); + adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar); + + ui_fill_headerbar(obj, headerbar); +#elif GTK_MAJOR_VERSION >= 4 + WINDOW_SET_CONTENT(obj->widget, vbox); +#else gtk_container_add(GTK_CONTAINER(obj->widget), vbox); if(!simple) { @@ -130,9 +149,12 @@ //GtkWidget *hb = ui_create_headerbar(obj); //gtk_window_set_titlebar(GTK_WINDOW(obj->widget), hb); } +#endif // window content // the content has a (TODO: not yet) configurable frame + // TODO: really? why + /* GtkWidget *frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); @@ -141,6 +163,10 @@ GtkWidget *content_box = ui_gtk_vbox_new(0); gtk_container_add(GTK_CONTAINER(frame), content_box); obj->container = ui_box_container(obj, content_box); + */ + GtkWidget *content_box = ui_gtk_vbox_new(0); + BOX_ADD(GTK_BOX(vbox), content_box); + obj->container = ui_box_container(obj, content_box, UI_CONTAINER_VBOX); nwindows++; return obj; @@ -173,7 +199,7 @@ if(data->customdata) { GtkWidget *entry = data->customdata; - evt.eventdata = (void*)gtk_entry_get_text(GTK_ENTRY(entry)); + evt.eventdata = (void*)ENTRY_GET_TEXT(GTK_ENTRY(entry)); } @@ -186,7 +212,7 @@ data->callback(&evt, data->userdata); } - gtk_widget_destroy(GTK_WIDGET(self)); + WINDOW_DESTROY(GTK_WIDGET(self)); } void ui_dialog_create(UiObject *parent, UiDialogArgs args) { @@ -208,13 +234,13 @@ GtkWidget *content_area = gtk_dialog_get_content_area(dialog); if(args.content) { GtkWidget *label = gtk_label_new(args.content); - gtk_container_add(GTK_CONTAINER(content_area), label); + BOX_ADD(content_area, label); } GtkWidget *textfield = NULL; if(args.input) { textfield = gtk_entry_new(); - gtk_container_add(GTK_CONTAINER(content_area), textfield); + BOX_ADD(content_area, textfield); } UiEventData *event = malloc(sizeof(UiEventData)); @@ -229,9 +255,15 @@ G_CALLBACK(ui_dialog_response), event); - gtk_widget_show_all(GTK_WIDGET(dialog_w)); + WINDOW_SHOW(GTK_WIDGET(dialog_w)); } + +#if GTK_MAJOR_VERSION >= 4 +static void ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { + // TODO +} +#else static void ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { char *button; char *title; @@ -272,7 +304,7 @@ if((mode & UI_FILEDIALOG_SELECT_MULTI) == UI_FILEDIALOG_SELECT_MULTI) { gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); } - + UiEvent evt; evt.obj = obj; evt.document = evt.obj->ctx->document; @@ -310,6 +342,7 @@ gtk_widget_destroy(dialog); } +#endif void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { ui_gtkfilechooser(obj, GTK_FILE_CHOOSER_ACTION_OPEN, mode, file_selected_callback, cbdata);
--- a/ui/ui/icons.h Mon Jun 17 21:20:58 2024 +0200 +++ b/ui/ui/icons.h Sun Sep 29 13:32:51 2024 +0200 @@ -35,7 +35,7 @@ extern "C" { #endif -#ifdef UI_GTK3 +#ifdef UI_GTK #define UI_ICON_HOME "go-home" #define UI_ICON_NEW_WINDOW "list-add" @@ -50,7 +50,7 @@ #define UI_ICON_GO_BACK "go-previous" #define UI_ICON_GO_FORWARD "go-next" -#endif /* UI_GTK3 */ +#endif /* UI_GTK */