ui/qt/container.cpp

Sun, 19 Oct 2025 21:20:08 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 19 Oct 2025 21:20:08 +0200
changeset 112
c3f2f16fa4b8
parent 109
c3dfcb8f0be7
permissions
-rw-r--r--

update toolkit

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2014 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 "container.h"
#include "../common/object.h"
#include "../common/container.h"

#include <cx/mempool.h>

#include <QSpacerItem>
#include <QStackedWidget>
#include <QLabel>
#include <QDockWidget>

static void delete_container(UiContainerPrivate *ct) {
    delete ct;
}

void ui_obj_add_container(UiObject *obj, UiContainerPrivate *ct) {
    UiContainerX *container = (UiContainerX*)ui_malloc(obj->ctx, sizeof(UiContainerX));
    container->close = 0;
    container->container = ct;
    container->prev = NULL;
    container->next = NULL;
    ct->container = container;
    cxMempoolRegister(obj->ctx->mp, ct, (cx_destructor_func)delete_container);
    uic_object_push_container(obj, container);
}

/* ------------------------ margin helper ------------------------ */

QWidget* ui_widget_set_margin(QWidget *w, int left, int right, int top, int bottom) {
    if((left | right | top | bottom) == 0) {
        return w;
    }
    QWidget* wrapper = new QWidget;
    QVBoxLayout* inner = new QVBoxLayout(wrapper);
    inner->setContentsMargins(left, top, right, bottom);
    inner->addWidget(w);
    
    // TODO: handle widget visibility changes
    
    return wrapper;
}

/* -------------------- UiBoxContainer -------------------- */

UiBoxContainer::UiBoxContainer(QBoxLayout* box) {
    this->box = box;
    this->direction = box->direction();
    box->setContentsMargins(QMargins(0,0,0,0));
    box->setSpacing(0);
}

void UiBoxContainer::add(QWidget* widget, UiLayout& layout) {
    bool fill = layout.fill;
    if(hasStretchedWidget && fill) {
        fill = false;
        fprintf(stderr, "UiError: container has 2 filled widgets");
    }
    
    uic_layout_setup_margin(&layout);
    widget = ui_widget_set_margin(widget, layout.margin_left, layout.margin_right, layout.margin_top, layout.margin_bottom);
    box->addWidget(widget);
    if(direction == Qt::LeftToRight) {
        box->setAlignment(widget, Qt::AlignTop);
    }
    
    if(!hasStretchedWidget) {
        QSpacerItem *newspace = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
        box->removeItem(space);
        box->addSpacerItem(newspace);
        space = newspace; 
    }
    
    if(fill) {
        hasStretchedWidget = true;
    }
    current = widget;
}

UIWIDGET ui_box(UiObject *obj, UiContainerArgs *args, QBoxLayout::Direction dir) {
    UiContainerPrivate *ctn = (UiContainerPrivate*)ui_obj_container(obj);
    UiLayout layout = UI_ARGS2LAYOUT(args);
    
    QWidget *widget = new QWidget();
    QBoxLayout *box = new QBoxLayout(dir);
    widget->setLayout(box);
    ctn->add(widget, layout);
    
    ui_obj_add_container(obj, new UiBoxContainer(box));
    
    return widget;
}

UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) {
    return ui_box(obj, args, QBoxLayout::TopToBottom);
}

UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args) {
    return ui_box(obj, args, QBoxLayout::LeftToRight);
}


/* -------------------- UiGridContainer -------------------- */

UiGridContainer::UiGridContainer(
        QGridLayout *grid,
        int margin,
        int columnspacing,
        int rowspacing,
        bool def_hexpand,
        bool def_vexpand,
        bool def_hfill,
        bool def_vfill)
{
    this->current = NULL;
    this->grid = grid;
    this->def_hexpand = def_hexpand;
    this->def_vexpand = def_vexpand;
    this->def_hfill = def_hfill;
    this->def_vfill = def_vfill;
    grid->setContentsMargins(QMargins(margin, margin, margin, margin));
    grid->setHorizontalSpacing(columnspacing);
    grid->setVerticalSpacing(rowspacing);
}

void UiGridContainer::add(QWidget* widget, UiLayout& layout) {
    if(container->newline) {
        x = 0;
        y++;
        container->newline = false;
    }
    
    uic_layout_setup_expand_fill(&layout, def_hexpand, def_vexpand, def_hfill, def_vfill);
    uic_layout_setup_margin(&layout);
    
    if(layout.hexpand) {
        col_expanding = true;
    }
    if(layout.vexpand) {
        row_expanding = true;
    }
    
    if(layout.hexpand) {
        grid->setColumnStretch(x, 1);
    }
    if(layout.vexpand) {
        grid->setRowStretch(y, 1);
    }
    
    Qt::Alignment alignment = 0;
    if(!layout.hfill) {
        alignment = Qt::AlignLeft;
    }
    if(!layout.vfill) {
        alignment = Qt::AlignTop;
    }
    
    int colspan = layout.colspan > 0 ? layout.colspan : 1;
    int rowspan = layout.rowspan > 0 ? layout.rowspan : 1;
    
    widget = ui_widget_set_margin(widget, layout.margin_left, layout.margin_right, layout.margin_top, layout.margin_bottom);
    grid->addWidget(widget, y, x, rowspan, colspan, alignment);
    
    if(x > max_x) {
        max_x = x;
    }
    if(y > max_y) {
        max_y = y;
    }
    
    x += colspan;
    
    current = widget;
}

void UiGridContainer::end() {
    if(!col_expanding) {
        QSpacerItem *filler = new QSpacerItem(0, 0);
        x = max_x + 1;
        grid->setColumnStretch(x, 1);
        grid->addItem(filler, 0, x, 1, 1, 0);
    }
    if(!row_expanding) {
        QSpacerItem *filler = new QSpacerItem(0, 0);
        y++;
        grid->setRowStretch(y, 1);
        grid->addItem(filler, y, 0, 1, 1, 0);
    }
}

UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
    UiContainerPrivate *ctn = (UiContainerPrivate*)ui_obj_container(obj);
    UiLayout layout = UI_ARGS2LAYOUT(args);
    
    QWidget *widget = new QWidget();
    QGridLayout *grid = new QGridLayout();
    widget->setLayout(grid);
    ctn->add(widget, layout);
    
    ui_obj_add_container(obj, new UiGridContainer(
            grid,
            args->margin,
            args->columnspacing,
            args->rowspacing,
            args->def_hexpand,
            args->def_vexpand,
            args->def_hfill,
            args->def_vfill));
    
    return widget;
}


/* ---------------------------- UiSidebar ---------------------------- */

UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
    QVariant v = obj->widget->property("ui_sidebar");
    QDockWidget *dock = (QDockWidget*)v.value<void*>();
    if(!dock) {
        fprintf(stderr, "Error: window is not configured for sidebar\n");
        return nullptr;
    }
    
    QWidget *widget = new QWidget();
    QBoxLayout *box = new QBoxLayout(QBoxLayout::TopToBottom);
    widget->setLayout(box);
    dock->setWidget(widget);
    
    ui_obj_add_container(obj, new UiBoxContainer(box));
    
    return dock;
}

/* -------------------- Container Helper Functions -------------------- */

void ui_container_begin_close(UiObject *obj) {
    obj->container_end->close = true;
}

int ui_container_finish(UiObject *obj) {
    if(obj->container_end->close) {
        UiContainerPrivate *ctn = (UiContainerPrivate*)obj->container_end->container;
        ctn->end();
        ui_end_new(obj);
        return 0;
    }
    return 1;
}

mercurial