Tue, 08 Oct 2024 22:19:24 +0200
add tabview value/varname parameter
/* * 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 <stdio.h> #include <stdlib.h> #include <limits.h> #include "container.h" #include "toolkit.h" #include "../common/context.h" #include "../common/object.h" void ui_container_begin_close(UiObject *obj) { UiContainer *ct = uic_get_current_container(obj); ct->close = 1; } int ui_container_finish(UiObject *obj) { UiContainer *ct = uic_get_current_container(obj); if(ct->close) { ui_end(obj); return 0; } return 1; } GtkWidget* ui_gtk_vbox_new(int spacing) { #if GTK_MAJOR_VERSION >= 3 return gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing); #else return gtk_vbox_new(FALSE, spacing); #endif } GtkWidget* ui_gtk_hbox_new(int spacing) { #if GTK_MAJOR_VERSION >= 3 return gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing); #else return gtk_hbox_new(FALSE, spacing); #endif } /* -------------------- Box Container -------------------- */ 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; } void ui_box_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { UiBoxContainer *bc = (UiBoxContainer*)ct; if(ct->layout.fill != UI_LAYOUT_UNDEFINED) { fill = ui_lb2bool(ct->layout.fill); } if(bc->has_fill && fill) { fprintf(stderr, "UiError: container has 2 filled widgets"); fill = FALSE; } if(fill) { bc->has_fill = TRUE; } 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; } UiContainer* ui_grid_container(UiObject *obj, GtkWidget *grid) { UiGridContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiGridContainer)); ct->container.widget = grid; ct->container.add = ui_grid_container_add; UI_GTK_V2(ct->width = 0); UI_GTK_V2(ct->height = 1); return (UiContainer*)ct; } #if GTK_MAJOR_VERSION >= 3 void ui_grid_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { UiGridContainer *grid = (UiGridContainer*)ct; if(ct->layout.newline) { grid->x = 0; grid->y++; ct->layout.newline = FALSE; } int hexpand = FALSE; int vexpand = FALSE; if(ct->layout.hexpand != UI_LAYOUT_UNDEFINED) { hexpand = ct->layout.hexpand; } if(ct->layout.vexpand != UI_LAYOUT_UNDEFINED) { vexpand = ct->layout.vexpand; } 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; gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, colspan, rowspan); grid->x += colspan; ui_reset_layout(ct->layout); ct->current = widget; } #endif #ifdef UI_GTK2 void ui_grid_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { UiGridContainer *grid = (UiGridContainer*)ct; if(ct->layout.newline) { grid->x = 0; grid->y++; ct->layout.newline = FALSE; } int hexpand = FALSE; int vexpand = FALSE; if(ct->layout.hexpand != UI_LAYOUT_UNDEFINED) { hexpand = ct->layout.hexpand; } if(ct->layout.vexpand != UI_LAYOUT_UNDEFINED) { vexpand = ct->layout.vexpand; } GtkAttachOptions xoptions = hexpand ? GTK_FILL | GTK_EXPAND : GTK_FILL; GtkAttachOptions yoptions = vexpand ? GTK_FILL | GTK_EXPAND : GTK_FILL; int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1; int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1; // TODO: use colspan/rowspan gtk_table_attach(GTK_TABLE(ct->widget), widget, grid->x, grid->x+1, grid->y, grid->y+1, xoptions, yoptions, 0, 0); grid->x++; int nw = grid->x > grid->width ? grid->x : grid->width; if(grid->x > grid->width || grid->y > grid->height) { grid->width = nw; grid->height = grid->y + 1; gtk_table_resize(GTK_TABLE(ct->widget), grid->width, grid->height); } ui_reset_layout(ct->layout); ct->current = widget; } #endif UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) { UiContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiContainer)); ct->widget = scrolledwindow; ct->add = ui_scrolledwindow_container_add; return ct; } void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { // TODO: check if the widget implements GtkScrollable SCROLLEDWINDOW_SET_CHILD(ct->widget, widget); ui_reset_layout(ct->layout); ct->current = widget; } UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview) { UiTabViewContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiTabViewContainer)); ct->container.widget = tabview; ct->container.add = ui_tabview_container_add; return (UiContainer*)ct; } void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { UiGtkTabView *data = ui_widget_get_tabview_data(ct->widget); if(!data) { fprintf(stderr, "UI Error: widget is not a tabview"); return; } data->add_tab(ct->widget, -1, ct->layout.label, widget); ui_reset_layout(ct->layout); ct->current = widget; } static GtkWidget* box_set_margin(GtkWidget *box, int margin) { GtkWidget *ret = box; #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 gtk_widget_set_margin_left(box, margin); gtk_widget_set_margin_right(box, margin); #endif gtk_widget_set_margin_top(box, margin); gtk_widget_set_margin_bottom(box, margin); #elif defined(UI_GTK2) GtkWidget *a = gtk_alignment_new(0.5, 0.5, 1, 1); gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin, margin, margin, margin); gtk_container_add(GTK_CONTAINER(a), box); ret = a; #endif return ret; } UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, UiSubContainerType type) { UiObject *current = uic_current_obj(obj); UiContainer *ct = current->container; UI_APPLY_LAYOUT1(current, args); GtkWidget *box = type == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args.spacing) : ui_gtk_hbox_new(args.spacing); ui_set_name_and_style(box, args.name, args.style_class); GtkWidget *widget = args.margin > 0 ? box_set_margin(box, args.margin) : box; ct->add(ct, widget, TRUE); UiObject *newobj = uic_object_new(obj, box); newobj->container = ui_box_container(obj, box, type); uic_obj_add(obj, newobj); return widget; } UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args) { return ui_box_create(obj, args, UI_CONTAINER_VBOX); } UIEXPORT UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args) { 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; GtkWidget *grid = create_grid(args.columnspacing, args.rowspacing); ui_set_name_and_style(grid, args.name, args.style_class); widget = box_set_margin(grid, args.margin); current->container->add(current->container, widget, TRUE); UiObject *newobj = uic_object_new(obj, grid); newobj->container = ui_grid_container(obj, grid); uic_obj_add(obj, newobj); return widget; } UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs args) { UiObject* current = uic_current_obj(obj); UI_APPLY_LAYOUT1(current, args); GtkWidget *sw = SCROLLEDWINDOW_NEW(); ui_set_name_and_style(sw, args.name, args.style_class); UiObject *newobj = uic_object_new(obj, sw); newobj->container = ui_scrolledwindow_container(obj, sw); uic_obj_add(obj, newobj); return sw; } void ui_select_tab(UIWIDGET tabview, int tab) { gtk_notebook_set_current_page(GTK_NOTEBOOK(tabview), tab); } void ui_notebook_tab_select(UIWIDGET tabview, int tab) { gtk_notebook_set_current_page(GTK_NOTEBOOK(tabview), tab); } void ui_notebook_tab_remove(UIWIDGET tabview, int tab) { gtk_notebook_remove_page(GTK_NOTEBOOK(tabview), tab); } void ui_notebook_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) { gtk_notebook_insert_page( GTK_NOTEBOOK(widget), child, gtk_label_new(name), index); } int64_t ui_notebook_get(UiInteger *i) { GtkNotebook *nb = i->obj; i->value = gtk_notebook_get_current_page(nb); return i->value; } void ui_notebook_set(UiInteger *i, int64_t value) { GtkNotebook *nb = i->obj; gtk_notebook_set_current_page(nb, value); i->value = gtk_notebook_get_current_page(nb); } UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview) { return g_object_get_data(G_OBJECT(tabview), "ui_tabview"); } typedef int64_t(*ui_tabview_get_func)(UiInteger*); typedef void (*ui_tabview_set_func)(UiInteger*, int64_t); UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs args) { UiGtkTabView *data = malloc(sizeof(UiGtkTabView)); data->margin = args.margin; data->spacing = args.spacing; data->columnspacing = args.columnspacing; data->rowspacing = args.rowspacing; ui_tabview_get_func getfunc = NULL; ui_tabview_set_func setfunc = NULL; GtkWidget *widget = NULL; switch(args.tabview) { case UI_TABVIEW_DOC: { // TODO break; } case UI_TABVIEW_NAVIGATION_SIDE: { // TODO break; } case UI_TABVIEW_DEFAULT: /* fall through */ case UI_TABVIEW_NAVIGATION_TOP: /* fall through */ case UI_TABVIEW_NAVIGATION_TOP2: { widget = gtk_notebook_new(); data->select_tab = ui_notebook_tab_select; data->remove_tab = ui_notebook_tab_remove; data->add_tab = ui_notebook_tab_add; getfunc = ui_notebook_get; setfunc = ui_notebook_set; /* fall through */ } case UI_TABVIEW_INVISIBLE: { gtk_notebook_set_show_tabs(GTK_NOTEBOOK(widget), FALSE); gtk_notebook_set_show_border(GTK_NOTEBOOK(widget), FALSE); break; } } UiObject* current = uic_current_obj(obj); if(args.value || args.varname) { UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER); UiInteger *i = var->value; i->get = getfunc; i->set = setfunc; i->obj = widget; } g_object_set_data(G_OBJECT(widget), "ui_tabview", data); data->widget = widget; data->subcontainer = args.subcontainer; UI_APPLY_LAYOUT1(current, args); current->container->add(current->container, widget, TRUE); UiObject *newobj = uic_object_new(obj, widget); newobj->container = ui_tabview_container(obj, widget); uic_obj_add(obj, newobj); data->obj = newobj; return widget; } void ui_tab_create(UiObject* obj, const char* title) { UiObject* current = uic_current_obj(obj); UiGtkTabView *data = ui_widget_get_tabview_data(current->widget); if(!data) { fprintf(stderr, "UI Error: widget is not a tabview\n"); return; } UiObject *newobj = ui_tabview_add(data->widget, title, -1); current->next = newobj; } void ui_tabview_select(UIWIDGET tabview, int tab) { UiGtkTabView *data = ui_widget_get_tabview_data(tabview); if(!data) { fprintf(stderr, "UI Error: widget is not a tabview\n"); return; } data->select_tab(tabview, tab); } void ui_tabview_remove(UIWIDGET tabview, int tab) { UiGtkTabView *data = ui_widget_get_tabview_data(tabview); if(!data) { fprintf(stderr, "UI Error: widget is not a tabview\n"); return; } data->remove_tab(tabview, tab); } UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) { UiGtkTabView *data = ui_widget_get_tabview_data(tabview); if(!data) { fprintf(stderr, "UI Error: widget is not a tabview\n"); return NULL; } UiObject *newobj = cxCalloc(data->obj->ctx->allocator, 1, sizeof(UiObject)); newobj->ctx = data->obj->ctx; GtkWidget *sub; switch(data->subcontainer) { default: { sub = ui_gtk_vbox_new(data->spacing); newobj->container = ui_box_container(newobj, sub, data->subcontainer); break; } case UI_CONTAINER_HBOX: { sub = ui_gtk_hbox_new(data->spacing); newobj->container = ui_box_container(newobj, sub, data->subcontainer); break; } case UI_CONTAINER_GRID: { sub = create_grid(data->columnspacing, data->rowspacing); newobj->container = ui_grid_container(newobj, sub); break; } } newobj->widget = sub; GtkWidget *widget = box_set_margin(sub, data->margin); data->add_tab(data->widget, tab_index, name, widget); return newobj; } /* -------------------- Splitpane -------------------- */ static GtkWidget* create_paned(UiOrientation orientation) { #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); } #else switch(orientation) { case UI_HORIZONTAL: return gtk_hpaned_new(); case UI_VERTICAL: return gtk_vpaned_new(); } #endif return NULL; } /* * -------------------- Layout Functions -------------------- * * functions for setting layout attributes for the current container * */ void ui_layout_fill(UiObject *obj, UiBool fill) { UiContainer *ct = uic_get_current_container(obj); ct->layout.fill = ui_bool2lb(fill); } void ui_layout_hexpand(UiObject *obj, UiBool expand) { UiContainer *ct = uic_get_current_container(obj); ct->layout.hexpand = expand; } void ui_layout_vexpand(UiObject *obj, UiBool expand) { UiContainer *ct = uic_get_current_container(obj); ct->layout.vexpand = expand; } void ui_layout_colspan(UiObject* obj, int cols) { UiContainer* ct = uic_get_current_container(obj); ct->layout.colspan = cols; } void ui_layout_rowspan(UiObject* obj, int rows) { UiContainer* ct = uic_get_current_container(obj); ct->layout.rowspan = rows; } void ui_newline(UiObject *obj) { UiContainer *ct = uic_get_current_container(obj); ct->layout.newline = TRUE; }