pane.c (12753B)
1 #include <termbox.h> 2 #include <stdarg.h> 3 #include <stdlib.h> 4 5 #include "pane.h" 6 #include "utils.h" 7 8 #define T Pane_T 9 struct T { 10 int horizontal; 11 int children_num; 12 T *children; 13 int *rules; 14 15 int x; 16 int y; 17 18 int width; 19 int height; 20 21 int active; 22 T direction[4]; 23 24 widget_T widget; 25 26 char *title; 27 int index; 28 int child_no; 29 T parent; 30 }; 31 32 int pane_hasBorder(T self) 33 { 34 return !self->children; 35 } 36 37 int pane_x(T self) 38 { 39 if (pane_hasBorder(self)) 40 return self->x + 1; 41 else 42 return self->x; 43 } 44 45 int pane_y(T self) 46 { 47 if (pane_hasBorder(self)) 48 return self->y + 1; 49 else 50 return self->y; 51 } 52 int pane_width(T self) 53 { 54 if (pane_hasBorder(self)) 55 return self->width - 2; 56 else 57 return self->width; 58 } 59 int pane_height(T self) 60 { 61 if (pane_hasBorder(self)) 62 return self->height - 2; 63 else 64 return self->height; 65 } 66 67 #define UP(pane) pane->direction[0] 68 #define RIGHT(pane) pane->direction[1] 69 #define DOWN(pane) pane->direction[2] 70 #define LEFT(pane) pane->direction[3] 71 72 Pane_T MAIN; 73 Pane_T ACTIVE; 74 75 #define BORDER_HORIZONTAL "─" 76 #define BORDER_VERTICAL "│" 77 #define BORDER_CORNER_1 "┌" 78 #define BORDER_CORNER_2 "┐" 79 #define BORDER_CORNER_3 "└" 80 #define BORDER_CORNER_4 "┘" 81 82 void draw_title(T self) 83 { 84 if (self->title) 85 tb_printf(self->x + 1, self->y, TB_RED, 0, " %s ", self->title); 86 else 87 tb_printf(self->x + 1, self->y, TB_RED, 0, " %d ", self->index); 88 } 89 90 void draw_border(int x, int y, int x_max, int y_max, uintattr_t color) 91 { 92 size_t i; 93 94 for (i = x + 1; i < x_max; i++) { 95 tb_print(i, y, color, 0, BORDER_HORIZONTAL); 96 tb_print(i, y_max, color, 0, BORDER_HORIZONTAL); 97 } 98 99 for (i = y + 1; i < y_max; i++) { 100 tb_print(x, i, color, 0, BORDER_VERTICAL); 101 tb_print(x_max, i, color, 0, BORDER_VERTICAL); 102 } 103 104 tb_print(x, y, color, 0, BORDER_CORNER_1); 105 tb_print(x_max, y, color, 0, BORDER_CORNER_2); 106 tb_print(x, y_max, color, 0, BORDER_CORNER_3); 107 tb_print(x_max, y_max, color, 0, BORDER_CORNER_4); 108 } 109 110 void pane_draw_border(T self) 111 { 112 int x_max = self->x + self->width - 1; 113 int y_max = self->y + self->height - 1; 114 int color = (self == ACTIVE) ? TB_BLUE : TB_GREEN; 115 116 draw_border(self->x, self->y, x_max, y_max, color); 117 draw_title(self); 118 } 119 120 void pane_clear(T self, int clear_border) 121 { 122 size_t i, j; 123 int border = !clear_border; 124 125 for (j = border; j < self->height - border; j++) 126 for (i = border; i < self->width - border; i++) 127 tb_printf(self->x + i, self->y + j, TB_BLACK, 0, " "); 128 } 129 130 #define SPLIT_FORMULA(ac, ad, sc, sd) \ 131 { \ 132 int total = 0, part, next; \ 133 size_t i; \ 134 int available = self->ad; \ 135 int last_split = -1; \ 136 \ 137 for (i = 0; i < self->children_num; i++) { \ 138 self->children[i]->sc = self->sc; \ 139 self->children[i]->sd = self->sd; \ 140 if (self->rules[i] > 0) { \ 141 total += self->rules[i]; \ 142 last_split = i; \ 143 } else { \ 144 available += self->rules[i]; \ 145 } \ 146 } \ 147 \ 148 part = available / total; \ 149 next = self->ac; \ 150 total = 0; \ 151 for (i = 0; i < last_split; i++) { \ 152 self->children[i]->ac = next; \ 153 if (self->rules[i] > 0) { \ 154 total += self->children[i]->ad = \ 155 part * self->rules[i]; \ 156 } else { \ 157 self->children[i]->ad = -self->rules[i]; \ 158 } \ 159 next += self->children[i]->ad; \ 160 } \ 161 \ 162 self->children[i]->ac = next; \ 163 self->children[i]->ad = available - total; \ 164 next += self->children[i]->ad; \ 165 \ 166 for (i = i + 1; i < self->children_num; i++) { \ 167 self->children[i]->ac = next; \ 168 self->children[i]->ad = -self->rules[i]; \ 169 next += self->children[i]->ad; \ 170 } \ 171 } 172 173 #define wrap_or_slap(dir, index) \ 174 dir(child) = (dir(self) && dir(self) != self) ? dir(self) : \ 175 self->children[index]; 176 177 #define CONNECTION_FORMULA(af, ab, s1, s2) \ 178 { \ 179 T child; \ 180 for (i = 0; i < self->children_num; i++) { \ 181 child = self->children[i]; \ 182 wrap_or_slap(s1, i); \ 183 wrap_or_slap(s2, i); \ 184 } \ 185 \ 186 for (i = 1; i < self->children_num; i++) \ 187 ab(self->children[i]) = self->children[i - 1]; \ 188 child = self->children[0]; \ 189 wrap_or_slap(ab, self->children_num - 1); \ 190 \ 191 for (i = 0; i < self->children_num - 1; i++) \ 192 af(self->children[i]) = self->children[i + 1]; \ 193 child = self->children[self->children_num - 1]; \ 194 wrap_or_slap(af, 0); \ 195 } 196 197 int calc_children(T self) 198 { 199 if (self->horizontal) 200 SPLIT_FORMULA(x, width, y, height) 201 else 202 SPLIT_FORMULA(y, height, x, width) 203 204 return 1; 205 } 206 207 int pane_resize(T self) 208 { 209 size_t i; 210 211 if (self->widget) { 212 self->widget->resized = 1; 213 self->widget->callback(self->widget); 214 } 215 216 if (self->children_num == 0) { 217 pane_draw_border(self); 218 return 1; 219 } 220 221 calc_children(self); 222 for (i = 0; i < self->children_num; i++) 223 pane_resize(self->children[i]); 224 225 return 1; 226 } 227 228 int pane_handle_resize(void) 229 { 230 MAIN->height = tb_height(); 231 MAIN->width = tb_width(); 232 MAIN->x = 0; 233 MAIN->y = 0; 234 MAIN->direction[0] = NULL; 235 MAIN->direction[1] = NULL; 236 MAIN->direction[2] = NULL; 237 MAIN->direction[3] = NULL; 238 239 tb_clear(); 240 pane_resize(MAIN); 241 tb_present(); 242 243 return 1; 244 } 245 246 int pane_start(int active) 247 { 248 tb_init(); 249 MAIN = calloc(1, sizeof(*MAIN)); 250 MAIN->height = tb_height(); 251 MAIN->width = tb_width(); 252 MAIN->parent = MAIN; 253 pane_handle_resize(); 254 255 if (active) { 256 ACTIVE = MAIN; 257 MAIN->active = 1; 258 } 259 260 return 1; 261 } 262 263 int pane_stop(void) 264 { 265 return tb_shutdown(); 266 } 267 268 T pane_child(T self, int index) 269 { 270 return self->children[index]; 271 } 272 273 widget_T pane_widget(T self) 274 { 275 return self->widget; 276 } 277 278 void split(T self, size_t count, va_list ap) 279 { 280 static int index = 1; 281 size_t i; 282 283 self->children_num = count; 284 self->children = malloc(self->children_num * sizeof(T)); 285 self->rules = malloc(self->children_num * sizeof(int)); 286 287 for (i = 0; i < self->children_num; i++) { 288 self->children[i] = calloc(1, sizeof(struct T)); 289 self->rules[i] = va_arg(ap, int); 290 291 self->children[i]->index = index++; 292 self->children[i]->parent = self; 293 self->children[i]->child_no = i; 294 } 295 296 if (self->active) { 297 self->active = 0; 298 self->children[0]->active = 1; 299 ACTIVE = self->children[0]; 300 } 301 302 if (self->widget) 303 widget_deactivate(self->widget); 304 305 pane_resize(self); 306 } 307 308 void pane_unsplit(T self) 309 { 310 size_t i; 311 int active = 0; 312 313 if (self->children_num) { 314 for (i = 0; i < self->children_num; i++) { 315 pane_unsplit(self->children[i]); 316 if (self->children[i]->active) 317 active = 1; 318 free(self->children[i]); 319 } 320 free(self->children); 321 free(self->rules); 322 self->children = NULL; 323 self->rules = NULL; 324 } else { 325 active = self->active; 326 } 327 self->children_num = 0; 328 self->active = active; 329 if (active) { 330 ACTIVE = self; 331 } 332 pane_clear(self, 1); 333 pane_draw_border(self); 334 tb_present(); 335 } 336 337 T *pane_split(T self, size_t count, ...) 338 { 339 size_t i; 340 va_list ap; 341 342 self->horizontal = 1; 343 va_start(ap, count); 344 split(self, count, ap); 345 va_end(ap); 346 347 CONNECTION_FORMULA(RIGHT, LEFT, UP, DOWN) 348 349 return self->children; 350 } 351 352 T *pane_vsplit(T self, size_t count, ...) 353 { 354 size_t i; 355 va_list ap; 356 357 self->horizontal = 0; 358 va_start(ap, count); 359 split(self, count, ap); 360 va_end(ap); 361 362 CONNECTION_FORMULA(DOWN, UP, LEFT, RIGHT) 363 364 return self->children; 365 } 366 367 void pane_setTitle(T self, char *title) 368 { 369 self->title = title; 370 if (self->children_num == 0) { 371 pane_draw_border(self); 372 draw_title(self); 373 } 374 } 375 376 void setActive(int dir) 377 { 378 T old = ACTIVE, tmp; 379 if (!ACTIVE->direction[dir]) 380 return; 381 382 ACTIVE = ACTIVE->direction[dir]; 383 384 while (ACTIVE->children_num) { 385 int last = ACTIVE->children_num - 1; 386 if (!ACTIVE->horizontal) { 387 if (dir == 0 || dir == 2) { 388 ACTIVE = ACTIVE->children[dir == 2 ? 0 : last]; 389 continue; 390 } 391 } else { 392 if (dir == 1 || dir == 3) { 393 ACTIVE = ACTIVE->children[dir == 1 ? 0 : last]; 394 continue; 395 } 396 } 397 ACTIVE = ACTIVE->children[ACTIVE->active]; 398 } 399 400 old->active = 0; 401 ACTIVE->active = 1; 402 403 for (tmp = ACTIVE; tmp->parent != tmp; tmp = tmp->parent) { 404 tmp->parent->active = tmp->child_no; 405 } 406 407 pane_draw_border(old); 408 pane_draw_border(ACTIVE); 409 tb_present(); 410 } 411 412 int centerVertical(Pane_T pane, int len) 413 { 414 if (len > pane_height(pane)) 415 return pane_hasBorder(pane) ? 1 : 0; 416 417 return pane_y(pane) + (pane_height(pane) - len) / 2; 418 } 419 420 int centerHorisontal(Pane_T pane, int len) 421 { 422 if (len > pane_width(pane)) 423 return pane_hasBorder(pane) ? 1 : 0; 424 425 return pane_x(pane) + (pane_width(pane) - len - 2) / 2 + 1; 426 } 427 428 /* DATA */ 429 430 data_T data_new(void *payload, data_handler handler) 431 { 432 data_T data = calloc(1, sizeof(*data)); 433 data->handle_input = handler; 434 data->payload = payload; 435 data->widget_num = 0; 436 data->widgets = NULL; 437 return data; 438 } 439 440 /* WIDGET */ 441 442 int widget_activate(widget_T self, Pane_T pane) 443 { 444 self->pane = pane; 445 pane->widget = self; 446 pane_setTitle(pane, self->title); 447 448 self->callback(self); 449 tb_present(); 450 451 return 1; 452 } 453 454 int widget_deactivate(widget_T self) 455 { 456 size_t i, j; 457 458 pane_setTitle(self->pane, NULL); 459 self->pane->widget = NULL; 460 self->pane = NULL; 461 462 data_T data = self->data; 463 for (i = j = 0; i < data->widget_num; i++) 464 if (data->widgets[i] != self) 465 data->widgets[j++] = data->widgets[i]; 466 data->widget_num = j; 467 468 return 1; 469 } 470 471 void widget_setData(widget_T self, data_T data) 472 { 473 self->inited = 0; 474 self->data = data; 475 data->widgets = realloc(data->widgets, (data->widget_num + 1) * 476 sizeof(*data->widgets)); 477 data->widgets[data->widget_num++] = self; 478 } 479 480 int pane_handle_input(Pane_T self, struct tb_event ev) 481 { 482 size_t i; 483 int res; 484 widget_T widget; 485 data_T data; 486 487 if ((widget = pane_widget(self)) && (data = widget->data)) { 488 if (data->handle_input) { 489 switch ((res = data->handle_input(data, ev))) { 490 case INPUT_HANDLED: { 491 data = widget->data; 492 size_t size = data->widget_num; 493 for (i = 0; i < size; i++) { 494 widget = data->widgets[i]; 495 widget->callback(widget); 496 size = data->widget_num; 497 } 498 tb_present(); 499 return 1; 500 } 501 case INPUT_IGNORED: 502 case INPUT_REFORM: 503 return res; 504 } 505 } 506 } 507 return INPUT_IGNORED; 508 } 509 510 int pane_recursive_handle(Pane_T self, struct tb_event ev) 511 { 512 size_t i; 513 int res; 514 515 if (!self) 516 return 0; 517 518 switch ((res = pane_handle_input(self, ev))) { 519 case INPUT_HANDLED: 520 case INPUT_REFORM: 521 return res; 522 } 523 524 for (i = 0; i < self->children_num; i++) 525 switch ((res = pane_recursive_handle(self->children[i], ev))) { 526 case INPUT_IGNORED: 527 continue; 528 case INPUT_HANDLED: 529 case INPUT_REFORM: 530 return res; 531 } 532 533 return INPUT_IGNORED; 534 } 535 536 void pane_event_loop(void) 537 { 538 struct tb_event ev; 539 540 while (1) { 541 tb_poll_event(&ev); 542 switch (ev.type) { 543 case TB_EVENT_RESIZE: 544 pane_handle_resize(); 545 continue; 546 case TB_EVENT_KEY: { 547 if (ev.key == TB_KEY_ESC) { 548 return; 549 } 550 551 if (ACTIVE) { 552 switch (ev.key) { 553 case TB_KEY_ARROW_UP: 554 setActive(0); 555 continue; 556 case TB_KEY_ARROW_RIGHT: 557 setActive(1); 558 continue; 559 case TB_KEY_ARROW_DOWN: 560 setActive(2); 561 continue; 562 case TB_KEY_ARROW_LEFT: 563 setActive(3); 564 continue; 565 } 566 if (!pane_handle_input(ACTIVE, ev)) 567 continue; 568 break; 569 } else { 570 pane_recursive_handle(MAIN, ev); 571 } 572 } 573 } 574 } 575 }