1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2024 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 #import "Container.h"
30 #import "GridLayout.h"
31
32 /* ------------------------- container classes ------------------------- */
33
34 @implementation BoxContainer
35
36 @synthesize label=_label;
37 @synthesize uilayout=_uilayout;
38 @synthesize newline=_newline;
39
40 - (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing {
41 self = [super init];
42 _label = NULL;
43 _uilayout = (UiLayout){ 0 };
44 _newline = false;
45
46 self.distribution = NSStackViewDistributionFillProportionally;
47 self.spacing = spacing;
48
49 self.orientation = orientation;
50 if(orientation == NSUserInterfaceLayoutOrientationHorizontal) {
51 self.alignment = NSLayoutAttributeHeight;
52 } else {
53 self.alignment = NSLayoutAttributeWidth;
54 }
55
56
57 return self;
58 }
59
60 - (void) addView:(NSView*)view fill:(BOOL)fill {
61 if(_uilayout.fill != UI_LAYOUT_UNDEFINED) {
62 fill = ui_lb2bool(_uilayout.fill);
63 }
64
65 [self addArrangedSubview:view];
66
67 if(self.orientation == NSUserInterfaceLayoutOrientationHorizontal) {
68 [view.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES;
69 if(!fill) {
70 [view.widthAnchor constraintEqualToConstant:view.intrinsicContentSize.width].active = YES;
71 }
72 } else {
73 [view.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES;
74 if(!fill) {
75 [view.heightAnchor constraintEqualToConstant:view.intrinsicContentSize.height].active = YES;
76 }
77 }
78
79 // at the moment, only the fill layout option needs to be reset
80 _uilayout.fill = UI_DEFAULT;
81 }
82
83 @end
84
85
86
87 /* -------------------- public container functions --------------------- */
88
89 static UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, NSUserInterfaceLayoutOrientation orientation) {
90 BoxContainer *box = [[BoxContainer alloc] init:orientation spacing:args.spacing];
91 box.translatesAutoresizingMaskIntoConstraints = false;
92
93 // add box to the parent
94 UiLayout layout = UI_INIT_LAYOUT(args);
95 ui_container_add(obj, box, &layout, TRUE);
96
97 // add new box to the obj container chain
98 uic_object_push_container(obj, ui_create_container(obj, box));
99
100 return (__bridge void*)box;
101 }
102
103 UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args) {
104 return ui_box_create(obj, args, NSUserInterfaceLayoutOrientationVertical);
105 }
106
107 UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args) {
108 return ui_box_create(obj, args, NSUserInterfaceLayoutOrientationHorizontal);
109 }
110
111 UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args) {
112 GridLayout *grid = [[GridLayout alloc] init];
113 grid.translatesAutoresizingMaskIntoConstraints = false;
114
115 // add box to the parent
116 UiLayout layout = UI_INIT_LAYOUT(args);
117 ui_container_add(obj, grid, &layout, TRUE);
118
119 // add new box to the obj container chain
120 uic_object_push_container(obj, ui_create_container(obj, grid));
121
122 return (__bridge void*)grid;
123 }
124
125
126 void ui_container_begin_close(UiObject *obj) {
127 UiContainerX *ct = obj->container_end;
128 ct->close = 1;
129 }
130
131 int ui_container_finish(UiObject *obj) {
132 UiContainerX *ct = obj->container_end;
133 if(ct->close) {
134 ui_end_new(obj);
135 return 0;
136 }
137 return 1;
138 }
139
140 /* ------------------------- private functions ------------------------- */
141
142 UiContainerX* ui_create_container(UiObject *obj, id<Container> container) {
143 UiContainerX *ctn = ui_malloc(obj->ctx, sizeof(UiContainerX));
144 ctn->container = (__bridge void*)container;
145 ctn->close = 0;
146 ctn->prev = NULL;
147 ctn->next = NULL;
148 return ctn;
149 }
150
151 void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout, UiBool fill) {
152 UiContainerX *ctn = obj->container_end;
153 id<Container> container = (__bridge id<Container>)ctn->container;
154 container.uilayout = *layout;
155 [container addView:view fill:fill];
156 }
157
158 /* ---------------------- public layout functions ----------------------- */
159
160 void ui_newline(UiObject *obj) {
161 UiContainerX *ctn = obj->container_end;
162 if(ctn) {
163 id<Container> container = (__bridge id<Container>)ctn->container;
164 container.newline = TRUE;
165 } else {
166 fprintf(stderr, "Error: obj has no container\n");
167 }
168 }
169