UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2017 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <limits.h> 32 33 #include "container.h" 34 #include "toolkit.h" 35 #include "headerbar.h" 36 37 #include "../common/context.h" 38 #include "../common/object.h" 39 40 41 void ui_container_begin_close(UiObject *obj) { 42 UiContainer *ct = uic_get_current_container(obj); 43 ct->close = 1; 44 } 45 46 int ui_container_finish(UiObject *obj) { 47 UiContainer *ct = uic_get_current_container(obj); 48 if(ct->close) { 49 ui_end(obj); 50 return 0; 51 } 52 return 1; 53 } 54 55 GtkWidget* ui_gtk_vbox_new(int spacing) { 56 #if GTK_MAJOR_VERSION >= 3 57 return gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing); 58 #else 59 return gtk_vbox_new(FALSE, spacing); 60 #endif 61 } 62 63 GtkWidget* ui_gtk_hbox_new(int spacing) { 64 #if GTK_MAJOR_VERSION >= 3 65 return gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing); 66 #else 67 return gtk_hbox_new(FALSE, spacing); 68 #endif 69 } 70 71 GtkWidget* ui_subcontainer_create( 72 UiSubContainerType type, 73 UiObject *newobj, 74 int spacing, 75 int columnspacing, 76 int rowspacing, 77 int margin) 78 { 79 GtkWidget *sub = NULL; 80 GtkWidget *add = NULL; 81 switch(type) { 82 default: { 83 sub = ui_gtk_vbox_new(spacing); 84 add = ui_box_set_margin(sub, margin); 85 newobj->container = ui_box_container(newobj, sub, type); 86 newobj->widget = sub; 87 break; 88 } 89 case UI_CONTAINER_HBOX: { 90 sub = ui_gtk_hbox_new(spacing); 91 add = ui_box_set_margin(sub, margin); 92 newobj->container = ui_box_container(newobj, sub, type); 93 newobj->widget = sub; 94 break; 95 } 96 case UI_CONTAINER_GRID: { 97 sub = ui_create_grid_widget(columnspacing, rowspacing); 98 add = ui_box_set_margin(sub, margin); 99 newobj->container = ui_grid_container(newobj, sub); 100 newobj->widget = sub; 101 break; 102 } 103 case UI_CONTAINER_NO_SUB: { 104 break; 105 } 106 } 107 return add; 108 } 109 110 111 /* -------------------- Box Container -------------------- */ 112 UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) { 113 UiBoxContainer *ct = cxCalloc( 114 obj->ctx->allocator, 115 1, 116 sizeof(UiBoxContainer)); 117 ct->container.widget = box; 118 ct->container.add = ui_box_container_add; 119 ct->type = type; 120 return (UiContainer*)ct; 121 } 122 123 void ui_box_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { 124 UiBoxContainer *bc = (UiBoxContainer*)ct; 125 if(ct->layout.fill != UI_LAYOUT_UNDEFINED) { 126 fill = ui_lb2bool(ct->layout.fill); 127 } 128 129 if(bc->has_fill && fill) { 130 fprintf(stderr, "UiError: container has 2 filled widgets"); 131 fill = FALSE; 132 } 133 if(fill) { 134 bc->has_fill = TRUE; 135 } 136 137 UiBool expand = fill; 138 #if GTK_MAJOR_VERSION >= 4 139 gtk_box_append(GTK_BOX(ct->widget), widget); 140 GtkAlign align = expand ? GTK_ALIGN_FILL : GTK_ALIGN_START; 141 if(bc->type == UI_CONTAINER_VBOX) { 142 gtk_widget_set_valign(widget, align); 143 gtk_widget_set_vexpand(widget, expand); 144 gtk_widget_set_hexpand(widget, TRUE); 145 } else if(bc->type == UI_CONTAINER_HBOX) { 146 gtk_widget_set_halign(widget, align); 147 gtk_widget_set_hexpand(widget, expand); 148 gtk_widget_set_vexpand(widget, TRUE); 149 } 150 151 #else 152 gtk_box_pack_start(GTK_BOX(ct->widget), widget, expand, fill, 0); 153 #endif 154 155 ui_reset_layout(ct->layout); 156 ct->current = widget; 157 } 158 159 UiContainer* ui_grid_container(UiObject *obj, GtkWidget *grid) { 160 UiGridContainer *ct = cxCalloc( 161 obj->ctx->allocator, 162 1, 163 sizeof(UiGridContainer)); 164 ct->container.widget = grid; 165 ct->container.add = ui_grid_container_add; 166 UI_GTK_V2(ct->width = 0); 167 UI_GTK_V2(ct->height = 1); 168 return (UiContainer*)ct; 169 } 170 171 #if GTK_MAJOR_VERSION >= 3 172 void ui_grid_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { 173 UiGridContainer *grid = (UiGridContainer*)ct; 174 175 if(ct->layout.newline) { 176 grid->x = 0; 177 grid->y++; 178 ct->layout.newline = FALSE; 179 } 180 181 int hexpand = FALSE; 182 int vexpand = FALSE; 183 int hfill = FALSE; 184 int vfill = FALSE; 185 if(ct->layout.fill != UI_LAYOUT_UNDEFINED) { 186 fill = ui_lb2bool(ct->layout.fill); 187 } 188 if(ct->layout.hexpand != UI_LAYOUT_UNDEFINED) { 189 hexpand = ct->layout.hexpand; 190 hfill = TRUE; 191 } 192 if(ct->layout.vexpand != UI_LAYOUT_UNDEFINED) { 193 vexpand = ct->layout.vexpand; 194 vfill = TRUE; 195 } 196 if(fill) { 197 hfill = TRUE; 198 vfill = TRUE; 199 } 200 201 if(!hfill) { 202 gtk_widget_set_halign(widget, GTK_ALIGN_START); 203 } 204 if(!vfill) { 205 gtk_widget_set_valign(widget, GTK_ALIGN_START); 206 } 207 208 gtk_widget_set_hexpand(widget, hexpand); 209 gtk_widget_set_vexpand(widget, vexpand); 210 211 int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1; 212 int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1; 213 214 gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, colspan, rowspan); 215 grid->x += colspan; 216 217 ui_reset_layout(ct->layout); 218 ct->current = widget; 219 } 220 #endif 221 #ifdef UI_GTK2 222 void ui_grid_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { 223 UiGridContainer *grid = (UiGridContainer*)ct; 224 225 if(ct->layout.newline) { 226 grid->x = 0; 227 grid->y++; 228 ct->layout.newline = FALSE; 229 } 230 231 int hexpand = FALSE; 232 int vexpand = FALSE; 233 if(ct->layout.hexpand != UI_LAYOUT_UNDEFINED) { 234 hexpand = ct->layout.hexpand; 235 } 236 if(ct->layout.vexpand != UI_LAYOUT_UNDEFINED) { 237 vexpand = ct->layout.vexpand; 238 } 239 GtkAttachOptions xoptions = hexpand ? GTK_FILL | GTK_EXPAND : GTK_FILL; 240 GtkAttachOptions yoptions = vexpand ? GTK_FILL | GTK_EXPAND : GTK_FILL; 241 242 int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1; 243 int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1; 244 // TODO: use colspan/rowspan 245 246 gtk_table_attach(GTK_TABLE(ct->widget), widget, grid->x, grid->x+1, grid->y, grid->y+1, xoptions, yoptions, 0, 0); 247 grid->x++; 248 int nw = grid->x > grid->width ? grid->x : grid->width; 249 if(grid->x > grid->width || grid->y > grid->height) { 250 grid->width = nw; 251 grid->height = grid->y + 1; 252 gtk_table_resize(GTK_TABLE(ct->widget), grid->width, grid->height); 253 } 254 255 ui_reset_layout(ct->layout); 256 ct->current = widget; 257 } 258 #endif 259 260 UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame) { 261 UiContainer *ct = cxCalloc( 262 obj->ctx->allocator, 263 1, 264 sizeof(UiContainer)); 265 ct->widget = frame; 266 ct->add = ui_frame_container_add; 267 return ct; 268 } 269 270 void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { 271 FRAME_SET_CHILD(ct->widget, widget); 272 } 273 274 UiContainer* ui_expander_container(UiObject *obj, GtkWidget *expander) { 275 UiContainer *ct = cxCalloc( 276 obj->ctx->allocator, 277 1, 278 sizeof(UiContainer)); 279 ct->widget = expander; 280 ct->add = ui_expander_container_add; 281 return ct; 282 } 283 284 void ui_expander_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { 285 EXPANDER_SET_CHILD(ct->widget, widget); 286 } 287 288 void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { 289 // TODO: check if the widget implements GtkScrollable 290 SCROLLEDWINDOW_SET_CHILD(ct->widget, widget); 291 ui_reset_layout(ct->layout); 292 ct->current = widget; 293 } 294 295 UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) { 296 UiContainer *ct = cxCalloc( 297 obj->ctx->allocator, 298 1, 299 sizeof(UiContainer)); 300 ct->widget = scrolledwindow; 301 ct->add = ui_scrolledwindow_container_add; 302 return ct; 303 } 304 305 UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview) { 306 UiTabViewContainer *ct = cxCalloc( 307 obj->ctx->allocator, 308 1, 309 sizeof(UiTabViewContainer)); 310 ct->container.widget = tabview; 311 ct->container.add = ui_tabview_container_add; 312 return (UiContainer*)ct; 313 } 314 315 void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { 316 UiGtkTabView *data = ui_widget_get_tabview_data(ct->widget); 317 if(!data) { 318 fprintf(stderr, "UI Error: widget is not a tabview"); 319 return; 320 } 321 data->add_tab(ct->widget, -1, ct->layout.label, widget); 322 323 ui_reset_layout(ct->layout); 324 ct->current = widget; 325 } 326 327 328 329 GtkWidget* ui_box_set_margin(GtkWidget *box, int margin) { 330 GtkWidget *ret = box; 331 #if GTK_MAJOR_VERSION >= 3 332 #if GTK_MAJOR_VERSION * 1000 + GTK_MINOR_VERSION >= 3012 333 gtk_widget_set_margin_start(box, margin); 334 gtk_widget_set_margin_end(box, margin); 335 #else 336 gtk_widget_set_margin_left(box, margin); 337 gtk_widget_set_margin_right(box, margin); 338 #endif 339 gtk_widget_set_margin_top(box, margin); 340 gtk_widget_set_margin_bottom(box, margin); 341 #elif defined(UI_GTK2) 342 GtkWidget *a = gtk_alignment_new(0.5, 0.5, 1, 1); 343 gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin, margin, margin, margin); 344 gtk_container_add(GTK_CONTAINER(a), box); 345 ret = a; 346 #endif 347 return ret; 348 } 349 350 UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, UiSubContainerType type) { 351 UiObject *current = uic_current_obj(obj); 352 UiContainer *ct = current->container; 353 UI_APPLY_LAYOUT1(current, args); 354 355 GtkWidget *box = type == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args.spacing) : ui_gtk_hbox_new(args.spacing); 356 ui_set_name_and_style(box, args.name, args.style_class); 357 GtkWidget *widget = args.margin > 0 ? ui_box_set_margin(box, args.margin) : box; 358 ct->add(ct, widget, TRUE); 359 360 UiObject *newobj = uic_object_new(obj, box); 361 newobj->container = ui_box_container(obj, box, type); 362 uic_obj_add(obj, newobj); 363 364 return widget; 365 } 366 367 UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args) { 368 return ui_box_create(obj, args, UI_CONTAINER_VBOX); 369 } 370 371 UIEXPORT UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args) { 372 return ui_box_create(obj, args, UI_CONTAINER_HBOX); 373 } 374 375 GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing) { 376 #if GTK_MAJOR_VERSION >= 3 377 GtkWidget *grid = gtk_grid_new(); 378 gtk_grid_set_column_spacing(GTK_GRID(grid), colspacing); 379 gtk_grid_set_row_spacing(GTK_GRID(grid), rowspacing); 380 #else 381 GtkWidget *grid = gtk_table_new(1, 1, FALSE); 382 gtk_table_set_col_spacings(GTK_TABLE(grid), colspacing); 383 gtk_table_set_row_spacings(GTK_TABLE(grid), rowspacing); 384 #endif 385 return grid; 386 } 387 388 UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args) { 389 UiObject* current = uic_current_obj(obj); 390 UI_APPLY_LAYOUT1(current, args); 391 GtkWidget *widget; 392 393 GtkWidget *grid = ui_create_grid_widget(args.columnspacing, args.rowspacing); 394 ui_set_name_and_style(grid, args.name, args.style_class); 395 widget = ui_box_set_margin(grid, args.margin); 396 current->container->add(current->container, widget, TRUE); 397 398 UiObject *newobj = uic_object_new(obj, grid); 399 newobj->container = ui_grid_container(obj, grid); 400 uic_obj_add(obj, newobj); 401 402 return widget; 403 } 404 405 UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs args) { 406 UiObject* current = uic_current_obj(obj); 407 UI_APPLY_LAYOUT1(current, args); 408 409 GtkWidget *frame = gtk_frame_new(args.label); 410 UiObject *newobj = uic_object_new(obj, frame); 411 GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin); 412 if(sub) { 413 FRAME_SET_CHILD(frame, sub); 414 } else { 415 newobj->widget = frame; 416 newobj->container = ui_frame_container(obj, frame); 417 } 418 current->container->add(current->container, frame, FALSE); 419 uic_obj_add(obj, newobj); 420 421 return frame; 422 } 423 424 UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs args) { 425 UiObject* current = uic_current_obj(obj); 426 UI_APPLY_LAYOUT1(current, args); 427 428 GtkWidget *expander = gtk_expander_new(args.label); 429 gtk_expander_set_expanded(GTK_EXPANDER(expander), args.isexpanded); 430 UiObject *newobj = uic_object_new(obj, expander); 431 GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin); 432 if(sub) { 433 EXPANDER_SET_CHILD(expander, sub); 434 } else { 435 newobj->widget = expander; 436 newobj->container = ui_expander_container(obj, expander); 437 } 438 current->container->add(current->container, expander, FALSE); 439 uic_obj_add(obj, newobj); 440 441 return expander; 442 } 443 444 445 UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs args) { 446 UiObject* current = uic_current_obj(obj); 447 UI_APPLY_LAYOUT1(current, args); 448 449 GtkWidget *sw = SCROLLEDWINDOW_NEW(); 450 ui_set_name_and_style(sw, args.name, args.style_class); 451 GtkWidget *widget = ui_box_set_margin(sw, args.margin); 452 current->container->add(current->container, widget, TRUE); 453 454 UiObject *newobj = uic_object_new(obj, sw); 455 GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin); 456 if(sub) { 457 SCROLLEDWINDOW_SET_CHILD(sw, sub); 458 } else { 459 newobj->widget = sw; 460 newobj->container = ui_scrolledwindow_container(obj, sw); 461 } 462 463 uic_obj_add(obj, newobj); 464 465 return sw; 466 } 467 468 469 void ui_notebook_tab_select(UIWIDGET tabview, int tab) { 470 gtk_notebook_set_current_page(GTK_NOTEBOOK(tabview), tab); 471 } 472 473 void ui_notebook_tab_remove(UIWIDGET tabview, int tab) { 474 gtk_notebook_remove_page(GTK_NOTEBOOK(tabview), tab); 475 } 476 477 void ui_notebook_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) { 478 gtk_notebook_insert_page( 479 GTK_NOTEBOOK(widget), 480 child, 481 gtk_label_new(name), 482 index); 483 } 484 485 int64_t ui_notebook_get(UiInteger *i) { 486 GtkNotebook *nb = i->obj; 487 i->value = gtk_notebook_get_current_page(nb); 488 return i->value; 489 } 490 491 void ui_notebook_set(UiInteger *i, int64_t value) { 492 GtkNotebook *nb = i->obj; 493 gtk_notebook_set_current_page(nb, value); 494 i->value = gtk_notebook_get_current_page(nb); 495 } 496 497 498 #if GTK_MAJOR_VERSION >= 4 499 static int stack_set_page(GtkWidget *stack, int index) { 500 GtkSelectionModel *pages = gtk_stack_get_pages(GTK_STACK(stack)); 501 GListModel *list = G_LIST_MODEL(pages); 502 GtkStackPage *page = g_list_model_get_item(list, index); 503 if(page) { 504 gtk_stack_set_visible_child(GTK_STACK(stack), gtk_stack_page_get_child(page)); 505 } else { 506 fprintf(stderr, "UI Error: ui_stack_set value out of bounds\n"); 507 return -1; 508 } 509 return index; 510 } 511 512 void ui_stack_tab_select(UIWIDGET tabview, int tab) { 513 stack_set_page(tabview, tab); 514 } 515 516 void ui_stack_tab_remove(UIWIDGET tabview, int tab) { 517 GtkStack *stack = GTK_STACK(tabview); 518 GtkWidget *current = gtk_stack_get_visible_child(stack); 519 GtkSelectionModel *pages = gtk_stack_get_pages(stack); 520 GListModel *list = G_LIST_MODEL(pages); 521 GtkStackPage *page = g_list_model_get_item(list, tab); 522 if(page) { 523 gtk_stack_remove(stack, gtk_stack_page_get_child(page)); 524 } 525 } 526 527 void ui_stack_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) { 528 (void)gtk_stack_add_titled(GTK_STACK(widget), child, name, name); 529 } 530 531 int64_t ui_stack_get(UiInteger *i) { 532 GtkStack *stack = GTK_STACK(i->obj); 533 GtkWidget *current = gtk_stack_get_visible_child(stack); 534 GtkSelectionModel *pages = gtk_stack_get_pages(stack); 535 GListModel *list = G_LIST_MODEL(pages); 536 int nitems = g_list_model_get_n_items(list); 537 for(int p=0;p<nitems;p++) { 538 GtkStackPage *page = g_list_model_get_item(list, p); 539 GtkWidget *child = gtk_stack_page_get_child(page); 540 if(child == current) { 541 i->value = p; 542 break; 543 } 544 } 545 return i->value; 546 } 547 548 void ui_stack_set(UiInteger *i, int64_t value) { 549 GtkWidget *widget = i->obj; 550 if(stack_set_page(widget, value) >= 0) { 551 i->value = value; 552 } 553 } 554 #elif GTK_MAJOR_VERSION >= 3 555 static GtkWidget* stack_get_child(GtkWidget *stack, int index) { 556 GList *children = gtk_container_get_children(GTK_CONTAINER(stack)); 557 if(children) { 558 return g_list_nth_data(children, index); 559 } 560 return NULL; 561 } 562 563 void ui_stack_tab_select(UIWIDGET tabview, int tab) { 564 GtkWidget *child = stack_get_child(tabview, tab); 565 if(child) { 566 gtk_stack_set_visible_child(GTK_STACK(tabview), child); 567 } 568 } 569 570 void ui_stack_tab_remove(UIWIDGET tabview, int tab) { 571 GtkWidget *child = stack_get_child(tabview, tab); 572 if(child) { 573 gtk_container_remove(GTK_CONTAINER(tabview), child); 574 } 575 } 576 577 void ui_stack_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) { 578 gtk_stack_add_titled(GTK_STACK(widget), child, name, name); 579 } 580 581 int64_t ui_stack_get(UiInteger *i) { 582 GtkWidget *visible = gtk_stack_get_visible_child(GTK_STACK(i->obj)); 583 GList *children = gtk_container_get_children(GTK_CONTAINER(i->obj)); 584 GList *elm = children; 585 int n = 0; 586 int64_t v = -1; 587 while(elm) { 588 GtkWidget *child = elm->data; 589 if(child == visible) { 590 v = n; 591 break; 592 } 593 594 elm = elm->next; 595 n++; 596 } 597 g_list_free(children); 598 i->value = v; 599 return v; 600 } 601 602 void ui_stack_set(UiInteger *i, int64_t value) { 603 GtkWidget *child = stack_get_child(i->obj, value); 604 if(child) { 605 gtk_stack_set_visible_child(GTK_STACK(i->obj), child); 606 i->value = value; 607 } 608 } 609 610 #endif 611 612 613 614 615 UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview) { 616 return g_object_get_data(G_OBJECT(tabview), "ui_tabview"); 617 } 618 619 typedef int64_t(*ui_tabview_get_func)(UiInteger*); 620 typedef void (*ui_tabview_set_func)(UiInteger*, int64_t); 621 622 UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs args) { 623 UiGtkTabView *data = malloc(sizeof(UiGtkTabView)); 624 data->margin = args.margin; 625 data->spacing = args.spacing; 626 data->columnspacing = args.columnspacing; 627 data->rowspacing = args.rowspacing; 628 629 ui_tabview_get_func getfunc = NULL; 630 ui_tabview_set_func setfunc = NULL; 631 632 GtkWidget *widget = NULL; 633 GtkWidget *data_widget = NULL; 634 switch(args.tabview) { 635 case UI_TABVIEW_DOC: { 636 // TODO 637 break; 638 } 639 case UI_TABVIEW_NAVIGATION_SIDE: { 640 #if GTK_CHECK_VERSION(3, 10, 0) 641 widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); 642 GtkWidget *sidebar = gtk_stack_sidebar_new(); 643 BOX_ADD(widget, sidebar); 644 GtkWidget *stack = gtk_stack_new(); 645 gtk_stack_set_transition_type (GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN); 646 gtk_stack_sidebar_set_stack(GTK_STACK_SIDEBAR(sidebar), GTK_STACK(stack)); 647 BOX_ADD_EXPAND(widget, stack); 648 data->select_tab = ui_stack_tab_select; 649 data->remove_tab = ui_stack_tab_remove; 650 data->add_tab = ui_stack_tab_add; 651 getfunc = ui_stack_get; 652 setfunc = ui_stack_set; 653 data_widget = stack; 654 #else 655 // TODO 656 #endif 657 break; 658 } 659 case UI_TABVIEW_DEFAULT: /* fall through */ 660 case UI_TABVIEW_NAVIGATION_TOP: /* fall through */ 661 case UI_TABVIEW_INVISIBLE: /* fall through */ 662 case UI_TABVIEW_NAVIGATION_TOP2: { 663 widget = gtk_notebook_new(); 664 data_widget = widget; 665 data->select_tab = ui_notebook_tab_select; 666 data->remove_tab = ui_notebook_tab_remove; 667 data->add_tab = ui_notebook_tab_add; 668 getfunc = ui_notebook_get; 669 setfunc = ui_notebook_set; 670 if(args.tabview == UI_TABVIEW_INVISIBLE) { 671 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(widget), FALSE); 672 gtk_notebook_set_show_border(GTK_NOTEBOOK(widget), FALSE); 673 } 674 break; 675 } 676 } 677 678 UiObject* current = uic_current_obj(obj); 679 if(args.value || args.varname) { 680 UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER); 681 UiInteger *i = var->value; 682 i->get = getfunc; 683 i->set = setfunc; 684 i->obj = data_widget; 685 } 686 687 g_object_set_data(G_OBJECT(widget), "ui_tabview", data); 688 data->widget = data_widget; 689 data->subcontainer = args.subcontainer; 690 691 UI_APPLY_LAYOUT1(current, args); 692 current->container->add(current->container, widget, TRUE); 693 694 UiObject *newobj = uic_object_new(obj, widget); 695 newobj->container = ui_tabview_container(obj, widget); 696 uic_obj_add(obj, newobj); 697 data->obj = newobj; 698 699 return widget; 700 } 701 702 void ui_tab_create(UiObject* obj, const char* title) { 703 UiObject* current = uic_current_obj(obj); 704 UiGtkTabView *data = ui_widget_get_tabview_data(current->widget); 705 if(!data) { 706 fprintf(stderr, "UI Error: widget is not a tabview\n"); 707 return; 708 } 709 710 UiObject *newobj = ui_tabview_add(current->widget, title, -1); 711 current->next = newobj; 712 } 713 714 715 716 void ui_tabview_select(UIWIDGET tabview, int tab) { 717 UiGtkTabView *data = ui_widget_get_tabview_data(tabview); 718 if(!data) { 719 fprintf(stderr, "UI Error: widget is not a tabview\n"); 720 return; 721 } 722 data->select_tab(tabview, tab); 723 } 724 725 void ui_tabview_remove(UIWIDGET tabview, int tab) { 726 UiGtkTabView *data = ui_widget_get_tabview_data(tabview); 727 if(!data) { 728 fprintf(stderr, "UI Error: widget is not a tabview\n"); 729 return; 730 } 731 data->remove_tab(tabview, tab); 732 } 733 734 UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) { 735 UiGtkTabView *data = ui_widget_get_tabview_data(tabview); 736 if(!data) { 737 fprintf(stderr, "UI Error: widget is not a tabview\n"); 738 return NULL; 739 } 740 741 UiObject *newobj = cxCalloc(data->obj->ctx->allocator, 1, sizeof(UiObject)); 742 newobj->ctx = data->obj->ctx; 743 744 GtkWidget *sub; 745 switch(data->subcontainer) { 746 default: { 747 sub = ui_gtk_vbox_new(data->spacing); 748 newobj->container = ui_box_container(newobj, sub, data->subcontainer); 749 break; 750 } 751 case UI_CONTAINER_HBOX: { 752 sub = ui_gtk_hbox_new(data->spacing); 753 newobj->container = ui_box_container(newobj, sub, data->subcontainer); 754 break; 755 } 756 case UI_CONTAINER_GRID: { 757 sub = ui_create_grid_widget(data->columnspacing, data->rowspacing); 758 newobj->container = ui_grid_container(newobj, sub); 759 break; 760 } 761 } 762 newobj->widget = sub; 763 GtkWidget *widget = ui_box_set_margin(sub, data->margin); 764 765 data->add_tab(data->widget, tab_index, name, widget); 766 767 return newobj; 768 } 769 770 771 /* -------------------- Headerbar -------------------- */ 772 773 static void hb_set_part(UiObject *obj, int part) { 774 UiObject* current = uic_current_obj(obj); 775 GtkWidget *headerbar = current->widget; 776 777 UiHeaderbarContainer *hb = cxCalloc( 778 obj->ctx->allocator, 779 1, 780 sizeof(UiHeaderbarContainer)); 781 memcpy(hb, current->container, sizeof(UiHeaderbarContainer)); 782 783 UiObject *newobj = uic_object_new(obj, headerbar); 784 newobj->container = (UiContainer*)hb; 785 uic_obj_add(obj, newobj); 786 787 hb->part = part; 788 } 789 790 void ui_headerbar_start_create(UiObject *obj) { 791 hb_set_part(obj, 0); 792 } 793 794 void ui_headerbar_center_create(UiObject *obj) { 795 hb_set_part(obj, 2); 796 } 797 798 void ui_headerbar_end_create(UiObject *obj) { 799 hb_set_part(obj, 1); 800 } 801 802 UIWIDGET ui_headerbar_fallback_create(UiObject *obj, UiHeaderbarArgs args) { 803 UiObject *current = uic_current_obj(obj); 804 UiContainer *ct = current->container; 805 UI_APPLY_LAYOUT1(current, args); 806 807 GtkWidget *box = ui_gtk_hbox_new(args.alt_spacing); 808 ui_set_name_and_style(box, args.name, args.style_class); 809 ct->add(ct, box, FALSE); 810 811 UiObject *newobj = uic_object_new(obj, box); 812 newobj->container = ui_headerbar_fallback_container(obj, box); 813 uic_obj_add(obj, newobj); 814 815 return box; 816 } 817 818 static void hb_fallback_set_part(UiObject *obj, int part) { 819 UiObject* current = uic_current_obj(obj); 820 GtkWidget *headerbar = current->widget; 821 822 UiObject *newobj = uic_object_new(obj, headerbar); 823 newobj->container = ui_headerbar_container(obj, headerbar); 824 uic_obj_add(obj, newobj); 825 826 UiHeaderbarContainer *hb = (UiHeaderbarContainer*)newobj->container; 827 hb->part = part; 828 } 829 830 UiContainer* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar) { 831 UiHeaderbarContainer *ct = cxCalloc( 832 obj->ctx->allocator, 833 1, 834 sizeof(UiHeaderbarContainer)); 835 ct->container.widget = headerbar; 836 ct->container.add = ui_headerbar_fallback_container_add; 837 return (UiContainer*)ct; 838 } 839 840 void ui_headerbar_fallback_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { 841 UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct; 842 BOX_ADD(ct->widget, widget); 843 } 844 845 #if GTK_CHECK_VERSION(3, 10, 0) 846 847 UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args) { 848 GtkWidget *headerbar = g_object_get_data(G_OBJECT(obj->widget), "ui_headerbar"); 849 if(!headerbar) { 850 return ui_headerbar_fallback_create(obj, args); 851 } 852 853 UiObject *newobj = uic_object_new(obj, headerbar); 854 newobj->container = ui_headerbar_container(obj, headerbar); 855 uic_obj_add(obj, newobj); 856 857 return headerbar; 858 } 859 860 UiContainer* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar) { 861 UiHeaderbarContainer *ct = cxCalloc( 862 obj->ctx->allocator, 863 1, 864 sizeof(UiHeaderbarContainer)); 865 ct->container.widget = headerbar; 866 ct->container.add = ui_headerbar_container_add; 867 return (UiContainer*)ct; 868 } 869 870 void ui_headerbar_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { 871 UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct; 872 if(hb->part == 0) { 873 UI_HEADERBAR_PACK_START(ct->widget, widget); 874 } else if(hb->part == 1) { 875 UI_HEADERBAR_PACK_END(ct->widget, widget); 876 } else if(hb->part == 2) { 877 if(!hb->centerbox) { 878 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); 879 hb->centerbox = box; 880 UI_HEADERBAR_SET_TITLE_WIDGET(ct->widget, box); 881 } 882 BOX_ADD(hb->centerbox, widget); 883 } 884 } 885 886 #else 887 888 UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args) { 889 return ui_headerbar_fallback_create(obj, args); 890 } 891 892 #endif 893 894 /* -------------------- Sidebar -------------------- */ 895 896 #ifdef UI_LIBADWAITA 897 UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args) { 898 GtkWidget *sidebar_toolbar_view = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar"); 899 if(!sidebar_toolbar_view) { 900 fprintf(stderr, "Error: window is not configured for sidebar\n"); 901 return NULL; 902 } 903 904 GtkWidget *box = ui_gtk_vbox_new(args.spacing); 905 ui_box_set_margin(box, args.margin); 906 adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), box); 907 908 UiObject *newobj = uic_object_new(obj, box); 909 newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX); 910 uic_obj_add(obj, newobj); 911 912 return box; 913 } 914 #else 915 UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args) { 916 GtkWidget *sidebar_vbox = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar"); 917 918 GtkWidget *box = ui_gtk_vbox_new(args.spacing); 919 ui_box_set_margin(box, args.margin); 920 BOX_ADD_EXPAND(sidebar_vbox, box); 921 922 UiObject *newobj = uic_object_new(obj, box); 923 newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX); 924 uic_obj_add(obj, newobj); 925 926 return box; 927 } 928 #endif 929 930 /* -------------------- Splitpane -------------------- */ 931 932 static GtkWidget* create_paned(UiOrientation orientation) { 933 #if GTK_MAJOR_VERSION >= 3 934 switch(orientation) { 935 case UI_HORIZONTAL: return gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); 936 case UI_VERTICAL: return gtk_paned_new(GTK_ORIENTATION_VERTICAL); 937 } 938 #else 939 switch(orientation) { 940 case UI_HORIZONTAL: return gtk_hpaned_new(); 941 case UI_VERTICAL: return gtk_vpaned_new(); 942 } 943 #endif 944 return NULL; 945 } 946 947 948 949 950 951 952 /* 953 * -------------------- Layout Functions -------------------- 954 * 955 * functions for setting layout attributes for the current container 956 * 957 */ 958 959 void ui_layout_fill(UiObject *obj, UiBool fill) { 960 UiContainer *ct = uic_get_current_container(obj); 961 ct->layout.fill = ui_bool2lb(fill); 962 } 963 964 void ui_layout_hexpand(UiObject *obj, UiBool expand) { 965 UiContainer *ct = uic_get_current_container(obj); 966 ct->layout.hexpand = expand; 967 } 968 969 void ui_layout_vexpand(UiObject *obj, UiBool expand) { 970 UiContainer *ct = uic_get_current_container(obj); 971 ct->layout.vexpand = expand; 972 } 973 974 void ui_layout_hfill(UiObject *obj, UiBool fill) { 975 UiContainer *ct = uic_get_current_container(obj); 976 ct->layout.hfill = fill; 977 } 978 979 void ui_layout_vfill(UiObject *obj, UiBool fill) { 980 UiContainer *ct = uic_get_current_container(obj); 981 ct->layout.vfill = fill; 982 } 983 984 void ui_layout_colspan(UiObject* obj, int cols) { 985 UiContainer* ct = uic_get_current_container(obj); 986 ct->layout.colspan = cols; 987 } 988 989 void ui_layout_rowspan(UiObject* obj, int rows) { 990 UiContainer* ct = uic_get_current_container(obj); 991 ct->layout.rowspan = rows; 992 } 993 994 void ui_newline(UiObject *obj) { 995 UiContainer *ct = uic_get_current_container(obj); 996 ct->layout.newline = TRUE; 997 } 998 999