Ticket #7461: MythUI-DynamicLayout-v1.1.diff
File MythUI-DynamicLayout-v1.1.diff, 62.5 KB (added by , 16 years ago) |
---|
-
libs/libmythui/mythuibuttonlist.h
9 9 #include "mythuitype.h" 10 10 #include "mythuiimage.h" 11 11 #include "mythuitext.h" 12 #include "mythuigroup.h" 12 13 13 14 class MythUIButtonList; 14 15 class MythFontProperties; … … 143 144 uint GetVisibleCount() const { return m_itemsVisible; } 144 145 bool IsEmpty() const; 145 146 146 enum ScrollStyle { ScrollFree, ScrollCenter };147 enum ScrollStyle { ScrollFree, ScrollCenter, ScrollGroupCenter }; 147 148 enum LayoutType { LayoutVertical, LayoutHorizontal, LayoutGrid }; 149 enum ArrangeType { ArrangeFixed, ArrangeFill, ArrangeSpread, ArrangeStack }; 148 150 enum MovementUnit { MoveItem, MoveColumn, MoveRow, MovePage, MoveMax, 149 151 MoveMid, MoveByAmount }; 150 152 enum WrapStyle { WrapCaptive = -1, WrapNone = 0, WrapSelect, WrapItems }; 151 153 154 void InitButton(int itemIdx, MythUIStateType* & realButton, 155 MythUIButtonListItem* & buttonItem); 156 int PageUp(void); 157 int PageDown(void); 152 158 virtual bool MoveDown(MovementUnit unit = MoveItem, uint amount = 0); 153 159 virtual bool MoveUp(MovementUnit unit = MoveItem, uint amount = 0); 154 160 bool MoveToNamedPosition(const QString &position_name); … … 173 179 174 180 void InsertItem(MythUIButtonListItem *item); 175 181 182 int minButtonWidth(const MythRect & area); 183 int minButtonHeight(const MythRect & area); 184 MythUIGroup *PrepareButton(int buttonIdx, int itemIdx, 185 int & selectedIdx, int & button_shift); 186 bool DistributeRow(int & first_button, int & last_button, 187 int & first_item, int & last_item, 188 int & selected_column, 189 bool grow_left, bool grow_right, 190 int ** col_widths, int & row_height, 191 int total_height, int split_height, 192 int & col_cnt, bool & wrapped); 193 bool DistributeCols(int & first_button, int & last_button, 194 int & first_item, int & last_item, 195 int & selected_column, int & selected_row, 196 int ** col_widths, QList<int> & row_heights, 197 int & top_height, int & bottom_height, 198 bool & wrapped); 199 bool DistributeButtons(void); 200 void SetPosition(void); 176 201 void SetPositionArrowStates(void); 177 202 178 203 void updateLCD(void); … … 192 217 193 218 /**/ 194 219 195 LayoutType m_layout; 220 LayoutType m_layout; 221 ArrangeType m_arrange; 196 222 ScrollStyle m_scrollStyle; 197 WrapStyle m_wrapStyle; 223 WrapStyle m_wrapStyle; 224 int m_alignment; 198 225 199 226 MythRect m_contentsRect; 200 227 … … 203 230 int m_itemHorizSpacing; 204 231 int m_itemVertSpacing; 205 232 uint m_itemsVisible; 233 int m_maxVisible; 206 234 int m_rows; 207 235 int m_columns; 236 int m_leftColumns, m_rightColumns; 237 int m_topRows, m_bottomRows; 208 238 209 239 bool m_active; 210 240 bool m_showArrow; … … 212 242 MythUIStateType *m_upArrow; 213 243 MythUIStateType *m_downArrow; 214 244 245 MythUIStateType *m_buttontemplate; 246 215 247 QVector<MythUIStateType *> m_ButtonList; 216 248 QMap<int, MythUIButtonListItem *> m_ButtonToItem; 217 249 -
libs/libmythui/mythuibuttonlist.cpp
10 10 #include "mythverbose.h" 11 11 12 12 // mythui headers 13 #include "mythuigroup.h"14 13 #include "mythmainwindow.h" 15 14 #include "mythuistatetype.h" 16 15 #include "lcddevice.h" … … 42 41 m_contentsRect = MythRect(0,0,0,0); 43 42 44 43 m_layout = LayoutVertical; 44 m_arrange = ArrangeFixed; 45 m_alignment = Qt::AlignLeft | Qt::AlignTop; 45 46 m_scrollStyle = ScrollFree; 46 47 m_wrapStyle = WrapNone; 47 48 … … 61 62 m_itemHeight = 0; 62 63 m_itemWidth = 0; 63 64 m_itemsVisible = 0; 65 m_maxVisible = 0; 64 66 m_columns = 0; 65 67 m_rows = 0; 68 m_leftColumns = 0; 69 m_rightColumns = 0; 70 m_topRows = 0; 71 m_bottomRows = 0; 66 72 m_lcdTitle = ""; 67 73 68 74 m_upArrow = m_downArrow = NULL; 69 75 76 m_buttontemplate = NULL; 77 70 78 SetCanTakeFocus(true); 71 79 72 80 connect(this, SIGNAL(TakingFocus()), this, SLOT(Select())); … … 137 145 SetRedraw(); 138 146 } 139 147 140 void MythUIButtonList::SanitizePosition(void) 148 /* 149 * The "width" of a button determines it relative position when using 150 * Dynamic-Layout. 151 * 152 * If a button has a negative offset, that offset needs accounted for 153 * to position the button in proper releation to the surrounding buttons. 154 */ 155 int MythUIButtonList::minButtonWidth(const MythRect & area) 141 156 { 142 if (m_selPosition < 0) 143 m_selPosition = (m_wrapStyle > WrapNone) ? m_itemList.size() - 1 : 0; 144 else if (m_selPosition >= m_itemList.size()) 145 m_selPosition = (m_wrapStyle > WrapNone) ? 0 : m_itemList.size() - 1; 157 int width = area.width(); 158 159 /* 160 * Assume if an overlap is allowed on the left, the same overlap 161 * is on the right 162 */ 163 if (area.x() < 0) 164 width += (area.x() * 2 - 1); // x is negative 165 else 166 width += area.x(); 167 168 while (width < 0) 169 width -= area.x(); // Oops 170 171 return width; 146 172 } 147 173 148 void MythUIButtonList::SetPositionArrowStates(void) 174 /* 175 * The "height" of a button determines it relative position when using 176 * Dynamic-Layout. 177 * 178 * If a button has a negative offset, that offset needs accounted for 179 * to position the button in proper releation to the surrounding buttons. 180 */ 181 int MythUIButtonList::minButtonHeight(const MythRect & area) 149 182 { 150 if (!m_initialized) 151 Init(); 183 int height = area.height(); 152 184 153 if (m_clearing) 154 return; 185 /* 186 * Assume if an overlap is allowed on the top, the same overlap 187 * is on the bottom 188 */ 189 if (area.y() < 0) 190 height += (area.y() * 2 - 1); 191 else 192 height += area.y(); 155 193 156 m_needsUpdate = false; 194 while (height < 0) 195 height -= area.y(); // Oops 157 196 158 m_ButtonToItem.clear(); 197 return height; 198 } 159 199 160 if (m_ButtonList.size() > 0) 200 /* 201 * For Dynamic-Layout, buttons are allocated as needed. If the list is 202 * being redrawn, re-use any previously allocated buttons. 203 */ 204 MythUIGroup *MythUIButtonList::PrepareButton(int buttonIdx, int itemIdx, 205 int & selectedIdx, 206 int & button_shift) 207 { 208 MythUIStateType *realButton; 209 MythUIGroup *buttonstate; 210 MythUIButtonListItem* buttonItem = m_itemList[itemIdx]; 211 212 buttonIdx += button_shift; 213 if (buttonIdx < 0 || buttonIdx + 1 > m_maxVisible) 161 214 { 162 int button = 0; 215 QString name = QString("buttonlist button %1").arg(m_maxVisible); 216 MythUIStateType *button = new MythUIStateType(this, name); 217 button->CopyFrom(m_buttontemplate); 218 if (buttonIdx < 0) 219 { 220 /* 221 * If a new button is needed in the front of the list, previously 222 * existing buttons need shifted to the right. 223 */ 224 m_ButtonList.prepend(button); 225 buttonIdx = 0; 226 ++button_shift; 227 if (selectedIdx >= 0) 228 ++selectedIdx; 229 } 230 else 231 m_ButtonList.append(button); 232 ++m_maxVisible; 233 } 163 234 164 // set topitem, top position 165 SanitizePosition(); 235 realButton = m_ButtonList[buttonIdx]; 236 m_ButtonToItem[buttonIdx] = buttonItem; 237 buttonItem->SetToRealButton(realButton, itemIdx == m_selPosition); 238 buttonstate = dynamic_cast<MythUIGroup *>(realButton->GetCurrentState()); 166 239 167 switch (m_scrollStyle) 240 if (itemIdx == m_selPosition) 241 selectedIdx = buttonIdx; 242 243 return buttonstate; 244 } 245 246 /* 247 * Dynamically layout the buttons on a row. 248 */ 249 bool MythUIButtonList::DistributeRow(int & first_button, int & last_button, 250 int & first_item, int & last_item, 251 int & selected_column, 252 bool grow_left, bool grow_right, 253 int ** col_widths, int & row_height, 254 int total_height, int split_height, 255 int & col_cnt, bool & wrapped) 256 { 257 MythUIGroup *buttonstate; 258 int initial_first_button, initial_last_button; 259 int initial_first_item, initial_last_item; 260 int col_idx, left_cnt = 0; 261 int width, height; 262 int max_width, max_height; 263 int left_width, right_width; 264 int begin, end; 265 bool underflow; 266 bool added; 267 bool hsplit, vsplit; 268 int selectedIdx; 269 int button_shift; 270 271 selectedIdx = -1; 272 button_shift = 0; 273 col_cnt = 1; 274 275 if (last_item + 1 > m_itemCount || last_item < 0 || first_item < 0) 276 return false; 277 278 /* 279 * Allocate a button on the row. With a vertical layout, there is 280 * only one button per row, and this would be it. 281 */ 282 if (grow_right) 283 buttonstate = PrepareButton(last_button, last_item, 284 selectedIdx, button_shift); 285 else 286 buttonstate = PrepareButton(first_button, first_item, 287 selectedIdx, button_shift); 288 289 if (buttonstate == NULL) 290 { 291 VERBOSE(VB_IMPORTANT, QString("Failed to query buttonlist state: %1") 292 .arg(last_button)); 293 return false; 294 } 295 296 // Note size of initial button. 297 max_width = m_contentsRect.width(); 298 max_height = m_contentsRect.height(); 299 row_height = minButtonHeight(buttonstate->GetArea()); 300 width = minButtonWidth(buttonstate->GetArea()); 301 302 /* 303 * If the selected button should be centered, don't allow new buttons 304 * to take up more than half the allowed area. 305 */ 306 vsplit = (m_scrollStyle == ScrollCenter); 307 hsplit = vsplit && grow_left && grow_right; 308 if (hsplit) 309 { 310 max_width /= 2; 311 left_width = right_width = (width / 2); 312 } 313 else 314 { 315 if (grow_right) 168 316 { 169 case ScrollCenter: 170 m_topPosition = qMax(m_selPosition - (int)((float)m_itemsVisible / 2), 0); 171 break; 172 case ScrollFree: 317 left_width = 0; 318 right_width = width; 319 } 320 else 321 { 322 left_width = width; 323 right_width = 0; 324 } 325 } 326 if (vsplit) 327 max_height /= 2; 328 329 /* 330 * If total_height == 0, then this is the first row, so allow any height. 331 * Otherwide, If adding a button to a column would exceed the 332 * parent height, abort 333 */ 334 if (total_height > 0 && 335 ((vsplit ? split_height : total_height) + 336 m_itemVertSpacing + row_height > max_height)) 337 { 338 VERBOSE(VB_GENERAL|VB_EXTRA, 339 QString("%1 Height exceeded %2 + (%3) + %4 = %5 " 340 "which is > %6") 341 .arg(vsplit ? "Centering" : "Total") 342 .arg(split_height).arg(m_itemVertSpacing).arg(row_height) 343 .arg(split_height + m_itemVertSpacing + row_height) 344 .arg(max_height)); 345 first_button += button_shift; 346 last_button += button_shift; 347 return false; 348 } 349 350 VERBOSE(VB_GENERAL|VB_EXTRA, QString("Added button item %1 " 351 "width %2 height %3") 352 .arg(grow_right ? last_item : first_item) 353 .arg(width).arg(row_height)); 354 355 initial_first_button = first_button; 356 initial_last_button = last_button; 357 initial_first_item = first_item; 358 initial_last_item = last_item; 359 360 /* 361 * if col_widths is not NULL, then grow_left & grow_right 362 * are mutually exclusive. So, col_idx can be anchored from 363 * the left or right. 364 */ 365 if (grow_right) 366 col_idx = 0; 367 else 368 col_idx = m_columns - 1; 369 370 // Add butons until no more fit. 371 added = (m_layout != LayoutVertical); 372 while (added) 373 { 374 added = false; 375 376 // If a grid, maintain same number of columns on each row. 377 if (grow_right && col_cnt < m_columns) 378 { 379 if (wrapped) 380 end = first_item; 381 else 173 382 { 174 int adjust = 0; 175 if (m_topPosition == -1 || m_keepSelAtBottom) 383 // Are we allowed to wrap when we run out of items? 384 if (m_wrapStyle == WrapItems && 385 (hsplit || m_scrollStyle != ScrollFree) && 386 last_item + 1 == m_itemCount) 176 387 { 177 if (m_topPosition == -1) 178 m_topPosition = 0; 179 if (m_layout == LayoutHorizontal) 180 adjust = 1 - m_itemsVisible; 181 else 182 adjust = m_columns - m_itemsVisible; 183 m_keepSelAtBottom = false; 388 last_item = -1; 389 wrapped = true; 390 end = first_item; 184 391 } 392 else 393 end = m_itemCount; 394 } 185 395 186 if (m_selPosition < m_topPosition || 187 m_topPosition + (int)m_itemsVisible <= m_selPosition) 396 if (last_item + 1 < end) 397 { 398 // Allocate next button to the right. 399 buttonstate = PrepareButton(last_button + 1, last_item + 1, 400 selectedIdx, button_shift); 401 if (buttonstate == NULL) 402 continue; 403 width = minButtonWidth(buttonstate->GetArea()); 404 405 // For grids, use the widest button in a column 406 if (*col_widths && width < (*col_widths)[col_idx]) 407 width = (*col_widths)[col_idx]; 408 409 // Does the button fit? 410 if ((hsplit ? right_width : left_width + right_width) + 411 m_itemHorizSpacing + width > max_width) 188 412 { 189 if (m_layout == LayoutHorizontal) 190 m_topPosition = m_selPosition + adjust; 191 else 192 m_topPosition = (m_selPosition + adjust) / 193 m_columns * m_columns; 413 int total = hsplit ? right_width : left_width + right_width; 414 VERBOSE(VB_GENERAL|VB_EXTRA, 415 QString("button on right would exceed width: " 416 "%1+(%2)+%3 == %4 which is > %5") 417 .arg(total).arg(m_itemHorizSpacing).arg(width) 418 .arg(total + m_itemHorizSpacing + width) 419 .arg(max_width)); 194 420 } 421 else 422 { 423 added = true; 424 ++col_cnt; 425 ++last_button; 426 ++last_item; 427 ++col_idx; 428 right_width += m_itemHorizSpacing + width; 429 height = minButtonHeight(buttonstate->GetArea()); 430 if (row_height < height) 431 row_height = height; 432 VERBOSE(VB_GENERAL|VB_EXTRA, 433 QString("Added button item %1 " 434 "r.width %2 height %3 total width %4+%5") 435 .arg(last_item).arg(width).arg(height) 436 .arg(left_width).arg(right_width)); 437 } 438 } 439 else 440 underflow = true; 441 } 195 442 196 m_topPosition = qMax(m_topPosition, 0); 197 break; 443 // If a grid, maintain same number of columns on each row. 444 if (grow_left && col_cnt < m_columns) 445 { 446 if (wrapped) 447 end = last_item + 1; 448 else 449 { 450 // Are we allowed to wrap when we run out of items? 451 if (m_wrapStyle == WrapItems && 452 (hsplit || m_scrollStyle != ScrollFree) && 453 first_item == 0) 454 { 455 first_item = m_itemCount; 456 wrapped = true; 457 end = last_item + 1; 458 } 459 else 460 end = 0; 198 461 } 462 463 if (first_item > end) 464 { 465 buttonstate = PrepareButton(first_button - 1, first_item - 1, 466 selectedIdx, button_shift); 467 if (buttonstate == NULL) 468 continue; 469 width = minButtonWidth(buttonstate->GetArea()); 470 471 // For grids, use the widest button in a column 472 if (*col_widths && width < (*col_widths)[col_idx]) 473 width = (*col_widths)[col_idx]; 474 475 // Does the button fit? 476 if ((hsplit ? left_width : left_width + right_width) + 477 m_itemHorizSpacing + width > max_width) 478 { 479 int total = hsplit ? left_width : left_width + right_width; 480 VERBOSE(VB_GENERAL|VB_EXTRA, 481 QString("button on left would exceed width: " 482 "%1+(%2)+%3 == %4 which is > %5") 483 .arg(total).arg(m_itemHorizSpacing).arg(width) 484 .arg(total + m_itemHorizSpacing + width) 485 .arg(max_width)); 486 } 487 else 488 { 489 added = true; 490 --first_button; 491 --first_item; 492 --col_idx; 493 ++col_cnt; 494 ++left_cnt; 495 left_width += m_itemHorizSpacing + width; 496 height = minButtonHeight(buttonstate->GetArea()); 497 if (row_height < height) 498 row_height = height; 499 VERBOSE(VB_GENERAL|VB_EXTRA, 500 QString("Added button item %1 " 501 "l.width %2 height %3 total width %4+%5") 502 .arg(first_item).arg(width).arg(height) 503 .arg(left_width).arg(right_width)); 504 } 505 } 506 else 507 underflow = true; 199 508 } 509 } 200 510 201 QList<MythUIButtonListItem*>::iterator it = m_itemList.begin() + m_topPosition; 511 /* 512 * If total_height == 0, then this is the first row, so allow any height. 513 * Otherwide, If adding a button to a column would exceed the 514 * parent height, abort 515 */ 516 if (total_height > 0 && 517 ((vsplit ? split_height : total_height) + 518 m_itemVertSpacing + row_height > max_height)) 519 { 520 VERBOSE(VB_GENERAL|VB_EXTRA, 521 QString("%1 Height exceeded %2 + (%3) + %4 = %5 " 522 "which is > %6") 523 .arg(vsplit ? "Centering" : "Total") 524 .arg(split_height).arg(m_itemVertSpacing).arg(row_height) 525 .arg(split_height + m_itemVertSpacing + row_height) 526 .arg(max_height)); 527 first_button = initial_first_button + button_shift; 528 last_button = initial_last_button + button_shift; 529 first_item = initial_first_item; 530 last_item = initial_last_item; 531 return false; 532 } 202 533 534 if (*col_widths == 0) 535 { 536 /* 537 * Allocate array to hold columns widths, now that we know 538 * how many columns there are. 539 */ 540 *col_widths = new int[col_cnt]; 541 for (col_idx = 0; col_idx < col_cnt; ++col_idx) 542 (*col_widths)[col_idx] = 0; 543 544 } 545 546 // Adjust for insertions on the front. 547 first_button += button_shift; 548 last_button += button_shift; 549 550 // It fits, so so note max column widths 551 MythUIStateType *realButton; 552 int buttonIdx; 553 554 if (grow_left) 555 { 556 begin = first_button; 557 end = first_button + col_cnt; 558 } 559 else 560 { 561 end = last_button + 1; 562 begin = end - col_cnt; 563 } 564 565 for (buttonIdx = begin, col_idx = 0; 566 buttonIdx < end; ++buttonIdx, ++col_idx) 567 { 568 realButton = m_ButtonList[buttonIdx]; 569 buttonstate = dynamic_cast<MythUIGroup *> 570 (realButton->GetCurrentState()); 571 width = minButtonWidth(buttonstate->GetArea()); 572 if ((*col_widths)[col_idx] < width) 573 (*col_widths)[col_idx] = width; 574 575 // Make note of which column as the selected button 576 if (selectedIdx == buttonIdx) 577 selected_column = col_idx; 578 } 579 580 /* 581 An underflow indicates we ran out of items, not that the 582 buttons did not fit on the row. 583 */ 584 if (total_height && underflow && col_cnt < m_columns) 585 col_cnt = m_columns; 586 587 return true; 588 } 589 590 /* 591 * Dynamically layout columns 592 */ 593 bool MythUIButtonList::DistributeCols(int & first_button, int & last_button, 594 int & first_item, int & last_item, 595 int & selected_column, int & selected_row, 596 int ** col_widths, 597 QList<int> & row_heights, 598 int & top_height, int & bottom_height, 599 bool & wrapped) 600 { 601 int col_cnt; 602 int height; 603 int row_cnt = 1; 604 int end; 605 bool added; 606 607 do 608 { 609 added = false; 610 611 if (wrapped) 612 end = first_item; 613 else 614 { 615 // Are we allowed to wrap when we run out of items? 616 if (m_wrapStyle == WrapItems && 617 (m_scrollStyle == ScrollCenter || 618 m_scrollStyle == ScrollGroupCenter) && 619 last_item + 1 == m_itemCount) 620 { 621 last_item = -1; 622 wrapped = true; 623 end = first_item; 624 } 625 else 626 end = m_itemCount; 627 } 628 629 if (last_item + 1 < end) 630 { 631 // Does another row fit? 632 if (DistributeRow(first_button, ++last_button, 633 first_item, ++last_item, selected_column, 634 false, true, col_widths, height, 635 top_height + bottom_height, bottom_height, 636 col_cnt, wrapped)) 637 { 638 if (col_cnt < m_columns) 639 return false; // Need to try again with fewer cols 640 641 if (selected_row == -1 && selected_column != -1) 642 selected_row = row_heights.size(); 643 ++row_cnt; 644 row_heights.push_back(height); 645 bottom_height += (height + m_itemVertSpacing); 646 added = true; 647 } 648 else 649 { 650 --last_button; 651 --last_item; 652 } 653 } 654 655 if (wrapped) 656 end = last_item + 1; 657 else 658 { 659 // Are we allowed to wrap when we run out of items? 660 if (m_wrapStyle == WrapItems && 661 (m_scrollStyle == ScrollCenter || 662 m_scrollStyle == ScrollGroupCenter) && 663 first_item == 0) 664 { 665 first_item = m_itemCount; 666 wrapped = true; 667 end = last_item + 1; 668 } 669 else 670 end = 0; 671 } 672 673 if (first_item > end) 674 { 675 // Can we insert another row? 676 if (DistributeRow(--first_button, last_button, 677 --first_item, last_item, selected_column, 678 true, false, col_widths, height, 679 top_height + bottom_height, top_height, 680 col_cnt, wrapped)) 681 { 682 if (col_cnt < m_columns) 683 return false; // Need to try again with fewer cols 684 685 if (selected_row == -1 && selected_column != -1) 686 selected_row = row_heights.size(); 687 else if (selected_row != -1) 688 ++selected_row; 689 ++row_cnt; 690 row_heights.push_front(height); 691 top_height += (height + m_itemVertSpacing); 692 added = true; 693 } 694 else 695 { 696 ++first_button; 697 ++first_item; 698 } 699 } 700 } while (added); 701 702 return true; 703 } 704 705 /* 706 * Dynamically layout as many buttons as will fit in the area. 707 */ 708 bool MythUIButtonList::DistributeButtons(void) 709 { 710 int first_button, last_button, start_button, start_item; 711 int first_item, last_item; 712 int *col_widths; 713 int col_cnt; 714 int selected_column, selected_row; 715 bool wrapped = false; 716 bool grow_left = true; 717 718 QList<int> row_heights; 719 QList<int>::iterator Iheight; 720 int height, top_height, bottom_height; 721 722 start_item = m_selPosition; 723 selected_column = selected_row = -1; 724 top_height = bottom_height = 0; 725 col_widths = 0; 726 727 VERBOSE(VB_GENERAL|VB_EXTRA, QString("DistributeButtons: " 728 "selected item %1 total items %2") 729 .arg(start_item).arg(m_itemCount)); 730 731 /* 732 * Try fewer and fewer columns until each row can fit the same 733 * number of columns. 734 */ 735 for (m_columns = m_itemCount; m_columns > 0; ) 736 { 737 first_item = last_item = start_item; 738 739 /* 740 * Drawing starts at start_button, and radiates from there. 741 * Attempt to pick a start_button which will minimize the need for new 742 * button allocations. 743 */ 744 switch (m_scrollStyle) 745 { 746 case ScrollCenter: 747 case ScrollGroupCenter: 748 start_button = qMax((m_maxVisible / 2) - 1, 0); 749 break; 750 case ScrollFree: 751 if (m_layout == LayoutGrid) 752 { 753 start_button = 0; 754 first_item = last_item = 0; 755 grow_left = false; 756 } 757 else if (!m_ButtonList.empty()) 758 { 759 if (m_itemCount - m_selPosition - 1 < 760 static_cast<int>(m_ButtonList.size()) / 2) 761 start_button = m_ButtonList.size() - 762 (m_itemCount - m_selPosition) + 1; 763 else if (m_selPosition > 764 static_cast<int>(m_ButtonList.size()) / 2) 765 start_button = (m_ButtonList.size() / 2); 766 else 767 start_button = m_selPosition; 768 } 769 else 770 start_button = 0; 771 break; 772 } 773 774 first_button = last_button = start_button; 775 row_heights.clear(); 776 777 // Process row with selected button, and set starting val for m_columns. 778 if (!DistributeRow(first_button, last_button, 779 first_item, last_item, selected_column, 780 grow_left, true, &col_widths, 781 height, 0, 0, col_cnt, wrapped)) 782 return false; 783 m_columns = col_cnt; 784 785 if (m_layout == LayoutGrid && m_scrollStyle == ScrollFree) 786 { 787 /* 788 * Now that we know how many columns there are, we can start 789 * the grid layout for real. 790 */ 791 start_item = (m_selPosition / m_columns) * m_columns; 792 first_item = last_item = start_item; 793 794 /* 795 * Attempt to pick a start_button which will minimize the need 796 * for new button allocations. 797 */ 798 start_button = qMax((int)m_ButtonList.size() / 2, 0); 799 start_button = (start_button / qMax(m_columns, 1)) * m_columns; 800 if (start_button < m_itemCount / 2 && 801 m_itemCount - m_selPosition - 1 < (int)m_ButtonList.size() / 2) 802 start_button += m_columns; 803 first_button = last_button = start_button; 804 805 // Now do initial row layout again with our new knowledge 806 selected_column = selected_row = -1; 807 if (!DistributeRow(first_button, last_button, 808 first_item, last_item, selected_column, 809 grow_left, true, &col_widths, 810 height, 0, 0, col_cnt, wrapped)) 811 return false; 812 } 813 814 if (selected_column != -1) 815 selected_row = 0; 816 row_heights.push_back(height); 203 817 if (m_scrollStyle == ScrollCenter) 818 top_height = bottom_height = (height / 2); 819 else 820 bottom_height = height; 821 822 if (m_layout == LayoutHorizontal) 823 break; 824 825 // As as many columns as will fit. 826 if (DistributeCols(first_button, last_button, 827 first_item, last_item, 828 selected_column, selected_row, 829 &col_widths, row_heights, 830 top_height, bottom_height, wrapped)) 831 break; // Buttons fit on each row, so done 832 833 delete[] col_widths; 834 col_widths = 0; 835 836 --m_columns; 837 start_item = m_selPosition; 838 } 839 840 m_rows = row_heights.size(); 841 842 VERBOSE(VB_GENERAL|VB_EXTRA, 843 QString("%1 rows, %2 columns fit inside parent area %3x%4") 844 .arg(m_rows).arg(m_columns).arg(m_contentsRect.width()) 845 .arg(m_contentsRect.height())); 846 847 if (col_widths == 0) 848 return false; 849 850 int total, row, col; 851 int left_spacing, right_spacing, top_spacing, bottom_spacing; 852 int x, y, x_init, x_adj, y_adj; 853 QString status_msg; 854 855 /* 856 * Calculate heights of buttons on each side of selected button 857 */ 858 top_height = bottom_height = m_topRows = m_bottomRows = 0; 859 860 status_msg = "Row heights: "; 861 for (row = 0; row < m_rows; ++row) 862 { 863 if ( row != 0) 864 status_msg += ", "; 865 866 if (row == selected_row) 204 867 { 205 if (m_selPosition <= (int)(m_itemsVisible/2)) 868 status_msg += '['; 869 top_height += (row_heights[row] / 2); 870 bottom_height += ((row_heights[row] / 2) + (row_heights[row] % 2)); 871 } 872 else 873 { 874 if (bottom_height) 206 875 { 207 button = (m_itemsVisible / 2) - m_selPosition; 208 if (m_wrapStyle == WrapItems && button > 0 && 209 m_itemCount >= (int)m_itemsVisible) 876 bottom_height += m_itemVertSpacing + row_heights[row]; 877 ++m_bottomRows; 878 } 879 else 880 { 881 top_height += row_heights[row] + m_itemVertSpacing; 882 ++m_topRows; 883 } 884 } 885 886 status_msg += QString("%1").arg(row_heights[row]); 887 if (row == selected_row) 888 status_msg += ']'; 889 } 890 891 /* 892 * How much extra space should there be between buttons? 893 */ 894 if (m_arrange == ArrangeStack || m_layout == LayoutHorizontal) 895 { 896 // None 897 top_spacing = bottom_spacing = 0; 898 } 899 else 900 { 901 if (m_rows < 2) 902 { 903 // Equal space on both sides of single row 904 top_spacing = bottom_spacing = 905 (m_contentsRect.height() - top_height) / 2; 906 } 907 else 908 { 909 if (m_scrollStyle == ScrollCenter) 910 { 911 // Selected button needs to end up in the middle of area 912 top_spacing = m_topRows ? (m_contentsRect.height() / 2 - 913 top_height) / m_topRows : 0; 914 bottom_spacing = m_bottomRows ? (m_contentsRect.height() / 2 - 915 bottom_height) / m_bottomRows : 0; 916 if (m_arrange == ArrangeSpread) 210 917 { 211 it = m_itemList.end() - button; 212 button = 0; 918 // Use same spacing on both sides of selected button 919 if (!m_topRows || top_spacing > bottom_spacing) 920 top_spacing = bottom_spacing; 921 else 922 bottom_spacing = top_spacing; 213 923 } 214 924 } 215 else if ((m_itemCount - m_selPosition) < (int)(m_itemsVisible/2))925 else 216 926 { 217 it = m_itemList.begin() + m_selPosition - (m_itemsVisible/2); 927 // Buttons will be evenly spread out to fill entire area 928 top_spacing = bottom_spacing = (m_contentsRect.height() - 929 (top_height + bottom_height)) / 930 (m_topRows + m_bottomRows); 218 931 } 219 932 } 220 else if (m_drawFromBottom && m_itemCount < (int)m_itemsVisible) 221 button = m_itemsVisible - m_itemCount; 933 // Add in intra-button space size 934 top_height += (top_spacing * m_topRows); 935 bottom_height += (bottom_spacing * m_bottomRows); 936 } 222 937 223 for (int i = 0; i < button; i++) 224 m_ButtonList[i]->SetVisible(false); 938 /* 939 * Calculate top margin 940 */ 941 y = m_contentsRect.y(); 942 if (m_alignment & Qt::AlignVCenter && m_arrange != ArrangeFill) 943 { 944 if (m_scrollStyle == ScrollCenter) 945 { 946 // Adjust to compensate for top height less than bottom height 947 y += qMax(bottom_height - top_height, 0); 948 total = qMax(top_height, bottom_height) * 2; 949 } 950 else 951 total = top_height + bottom_height; 225 952 226 bool seenSelected = false; 953 // Adjust top margin so selected button ends up in the middle 954 y += (qMax(m_contentsRect.height() - total, 2) / 2); 955 } 956 else if (m_alignment & Qt::AlignBottom && m_arrange == ArrangeStack) 957 { 958 // Adjust top margin so buttons are bottom justified 959 y += qMax(m_contentsRect.height() - 960 (top_height + bottom_height), 0); 961 } 227 962 228 MythUIStateType *realButton = NULL; 229 MythUIButtonListItem *buttonItem = NULL; 963 status_msg += QString(" spacing top %1 bottom %2 fixed %3 offset %4") 964 .arg(top_spacing).arg(bottom_spacing) 965 .arg(m_itemVertSpacing).arg(y); 230 966 231 if (it < m_itemList.begin()) 232 it = m_itemList.begin(); 967 VERBOSE(VB_GENERAL|VB_EXTRA, status_msg); 233 968 234 int curItem = GetItemPos(*it); 235 while (it < m_itemList.end() && button < (int)m_itemsVisible) 236 { 237 realButton = m_ButtonList[button]; 238 buttonItem = *it; 969 /* 970 * Calculate width of buttons on each side of selected button 971 */ 972 int left_width, right_width; 239 973 240 if (!realButton || !buttonItem) 241 break; 974 left_width = right_width = m_leftColumns = m_rightColumns = 0; 242 975 243 bool selected = false; 244 if (!seenSelected && (curItem == m_selPosition)) 976 status_msg = "Col widths: "; 977 for (col = 0; col < m_columns; ++col) 978 { 979 if (col != 0) 980 status_msg += ", "; 981 982 if (col == selected_column) 983 { 984 status_msg += '['; 985 left_width += (col_widths[col] / 2); 986 right_width += ((col_widths[col] / 2) + (col_widths[col] % 2)); 987 } 988 else 989 { 990 if (right_width) 245 991 { 246 seenSelected = true;247 selected = true;992 right_width += m_itemHorizSpacing + col_widths[col]; 993 ++m_rightColumns; 248 994 } 995 else 996 { 997 left_width += col_widths[col] + m_itemHorizSpacing; 998 ++m_leftColumns; 999 } 1000 } 249 1001 250 m_ButtonToItem[button] = buttonItem; 251 buttonItem->SetToRealButton(realButton, selected); 252 realButton->SetVisible(true); 1002 status_msg += QString("%1").arg(col_widths[col]); 1003 if (col == selected_column) 1004 status_msg += ']'; 1005 } 253 1006 254 if (m_wrapStyle == WrapItems && it == (m_itemList.end()-1) && 255 m_itemCount >= (int)m_itemsVisible) 1007 /* 1008 * How much extra space should there be between buttons? 1009 */ 1010 if (m_arrange == ArrangeStack || m_layout == LayoutVertical) 1011 { 1012 // None 1013 left_spacing = right_spacing = 0; 1014 } 1015 else 1016 { 1017 if (m_columns < 2) 1018 { 1019 // Equal space on both sides of single column 1020 left_spacing = right_spacing = 1021 (m_contentsRect.width() - left_width) / 2; 1022 } 1023 else 1024 { 1025 if (m_scrollStyle == ScrollCenter) 256 1026 { 257 it = m_itemList.begin(); 258 curItem = 0; 1027 // Selected button needs to end up in the middle 1028 left_spacing = m_leftColumns ? (m_contentsRect.width() / 2 - 1029 left_width) / m_leftColumns : 0; 1030 right_spacing = m_rightColumns ? (m_contentsRect.width() / 2 - 1031 right_width) / m_rightColumns : 0; 1032 1033 if (m_arrange == ArrangeSpread) 1034 { 1035 // Use same spacing on both sides of selected button 1036 if (!m_leftColumns || left_spacing > right_spacing) 1037 left_spacing = right_spacing; 1038 else 1039 right_spacing = left_spacing; 1040 } 259 1041 } 260 1042 else 261 1043 { 262 ++it; 263 ++curItem; 1044 // Buttons will be evenly spread out to fill entire area 1045 left_spacing = right_spacing = (m_contentsRect.width() - 1046 (left_width + right_width)) / 1047 (m_leftColumns + m_rightColumns); 264 1048 } 1049 } 1050 // Add in intra-button space size 1051 left_width += (left_spacing * m_leftColumns); 1052 right_width += (right_spacing * m_rightColumns); 1053 } 265 1054 266 button++; 1055 /* 1056 * Calculate left margin 1057 */ 1058 x_init = m_contentsRect.x(); 1059 if (m_alignment & Qt::AlignHCenter && m_arrange != ArrangeFill) 1060 { 1061 if (m_scrollStyle == ScrollCenter) 1062 { 1063 // Compensate for left being smaller than right 1064 x_init += qMax(right_width - left_width, 0); 1065 total = qMax(left_width, right_width) * 2; 267 1066 } 1067 else 1068 total = left_width + right_width; 268 1069 269 for (; button < (int)m_itemsVisible; button++) 270 m_ButtonList[button]->SetVisible(false); 1070 // Adjust left margin so selected button ends up in the middle 1071 x_init += (qMax(m_contentsRect.width() - total, 2) / 2); 1072 } 1073 else if (m_alignment & Qt::AlignRight && m_arrange == ArrangeStack) 1074 { 1075 // Adjust left margin, so buttons are right justified 1076 x_init += qMax(m_contentsRect.width() - 1077 (left_width + right_width), 0); 1078 } 271 1079 1080 status_msg += QString(" spacing left %1 right %2 fixed %3 offset %4") 1081 .arg(left_spacing).arg(right_spacing) 1082 .arg(m_itemHorizSpacing).arg(x_init); 1083 VERBOSE(VB_GENERAL|VB_EXTRA, status_msg); 1084 1085 top_spacing += m_itemVertSpacing; 1086 bottom_spacing += m_itemVertSpacing; 1087 left_spacing += m_itemHorizSpacing; 1088 right_spacing += m_itemHorizSpacing; 1089 1090 MythUIStateType *realButton = NULL; 1091 MythUIGroup *buttonstate; 1092 1093 // Calculate position of each button 1094 int vertical_spacing, horizontal_spacing; 1095 int buttonIdx = first_button; 1096 1097 vertical_spacing = top_spacing; 1098 for (row = 0; row < m_rows; ++row) 1099 { 1100 x = x_init; 1101 horizontal_spacing = left_spacing; 1102 for (col = 0; col < m_columns && buttonIdx <= last_button; ++col) 1103 { 1104 realButton = m_ButtonList[buttonIdx]; 1105 buttonstate = dynamic_cast<MythUIGroup *> 1106 (realButton->GetCurrentState()); 1107 1108 // Center button within width of column 1109 if (m_alignment & Qt::AlignHCenter) 1110 x_adj = (col_widths[col] - 1111 minButtonWidth(buttonstate->GetArea())) / 2; 1112 else if (m_alignment & Qt::AlignRight) 1113 x_adj = (col_widths[col] - 1114 minButtonWidth(buttonstate->GetArea())); 1115 else 1116 x_adj = 0; 1117 1118 // Center button within height of row. 1119 if (m_alignment & Qt::AlignVCenter) 1120 y_adj = (row_heights[row] - 1121 minButtonHeight(buttonstate->GetArea())) / 2; 1122 else if (m_alignment & Qt::AlignBottom) 1123 y_adj = (row_heights[row] - 1124 minButtonHeight(buttonstate->GetArea())); 1125 else 1126 y_adj = 0; 1127 1128 // Set position of button 1129 realButton->SetPosition(x + x_adj, y + y_adj); 1130 realButton->SetVisible(true); 1131 1132 if (col == selected_column) 1133 horizontal_spacing = right_spacing; 1134 1135 x += col_widths[col] + horizontal_spacing; 1136 ++buttonIdx; 1137 } 1138 if (row == selected_row) 1139 vertical_spacing = bottom_spacing; 1140 1141 y += row_heights[row] + vertical_spacing; 272 1142 } 273 1143 1144 m_itemsVisible = m_columns * m_rows; 1145 1146 // Hide buttons before first active button 1147 for (buttonIdx = 0; buttonIdx < first_button; ++buttonIdx) 1148 m_ButtonList[buttonIdx]->SetVisible(false); 1149 // Hide buttons after last active buttons. 1150 for (buttonIdx = m_maxVisible - 1; buttonIdx > last_button; --buttonIdx) 1151 m_ButtonList[buttonIdx]->SetVisible(false); 1152 1153 // Set m_topPosition so arrows are displayed correctly. 1154 if (m_scrollStyle == ScrollCenter || m_scrollStyle == ScrollGroupCenter) 1155 m_topPosition = static_cast<int>(m_itemsVisible) < m_itemCount; 1156 else 1157 m_topPosition = first_item; 1158 1159 delete[] col_widths; 1160 return true; 1161 } 1162 1163 void MythUIButtonList::SetPosition(void) 1164 { 1165 if (m_ButtonList.size() == 0) 1166 return; 1167 1168 int button = 0; 1169 1170 switch (m_scrollStyle) 1171 { 1172 case ScrollCenter: 1173 case ScrollGroupCenter: 1174 m_topPosition = qMax(m_selPosition - (int)((float)m_itemsVisible / 2), 0); 1175 break; 1176 case ScrollFree: 1177 { 1178 int adjust = 0; 1179 if (m_topPosition == -1 || m_keepSelAtBottom) 1180 { 1181 if (m_topPosition == -1) 1182 m_topPosition = 0; 1183 if (m_layout == LayoutHorizontal) 1184 adjust = 1 - m_itemsVisible; 1185 else 1186 adjust = m_columns - m_itemsVisible; 1187 m_keepSelAtBottom = false; 1188 } 1189 1190 if (m_selPosition < m_topPosition || 1191 m_topPosition + (int)m_itemsVisible <= m_selPosition) 1192 { 1193 if (m_layout == LayoutHorizontal) 1194 m_topPosition = m_selPosition + adjust; 1195 else 1196 m_topPosition = (m_selPosition + adjust) / 1197 m_columns * m_columns; 1198 } 1199 1200 m_topPosition = qMax(m_topPosition, 0); 1201 break; 1202 } 1203 } 1204 1205 QList<MythUIButtonListItem*>::iterator it = m_itemList.begin() + m_topPosition; 1206 1207 if (m_scrollStyle == ScrollCenter || m_scrollStyle == ScrollGroupCenter) 1208 { 1209 if (m_selPosition <= (int)(m_itemsVisible/2)) 1210 { 1211 button = (m_itemsVisible / 2) - m_selPosition; 1212 if (m_wrapStyle == WrapItems && button > 0 && 1213 m_itemCount >= (int)m_itemsVisible) 1214 { 1215 it = m_itemList.end() - button; 1216 button = 0; 1217 } 1218 } 1219 else if ((m_itemCount - m_selPosition) < (int)(m_itemsVisible/2)) 1220 { 1221 it = m_itemList.begin() + m_selPosition - (m_itemsVisible/2); 1222 } 1223 } 1224 else if (m_drawFromBottom && m_itemCount < (int)m_itemsVisible) 1225 button = m_itemsVisible - m_itemCount; 1226 1227 for (int i = 0; i < button; i++) 1228 m_ButtonList[i]->SetVisible(false); 1229 1230 bool seenSelected = false; 1231 1232 MythUIStateType *realButton = NULL; 1233 MythUIButtonListItem *buttonItem = NULL; 1234 1235 if (it < m_itemList.begin()) 1236 it = m_itemList.begin(); 1237 1238 int curItem = GetItemPos(*it); 1239 while (it < m_itemList.end() && button < (int)m_itemsVisible) 1240 { 1241 realButton = m_ButtonList[button]; 1242 buttonItem = *it; 1243 1244 if (!realButton || !buttonItem) 1245 break; 1246 1247 bool selected = false; 1248 if (!seenSelected && (curItem == m_selPosition)) 1249 { 1250 seenSelected = true; 1251 selected = true; 1252 } 1253 1254 m_ButtonToItem[button] = buttonItem; 1255 buttonItem->SetToRealButton(realButton, selected); 1256 realButton->SetVisible(true); 1257 1258 if (m_wrapStyle == WrapItems && it == (m_itemList.end()-1) && 1259 m_itemCount >= (int)m_itemsVisible) 1260 { 1261 it = m_itemList.begin(); 1262 curItem = 0; 1263 } 1264 else 1265 { 1266 ++it; 1267 ++curItem; 1268 } 1269 1270 button++; 1271 } 1272 1273 for (; button < (int)m_itemsVisible; button++) 1274 m_ButtonList[button]->SetVisible(false); 1275 1276 } 1277 1278 void MythUIButtonList::SanitizePosition(void) 1279 { 1280 if (m_selPosition < 0) 1281 m_selPosition = (m_wrapStyle > WrapNone) ? m_itemList.size() - 1 : 0; 1282 else if (m_selPosition >= m_itemList.size()) 1283 m_selPosition = (m_wrapStyle > WrapNone) ? 0 : m_itemList.size() - 1; 1284 } 1285 1286 void MythUIButtonList::SetPositionArrowStates() 1287 { 1288 if (!m_initialized) 1289 Init(); 1290 1291 if (!m_initialized) 1292 return; 1293 1294 if (m_clearing) 1295 return; 1296 1297 m_needsUpdate = false; 1298 1299 // set topitem, top position 1300 SanitizePosition(); 1301 m_ButtonToItem.clear(); 1302 1303 if (m_arrange == ArrangeFixed) 1304 SetPosition(); 1305 else 1306 DistributeButtons(); 1307 274 1308 updateLCD(); 275 1309 276 1310 if (!m_downArrow || !m_upArrow) … … 485 1519 return m_itemList.indexOf(item); 486 1520 } 487 1521 1522 void MythUIButtonList::InitButton(int itemIdx, MythUIStateType* & realButton, 1523 MythUIButtonListItem* & buttonItem) 1524 { 1525 buttonItem = m_itemList[itemIdx]; 1526 1527 if (m_maxVisible == 0) 1528 { 1529 QString name("buttonlist button 0"); 1530 MythUIStateType *button = new MythUIStateType(this, name); 1531 button->CopyFrom(m_buttontemplate); 1532 m_ButtonList.append(button); 1533 ++m_maxVisible; 1534 } 1535 1536 realButton = m_ButtonList[0]; 1537 m_ButtonToItem[0] = buttonItem; 1538 } 1539 1540 /* 1541 * PageUp and PageDown are helpers when Dynamic layout is being used. 1542 * 1543 * When buttons are layed out dynamically, the number of buttons on the next 1544 * page, may not equal the number of buttons on the current page. Dynamic 1545 * layout is always center-weighted, so attempt to figure out which button 1546 * is near the middle on the next page. 1547 */ 1548 1549 int MythUIButtonList::PageUp(void) 1550 { 1551 int pos = m_selPosition; 1552 int num_items = m_itemList.size(); 1553 int total = 0; 1554 MythUIGroup* buttonstate; 1555 MythUIStateType* realButton; 1556 MythUIButtonListItem* buttonItem; 1557 1558 /* 1559 * /On the new page/ 1560 * If the number of buttons before the selected button does not equal 1561 * the number of buttons after the selected button, this logic can 1562 * undershoot the new selected button. That is better than overshooting 1563 * though. 1564 * 1565 * To fix this would require laying out the new page and then figuring 1566 * out which button should be selected, but this is already complex enough. 1567 */ 1568 1569 if (m_layout == LayoutHorizontal) 1570 { 1571 pos -= (m_leftColumns + 1); 1572 1573 int max_width = m_contentsRect.width() / 2; 1574 for ( ; pos >= 0; --pos) 1575 { 1576 InitButton(pos, realButton, buttonItem); 1577 buttonItem->SetToRealButton(realButton, true); 1578 buttonstate = dynamic_cast<MythUIGroup *> 1579 (realButton->GetCurrentState()); 1580 if (buttonstate == NULL) 1581 { 1582 VERBOSE(VB_IMPORTANT, 1583 QString("PageUp: Failed to query buttonlist state")); 1584 return pos; 1585 } 1586 if (total + m_itemHorizSpacing + 1587 buttonstate->GetArea().width() / 2 >= max_width) 1588 return pos + 1; 1589 1590 buttonItem->SetToRealButton(realButton, false); 1591 buttonstate = dynamic_cast<MythUIGroup *> 1592 (realButton->GetCurrentState()); 1593 total += m_itemHorizSpacing + buttonstate->GetArea().width(); 1594 } 1595 return 0; 1596 } 1597 1598 // Grid or Vertical 1599 int dec; 1600 1601 if (m_layout == LayoutGrid) 1602 { 1603 /* 1604 * Adjusting using bottomRow:TopRow only works if new page 1605 * has the same ratio as the previous page, but that is common 1606 * with the grid layout, so go for it. If themers start doing 1607 * grids where this is not true, then this will need to be modified. 1608 */ 1609 pos -= (m_columns * (m_topRows + 1 + 1610 qMax(m_bottomRows - m_topRows, 0))); 1611 dec = m_columns; 1612 } 1613 else 1614 { 1615 pos -= (m_topRows + 1); 1616 dec = 1; 1617 } 1618 1619 int max_height = m_contentsRect.height() / 2; 1620 for ( ; pos >= 0; pos -= dec) 1621 { 1622 InitButton(pos, realButton, buttonItem); 1623 buttonItem->SetToRealButton(realButton, true); 1624 buttonstate = dynamic_cast<MythUIGroup *> 1625 (realButton->GetCurrentState()); 1626 if (buttonstate == NULL) 1627 { 1628 VERBOSE(VB_IMPORTANT, 1629 QString("PageUp: Failed to query buttonlist state")); 1630 return pos; 1631 } 1632 if (total + m_itemHorizSpacing + 1633 buttonstate->GetArea().height() / 2 >= max_height) 1634 return pos + dec; 1635 1636 buttonItem->SetToRealButton(realButton, false); 1637 buttonstate = dynamic_cast<MythUIGroup *> 1638 (realButton->GetCurrentState()); 1639 total += m_itemHorizSpacing + buttonstate->GetArea().height(); 1640 } 1641 return 0; 1642 } 1643 1644 int MythUIButtonList::PageDown(void) 1645 { 1646 int pos = m_selPosition; 1647 int num_items = m_itemList.size(); 1648 int total = 0; 1649 MythUIGroup* buttonstate; 1650 MythUIStateType* realButton; 1651 MythUIButtonListItem* buttonItem; 1652 1653 /* 1654 * /On the new page/ 1655 * If the number of buttons before the selected button does not equal 1656 * the number of buttons after the selected button, this logic can 1657 * undershoot the new selected button. That is better than overshooting 1658 * though. 1659 * 1660 * To fix this would require laying out the new page and then figuring 1661 * out which button should be selected, but this is already complex enough. 1662 */ 1663 1664 if (m_layout == LayoutHorizontal) 1665 { 1666 pos += (m_rightColumns + 1); 1667 1668 int max_width = m_contentsRect.width() / 2; 1669 for ( ; pos < num_items; ++pos) 1670 { 1671 InitButton(pos, realButton, buttonItem); 1672 buttonItem->SetToRealButton(realButton, true); 1673 buttonstate = dynamic_cast<MythUIGroup *> 1674 (realButton->GetCurrentState()); 1675 if (buttonstate == NULL) 1676 { 1677 VERBOSE(VB_IMPORTANT, 1678 QString("PageDown: Failed to query buttonlist state")); 1679 return pos; 1680 } 1681 if (total + m_itemHorizSpacing + 1682 buttonstate->GetArea().width() / 2 >= max_width) 1683 return pos - 1; 1684 1685 buttonItem->SetToRealButton(realButton, false); 1686 buttonstate = dynamic_cast<MythUIGroup *> 1687 (realButton->GetCurrentState()); 1688 total += m_itemHorizSpacing + buttonstate->GetArea().width(); 1689 } 1690 return num_items - 1; 1691 } 1692 1693 // Grid or Vertical 1694 int inc; 1695 1696 if (m_layout == LayoutGrid) 1697 { 1698 /* 1699 * Adjusting using bottomRow:TopRow only works if new page 1700 * has the same ratio as the previous page, but that is common 1701 * with the grid layout, so go for it. If themers start doing 1702 * grids where this is not true, then this will need to be modified. 1703 */ 1704 pos += (m_columns * (m_bottomRows + 1 + 1705 qMax(m_topRows - m_bottomRows, 0))); 1706 inc = m_columns; 1707 } 1708 else 1709 { 1710 pos += (m_bottomRows + 1); 1711 inc = 1; 1712 } 1713 1714 int max_height = m_contentsRect.height() / 2; 1715 for ( ; pos < num_items; pos += inc) 1716 { 1717 InitButton(pos, realButton, buttonItem); 1718 buttonItem->SetToRealButton(realButton, true); 1719 buttonstate = dynamic_cast<MythUIGroup *> 1720 (realButton->GetCurrentState()); 1721 if (buttonstate == NULL) 1722 { 1723 VERBOSE(VB_IMPORTANT, 1724 QString("PageDown: Failed to query buttonlist state")); 1725 return pos; 1726 } 1727 if (total + m_itemHorizSpacing + 1728 buttonstate->GetArea().height() / 2 >= max_height) 1729 return pos - inc; 1730 1731 buttonItem->SetToRealButton(realButton, false); 1732 buttonstate = dynamic_cast<MythUIGroup *> 1733 (realButton->GetCurrentState()); 1734 total += m_itemHorizSpacing + buttonstate->GetArea().height(); 1735 } 1736 return num_items - 1; 1737 } 1738 488 1739 bool MythUIButtonList::MoveUp(MovementUnit unit, uint amount) 489 1740 { 490 1741 int pos = m_selPosition; … … 532 1783 return true; 533 1784 break; 534 1785 case MovePage: 535 m_selPosition = qMax(0, m_selPosition - (int)m_itemsVisible); 1786 if (m_arrange == ArrangeFixed) 1787 m_selPosition = qMax(0, m_selPosition - (int)m_itemsVisible); 1788 else 1789 m_selPosition = PageUp(); 536 1790 break; 537 1791 case MoveMid: 538 1792 m_selPosition = (int)(m_itemList.size() / 2); … … 589 1843 return true; 590 1844 break; 591 1845 case MoveRow: 592 if (((m_itemList.size()-1) / m_columns) > (pos / m_columns))1846 if (((m_itemList.size()-1) / qMax(m_columns, 0) ) > (pos / m_columns)) 593 1847 { 594 1848 for (int i = 0; i < m_columns; ++i) 595 1849 { … … 604 1858 return true; 605 1859 break; 606 1860 case MovePage: 607 m_selPosition = qMin(m_itemCount - 1, 1861 if (m_arrange == ArrangeFixed) 1862 m_selPosition = qMin(m_itemCount - 1, 608 1863 m_selPosition + (int)m_itemsVisible); 1864 else 1865 m_selPosition = PageDown(); 609 1866 break; 610 1867 case MoveMax: 611 1868 m_selPosition = m_itemCount - 1; … … 727 1984 if (m_downArrow) 728 1985 m_downArrow->SetVisible(true); 729 1986 730 MythUIStateType *buttontemplate = dynamic_cast<MythUIStateType *> 731 (GetChild("buttonitem")); 1987 m_contentsRect.CalculateArea(m_Area); 732 1988 733 if (!buttontemplate) 1989 m_buttontemplate = dynamic_cast<MythUIStateType *>(GetChild("buttonitem")); 1990 1991 if (!m_buttontemplate) 734 1992 { 735 1993 VERBOSE(VB_IMPORTANT, QString("Statetype buttonitem is required in " 736 1994 "mythuibuttonlist: %1") 737 1995 .arg(objectName())); 738 1996 return; 739 1997 } 740 1998 741 m_ contentsRect.CalculateArea(m_Area);1999 m_buttontemplate->SetVisible(false); 742 2000 743 buttontemplate->SetVisible(false); 2001 if (m_arrange == ArrangeFixed) 2002 { 744 2003 745 MythRect buttonItemArea; 2004 /* 2005 * If fixed spacing is defined, then use the "active" state size 2006 * to predictively determine the position of each button. 2007 */ 2008 MythRect buttonItemArea; 746 2009 747 MythUIGroup *buttonActiveState = dynamic_cast<MythUIGroup *>748 (buttontemplate->GetState("active"));749 if (buttonActiveState)750 buttonItemArea = buttonActiveState->GetArea();751 else752 buttonItemArea =buttontemplate->GetArea();2010 MythUIGroup *buttonActiveState = dynamic_cast<MythUIGroup *> 2011 (m_buttontemplate->GetState("active")); 2012 if (buttonActiveState) 2013 buttonItemArea = buttonActiveState->GetArea(); 2014 else 2015 buttonItemArea = m_buttontemplate->GetArea(); 753 2016 754 buttonItemArea.CalculateArea(m_contentsRect);2017 buttonItemArea.CalculateArea(m_contentsRect); 755 2018 756 m_itemHeight = buttonItemArea.height();757 m_itemWidth = buttonItemArea.width();2019 m_itemHeight = buttonItemArea.height(); 2020 m_itemWidth = buttonItemArea.width(); 758 2021 759 CalculateVisibleItems();2022 CalculateVisibleItems(); 760 2023 761 int col = 1;762 int row = 1;2024 int col = 1; 2025 int row = 1; 763 2026 764 for (int i = 0; i < (int)m_itemsVisible; i++) 765 { 766 QString name = QString("buttonlist button %1").arg(i); 767 MythUIStateType *button = new MythUIStateType(this, name); 768 button->CopyFrom(buttontemplate); 769 770 if (col > m_columns) 2027 for (int i = 0; i < (int)m_itemsVisible; i++) 771 2028 { 772 col = 1;773 row++;774 }2029 QString name = QString("buttonlist button %1").arg(i); 2030 MythUIStateType *button = new MythUIStateType(this, name); 2031 button->CopyFrom(m_buttontemplate); 775 2032 776 button->SetPosition(GetButtonPosition(col, row)); 2033 if (col > m_columns) 2034 { 2035 col = 1; 2036 row++; 2037 } 777 2038 778 col++; 2039 button->SetPosition(GetButtonPosition(col, row)); 2040 col++; 779 2041 780 m_ButtonList.push_back(button);781 }2042 m_ButtonList.push_back(button); 2043 } 782 2044 783 // The following is pretty much a hack for the benefit of MythGallery784 // it scales images based on the button size and we need to give it the785 // largest button state so that the images are not too small786 // This can be removed once the disk based image caching is added to mythui,787 //since the mythgallery thumbnail generator can be ditched.788 MythUIGroup *buttonSelectedState = dynamic_cast<MythUIGroup *>789 (buttontemplate->GetState("selected"));2045 // The following is pretty much a hack for the benefit of MythGallery 2046 // it scales images based on the button size and we need to give it the 2047 // largest button state so that the images are not too small 2048 // This can be removed once the disk based image caching is added to 2049 // mythui, since the mythgallery thumbnail generator can be ditched. 2050 MythUIGroup *buttonSelectedState = dynamic_cast<MythUIGroup *> 2051 (m_buttontemplate->GetState("selected")); 790 2052 791 if (buttonSelectedState)792 {793 MythRect itemArea = buttonSelectedState->GetArea();794 itemArea.CalculateArea(m_contentsRect);2053 if (buttonSelectedState) 2054 { 2055 MythRect itemArea = buttonSelectedState->GetArea(); 2056 itemArea.CalculateArea(m_contentsRect); 795 2057 796 if (m_itemHeight < itemArea.height()) 797 m_itemHeight = itemArea.height(); 798 if (m_itemWidth < itemArea.width()) 799 m_itemWidth = itemArea.width(); 2058 if (m_itemHeight < itemArea.height()) 2059 m_itemHeight = itemArea.height(); 2060 if (m_itemWidth < itemArea.width()) 2061 m_itemWidth = itemArea.width(); 2062 } 2063 // End Hack 800 2064 } 801 // End Hack802 2065 803 2066 m_initialized = true; 804 2067 } … … 847 2110 if (m_layout == LayoutHorizontal) 848 2111 handled = MoveDown(MoveItem); 849 2112 else if (m_layout == LayoutGrid) 850 handled = MoveDown(MoveColumn); 2113 { 2114 if (m_scrollStyle == ScrollFree) 2115 handled = MoveDown(MoveColumn); 2116 else 2117 handled = MoveDown(MoveItem); 2118 } 851 2119 else 852 2120 handled = false; 853 2121 } … … 856 2124 if (m_layout == LayoutHorizontal) 857 2125 handled = MoveUp(MoveItem); 858 2126 else if (m_layout == LayoutGrid) 859 handled = MoveUp(MoveColumn); 2127 { 2128 if (m_scrollStyle == ScrollFree) 2129 handled = MoveUp(MoveColumn); 2130 else 2131 handled = MoveUp(MoveItem); 2132 } 860 2133 else 861 2134 handled = false; 862 2135 } … … 1007 2280 else 1008 2281 m_layout = LayoutVertical; 1009 2282 } 2283 else if (element.tagName() == "arrange") 2284 { 2285 QString arrange = getFirstText(element).toLower(); 2286 2287 if (arrange == "fill") 2288 m_arrange = ArrangeFill; 2289 else if (arrange == "spread") 2290 m_arrange = ArrangeSpread; 2291 else if (arrange == "stack") 2292 m_arrange = ArrangeStack; 2293 else 2294 m_arrange = ArrangeFixed; 2295 2296 } 2297 else if (element.tagName() == "align") 2298 { 2299 QString align = getFirstText(element).toLower(); 2300 m_alignment = parseAlignment(align); 2301 } 1010 2302 else if (element.tagName() == "scrollstyle") 1011 2303 { 1012 2304 QString layout = getFirstText(element).toLower(); 1013 2305 1014 2306 if (layout == "center") 1015 2307 m_scrollStyle = ScrollCenter; 2308 else if (layout == "groupcenter") 2309 m_scrollStyle = ScrollGroupCenter; 1016 2310 else if (layout == "free") 1017 2311 m_scrollStyle = ScrollFree; 1018 2312 } … … 1037 2331 m_itemVertSpacing = NormY(getFirstText(element).toInt()); 1038 2332 } 1039 2333 else if (element.tagName() == "drawfrombottom") 2334 { 1040 2335 m_drawFromBottom = parseBool(element); 2336 m_alignment |= Qt::AlignBottom; 2337 } 1041 2338 else 1042 2339 return MythUIType::ParseElement(element); 1043 2340 … … 1063 2360 return; 1064 2361 1065 2362 m_layout = lb->m_layout; 2363 m_arrange = lb->m_arrange; 2364 m_alignment = lb->m_alignment; 1066 2365 1067 2366 m_contentsRect = lb->m_contentsRect; 1068 2367 … … 1071 2370 m_itemHorizSpacing = lb->m_itemHorizSpacing; 1072 2371 m_itemVertSpacing = lb->m_itemVertSpacing; 1073 2372 m_itemsVisible = lb->m_itemsVisible; 2373 m_maxVisible = lb->m_maxVisible; 1074 2374 1075 2375 m_active = lb->m_active; 1076 2376 m_showArrow = lb->m_showArrow; … … 1608 2908 } 1609 2909 ++state_it; 1610 2910 } 2911 2912 1611 2913 }