ui/cocoa/container.m

Tue, 14 Oct 2025 09:45:44 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 14 Oct 2025 09:45:44 +0200
changeset 846
ffa983c223c1
parent 811
1391ba7e533f
child 847
50de0f36973f
permissions
-rw-r--r--

add frame container (Cocoa)

/*
 * 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.
 */

#import "Container.h"
#import "GridLayout.h"
#import "BoxContainer.h"

/* -------------------- public container functions --------------------- */

static UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, NSUserInterfaceLayoutOrientation orientation) {
    BoxContainer *box = [[BoxContainer alloc] init:orientation spacing:args->spacing];
    box.translatesAutoresizingMaskIntoConstraints = false;
    UiContainerX *container = ui_create_container(obj, box);
    box.container = container;
    
    // add box to the parent
    UiLayout layout = UI_INIT_LAYOUT(args);
    ui_container_add(obj, box, &layout);
    
    // add new box to the obj container chain
    uic_object_push_container(obj, container);
    
    return (__bridge void*)box;
}

UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) {
    return ui_box_create(obj, args, NSUserInterfaceLayoutOrientationVertical);
}

UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args) {
    return ui_box_create(obj, args, NSUserInterfaceLayoutOrientationHorizontal);
}

UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
    GridLayout *grid = [[GridLayout alloc] init];
    grid.translatesAutoresizingMaskIntoConstraints = false;
    grid.columnspacing = args->columnspacing;
    grid.rowspacing = args->rowspacing;
    UiContainerX *container = ui_create_container(obj, grid);
    grid.container = container;
    
    // add box to the parent
    UiLayout layout = UI_INIT_LAYOUT(args);
    ui_container_add(obj, grid, &layout);
    
    // add new box to the obj container chain
    uic_object_push_container(obj, container);
    
    return (__bridge void*)grid;
}

UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
    NSString *title = args->label ? [[NSString alloc]initWithUTF8String:args->label] : nil;
    FrameContainer *frame = [[FrameContainer alloc] init:title];    UiLayout layout = UI_ARGS2LAYOUT(args);
    ui_container_add(obj, frame, &layout);
    
    // add container to the chain
    UiContainerX *container;
    UiLayout subLayout = {0};
    switch(args->subcontainer) {
        default: {
            // UI_CONTAINER_NO_SUB
            container = ui_create_container(obj, frame);
            break;
        }
        case UI_CONTAINER_VBOX: {
            BoxContainer *box = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationVertical spacing:args->spacing];
            box.translatesAutoresizingMaskIntoConstraints = false;
            [frame addView:box layout:&subLayout];
            container = ui_create_container(obj, box);
            break;
        }
        case UI_CONTAINER_HBOX: {
            BoxContainer *box = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationHorizontal spacing:args->spacing];
            box.translatesAutoresizingMaskIntoConstraints = false;
            [frame addView:box layout:&subLayout];
            container = ui_create_container(obj, box);
            break;
        }
        case UI_CONTAINER_GRID: {
            GridLayout *grid = [[GridLayout alloc] init];
            grid.translatesAutoresizingMaskIntoConstraints = false;
            grid.columnspacing = args->columnspacing;
            grid.rowspacing = args->rowspacing;
            [frame addView:grid layout:&subLayout];
            container = ui_create_container(obj, grid);
            break;
        }
    }
    
    uic_object_push_container(obj, container);
    
    return NULL;
}

void ui_container_begin_close(UiObject *obj) {
    UiContainerX *ct = obj->container_end;
    ct->close = 1;
}

int ui_container_finish(UiObject *obj) {
    UiContainerX *ct = obj->container_end;
    if(ct->close) {
        ui_end_new(obj);
        return 0;
    }
    return 1;
}

/* -------------------------- Frame Container -------------------------- */

@implementation FrameContainer

@synthesize container = _container;

- (id)init:(NSString*)title {
    self = [super init];
    self.title = title;
    self.boxType = NSBoxPrimary;
    if(title != nil) {
        self.titlePosition = NSAtTop;
    }
    return self;
}

- (void) addView:(NSView*)view layout:(UiLayout*)layout {
    [self.contentView addSubview:view];
    view.translatesAutoresizingMaskIntoConstraints = NO;
    [NSLayoutConstraint activateConstraints:@[
        [view.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:0],
        [view.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:0],
        [view.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-0],
        [view.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-0]
    ]];
}

@end

/* ------------------------- private functions ------------------------- */

UiContainerX* ui_create_container(UiObject *obj, id<Container> container) {
    UiContainerX *ctn = ui_malloc(obj->ctx, sizeof(UiContainerX));
    ctn->container = (__bridge void*)container;
    ctn->close = 0;
    ctn->prev = NULL;
    ctn->next = NULL;
    container.container = ctn;
    return ctn;
}

void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout) {
    UiContainerX *ctn = obj->container_end;
    id<Container> container = (__bridge id<Container>)ctn->container;
    UiLayout adjustedLayout = *layout;
    if(adjustedLayout.margin > 0) {
        adjustedLayout.margin_left = adjustedLayout.margin;
        adjustedLayout.margin_right = adjustedLayout.margin;
        adjustedLayout.margin_top = adjustedLayout.margin;
        adjustedLayout.margin_bottom = adjustedLayout.margin;
    }
    [container addView:view layout:layout];
}

mercurial