Sun, 10 Nov 2024 09:12:30 +0100
toolkit update, small UI adjustments
/* * 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 "headerbar.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 } GtkWidget* ui_subcontainer_create( UiSubContainerType type, UiObject *newobj, int spacing, int columnspacing, int rowspacing, int margin) { GtkWidget *sub = NULL; GtkWidget *add = NULL; switch(type) { default: { sub = ui_gtk_vbox_new(spacing); add = ui_box_set_margin(sub, margin); newobj->container = ui_box_container(newobj, sub, type); newobj->widget = sub; break; } case UI_CONTAINER_HBOX: { sub = ui_gtk_hbox_new(spacing); add = ui_box_set_margin(sub, margin); newobj->container = ui_box_container(newobj, sub, type); newobj->widget = sub; break; } case UI_CONTAINER_GRID: { sub = ui_create_grid_widget(columnspacing, rowspacing); add = ui_box_set_margin(sub, margin); newobj->container = ui_grid_container(newobj, sub); newobj->widget = sub; break; } case UI_CONTAINER_NO_SUB: { break; } } return add; } /* -------------------- 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; int hfill = FALSE; int vfill = FALSE; if(ct->layout.fill != UI_LAYOUT_UNDEFINED) { fill = ui_lb2bool(ct->layout.fill); } if(ct->layout.hexpand != UI_LAYOUT_UNDEFINED) { hexpand = ct->layout.hexpand; hfill = TRUE; } if(ct->layout.vexpand != UI_LAYOUT_UNDEFINED) { vexpand = ct->layout.vexpand; vfill = TRUE; } if(fill) { hfill = TRUE; vfill = TRUE; } if(!hfill) { gtk_widget_set_halign(widget, GTK_ALIGN_START); } if(!vfill) { gtk_widget_set_valign(widget, GTK_ALIGN_START); } 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_frame_container(UiObject *obj, GtkWidget *frame) { UiContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiContainer)); ct->widget = frame; ct->add = ui_frame_container_add; return ct; } void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { FRAME_SET_CHILD(ct->widget, widget); } UiContainer* ui_expander_container(UiObject *obj, GtkWidget *expander) { UiContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiContainer)); ct->widget = expander; ct->add = ui_expander_container_add; return ct; } void ui_expander_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { EXPANDER_SET_CHILD(ct->widget, widget); } 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_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; } 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; } GtkWidget* ui_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 ? ui_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); } GtkWidget* ui_create_grid_widget(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 = ui_create_grid_widget(args.columnspacing, args.rowspacing); ui_set_name_and_style(grid, args.name, args.style_class); widget = ui_box_set_margin(grid, args.margin); current->container->add(current->container, widget, TRUE); UiObject *newobj = uic_object_new(obj, grid); newobj->container = ui_grid_container(obj, grid); uic_obj_add(obj, newobj); return widget; } UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs args) { UiObject* current = uic_current_obj(obj); UI_APPLY_LAYOUT1(current, args); GtkWidget *frame = gtk_frame_new(args.label); UiObject *newobj = uic_object_new(obj, frame); GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin); if(sub) { FRAME_SET_CHILD(frame, sub); } else { newobj->widget = frame; newobj->container = ui_frame_container(obj, frame); } current->container->add(current->container, frame, FALSE); uic_obj_add(obj, newobj); return frame; } UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs args) { UiObject* current = uic_current_obj(obj); UI_APPLY_LAYOUT1(current, args); GtkWidget *expander = gtk_expander_new(args.label); gtk_expander_set_expanded(GTK_EXPANDER(expander), args.isexpanded); UiObject *newobj = uic_object_new(obj, expander); GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin); if(sub) { EXPANDER_SET_CHILD(expander, sub); } else { newobj->widget = expander; newobj->container = ui_expander_container(obj, expander); } current->container->add(current->container, expander, FALSE); uic_obj_add(obj, newobj); return expander; } 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); GtkWidget *widget = ui_box_set_margin(sw, args.margin); current->container->add(current->container, widget, TRUE); UiObject *newobj = uic_object_new(obj, sw); GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin); if(sub) { SCROLLEDWINDOW_SET_CHILD(sw, sub); } else { newobj->widget = sw; newobj->container = ui_scrolledwindow_container(obj, sw); } uic_obj_add(obj, newobj); return sw; } 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); } #if GTK_MAJOR_VERSION >= 4 static int stack_set_page(GtkWidget *stack, int index) { GtkSelectionModel *pages = gtk_stack_get_pages(GTK_STACK(stack)); GListModel *list = G_LIST_MODEL(pages); GtkStackPage *page = g_list_model_get_item(list, index); if(page) { gtk_stack_set_visible_child(GTK_STACK(stack), gtk_stack_page_get_child(page)); } else { fprintf(stderr, "UI Error: ui_stack_set value out of bounds\n"); return -1; } return index; } void ui_stack_tab_select(UIWIDGET tabview, int tab) { stack_set_page(tabview, tab); } void ui_stack_tab_remove(UIWIDGET tabview, int tab) { GtkStack *stack = GTK_STACK(tabview); GtkWidget *current = gtk_stack_get_visible_child(stack); GtkSelectionModel *pages = gtk_stack_get_pages(stack); GListModel *list = G_LIST_MODEL(pages); GtkStackPage *page = g_list_model_get_item(list, tab); if(page) { gtk_stack_remove(stack, gtk_stack_page_get_child(page)); } } void ui_stack_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) { (void)gtk_stack_add_titled(GTK_STACK(widget), child, name, name); } int64_t ui_stack_get(UiInteger *i) { GtkStack *stack = GTK_STACK(i->obj); GtkWidget *current = gtk_stack_get_visible_child(stack); GtkSelectionModel *pages = gtk_stack_get_pages(stack); GListModel *list = G_LIST_MODEL(pages); int nitems = g_list_model_get_n_items(list); for(int p=0;p<nitems;p++) { GtkStackPage *page = g_list_model_get_item(list, p); GtkWidget *child = gtk_stack_page_get_child(page); if(child == current) { i->value = p; break; } } return i->value; } void ui_stack_set(UiInteger *i, int64_t value) { GtkWidget *widget = i->obj; if(stack_set_page(widget, value) >= 0) { i->value = value; } } #elif GTK_MAJOR_VERSION >= 3 static GtkWidget* stack_get_child(GtkWidget *stack, int index) { GList *children = gtk_container_get_children(GTK_CONTAINER(stack)); if(children) { return g_list_nth_data(children, index); } return NULL; } void ui_stack_tab_select(UIWIDGET tabview, int tab) { GtkWidget *child = stack_get_child(tabview, tab); if(child) { gtk_stack_set_visible_child(GTK_STACK(tabview), child); } } void ui_stack_tab_remove(UIWIDGET tabview, int tab) { GtkWidget *child = stack_get_child(tabview, tab); if(child) { gtk_container_remove(GTK_CONTAINER(tabview), child); } } void ui_stack_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) { gtk_stack_add_titled(GTK_STACK(widget), child, name, name); } int64_t ui_stack_get(UiInteger *i) { GtkWidget *visible = gtk_stack_get_visible_child(GTK_STACK(i->obj)); GList *children = gtk_container_get_children(GTK_CONTAINER(i->obj)); GList *elm = children; int n = 0; int64_t v = -1; while(elm) { GtkWidget *child = elm->data; if(child == visible) { v = n; break; } elm = elm->next; n++; } g_list_free(children); i->value = v; return v; } void ui_stack_set(UiInteger *i, int64_t value) { GtkWidget *child = stack_get_child(i->obj, value); if(child) { gtk_stack_set_visible_child(GTK_STACK(i->obj), child); i->value = value; } } #endif 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; GtkWidget *data_widget = NULL; switch(args.tabview) { case UI_TABVIEW_DOC: { // TODO break; } case UI_TABVIEW_NAVIGATION_SIDE: { #if GTK_CHECK_VERSION(3, 10, 0) widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); GtkWidget *sidebar = gtk_stack_sidebar_new(); BOX_ADD(widget, sidebar); GtkWidget *stack = gtk_stack_new(); gtk_stack_set_transition_type (GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN); gtk_stack_sidebar_set_stack(GTK_STACK_SIDEBAR(sidebar), GTK_STACK(stack)); BOX_ADD_EXPAND(widget, stack); data->select_tab = ui_stack_tab_select; data->remove_tab = ui_stack_tab_remove; data->add_tab = ui_stack_tab_add; getfunc = ui_stack_get; setfunc = ui_stack_set; data_widget = stack; #else // TODO #endif break; } case UI_TABVIEW_DEFAULT: /* fall through */ case UI_TABVIEW_NAVIGATION_TOP: /* fall through */ case UI_TABVIEW_INVISIBLE: /* fall through */ case UI_TABVIEW_NAVIGATION_TOP2: { widget = gtk_notebook_new(); data_widget = widget; 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; if(args.tabview == 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 = data_widget; } g_object_set_data(G_OBJECT(widget), "ui_tabview", data); data->widget = data_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(current->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 = ui_create_grid_widget(data->columnspacing, data->rowspacing); newobj->container = ui_grid_container(newobj, sub); break; } } newobj->widget = sub; GtkWidget *widget = ui_box_set_margin(sub, data->margin); data->add_tab(data->widget, tab_index, name, widget); return newobj; } /* -------------------- Headerbar -------------------- */ static void hb_set_part(UiObject *obj, int part) { UiObject* current = uic_current_obj(obj); GtkWidget *headerbar = current->widget; UiHeaderbarContainer *hb = cxCalloc( obj->ctx->allocator, 1, sizeof(UiHeaderbarContainer)); memcpy(hb, current->container, sizeof(UiHeaderbarContainer)); UiObject *newobj = uic_object_new(obj, headerbar); newobj->container = (UiContainer*)hb; uic_obj_add(obj, newobj); hb->part = part; } void ui_headerbar_start_create(UiObject *obj) { hb_set_part(obj, 0); } void ui_headerbar_center_create(UiObject *obj) { hb_set_part(obj, 2); } void ui_headerbar_end_create(UiObject *obj) { hb_set_part(obj, 1); } UIWIDGET ui_headerbar_fallback_create(UiObject *obj, UiHeaderbarArgs args) { UiObject *current = uic_current_obj(obj); UiContainer *ct = current->container; UI_APPLY_LAYOUT1(current, args); GtkWidget *box = ui_gtk_hbox_new(args.alt_spacing); ui_set_name_and_style(box, args.name, args.style_class); ct->add(ct, box, FALSE); UiObject *newobj = uic_object_new(obj, box); newobj->container = ui_headerbar_fallback_container(obj, box); uic_obj_add(obj, newobj); return box; } static void hb_fallback_set_part(UiObject *obj, int part) { UiObject* current = uic_current_obj(obj); GtkWidget *headerbar = current->widget; UiObject *newobj = uic_object_new(obj, headerbar); newobj->container = ui_headerbar_container(obj, headerbar); uic_obj_add(obj, newobj); UiHeaderbarContainer *hb = (UiHeaderbarContainer*)newobj->container; hb->part = part; } UiContainer* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar) { UiHeaderbarContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiHeaderbarContainer)); ct->container.widget = headerbar; ct->container.add = ui_headerbar_fallback_container_add; return (UiContainer*)ct; } void ui_headerbar_fallback_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct; BOX_ADD(ct->widget, widget); } #if GTK_CHECK_VERSION(3, 10, 0) UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args) { GtkWidget *headerbar = g_object_get_data(G_OBJECT(obj->widget), "ui_headerbar"); if(!headerbar) { return ui_headerbar_fallback_create(obj, args); } UiObject *newobj = uic_object_new(obj, headerbar); newobj->container = ui_headerbar_container(obj, headerbar); uic_obj_add(obj, newobj); return headerbar; } UiContainer* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar) { UiHeaderbarContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiHeaderbarContainer)); ct->container.widget = headerbar; ct->container.add = ui_headerbar_container_add; return (UiContainer*)ct; } void ui_headerbar_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct; if(hb->part == 0) { UI_HEADERBAR_PACK_START(ct->widget, widget); } else if(hb->part == 1) { UI_HEADERBAR_PACK_END(ct->widget, widget); } else if(hb->part == 2) { if(!hb->centerbox) { GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); hb->centerbox = box; UI_HEADERBAR_SET_TITLE_WIDGET(ct->widget, box); } BOX_ADD(hb->centerbox, widget); } } #else UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args) { return ui_headerbar_fallback_create(obj, args); } #endif /* -------------------- 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_hfill(UiObject *obj, UiBool fill) { UiContainer *ct = uic_get_current_container(obj); ct->layout.hfill = fill; } void ui_layout_vfill(UiObject *obj, UiBool fill) { UiContainer *ct = uic_get_current_container(obj); ct->layout.vfill = fill; } 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; }