box.c (19290B)
1 /* 2 * Copyright (c) 2021-2023 lumidify <nobody@lumidify.org> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /* FIXME: implement other sticky options now supported by grid */ 18 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <stdarg.h> 22 #include <stdint.h> 23 #include <string.h> 24 25 #include "event.h" 26 #include "memory.h" 27 #include "color.h" 28 #include "rect.h" 29 #include "widget.h" 30 #include "ltk.h" 31 #include "util.h" 32 #include "scrollbar.h" 33 #include "box.h" 34 #include "cmd.h" 35 36 static void ltk_box_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip); 37 static ltk_box *ltk_box_create(ltk_window *window, const char *id, ltk_orientation orient); 38 static void ltk_box_destroy(ltk_widget *self, int shallow); 39 static void ltk_recalculate_box(ltk_widget *self); 40 static void ltk_box_child_size_change(ltk_widget *self, ltk_widget *widget); 41 static int ltk_box_add(ltk_window *window, ltk_widget *widget, ltk_box *box, ltk_sticky_mask sticky, ltk_error *err); 42 static int ltk_box_remove(ltk_widget *widget, ltk_widget *self, ltk_error *err); 43 /* static int ltk_box_clear(ltk_window *window, ltk_box *box, int shallow, ltk_error *err); */ 44 static void ltk_box_scroll(ltk_widget *self); 45 static int ltk_box_mouse_scroll(ltk_widget *self, ltk_scroll_event *event); 46 static ltk_widget *ltk_box_get_child_at_pos(ltk_widget *self, int x, int y); 47 static void ltk_box_ensure_rect_shown(ltk_widget *self, ltk_rect r); 48 49 static ltk_widget *ltk_box_prev_child(ltk_widget *self, ltk_widget *child); 50 static ltk_widget *ltk_box_next_child(ltk_widget *self, ltk_widget *child); 51 static ltk_widget *ltk_box_first_child(ltk_widget *self); 52 static ltk_widget *ltk_box_last_child(ltk_widget *self); 53 54 static ltk_widget *ltk_box_nearest_child(ltk_widget *self, ltk_rect rect); 55 static ltk_widget *ltk_box_nearest_child_left(ltk_widget *self, ltk_widget *widget); 56 static ltk_widget *ltk_box_nearest_child_right(ltk_widget *self, ltk_widget *widget); 57 static ltk_widget *ltk_box_nearest_child_above(ltk_widget *self, ltk_widget *widget); 58 static ltk_widget *ltk_box_nearest_child_below(ltk_widget *self, ltk_widget *widget); 59 60 static struct ltk_widget_vtable vtable = { 61 .change_state = NULL, 62 .hide = NULL, 63 .draw = <k_box_draw, 64 .destroy = <k_box_destroy, 65 .resize = <k_recalculate_box, 66 .child_size_change = <k_box_child_size_change, 67 .remove_child = <k_box_remove, 68 .key_press = NULL, 69 .key_release = NULL, 70 .mouse_press = NULL, 71 .mouse_scroll = <k_box_mouse_scroll, 72 .mouse_release = NULL, 73 .motion_notify = NULL, 74 .get_child_at_pos = <k_box_get_child_at_pos, 75 .mouse_leave = NULL, 76 .mouse_enter = NULL, 77 .prev_child = <k_box_prev_child, 78 .next_child = <k_box_next_child, 79 .first_child = <k_box_first_child, 80 .last_child = <k_box_last_child, 81 .nearest_child = <k_box_nearest_child, 82 .nearest_child_left = <k_box_nearest_child_left, 83 .nearest_child_right = <k_box_nearest_child_right, 84 .nearest_child_above = <k_box_nearest_child_above, 85 .nearest_child_below = <k_box_nearest_child_below, 86 .ensure_rect_shown = <k_box_ensure_rect_shown, 87 .type = LTK_WIDGET_BOX, 88 .flags = 0, 89 }; 90 91 static int ltk_box_cmd_add( 92 ltk_window *window, 93 ltk_box *box, 94 ltk_cmd_token *tokens, 95 size_t num_tokens, 96 ltk_error *err); 97 static int ltk_box_cmd_remove( 98 ltk_window *window, 99 ltk_box *box, 100 ltk_cmd_token *tokens, 101 size_t num_tokens, 102 ltk_error *err); 103 /* 104 static int ltk_box_cmd_clear 105 ltk_window *window, 106 ltk_cmd_token *tokens, 107 size_t num_tokens, 108 ltk_error *err); 109 */ 110 static int ltk_box_cmd_create( 111 ltk_window *window, 112 ltk_box *box, 113 ltk_cmd_token *tokens, 114 size_t num_tokens, 115 ltk_error *err); 116 117 static void 118 ltk_box_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip) { 119 ltk_box *box = (ltk_box *)self; 120 ltk_widget *ptr; 121 /* FIXME: clip out scrollbar */ 122 ltk_rect real_clip = ltk_rect_intersect((ltk_rect){0, 0, self->lrect.w, self->lrect.h}, clip); 123 for (size_t i = 0; i < box->num_widgets; i++) { 124 ptr = box->widgets[i]; 125 /* FIXME: Maybe continue immediately if widget is 126 obviously outside of clipping rect */ 127 ptr->vtable->draw(ptr, s, x + ptr->lrect.x, y + ptr->lrect.y, ltk_rect_relative(ptr->lrect, real_clip)); 128 } 129 box->sc->widget.vtable->draw( 130 (ltk_widget *)box->sc, s, 131 x + box->sc->widget.lrect.x, 132 y + box->sc->widget.lrect.y, 133 ltk_rect_relative(box->sc->widget.lrect, real_clip) 134 ); 135 } 136 137 static ltk_box * 138 ltk_box_create(ltk_window *window, const char *id, ltk_orientation orient) { 139 ltk_box *box = ltk_malloc(sizeof(ltk_box)); 140 141 ltk_fill_widget_defaults(&box->widget, id, window, &vtable, 0, 0); 142 143 box->sc = ltk_scrollbar_create(window, orient, <k_box_scroll, box); 144 box->sc->widget.parent = &box->widget; 145 box->widgets = NULL; 146 box->num_alloc = 0; 147 box->num_widgets = 0; 148 box->orient = orient; 149 if (orient == LTK_HORIZONTAL) 150 box->widget.ideal_h = box->sc->widget.ideal_h; 151 else 152 box->widget.ideal_w = box->sc->widget.ideal_w; 153 ltk_recalculate_box(&box->widget); 154 155 return box; 156 } 157 158 static void 159 ltk_box_ensure_rect_shown(ltk_widget *self, ltk_rect r) { 160 ltk_box *box = (ltk_box *)self; 161 int delta = 0; 162 if (box->orient == LTK_HORIZONTAL) { 163 if (r.x + r.w > self->lrect.w && r.w <= self->lrect.w) 164 delta = r.x - (self->lrect.w - r.w); 165 else if (r.x < 0 || r.w > self->lrect.w) 166 delta = r.x; 167 } else { 168 if (r.y + r.h > self->lrect.h && r.h <= self->lrect.h) 169 delta = r.y - (self->lrect.h - r.h); 170 else if (r.y < 0 || r.h > self->lrect.h) 171 delta = r.y; 172 } 173 if (delta) 174 ltk_scrollbar_scroll(&box->sc->widget, delta, 0); 175 } 176 177 static void 178 ltk_box_destroy(ltk_widget *self, int shallow) { 179 ltk_box *box = (ltk_box *)self; 180 ltk_widget *ptr; 181 ltk_error err; 182 for (size_t i = 0; i < box->num_widgets; i++) { 183 ptr = box->widgets[i]; 184 ptr->parent = NULL; 185 if (!shallow) 186 ltk_widget_destroy(ptr, shallow, &err); 187 } 188 ltk_free(box->widgets); 189 box->sc->widget.parent = NULL; 190 /* FIXME: this is a bit weird because sc isn't a normal widget 191 (it isn't in the widget hash) */ 192 ltk_widget_destroy(&box->sc->widget, 0, &err); 193 ltk_free(box); 194 } 195 196 /* FIXME: Make this function name more consistent */ 197 /* FIXME: The widget positions are set with the old scrollbar->cur_pos, before the 198 virtual_size is set - this can cause problems when a widget changes its size 199 (in the scrolled direction) when resized. */ 200 /* FIXME: avoid complete recalculation when just scrolling (only position updated) */ 201 static void 202 ltk_recalculate_box(ltk_widget *self) { 203 ltk_box *box = (ltk_box *)self; 204 ltk_widget *ptr; 205 ltk_rect *sc_rect = &box->sc->widget.lrect; 206 int cur_pos = 0; 207 if (box->orient == LTK_HORIZONTAL) 208 sc_rect->h = box->sc->widget.ideal_h; 209 else 210 sc_rect->w = box->sc->widget.ideal_w; 211 for (size_t i = 0; i < box->num_widgets; i++) { 212 ptr = box->widgets[i]; 213 if (box->orient == LTK_HORIZONTAL) { 214 ptr->lrect.x = cur_pos - box->sc->cur_pos; 215 if (ptr->sticky & LTK_STICKY_TOP && ptr->sticky & LTK_STICKY_BOTTOM) 216 ptr->lrect.h = box->widget.lrect.h - sc_rect->h; 217 if (ptr->sticky & LTK_STICKY_TOP) 218 ptr->lrect.y = 0; 219 else if (ptr->sticky & LTK_STICKY_BOTTOM) 220 ptr->lrect.y = box->widget.lrect.h - ptr->lrect.h - sc_rect->h; 221 else 222 ptr->lrect.y = (box->widget.lrect.h - ptr->lrect.h) / 2; 223 cur_pos += ptr->lrect.w; 224 } else { 225 ptr->lrect.y = cur_pos - box->sc->cur_pos; 226 if (ptr->sticky & LTK_STICKY_LEFT && ptr->sticky & LTK_STICKY_RIGHT) 227 ptr->lrect.w = box->widget.lrect.w - sc_rect->w; 228 if (ptr->sticky & LTK_STICKY_LEFT) 229 ptr->lrect.x = 0; 230 else if (ptr->sticky & LTK_STICKY_RIGHT) 231 ptr->lrect.x = box->widget.lrect.w - ptr->lrect.w - sc_rect->w; 232 else 233 ptr->lrect.x = (box->widget.lrect.w - ptr->lrect.w) / 2; 234 cur_pos += ptr->lrect.h; 235 } 236 ptr->crect = ltk_rect_intersect((ltk_rect){0, 0, self->crect.w, self->crect.h}, ptr->lrect); 237 ltk_widget_resize(ptr); 238 } 239 ltk_scrollbar_set_virtual_size(box->sc, cur_pos); 240 if (box->orient == LTK_HORIZONTAL) { 241 sc_rect->x = 0; 242 sc_rect->y = box->widget.lrect.h - sc_rect->h; 243 sc_rect->w = box->widget.lrect.w; 244 } else { 245 sc_rect->x = box->widget.lrect.w - sc_rect->w; 246 sc_rect->y = 0; 247 sc_rect->h = box->widget.lrect.h; 248 } 249 *sc_rect = ltk_rect_intersect(*sc_rect, (ltk_rect){0, 0, box->widget.lrect.w, box->widget.lrect.h}); 250 box->sc->widget.crect = ltk_rect_intersect((ltk_rect){0, 0, self->crect.w, self->crect.h}, *sc_rect); 251 ltk_widget_resize((ltk_widget *)box->sc); 252 } 253 254 /* FIXME: This entire resizing thing is a bit weird. For instance, if a label 255 in a vertical box increases its height because its width has been decreased 256 and it is forced to wrap, should that just change the rect or also the 257 ideal size? Ideal size wouldn't really make sense here, but then the box 258 might be forced to add a scrollbar even though the parent widget would 259 actually give it more space if it knew that it needed it. */ 260 261 static void 262 ltk_box_child_size_change(ltk_widget *self, ltk_widget *widget) { 263 ltk_box *box = (ltk_box *)self; 264 short size_changed = 0; 265 /* This is always reset here - if it needs to be changed, 266 the resize function called by the last child_size_change 267 function will fix it */ 268 /* Note: This seems a bit weird, but if each widget set its rect itself, 269 that would also lead to weird things. For instance, if a butten is 270 added to after a box after being ungridded, and its rect was changed 271 by the grid (e.g. because of a column weight), who should reset the 272 rect if it doesn't have sticky set? Of course, the resize function 273 could also set all widgets even if they don't have any sticky 274 settings, but there'd probably be some catch as well. */ 275 /* FIXME: the same comment as in grid.c applies */ 276 int orig_w = widget->lrect.w; 277 int orig_h = widget->lrect.h; 278 widget->lrect.w = widget->ideal_w; 279 widget->lrect.h = widget->ideal_h; 280 int sc_w = box->sc->widget.lrect.w; 281 int sc_h = box->sc->widget.lrect.h; 282 if (box->orient == LTK_HORIZONTAL && widget->ideal_h + sc_h > box->widget.ideal_h) { 283 box->widget.ideal_h = widget->ideal_h + sc_h; 284 size_changed = 1; 285 } else if (box->orient == LTK_VERTICAL && widget->ideal_w + sc_w > box->widget.ideal_h) { 286 box->widget.ideal_w = widget->ideal_w + sc_w; 287 size_changed = 1; 288 } 289 290 if (size_changed && box->widget.parent && box->widget.parent->vtable->child_size_change) 291 box->widget.parent->vtable->child_size_change(box->widget.parent, (ltk_widget *)box); 292 else 293 ltk_recalculate_box((ltk_widget *)box); 294 if (orig_w != widget->lrect.w || orig_h != widget->lrect.h) 295 ltk_widget_resize(widget); 296 } 297 298 static int 299 ltk_box_add(ltk_window *window, ltk_widget *widget, ltk_box *box, ltk_sticky_mask sticky, ltk_error *err) { 300 if (widget->parent) { 301 err->type = ERR_WIDGET_IN_CONTAINER; 302 return 1; 303 } 304 if (box->num_widgets >= box->num_alloc) { 305 size_t new_size = box->num_alloc > 0 ? box->num_alloc * 2 : 4; 306 ltk_widget **new = ltk_realloc(box->widgets, new_size * sizeof(ltk_widget *)); 307 box->num_alloc = new_size; 308 box->widgets = new; 309 } 310 311 int sc_w = box->sc->widget.lrect.w; 312 int sc_h = box->sc->widget.lrect.h; 313 314 box->widgets[box->num_widgets++] = widget; 315 if (box->orient == LTK_HORIZONTAL) { 316 box->widget.ideal_w += widget->ideal_w; 317 if (widget->ideal_h + sc_h > box->widget.ideal_h) 318 box->widget.ideal_h = widget->ideal_h + sc_h; 319 } else { 320 box->widget.ideal_h += widget->ideal_h; 321 if (widget->ideal_w + sc_w > box->widget.ideal_w) 322 box->widget.ideal_w = widget->ideal_w + sc_w; 323 } 324 widget->parent = (ltk_widget *)box; 325 widget->sticky = sticky; 326 ltk_box_child_size_change((ltk_widget *)box, widget); 327 ltk_window_invalidate_widget_rect(window, &box->widget); 328 329 return 0; 330 } 331 332 static int 333 ltk_box_remove(ltk_widget *widget, ltk_widget *self, ltk_error *err) { 334 ltk_box *box = (ltk_box *)self; 335 int sc_w = box->sc->widget.lrect.w; 336 int sc_h = box->sc->widget.lrect.h; 337 if (widget->parent != (ltk_widget *)box) { 338 err->type = ERR_WIDGET_NOT_IN_CONTAINER; 339 return 1; 340 } 341 widget->parent = NULL; 342 for (size_t i = 0; i < box->num_widgets; i++) { 343 if (box->widgets[i] == widget) { 344 if (i < box->num_widgets - 1) 345 memmove(box->widgets + i, box->widgets + i + 1, 346 (box->num_widgets - i - 1) * sizeof(ltk_widget *)); 347 box->num_widgets--; 348 ltk_window_invalidate_widget_rect(widget->window, &box->widget); 349 /* search for new ideal width/height */ 350 /* FIXME: make this all a bit nicer and break the lines better */ 351 /* FIXME: other part of ideal size not updated */ 352 if (box->orient == LTK_HORIZONTAL && widget->ideal_h + sc_h == box->widget.ideal_h) { 353 box->widget.ideal_h = 0; 354 for (size_t j = 0; j < box->num_widgets; j++) { 355 if (box->widgets[j]->ideal_h + sc_h > box->widget.ideal_h) 356 box->widget.ideal_h = box->widgets[j]->ideal_h + sc_h; 357 } 358 if (box->widget.parent) 359 ltk_widget_resize(box->widget.parent); 360 } else if (box->orient == LTK_VERTICAL && widget->ideal_w + sc_w == box->widget.ideal_w) { 361 box->widget.ideal_w = 0; 362 for (size_t j = 0; j < box->num_widgets; j++) { 363 if (box->widgets[j]->ideal_w + sc_w > box->widget.ideal_w) 364 box->widget.ideal_w = box->widgets[j]->ideal_w + sc_w; 365 } 366 if (box->widget.parent) 367 ltk_widget_resize(box->widget.parent); 368 } 369 return 0; 370 } 371 } 372 373 err->type = ERR_WIDGET_NOT_IN_CONTAINER; 374 return 1; 375 } 376 377 /* FIXME: maybe come up with a more efficient method */ 378 static ltk_widget * 379 ltk_box_nearest_child(ltk_widget *self, ltk_rect rect) { 380 ltk_box *box = (ltk_box *)self; 381 ltk_widget *minw = NULL; 382 int min_dist = INT_MAX; 383 int cx = rect.x + rect.w / 2; 384 int cy = rect.y + rect.h / 2; 385 ltk_rect r; 386 int dist; 387 for (size_t i = 0; i < box->num_widgets; i++) { 388 r = box->widgets[i]->lrect; 389 dist = abs((r.x + r.w / 2) - cx) + abs((r.y + r.h / 2) - cy); 390 if (dist < min_dist) { 391 min_dist = dist; 392 minw = box->widgets[i]; 393 } 394 } 395 return minw; 396 } 397 398 static ltk_widget * 399 ltk_box_nearest_child_left(ltk_widget *self, ltk_widget *widget) { 400 ltk_box *box = (ltk_box *)self; 401 if (box->orient == LTK_VERTICAL) 402 return NULL; 403 return ltk_box_prev_child(self, widget); 404 } 405 406 static ltk_widget * 407 ltk_box_nearest_child_right(ltk_widget *self, ltk_widget *widget) { 408 ltk_box *box = (ltk_box *)self; 409 if (box->orient == LTK_VERTICAL) 410 return NULL; 411 return ltk_box_next_child(self, widget); 412 } 413 414 static ltk_widget * 415 ltk_box_nearest_child_above(ltk_widget *self, ltk_widget *widget) { 416 ltk_box *box = (ltk_box *)self; 417 if (box->orient == LTK_HORIZONTAL) 418 return NULL; 419 return ltk_box_prev_child(self, widget); 420 } 421 422 static ltk_widget * 423 ltk_box_nearest_child_below(ltk_widget *self, ltk_widget *widget) { 424 ltk_box *box = (ltk_box *)self; 425 if (box->orient == LTK_HORIZONTAL) 426 return NULL; 427 return ltk_box_next_child(self, widget); 428 } 429 430 static ltk_widget * 431 ltk_box_prev_child(ltk_widget *self, ltk_widget *child) { 432 ltk_box *box = (ltk_box *)self; 433 for (size_t i = box->num_widgets; i-- > 0;) { 434 if (box->widgets[i] == child) 435 return i > 0 ? box->widgets[i-1] : NULL; 436 } 437 return NULL; 438 } 439 440 static ltk_widget * 441 ltk_box_next_child(ltk_widget *self, ltk_widget *child) { 442 ltk_box *box = (ltk_box *)self; 443 for (size_t i = 0; i < box->num_widgets; i++) { 444 if (box->widgets[i] == child) 445 return i < box->num_widgets - 1 ? box->widgets[i+1] : NULL; 446 } 447 return NULL; 448 } 449 450 static ltk_widget * 451 ltk_box_first_child(ltk_widget *self) { 452 ltk_box *box = (ltk_box *)self; 453 return box->num_widgets > 0 ? box->widgets[0] : NULL; 454 } 455 456 static ltk_widget * 457 ltk_box_last_child(ltk_widget *self) { 458 ltk_box *box = (ltk_box *)self; 459 return box->num_widgets > 0 ? box->widgets[box->num_widgets-1] : NULL; 460 } 461 462 static void 463 ltk_box_scroll(ltk_widget *self) { 464 ltk_box *box = (ltk_box *)self; 465 ltk_recalculate_box(self); 466 ltk_window_invalidate_widget_rect(box->widget.window, &box->widget); 467 } 468 469 static ltk_widget * 470 ltk_box_get_child_at_pos(ltk_widget *self, int x, int y) { 471 ltk_box *box = (ltk_box *)self; 472 if (ltk_collide_rect(box->sc->widget.crect, x, y)) 473 return (ltk_widget *)box->sc; 474 for (size_t i = 0; i < box->num_widgets; i++) { 475 if (ltk_collide_rect(box->widgets[i]->crect, x, y)) 476 return box->widgets[i]; 477 } 478 return NULL; 479 } 480 481 static int 482 ltk_box_mouse_scroll(ltk_widget *self, ltk_scroll_event *event) { 483 ltk_box *box = (ltk_box *)self; 484 if (event->dy) { 485 /* FIXME: horizontal scrolling, etc. */ 486 /* FIXME: configure scrollstep */ 487 int delta = event->dy * -15; 488 ltk_scrollbar_scroll((ltk_widget *)box->sc, delta, 0); 489 ltk_point glob = ltk_widget_pos_to_global(self, event->x, event->y); 490 ltk_window_fake_motion_event(self->window, glob.x, glob.y); 491 return 1; 492 } 493 return 0; 494 } 495 496 /* box <box id> add <widget id> [sticky] */ 497 static int 498 ltk_box_cmd_add( 499 ltk_window *window, 500 ltk_box *box, 501 ltk_cmd_token *tokens, 502 size_t num_tokens, 503 ltk_error *err) { 504 ltk_cmdarg_parseinfo cmd[] = { 505 {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_ANY, .optional = 0}, 506 {.type = CMDARG_STICKY, .optional = 1}, 507 }; 508 if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) 509 return 1; 510 if (ltk_box_add(window, cmd[0].val.widget, box, cmd[1].initialized ? cmd[1].val.sticky : 0, err)) { 511 err->arg = 0; 512 return 1; 513 } 514 return 0; 515 } 516 517 /* box <box id> remove <widget id> */ 518 static int 519 ltk_box_cmd_remove( 520 ltk_window *window, 521 ltk_box *box, 522 ltk_cmd_token *tokens, 523 size_t num_tokens, 524 ltk_error *err) { 525 ltk_cmdarg_parseinfo cmd[] = { 526 {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_ANY, .optional = 0}, 527 }; 528 if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) 529 return 1; 530 if (ltk_box_remove(cmd[0].val.widget, (ltk_widget *)box, err)) { 531 err->arg = 0; 532 return 1; 533 } 534 return 0; 535 } 536 537 /* box <box id> create <orientation> */ 538 static int 539 ltk_box_cmd_create( 540 ltk_window *window, 541 ltk_box *box_unneeded, 542 ltk_cmd_token *tokens, 543 size_t num_tokens, 544 ltk_error *err) { 545 (void)box_unneeded; 546 ltk_cmdarg_parseinfo cmd[] = { 547 {.type = CMDARG_IGNORE, .optional = 0}, 548 {.type = CMDARG_STRING, .optional = 0}, 549 {.type = CMDARG_IGNORE, .optional = 0}, 550 {.type = CMDARG_ORIENTATION, .optional = 0}, 551 }; 552 if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) 553 return 1; 554 if (!ltk_widget_id_free(cmd[1].val.str)) { 555 err->type = ERR_WIDGET_ID_IN_USE; 556 err->arg = 1; 557 return 1; 558 } 559 ltk_box *box = ltk_box_create(window, cmd[1].val.str, cmd[3].val.orient); 560 ltk_set_widget((ltk_widget *)box, cmd[1].val.str); 561 562 return 0; 563 } 564 565 static struct box_cmd { 566 char *name; 567 int (*func)(ltk_window *, ltk_box *, ltk_cmd_token *, size_t, ltk_error *); 568 int needs_all; 569 } box_cmds[] = { 570 {"add", <k_box_cmd_add, 0}, 571 {"create", <k_box_cmd_create, 1}, 572 {"remove", <k_box_cmd_remove, 0}, 573 }; 574 575 GEN_CMD_HELPERS(ltk_box_cmd, LTK_WIDGET_BOX, ltk_box, box_cmds, struct box_cmd)