1 /* 2 * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/> 3 * (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com> 4 * 5 * This file is part of lsp-plugins 6 * Created on: 2 авг. 2017 г. 7 * 8 * lsp-plugins is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * any later version. 12 * 13 * lsp-plugins is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public License 19 * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>. 20 */ 21 22 #include <ui/tk/tk.h> 23 24 namespace lsp 25 { 26 namespace tk 27 { 28 const w_class_t LSPListBox::metadata = { "LSPListBox", &LSPComplexWidget::metadata }; 29 30 //----------------------------------------------------------------------------- 31 // LSPListBoxList implementation LSPListBoxList(LSPListBox * widget)32 LSPListBox::LSPListBoxList::LSPListBoxList(LSPListBox *widget) 33 { 34 pWidget = widget; 35 } 36 ~LSPListBoxList()37 LSPListBox::LSPListBoxList::~LSPListBoxList() 38 { 39 pWidget = NULL; 40 } 41 on_item_change(LSPListItem * item)42 void LSPListBox::LSPListBoxList::on_item_change(LSPListItem *item) 43 { 44 ssize_t index = pWidget->items()->index_of(item); 45 if (index >= 0) 46 pWidget->on_item_change(index, item); 47 } 48 on_item_add(size_t index)49 void LSPListBox::LSPListBoxList::on_item_add(size_t index) 50 { 51 pWidget->on_item_add(index); 52 } 53 on_item_remove(size_t index)54 void LSPListBox::LSPListBoxList::on_item_remove(size_t index) 55 { 56 pWidget->on_item_remove(index); 57 } 58 on_item_swap(size_t idx1,size_t idx2)59 void LSPListBox::LSPListBoxList::on_item_swap(size_t idx1, size_t idx2) 60 { 61 pWidget->on_item_swap(idx1, idx2); 62 } 63 on_item_clear()64 void LSPListBox::LSPListBoxList::on_item_clear() 65 { 66 pWidget->on_item_clear(); 67 } 68 69 //----------------------------------------------------------------------------- 70 // LSPListBoxSelection implementation 71 LSPListBoxSelection(LSPListBox * widget)72 LSPListBox::LSPListBoxSelection::LSPListBoxSelection(LSPListBox *widget) 73 { 74 pWidget = widget; 75 } 76 ~LSPListBoxSelection()77 LSPListBox::LSPListBoxSelection::~LSPListBoxSelection() 78 { 79 pWidget = NULL; 80 } 81 on_remove(ssize_t value)82 void LSPListBox::LSPListBoxSelection::on_remove(ssize_t value) 83 { 84 float fh = pWidget->sFont.height(); 85 ssize_t first = pWidget->sVBar.value() / fh; 86 ssize_t last = (pWidget->sVBar.value() + pWidget->sArea.nHeight + fh - 1) / fh; 87 88 if ((value >= first) || (value <= last)) 89 pWidget->query_draw(); 90 91 pWidget->on_selection_change(); 92 } 93 on_add(ssize_t value)94 void LSPListBox::LSPListBoxSelection::on_add(ssize_t value) 95 { 96 float fh = pWidget->sFont.height(); 97 ssize_t first = pWidget->sVBar.value() / fh; 98 ssize_t last = (pWidget->sVBar.value() + pWidget->sArea.nHeight + fh) / fh; 99 100 if ((value >= first) || (value <= last)) 101 pWidget->query_draw(); 102 103 pWidget->on_selection_change(); 104 } 105 validate(ssize_t value)106 bool LSPListBox::LSPListBoxSelection::validate(ssize_t value) 107 { 108 if (pWidget == NULL) 109 return false; 110 return (value >= 0) && (value < ssize_t(pWidget->sItems.size())); 111 } 112 request_fill(ssize_t * first,ssize_t * last)113 void LSPListBox::LSPListBoxSelection::request_fill(ssize_t *first, ssize_t *last) 114 { 115 *first = 0; 116 *last = (pWidget != NULL) ? pWidget->sItems.size() - 1 : -1; 117 } 118 on_fill()119 void LSPListBox::LSPListBoxSelection::on_fill() 120 { 121 pWidget->query_draw(); 122 pWidget->on_selection_change(); 123 } 124 on_clear()125 void LSPListBox::LSPListBoxSelection::on_clear() 126 { 127 pWidget->query_draw(); 128 pWidget->on_selection_change(); 129 } 130 131 //----------------------------------------------------------------------------- 132 // LSPListBox implementation LSPListBox(LSPDisplay * dpy)133 LSPListBox::LSPListBox(LSPDisplay *dpy): 134 LSPComplexWidget(dpy), 135 sItems(this), 136 sSelection(this), 137 sHBar(dpy, true), 138 sVBar(dpy, false), 139 sConstraints(this), 140 sColor(this), 141 sFont(this) 142 { 143 nFlags = 0; 144 nBMask = 0; 145 pArea = NULL; 146 147 pClass = &metadata; 148 } 149 ~LSPListBox()150 LSPListBox::~LSPListBox() 151 { 152 do_destroy(); 153 } 154 hide()155 bool LSPListBox::hide() 156 { 157 bool result = LSPComplexWidget::hide(); 158 159 if (result) 160 { 161 // Drop area to not to eat memory 162 if (pArea != NULL) 163 { 164 pArea->destroy(); 165 delete pArea; 166 pArea = NULL; 167 } 168 } 169 return result; 170 } 171 do_destroy()172 void LSPListBox::do_destroy() 173 { 174 // Clear contents 175 sItems.clear(); 176 sSelection.clear(); 177 sHBar.destroy(); 178 sVBar.destroy(); 179 180 // Drop area to not to eat memory 181 if (pArea != NULL) 182 { 183 pArea->destroy(); 184 delete pArea; 185 pArea = NULL; 186 } 187 } 188 init()189 status_t LSPListBox::init() 190 { 191 status_t result = LSPWidget::init(); 192 if (result != STATUS_OK) 193 return result; 194 195 init_color(C_LABEL_TEXT, &sColor); 196 init_color(C_LABEL_TEXT, sFont.color()); 197 198 result = sHBar.init(); 199 if (result != STATUS_OK) 200 return result; 201 result = sVBar.init(); 202 if (result != STATUS_OK) 203 return result; 204 205 sVBar.set_parent(this); 206 sHBar.set_parent(this); 207 sVBar.hide(); 208 sHBar.hide(); 209 210 sFont.init(); 211 sFont.set_size(12); 212 213 // Bind slots 214 ui_handler_id_t id = 0; 215 id = sSlots.add(LSPSLOT_CHANGE, slot_on_change, self()); 216 if (id >= 0) id = sSlots.add(LSPSLOT_SUBMIT, slot_on_submit, self()); 217 if (id >= 0) id = sSlots.add(LSPSLOT_HSCROLL, slot_on_hscroll, self()); 218 if (id >= 0) id = sSlots.add(LSPSLOT_VSCROLL, slot_on_vscroll, self()); 219 if (id >= 0) id = sVBar.slots()->bind(LSPSLOT_CHANGE, slot_on_sbar_vscroll, self()); 220 if (id >= 0) id = sHBar.slots()->bind(LSPSLOT_CHANGE, slot_on_sbar_hscroll, self()); 221 222 return (id >= 0) ? STATUS_OK : -id; 223 } 224 on_item_change(size_t index,LSPItem * item)225 void LSPListBox::on_item_change(size_t index, LSPItem *item) 226 { 227 float fh = sFont.height(); 228 ssize_t first = sVBar.value() / fh; 229 ssize_t last = (sVBar.value() + sArea.nHeight + fh - 1) / fh; 230 231 if ((ssize_t(index) >= first) || (ssize_t(index) <= last)) 232 query_draw(); 233 } 234 on_item_add(size_t index)235 void LSPListBox::on_item_add(size_t index) 236 { 237 realize(&sSize); 238 query_resize(); 239 } 240 on_item_remove(size_t index)241 void LSPListBox::on_item_remove(size_t index) 242 { 243 realize(&sSize); 244 query_resize(); 245 } 246 on_item_swap(size_t idx1,size_t idx2)247 void LSPListBox::on_item_swap(size_t idx1, size_t idx2) 248 { 249 float fh = sFont.height(); 250 ssize_t first = sVBar.value() / fh; 251 ssize_t last = (sVBar.value() + sArea.nHeight + fh - 1) / fh; 252 253 if ((ssize_t(idx1) >= first) || (ssize_t(idx1) <= last) || (ssize_t(idx2) >= first) || (ssize_t(idx2) <= last)) 254 query_draw(); 255 } 256 on_item_clear()257 void LSPListBox::on_item_clear() 258 { 259 realize(&sSize); 260 query_resize(); 261 } 262 on_selection_change()263 void LSPListBox::on_selection_change() 264 { 265 } 266 destroy()267 void LSPListBox::destroy() 268 { 269 do_destroy(); 270 LSPWidget::destroy(); 271 } 272 slot_on_sbar_vscroll(LSPWidget * sender,void * ptr,void * data)273 status_t LSPListBox::slot_on_sbar_vscroll(LSPWidget *sender, void *ptr, void *data) 274 { 275 if (ptr == NULL) 276 return STATUS_BAD_ARGUMENTS; 277 278 LSPWidget *w = static_cast<LSPWidget *>(ptr); 279 return w->slots()->execute(LSPSLOT_VSCROLL, sender, data); 280 } 281 slot_on_sbar_hscroll(LSPWidget * sender,void * ptr,void * data)282 status_t LSPListBox::slot_on_sbar_hscroll(LSPWidget *sender, void *ptr, void *data) 283 { 284 if (ptr == NULL) 285 return STATUS_BAD_ARGUMENTS; 286 287 LSPWidget *w = static_cast<LSPWidget *>(ptr); 288 return w->slots()->execute(LSPSLOT_HSCROLL, sender, data); 289 } 290 slot_on_change(LSPWidget * sender,void * ptr,void * data)291 status_t LSPListBox::slot_on_change(LSPWidget *sender, void *ptr, void *data) 292 { 293 LSPListBox *_this = widget_ptrcast<LSPListBox>(ptr); 294 return (_this != NULL) ? _this->on_change() : STATUS_BAD_ARGUMENTS; 295 } 296 slot_on_submit(LSPWidget * sender,void * ptr,void * data)297 status_t LSPListBox::slot_on_submit(LSPWidget *sender, void *ptr, void *data) 298 { 299 LSPListBox *_this = widget_ptrcast<LSPListBox>(ptr); 300 return (_this != NULL) ? _this->on_submit() : STATUS_BAD_ARGUMENTS; 301 } 302 slot_on_vscroll(LSPWidget * sender,void * ptr,void * data)303 status_t LSPListBox::slot_on_vscroll(LSPWidget *sender, void *ptr, void *data) 304 { 305 LSPListBox *_this = widget_ptrcast<LSPListBox>(ptr); 306 return (_this != NULL) ? _this->on_vscroll() : STATUS_BAD_ARGUMENTS; 307 } 308 slot_on_hscroll(LSPWidget * sender,void * ptr,void * data)309 status_t LSPListBox::slot_on_hscroll(LSPWidget *sender, void *ptr, void *data) 310 { 311 LSPListBox *_this = widget_ptrcast<LSPListBox>(ptr); 312 return (_this != NULL) ? _this->on_hscroll() : STATUS_BAD_ARGUMENTS; 313 } 314 find_widget(ssize_t x,ssize_t y)315 LSPWidget *LSPListBox::find_widget(ssize_t x, ssize_t y) 316 { 317 if (sHBar.visible() && sHBar.inside(x, y)) 318 return &sHBar; 319 if (sVBar.visible() && sVBar.inside(x, y)) 320 return &sVBar; 321 return NULL; 322 } 323 on_change()324 status_t LSPListBox::on_change() 325 { 326 return STATUS_OK; 327 } 328 on_submit()329 status_t LSPListBox::on_submit() 330 { 331 return STATUS_OK; 332 } 333 on_click(ssize_t x,ssize_t y)334 void LSPListBox::on_click(ssize_t x, ssize_t y) 335 { 336 lsp_trace("x=%d, y=%d, area={%d, %d, %d, %d}", 337 int(x), int(y), int(sArea.nLeft), int(sArea.nTop), int(sArea.nLeft + sArea.nWidth), int (sArea.nTop + sArea.nHeight)); 338 if ((x < sArea.nLeft) || (x >= (sArea.nLeft + sArea.nWidth))) 339 return; 340 else if ((y < sArea.nTop) || (y >= (sArea.nTop + sArea.nHeight))) 341 return; 342 343 x = x - sArea.nLeft; 344 y = y - sArea.nTop + sVBar.value(); 345 346 float fh = sFont.height(); 347 ssize_t item = y / fh; 348 lsp_trace("toggled item = %d", int(item)); 349 if (sSelection.multiple()) 350 { 351 sSelection.toggle_value(item); 352 sSlots.execute(LSPSLOT_CHANGE, this); 353 } 354 else 355 { 356 ssize_t old_value = sSelection.value(); 357 sSelection.set_value(item); 358 if (old_value != item) 359 sSlots.execute(LSPSLOT_CHANGE, this); 360 } 361 362 nFlags |= F_SUBMIT; 363 } 364 on_mouse_down(const ws_event_t * e)365 status_t LSPListBox::on_mouse_down(const ws_event_t *e) 366 { 367 lsp_trace("x=%d, y=%d, code=%x, bmask=%lx", int(e->nLeft), int(e->nTop), int(e->nCode), long(nBMask)); 368 take_focus(); 369 size_t mask = nBMask; 370 nBMask = mask | (1 << e->nCode); 371 372 if ((mask == 0) && (e->nCode == MCB_LEFT)) 373 { 374 nFlags |= F_MDOWN; 375 on_click(e->nLeft, e->nTop); 376 } 377 378 return STATUS_OK; 379 } 380 on_mouse_up(const ws_event_t * e)381 status_t LSPListBox::on_mouse_up(const ws_event_t *e) 382 { 383 lsp_trace("x=%d, y=%d, code=%x, bmask=%lx", int(e->nLeft), int(e->nTop), int(e->nCode), long(nBMask)); 384 nBMask = nBMask & (~(1 << e->nCode)); 385 if (nBMask == 0) 386 nFlags &= ~F_MDOWN; 387 388 if (nFlags & F_SUBMIT) 389 { 390 nFlags &= ~F_SUBMIT; 391 sSlots.execute(LSPSLOT_SUBMIT, this); 392 } 393 return STATUS_OK; 394 } 395 on_mouse_move(const ws_event_t * e)396 status_t LSPListBox::on_mouse_move(const ws_event_t *e) 397 { 398 if (sSelection.multiple()) 399 return STATUS_OK; 400 401 if (nBMask == (1 << MCB_LEFT)) 402 on_click(e->nLeft, e->nTop); 403 404 return STATUS_OK; 405 } 406 on_mouse_scroll(const ws_event_t * e)407 status_t LSPListBox::on_mouse_scroll(const ws_event_t *e) 408 { 409 if (e->nState & MCF_CONTROL) 410 { 411 ws_event_t xe = *e; 412 xe.nState &= ~MCF_CONTROL; 413 sHBar.handle_event(&xe); 414 } 415 else 416 sVBar.handle_event(e); 417 418 return STATUS_OK; 419 } 420 on_hscroll()421 status_t LSPListBox::on_hscroll() 422 { 423 query_draw(); 424 return STATUS_OK; 425 } 426 on_vscroll()427 status_t LSPListBox::on_vscroll() 428 { 429 query_draw(); 430 return STATUS_OK; 431 } 432 render(ISurface * s,bool force)433 void LSPListBox::render(ISurface *s, bool force) 434 { 435 // Check dirty flag 436 if (nFlags & REDRAW_SURFACE) 437 force = true; 438 439 // Draw list box 440 ISurface *lst = get_surface(s, sArea.nWidth, sArea.nHeight); 441 if (lst != NULL) 442 s->draw(lst, sArea.nLeft, sArea.nTop); 443 444 // Prepare palette 445 Color bg_color(sBgColor); 446 Color color(sColor); 447 color.scale_lightness(brightness()); 448 449 // Draw the frame around 450 size_t dx = (sVBar.visible()) ? 7 : 6; 451 size_t dy = (sHBar.visible()) ? 7 : 6; 452 453 s->fill_frame(sSize.nLeft, sSize.nTop, sArea.nWidth + dx, sArea.nHeight + dy, 454 sArea.nLeft, sArea.nTop, sArea.nWidth, sArea.nHeight, 455 bg_color); 456 457 bool aa = s->set_antialiasing(true); 458 s->wire_round_rect(sSize.nLeft + 0.5f, sSize.nTop + 0.5f, sArea.nWidth + 5, sArea.nHeight + 5, 2, SURFMASK_ALL_CORNER, 1, color); 459 s->set_antialiasing(aa); 460 461 // Finally, draw scroll bars 462 if (sHBar.visible()) 463 { 464 if ((sHBar.redraw_pending()) || (force)) 465 { 466 sHBar.render(s, false); 467 sHBar.commit_redraw(); 468 } 469 } 470 if (sVBar.visible()) 471 { 472 if ((sVBar.redraw_pending()) || (force)) 473 { 474 sVBar.render(s, false); 475 sVBar.commit_redraw(); 476 } 477 } 478 } 479 draw(ISurface * s)480 void LSPListBox::draw(ISurface *s) 481 { 482 // Prepare palette 483 Color bg_color(sBgColor); 484 Color color(sColor); 485 Color font(sFont.raw_color()); 486 487 color.scale_lightness(brightness()); 488 font.scale_lightness(brightness()); 489 490 // Draw background 491 s->clear(bg_color); 492 493 // Draw 494 font_parameters_t fp; 495 sFont.get_parameters(s, &fp); 496 497 ssize_t first = sVBar.value() / fp.Height; 498 ssize_t last = (sVBar.value() + sArea.nHeight + fp.Height - 1) / fp.Height; 499 ssize_t y = first * fp.Height - sVBar.value(); 500 501 LSPString text; 502 for ( ; first <= last; first++, y += fp.Height) 503 { 504 LSPItem *item = sItems.get(first); 505 if (item == NULL) 506 continue; 507 508 item->text()->format(&text, this); 509 if (sSelection.contains(first)) 510 { 511 s->fill_rect(0.0f, y, sArea.nWidth, fp.Height, font); 512 if (text.length() > 0) 513 sFont.draw(s, 1.0f, y + fp.Ascent, bg_color, &text); 514 } 515 else if (text.length() > 0) 516 sFont.draw(s, 1.0f, y + fp.Ascent, font, &text); 517 } 518 } 519 size_request(size_request_t * r)520 void LSPListBox::size_request(size_request_t *r) 521 { 522 size_request_t hbar, vbar; 523 hbar.nMinWidth = -1; 524 hbar.nMinHeight = -1; 525 hbar.nMaxWidth = -1; 526 hbar.nMaxHeight = -1; 527 vbar.nMinWidth = -1; 528 vbar.nMinHeight = -1; 529 vbar.nMaxWidth = -1; 530 vbar.nMaxHeight = -1; 531 532 sHBar.size_request(&hbar); 533 sVBar.size_request(&vbar); 534 535 // Estimate minimum size of bars 536 ssize_t width = 0, height = 0; 537 if (hbar.nMinWidth >= 0) 538 width += hbar.nMinWidth; 539 if (vbar.nMinWidth >= 0) 540 width += vbar.nMinWidth; 541 542 if (hbar.nMinHeight >= 0) 543 height += hbar.nMinHeight; 544 if (vbar.nMinHeight >= 0) 545 height += vbar.nMinHeight; 546 547 size_t padding = 6; 548 size_t n_items = sItems.size(); 549 if (n_items <= 0) 550 n_items ++; 551 ssize_t i_height = sFont.height() * n_items + padding; 552 if (height > i_height) 553 height = i_height; 554 555 // Fill final values 556 r->nMinWidth = width; 557 r->nMinHeight = height; 558 r->nMaxWidth = -1; 559 r->nMaxHeight = -1; 560 561 // Apply constraints 562 sConstraints.apply(r); 563 } 564 optimal_size_request(size_request_t * r)565 void LSPListBox::optimal_size_request(size_request_t *r) 566 { 567 r->nMinWidth = 0; 568 r->nMinHeight = 0; 569 r->nMaxWidth = 0; 570 r->nMaxHeight = 0; 571 572 font_parameters_t fp; 573 text_parameters_t tp; 574 575 ISurface *s = pDisplay->create_surface(1, 1); 576 if (s == NULL) 577 return; 578 579 sFont.get_parameters(&fp); 580 581 size_t padding = 6; 582 size_t n_items = sItems.size(); 583 584 LSPString text; 585 for (size_t i=0; i<n_items; ++i) 586 { 587 LSPItem *item = sItems.get(i); 588 if (item == NULL) 589 continue; 590 591 item->text()->format(&text, this); 592 if (text.is_empty()) 593 continue; 594 595 sFont.get_text_parameters(s, &tp, &text); 596 if (tp.Width > r->nMaxWidth) 597 r->nMaxWidth = tp.Width; 598 } 599 600 r->nMaxHeight = fp.Height * n_items + padding; 601 602 size_request_t vbar; 603 vbar.nMinWidth = -1; 604 vbar.nMinHeight = -1; 605 vbar.nMaxWidth = -1; 606 vbar.nMaxHeight = -1; 607 608 sVBar.size_request(&vbar); 609 if (vbar.nMinWidth > 0) 610 r->nMinWidth = vbar.nMinWidth * 2; 611 if (n_items > 2) 612 n_items = 4; 613 r->nMinHeight = fp.Height * n_items + padding*2; 614 if (r->nMaxWidth < r->nMinWidth) 615 r->nMaxWidth = r->nMinWidth; 616 if (r->nMaxHeight < r->nMinHeight) 617 r->nMaxHeight = r->nMinHeight; 618 619 s->destroy(); 620 delete s; 621 } 622 realize(const realize_t * r)623 void LSPListBox::realize(const realize_t *r) 624 { 625 size_request_t hbar, vbar; 626 hbar.nMinWidth = -1; 627 hbar.nMinHeight = -1; 628 hbar.nMaxWidth = -1; 629 hbar.nMaxHeight = -1; 630 vbar.nMinWidth = -1; 631 vbar.nMinHeight = -1; 632 vbar.nMaxWidth = -1; 633 vbar.nMaxHeight = -1; 634 635 sHBar.size_request(&hbar); 636 sVBar.size_request(&vbar); 637 638 size_t padding = 3; 639 640 size_t n_items = sItems.size(); 641 if (n_items <= 0) 642 n_items ++; 643 ssize_t i_height = sFont.height() * n_items + padding*2; 644 645 bool vb = ssize_t(r->nHeight) < i_height; 646 bool hb = false; // TODO 647 648 realize_t rh, rv; 649 650 // Estimate size for vertical and horizontal scroll bars 651 if (vb) 652 { 653 rv.nWidth = (vbar.nMinWidth > 0) ? vbar.nMinWidth : 12; 654 rv.nLeft = r->nLeft + r->nWidth - rv.nWidth; 655 rv.nTop = r->nTop; 656 rv.nHeight = r->nHeight; 657 } 658 else 659 rv.nWidth = 0; 660 661 if (hb) 662 { 663 rh.nHeight = (hbar.nMinHeight > 0) ? hbar.nMinHeight : 12; 664 rh.nLeft = r->nLeft; 665 rh.nTop = r->nTop + r->nHeight - rh.nHeight; 666 rh.nWidth = r->nWidth; 667 } 668 else 669 rh.nHeight = 0; 670 671 if (vb && hb) 672 { 673 rv.nHeight -= rh.nHeight; 674 rh.nWidth -= rv.nWidth; 675 } 676 677 if (vb) 678 { 679 // Realize and show 680 sVBar.realize(&rv); 681 sVBar.show(); 682 sVBar.query_draw(); 683 } 684 else 685 { 686 sVBar.hide(); 687 sVBar.set_value(0.0f); 688 } 689 690 if (hb) 691 { 692 sHBar.realize(&rh); 693 sHBar.show(); 694 sHBar.query_draw(); 695 } 696 else 697 { 698 sHBar.hide(); 699 sHBar.set_value(0.0f); 700 } 701 702 // Remember drawing area parameters 703 sArea.nLeft = r->nLeft + padding; 704 sArea.nTop = r->nTop + padding; 705 sArea.nWidth = r->nWidth - rv.nWidth - padding*2; 706 sArea.nHeight = r->nHeight - rh.nHeight - padding*2; 707 if (vb) 708 sArea.nWidth --; 709 if (hb) 710 sArea.nHeight --; 711 712 if (vb) 713 { 714 // Set scrolling parameters 715 sVBar.set_min_value(0.0f); 716 sVBar.set_max_value(i_height - r->nHeight + padding*2); 717 sVBar.set_tiny_step(sFont.height()); 718 sVBar.set_step(sArea.nHeight - sArea.nHeight%ssize_t(sFont.height())); 719 } 720 else 721 { 722 sVBar.set_min_value(0.0f); 723 sVBar.set_max_value(0.0f); 724 } 725 726 // Call parent method 727 LSPComplexWidget::realize(r); 728 } 729 } /* namespace tk */ 730 } /* namespace lsp */ 731