00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "gui/widgets/grid.hpp"
00016
00017 #include "foreach.hpp"
00018 #include "log.hpp"
00019
00020 #include <cassert>
00021 #include <numeric>
00022
00023 #define DBG_G LOG_STREAM_INDENT(debug, gui)
00024 #define LOG_G LOG_STREAM_INDENT(info, gui)
00025 #define WRN_G LOG_STREAM_INDENT(warn, gui)
00026 #define ERR_G LOG_STREAM_INDENT(err, gui)
00027
00028 #define DBG_G_D LOG_STREAM_INDENT(debug, gui_draw)
00029 #define LOG_G_D LOG_STREAM_INDENT(info, gui_draw)
00030 #define WRN_G_D LOG_STREAM_INDENT(warn, gui_draw)
00031 #define ERR_G_D LOG_STREAM_INDENT(err, gui_draw)
00032
00033 #define DBG_G_E LOG_STREAM_INDENT(debug, gui_event)
00034 #define LOG_G_E LOG_STREAM_INDENT(info, gui_event)
00035 #define WRN_G_E LOG_STREAM_INDENT(warn, gui_event)
00036 #define ERR_G_E LOG_STREAM_INDENT(err, gui_event)
00037
00038 #define DBG_G_P LOG_STREAM_INDENT(debug, gui_parse)
00039 #define LOG_G_P LOG_STREAM_INDENT(info, gui_parse)
00040 #define WRN_G_P LOG_STREAM_INDENT(warn, gui_parse)
00041 #define ERR_G_P LOG_STREAM_INDENT(err, gui_parse)
00042
00043
00044 namespace gui2 {
00045
00046 tgrid::tgrid(const unsigned rows, const unsigned cols) :
00047 rows_(rows),
00048 cols_(cols),
00049 best_row_height_(),
00050 best_col_width_(),
00051 minimum_row_height_(),
00052 minimum_col_width_(),
00053 row_height_(),
00054 col_width_(),
00055 row_grow_factor_(rows),
00056 col_grow_factor_(cols),
00057 children_(rows * cols)
00058 {
00059 }
00060
00061 tgrid::~tgrid()
00062 {
00063 for(std::vector<tchild>::iterator itor = children_.begin();
00064 itor != children_.end(); ++itor) {
00065
00066 if(itor->widget()) {
00067 delete itor->widget();
00068 }
00069 }
00070 }
00071
00072 void tgrid::add_child(twidget* widget, const unsigned row,
00073 const unsigned col, const unsigned flags, const unsigned border_size)
00074 {
00075 assert(row < rows_ && col < cols_);
00076
00077 tchild& cell = child(row, col);
00078
00079
00080 if(cell.widget()) {
00081
00082 WRN_G << "Grid: child '" << cell.id()
00083 << "' at cell '" << row << ',' << col << "' will be replaced.\n";
00084 delete cell.widget();
00085 }
00086
00087
00088 cell.set_flags(flags);
00089 cell.set_border_size(border_size);
00090 cell.set_widget(widget);
00091 if(cell.widget()) {
00092
00093 cell.set_id(cell.widget()->id());
00094 cell.widget()->set_parent(this);
00095 } else {
00096 cell.set_id("");
00097 }
00098
00099 clear_cache();
00100 }
00101
00102 void tgrid::set_rows(const unsigned rows)
00103 {
00104 if(rows == rows_) {
00105 return;
00106 }
00107
00108 set_rows_cols(rows, cols_);
00109 }
00110
00111 unsigned tgrid::add_row(const unsigned count)
00112 {
00113 assert(count);
00114
00115
00116
00117 unsigned result = rows_;
00118 set_rows_cols(rows_ + count, cols_);
00119 return result;
00120 }
00121
00122 void tgrid::set_cols(const unsigned cols)
00123 {
00124 if(cols == cols_) {
00125 return;
00126 }
00127
00128 set_rows_cols(rows_, cols);
00129 }
00130
00131 void tgrid::set_rows_cols(const unsigned rows, const unsigned cols)
00132 {
00133 if(rows == rows_ && cols == cols_) {
00134 return;
00135 }
00136
00137 if(!children_.empty()) {
00138 WRN_G << "Grid: resizing a non-empty grid may give unexpected problems.\n";
00139 }
00140
00141 rows_ = rows;
00142 cols_ = cols;
00143 row_grow_factor_.resize(rows);
00144 col_grow_factor_.resize(cols);
00145 children_.resize(rows_ * cols_);
00146 clear_cache();
00147 }
00148
00149 void tgrid::remove_child(const unsigned row, const unsigned col)
00150 {
00151 assert(row < rows_ && col < cols_);
00152
00153 tchild& cell = child(row, col);
00154
00155 cell.set_id("");
00156 cell.set_widget(0);
00157 clear_cache();
00158 }
00159
00160 void tgrid::remove_child(const std::string& id, const bool find_all)
00161 {
00162 for(std::vector<tchild>::iterator itor = children_.begin();
00163 itor != children_.end(); ++itor) {
00164
00165 if(itor->id() == id) {
00166 itor->set_id("");
00167 itor->set_widget(0);
00168 clear_cache();
00169
00170 if(!find_all) {
00171 break;
00172 }
00173 }
00174 }
00175 }
00176
00177 void tgrid::set_active(const bool active)
00178 {
00179 for(std::vector<tchild>::iterator itor = children_.begin();
00180 itor != children_.end(); ++itor) {
00181
00182 twidget* widget = itor->widget();
00183 if(!widget) {
00184 continue;
00185 }
00186
00187 tgrid* grid = dynamic_cast<tgrid*>(widget);
00188 if(grid) {
00189 grid->set_active(active);
00190 continue;
00191 }
00192
00193 tcontrol* control = dynamic_cast<tcontrol*>(widget);
00194 if(control) {
00195 control->set_active(active);
00196 }
00197 }
00198 }
00199
00200 bool tgrid::has_vertical_scrollbar() const
00201 {
00202 for(std::vector<tchild>::const_iterator itor = children_.begin();
00203 itor != children_.end(); ++itor) {
00204
00205
00206 if(itor->widget() && itor->widget()->has_vertical_scrollbar()) {
00207 return true;
00208 }
00209
00210 }
00211
00212
00213 return twidget::has_vertical_scrollbar();
00214 }
00215
00216 tpoint tgrid::get_minimum_size() const
00217 {
00218 return get_size("minimum", minimum_col_width_,
00219 minimum_row_height_, &tchild::get_minimum_size);
00220 }
00221
00222 tpoint tgrid::get_maximum_size() const
00223 {
00224
00225 return tpoint(0,0);
00226 }
00227
00228 tpoint tgrid::get_best_size() const
00229 {
00230 return get_size("best", best_col_width_,
00231 best_row_height_, &tchild::get_best_size);
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245 tpoint tgrid::get_size(const std::string& id, std::vector<unsigned>& width,
00246 std::vector<unsigned>& height, tpoint (tchild::*size_proc)() const) const
00247 {
00248 if(height.empty() || width.empty()) {
00249
00250 DBG_G << "Grid: calculate " << id << " size.\n";
00251
00252 height.resize(rows_, 0);
00253 width.resize(cols_, 0);
00254
00255
00256 for(unsigned row = 0; row < rows_; ++row) {
00257 for(unsigned col = 0; col < cols_; ++col) {
00258
00259 const tpoint size = (child(row, col).*size_proc)();
00260
00261 if(size.x > width[col]) {
00262 width[col] = size.x;
00263 }
00264
00265 if(size.y > height[row]) {
00266 height[row] = size.y;
00267 }
00268
00269 }
00270 }
00271 } else {
00272 DBG_G << "Grid: used cached " << id << " size.\n";
00273 }
00274
00275 for(unsigned row = 0; row < rows_; ++row) {
00276 DBG_G << "Grid: the " << id << " height for row " << row
00277 << " will be " << height[row] << ".\n";
00278 }
00279
00280 for(unsigned col = 0; col < cols_; ++col) {
00281 DBG_G << "Grid: the " << id << " width for col " << col
00282 << " will be " << width[col] << ".\n";
00283 }
00284
00285 return tpoint(
00286 std::accumulate(width.begin(), width.end(), 0),
00287 std::accumulate(height.begin(), height.end(), 0));
00288 }
00289
00290 void tgrid::set_size(const SDL_Rect& rect)
00291 {
00292 log_scope2(gui, "Grid: set size");
00293
00294 twidget::set_size(rect);
00295
00296 if(!rows_ || !cols_) {
00297 return;
00298 }
00299
00300 const tpoint orig(rect.x, rect.y);
00301 const tpoint size(rect.w, rect.h);
00302
00303 const tpoint best_size = get_best_size();
00304 row_height_ = best_row_height_;
00305 col_width_ = best_col_width_;
00306
00307 assert(row_height_.size() == rows_);
00308 assert(col_width_.size() == cols_);
00309 assert(row_grow_factor_.size() == rows_);
00310 assert(col_grow_factor_.size() == cols_);
00311 DBG_G << "Grid: best size " << best_size << " available size " << size << ".\n";
00312
00313 if(best_size == size) {
00314 row_height_ = best_row_height_;
00315 col_width_ = best_col_width_;
00316
00317 layout(orig);
00318 return;
00319 }
00320
00321 if(best_size < size) {
00322 row_height_ = best_row_height_;
00323 col_width_ = best_col_width_;
00324
00325
00326 if(size.x > best_size.x) {
00327 const unsigned w = size.x - best_size.x;
00328 unsigned w_size =
00329 std::accumulate(col_grow_factor_.begin(), col_grow_factor_.end(), 0);
00330 DBG_G << "Grid: extra width " << w << " will be divided amount "
00331 << w_size << " units in " << cols_ << " columns.\n";
00332
00333 if(w_size == 0) {
00334
00335 foreach(unsigned& val, col_grow_factor_) {
00336 val = 1;
00337 }
00338 w_size = cols_;
00339 }
00340
00341
00342 const unsigned w_normal = w / w_size;
00343 for(unsigned i = 0; i < cols_; ++i) {
00344 col_width_[i] += w_normal * col_grow_factor_[i];
00345 DBG_G << "Grid: column " << i << " with grow factor "
00346 << col_grow_factor_[i] << " set width to " << col_width_[i] << ".\n";
00347 }
00348
00349 }
00350
00351 if(size.y > best_size.y) {
00352 const unsigned h = size.y - best_size.y;
00353 unsigned h_size =
00354 std::accumulate(row_grow_factor_.begin(), row_grow_factor_.end(), 0);
00355 DBG_G << "Grid: extra height " << h << " will be divided amount "
00356 << h_size << " units in " << rows_ << " rows.\n";
00357
00358 if(h_size == 0) {
00359
00360 foreach(unsigned& val, row_grow_factor_) {
00361 val = 1;
00362 }
00363 h_size = rows_;
00364 }
00365
00366
00367 const unsigned h_normal = h / h_size;
00368 for(unsigned i = 0; i < rows_; ++i) {
00369 row_height_[i] += h_normal * row_grow_factor_[i];
00370 DBG_G << "Grid: row " << i << " with grow factor "
00371 << row_grow_factor_[i] << " set height to " << row_height_[i] << ".\n";
00372 }
00373 }
00374
00375 layout(orig);
00376 return;
00377
00378 }
00379
00380 if((best_size.x <= size.x )
00381 && (best_size.y <= size.y || has_vertical_scrollbar())) {
00382
00383
00384 const unsigned over_shoot = best_size.y - size.y;
00385
00386 bool set = false;
00387 row_height_ = best_row_height_;
00388 col_width_ = best_col_width_;
00389
00390 for(unsigned i = 0; i < rows_; ++i) {
00391 twidget* row = widget(i, 0);
00392 if(row && row->has_vertical_scrollbar()) {
00393 row_height_[i] -= over_shoot;
00394 set = true;
00395 break;
00396 }
00397
00398 }
00399
00400 assert(set);
00401 layout(orig);
00402 return;
00403 }
00404
00405
00406
00407
00408
00409 assert(false);
00410
00411
00412
00413
00414 }
00415
00416 twidget* tgrid::find_widget(const tpoint& coordinate, const bool must_be_active)
00417 {
00418 for(std::vector<tchild>::iterator itor = children_.begin();
00419 itor != children_.end(); ++itor) {
00420
00421 twidget* widget = itor->widget();
00422 if(!widget) {
00423 continue;
00424 }
00425
00426 widget = widget->find_widget(coordinate, must_be_active);
00427 if(widget) {
00428 clear_cache();
00429 return widget;
00430 }
00431
00432 }
00433
00434 return 0;
00435 }
00436
00437 const twidget* tgrid::find_widget(const tpoint& coordinate,
00438 const bool must_be_active) const
00439 {
00440 for(std::vector<tchild>::const_iterator itor = children_.begin();
00441 itor != children_.end(); ++itor) {
00442
00443 const twidget* widget = itor->widget();
00444 if(!widget) {
00445 continue;
00446 }
00447
00448 widget = widget->find_widget(coordinate, must_be_active);
00449 if(widget) {
00450 return widget;
00451 }
00452
00453 }
00454
00455 return 0;
00456 }
00457
00458 twidget* tgrid::find_widget(const std::string& id, const bool must_be_active)
00459 {
00460 for(std::vector<tchild>::iterator itor = children_.begin();
00461 itor != children_.end(); ++itor) {
00462
00463 twidget* widget = itor->widget();
00464 if(!widget) {
00465 continue;
00466 }
00467
00468 widget = widget->find_widget(id, must_be_active);
00469 if(widget) {
00470 clear_cache();
00471 return widget;
00472 }
00473
00474 }
00475
00476 return 0;
00477 }
00478
00479 const twidget* tgrid::find_widget(const std::string& id,
00480 const bool must_be_active) const
00481 {
00482 for(std::vector<tchild>::const_iterator itor = children_.begin();
00483 itor != children_.end(); ++itor) {
00484
00485 const twidget* widget = itor->widget();
00486 if(!widget) {
00487 continue;
00488 }
00489
00490 widget = widget->find_widget(id, must_be_active);
00491 if(widget) {
00492 return widget;
00493 }
00494
00495 }
00496
00497 return 0;
00498 }
00499
00500 bool tgrid::has_widget(const twidget* widget) const
00501 {
00502 for(std::vector<tchild>::const_iterator itor = children_.begin();
00503 itor != children_.end(); ++itor) {
00504
00505 if(itor->widget() == widget) {
00506 return true;
00507 }
00508 }
00509 return false;
00510 }
00511
00512 void tgrid::draw(surface& surface)
00513 {
00514 for(iterator itor = begin(); itor != end(); ++itor) {
00515 if(! *itor || !itor->dirty()) {
00516 continue;
00517 }
00518
00519 log_scope2(gui_draw, "Grid: draw child.");
00520
00521 itor->draw(surface);
00522 }
00523
00524 set_dirty(false);
00525 }
00526
00527 void tgrid::clear_cache()
00528 {
00529 best_row_height_.clear();
00530 best_col_width_.clear();
00531
00532 minimum_row_height_.clear();
00533 minimum_col_width_.clear();
00534 }
00535
00536 void tgrid::layout(const tpoint& origin)
00537 {
00538 tpoint orig = origin;
00539 for(unsigned row = 0; row < rows_; ++row) {
00540 for(unsigned col = 0; col < cols_; ++col) {
00541
00542 const tpoint size(col_width_[col], row_height_[row]);
00543 DBG_G << "Grid: set widget at " << row << ',' << col
00544 << " at origin " << orig << " with size " << size << ".\n";
00545
00546 if(child(row, col).widget()) {
00547 child(row, col).set_size(orig, size);
00548 }
00549
00550 orig.x += col_width_[col];
00551 }
00552 orig.y += row_height_[row];
00553 orig.x = origin.x;
00554 }
00555 }
00556
00557 tpoint tgrid::tchild::get_best_size() const
00558 {
00559 if(!widget_) {
00560 return border_space();
00561 }
00562
00563 if(widget_->dirty() || best_size_ == tpoint(0, 0)) {
00564 best_size_ = widget_->get_best_size() + border_space();
00565 }
00566
00567 return best_size_;
00568 }
00569
00570 tpoint tgrid::tchild::get_minimum_size() const
00571 {
00572 if(!widget_) {
00573 return border_space();
00574 }
00575
00576 if(widget_->dirty() || minimum_size_ == tpoint(0, 0)) {
00577 minimum_size_ = widget_->get_minimum_size() + border_space();
00578 }
00579
00580 return minimum_size_;
00581 }
00582
00583 tpoint tgrid::tchild::get_maximum_size() const
00584 {
00585 if(!widget_) {
00586 return tpoint(0, 0);
00587 }
00588
00589 if(widget_->dirty() || maximum_size_ == tpoint(0, 0)) {
00590 maximum_size_ = widget_->get_maximum_size();
00591
00592
00593
00594 if(maximum_size_ != tpoint(0, 0)) {
00595 maximum_size_ += border_space();
00596 }
00597 }
00598
00599 return maximum_size_;
00600 }
00601
00602 tpoint tgrid::tchild::border_space() const
00603 {
00604 tpoint result(0, 0);
00605
00606 if(border_size_) {
00607
00608 if(flags_ & BORDER_TOP) result.y += border_size_;
00609 if(flags_ & BORDER_BOTTOM) result.y += border_size_;
00610
00611 if(flags_ & BORDER_LEFT) result.x += border_size_;
00612 if(flags_ & BORDER_RIGHT) result.x += border_size_;
00613 }
00614
00615 return result;
00616 }
00617
00618 void tgrid::tchild::set_size(tpoint orig, tpoint size)
00619 {
00620 assert(widget());
00621
00622 if(border_size_) {
00623 if(flags_ & BORDER_TOP) {
00624 orig.y += border_size_;
00625 size.y -= border_size_;
00626 }
00627 if(flags_ & BORDER_BOTTOM) {
00628 size.y -= border_size_;
00629 }
00630
00631 if(flags_ & BORDER_LEFT) {
00632 orig.x += border_size_;
00633 size.x -= border_size_;
00634 }
00635 if(flags_ & BORDER_RIGHT) {
00636 size.x -= border_size_;
00637 }
00638 }
00639
00640
00641
00642 const tpoint best_size = widget()->get_best_size();
00643 if(size <= best_size) {
00644 DBG_G << "Grid cell: in best size range setting widget to "
00645 << orig << " x " << size << ".\n";
00646
00647 widget()->set_size(create_rect(orig, size));
00648 return;
00649 }
00650
00651 const tpoint maximum_size = widget()->get_maximum_size();
00652 if(flags_ & (HORIZONTAL_GROW_SEND_TO_CLIENT | HORIZONTAL_GROW_SEND_TO_CLIENT)) {
00653 if(maximum_size == tpoint(0,0) || size <= maximum_size) {
00654
00655 DBG_G << "Grid cell: in maximum size range setting widget to "
00656 << orig << " x " << size << ".\n";
00657
00658 widget()->set_size(create_rect(orig, size));
00659 return;
00660
00661 }
00662 }
00663
00664 tpoint widget_size = best_size;
00665 tpoint widget_orig = orig;
00666
00667 if(flags_ & HORIZONTAL_GROW_SEND_TO_CLIENT) {
00668 if(maximum_size.x) {
00669 widget_size.x = std::min(size.x, maximum_size.x);
00670 } else {
00671 widget_size.x = size.x;
00672 }
00673 DBG_G << "Grid cell: horizontal growing from "
00674 << best_size.x << " to " << widget_size.x << ".\n";
00675 }
00676
00677 if(flags_ & VERTICAL_GROW_SEND_TO_CLIENT) {
00678 if(maximum_size.y) {
00679 widget_size.y = std::min(size.y, maximum_size.y);
00680 } else {
00681 widget_size.y = size.y;
00682 }
00683 DBG_G << "Grid cell: vertical growing from "
00684 << best_size.y << " to " << widget_size.y << ".\n";
00685 }
00686
00687 if((flags_ & VERTICAL_ALIGN_TOP) == VERTICAL_ALIGN_TOP) {
00688
00689
00690 DBG_G << "Grid cell: vertically aligned at the top.\n";
00691
00692 } else if((flags_ & VERTICAL_ALIGN_CENTER) == VERTICAL_ALIGN_CENTER) {
00693
00694 widget_orig.y += (size.y - widget_size.y) / 2;
00695 DBG_G << "Grid cell: vertically centred.\n";
00696
00697 } else if((flags_ & VERTICAL_ALIGN_BOTTOM) == VERTICAL_ALIGN_BOTTOM) {
00698
00699 widget_orig.y += (size.y - widget_size.y);
00700 DBG_G << "Grid cell: vertically aligned at the bottom.\n";
00701
00702 } else {
00703 assert(false);
00704 }
00705
00706 if((flags_ & HORIZONTAL_ALIGN_LEFT) == HORIZONTAL_ALIGN_LEFT) {
00707
00708 DBG_G << "Grid cell: horizontally aligned at the left.\n";
00709
00710 } else if((flags_ & HORIZONTAL_ALIGN_CENTER) == HORIZONTAL_ALIGN_CENTER) {
00711
00712 widget_orig.x += (size.x - widget_size.x) / 2;
00713 DBG_G << "Grid cell: horizontally centred.\n";
00714
00715 } else if((flags_ & HORIZONTAL_ALIGN_RIGHT) == HORIZONTAL_ALIGN_RIGHT) {
00716
00717 widget_orig.x += (size.x - widget_size.x);
00718 DBG_G << "Grid cell: horizontally aligned at the right.\n";
00719
00720 } else {
00721 assert(false);
00722 }
00723
00724 DBG_G << "Grid cell: resize widget to "
00725 << widget_orig << " x " << widget_size << ".\n";
00726
00727
00728 widget()->set_size(create_rect(widget_orig, widget_size));
00729 }
00730
00731 }
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936