gui/widgets/grid.cpp

Go to the documentation of this file.
00001 /* $Id: grid.cpp 54266 2012-05-20 19:19:32Z mordante $ */
00002 /*
00003    Copyright (C) 2008 - 2012 by Mark de Wever <koraq@xs4all.nl>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #define GETTEXT_DOMAIN "wesnoth-lib"
00017 
00018 #include "gui/widgets/grid_private.hpp"
00019 
00020 #include "gui/auxiliary/iterator/walker_grid.hpp"
00021 #include "gui/auxiliary/log.hpp"
00022 #include "gui/auxiliary/layout_exception.hpp"
00023 #include "gui/widgets/control.hpp"
00024 
00025 #include <numeric>
00026 
00027 #define LOG_SCOPE_HEADER "tgrid [" + id() + "] " + __func__
00028 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
00029 #define LOG_IMPL_HEADER "tgrid [" + grid.id() + "] " + __func__ + ':'
00030 
00031 #define LOG_CHILD_SCOPE_HEADER "tgrid::tchild [" \
00032         + (widget_ ? widget_->id() : "-") + "] " + __func__
00033 #define LOG_CHILD_HEADER LOG_CHILD_SCOPE_HEADER + ':'
00034 
00035 namespace gui2 {
00036 
00037 tgrid::tgrid(const unsigned rows, const unsigned cols)
00038     : rows_(rows)
00039     , cols_(cols)
00040     , row_height_()
00041     , col_width_()
00042     , row_grow_factor_(rows)
00043     , col_grow_factor_(cols)
00044     , children_(rows * cols)
00045 {
00046 }
00047 
00048 tgrid::~tgrid()
00049 {
00050     // Delete the children in this destructor since resizing a vector copies the
00051     // children and thus frees the child prematurely.
00052     foreach(tchild& child, children_) {
00053         delete child.widget();
00054     }
00055 }
00056 
00057 unsigned tgrid::add_row(const unsigned count)
00058 {
00059     assert(count);
00060 
00061     //FIXME the warning in set_rows_cols should be killed.
00062 
00063     unsigned result = rows_;
00064     set_rows_cols(rows_ + count, cols_);
00065     return result;
00066 }
00067 
00068 void tgrid::set_child(twidget* widget, const unsigned row,
00069         const unsigned col, const unsigned flags, const unsigned border_size)
00070 {
00071     assert(row < rows_ && col < cols_);
00072     assert(flags & VERTICAL_MASK);
00073     assert(flags & HORIZONTAL_MASK);
00074 
00075     tchild& cell = child(row, col);
00076 
00077     // clear old child if any
00078     if(cell.widget()) {
00079         // free a child when overwriting it
00080         WRN_GUI_G << LOG_HEADER
00081                 << " child '" << cell.id()
00082                 << "' at cell '" << row << ',' << col
00083                 << "' will be replaced.\n";
00084         delete cell.widget();
00085     }
00086 
00087     // copy data
00088     cell.set_flags(flags);
00089     cell.set_border_size(border_size);
00090     cell.set_widget(widget);
00091     if(cell.widget()) {
00092         // make sure the new child is valid before deferring
00093         cell.widget()->set_parent(this);
00094     }
00095 }
00096 
00097 twidget* tgrid::swap_child(
00098         const std::string& id, twidget* widget, const bool recurse,
00099         twidget* new_parent)
00100 {
00101     assert(widget);
00102 
00103     foreach(tchild& child, children_) {
00104         if(child.id() != id) {
00105 
00106             if(recurse) {
00107                 // decent in the nested grids.
00108                 tgrid* grid = dynamic_cast<tgrid*>(child.widget());
00109                 if(grid) {
00110 
00111                     twidget* old = grid->swap_child(id, widget, true);
00112                     if(old) {
00113                         return old;
00114                     }
00115                 }
00116             }
00117 
00118             continue;
00119         }
00120 
00121         // When find the widget there should be a widget.
00122         twidget* old = child.widget();
00123         assert(old);
00124         old->set_parent(new_parent);
00125 
00126         widget->set_parent(this);
00127         child.set_widget(widget);
00128 
00129         return old;
00130     }
00131 
00132     return NULL;
00133 }
00134 
00135 void tgrid::remove_child(const unsigned row, const unsigned col)
00136 {
00137     assert(row < rows_ && col < cols_);
00138 
00139     tchild& cell = child(row, col);
00140 
00141     if(cell.widget()) {
00142         delete cell.widget();
00143     }
00144     cell.set_widget(NULL);
00145 }
00146 
00147 void tgrid::remove_child(const std::string& id, const bool find_all)
00148 {
00149     foreach(tchild& child, children_) {
00150 
00151         if(child.id() == id) {
00152             delete child.widget();
00153             child.set_widget(NULL);
00154 
00155             if(!find_all) {
00156                 break;
00157             }
00158         }
00159     }
00160 }
00161 
00162 void tgrid::set_active(const bool active)
00163 {
00164     foreach(tchild& child, children_) {
00165 
00166         twidget* widget = child.widget();
00167         if(!widget) {
00168             continue;
00169         }
00170 
00171         tgrid* grid = dynamic_cast<tgrid*>(widget);
00172         if(grid) {
00173             grid->set_active(active);
00174             continue;
00175         }
00176 
00177         tcontrol* control =  dynamic_cast<tcontrol*>(widget);
00178         if(control) {
00179             control->set_active(active);
00180         }
00181     }
00182 }
00183 
00184 void tgrid::layout_init(const bool full_initialization)
00185 {
00186     // Inherited.
00187     twidget::layout_init(full_initialization);
00188 
00189     // Clear child caches.
00190     foreach(tchild& child, children_) {
00191 
00192         child.layout_init(full_initialization);
00193 
00194     }
00195 }
00196 
00197 void tgrid::reduce_width(const unsigned maximum_width)
00198 {
00199     /***** ***** ***** ***** INIT ***** ***** ***** *****/
00200     log_scope2(log_gui_layout, LOG_SCOPE_HEADER);
00201     DBG_GUI_L << LOG_HEADER << " maximum width " << maximum_width << ".\n";
00202 
00203     tpoint size = get_best_size();
00204     if(size.x <= static_cast<int>(maximum_width)) {
00205         DBG_GUI_L << LOG_HEADER << " Already fits.\n";
00206         return;
00207     }
00208 
00209     /***** ***** ***** ***** Request resize ***** ***** ***** *****/
00210 
00211     request_reduce_width(maximum_width);
00212 
00213     size = get_best_size();
00214     if(size.x <= static_cast<int>(maximum_width)) {
00215         DBG_GUI_L << LOG_HEADER << " Resize request honoured.\n";
00216         return;
00217     }
00218 
00219     /***** ***** ***** ***** Demand resize ***** ***** ***** *****/
00220 
00221     /** @todo Implement. */
00222 
00223     /***** ***** ***** ***** Acknowlegde failure ***** ***** ***** *****/
00224 
00225     DBG_GUI_L << LOG_HEADER << " Resizing failed.\n";
00226 
00227     throw tlayout_exception_width_resize_failed();
00228 }
00229 
00230 void tgrid::request_reduce_width(const unsigned maximum_width)
00231 {
00232     tpoint size = get_best_size();
00233     if(size.x <= static_cast<int>(maximum_width)) {
00234         /** @todo this point shouldn't be reached, find out why it does. */
00235         return;
00236     }
00237 
00238     const unsigned too_wide = size.x - maximum_width;
00239     unsigned reduced = 0;
00240     for(size_t col = 0; col < cols_; ++col) {
00241         if(too_wide - reduced >=  col_width_[col]) {
00242             DBG_GUI_L << LOG_HEADER
00243                     << " column " << col
00244                     << " is too small to be reduced.\n";
00245             continue;
00246         }
00247 
00248         const unsigned wanted_width = col_width_[col] - (too_wide - reduced);
00249         const unsigned width = tgrid_implementation::
00250 				column_request_reduce_width(*this, col, wanted_width);
00251 
00252         if(width < col_width_[col]) {
00253             DBG_GUI_L << LOG_HEADER
00254                     << " reduced " << col_width_[col] - width
00255                     << " pixels for column " << col
00256                     << ".\n";
00257 
00258             size.x -= col_width_[col] - width;
00259             col_width_[col] = width;
00260         }
00261 
00262         if(size.x <= static_cast<int>(maximum_width)) {
00263             break;
00264         }
00265     }
00266 
00267     set_layout_size(calculate_best_size());
00268 }
00269 
00270 void tgrid::demand_reduce_width(const unsigned /*maximum_width*/)
00271 {
00272     /** @todo Implement. */
00273 }
00274 
00275 void tgrid::reduce_height(const unsigned maximum_height)
00276 {
00277     /***** ***** ***** ***** INIT ***** ***** ***** *****/
00278     log_scope2(log_gui_layout, LOG_SCOPE_HEADER);
00279     DBG_GUI_L << LOG_HEADER << " maximum height " << maximum_height << ".\n";
00280 
00281     tpoint size = get_best_size();
00282     if(size.y <= static_cast<int>(maximum_height)) {
00283         DBG_GUI_L << LOG_HEADER << " Already fits.\n";
00284         return;
00285     }
00286 
00287     /***** ***** ***** ***** Request resize ***** ***** ***** *****/
00288 
00289     request_reduce_height(maximum_height);
00290 
00291     size = get_best_size();
00292     if(size.y <= static_cast<int>(maximum_height)) {
00293         DBG_GUI_L << LOG_HEADER << " Resize request honoured.\n";
00294         return;
00295     }
00296 
00297     /***** ***** ***** ***** Demand resize ***** ***** ***** *****/
00298 
00299     /** @todo Implement. */
00300 
00301     /***** ***** ***** ***** Acknowlegde failure ***** ***** ***** *****/
00302 
00303     DBG_GUI_L << LOG_HEADER << " Resizing failed.\n";
00304 
00305     throw tlayout_exception_height_resize_failed();
00306 }
00307 
00308 void tgrid::request_reduce_height(const unsigned maximum_height)
00309 {
00310     tpoint size = get_best_size();
00311     if(size.y <= static_cast<int>(maximum_height)) {
00312         /** @todo this point shouldn't be reached, find out why it does. */
00313         return;
00314     }
00315 
00316     const unsigned too_high = size.y - maximum_height;
00317     unsigned reduced = 0;
00318     for(size_t row = 0; row < rows_; ++row) {
00319         unsigned wanted_height = row_height_[row] - (too_high - reduced);
00320         /**
00321          * @todo Improve this code.
00322          *
00323          * Now we try every item to be reduced, maybe items need a flag whether
00324          * or not to try to reduce and also eveluate whether the force
00325          * reduction is still needed.
00326          */
00327         if(too_high - reduced >=  row_height_[row]) {
00328             DBG_GUI_L << LOG_HEADER
00329                     << " row " << row
00330                     << " height " << row_height_[row]
00331                     << " want to reduce " << too_high
00332                     << " is too small to be reduced fully try 1 pixel.\n";
00333 
00334             wanted_height = 1;
00335         }
00336 
00337         const unsigned height = tgrid_implementation::
00338 				row_request_reduce_height(*this, row, wanted_height);
00339 
00340         if(height < row_height_[row]) {
00341             DBG_GUI_L << LOG_HEADER
00342                     << " row " << row
00343                     << " height " << row_height_[row]
00344                     << " want to reduce " << too_high
00345                     << " reduced " << row_height_[row] - height
00346                     << " pixels.\n";
00347 
00348             size.y -= row_height_[row] - height;
00349             row_height_[row] = height;
00350         }
00351 
00352         if(size.y <= static_cast<int>(maximum_height)) {
00353             break;
00354         }
00355     }
00356 
00357     size = calculate_best_size();
00358 
00359     DBG_GUI_L << LOG_HEADER
00360             << " Requested maximum " << maximum_height
00361             << " resulting height " << size.y
00362             << ".\n";
00363 
00364     set_layout_size(size);
00365 }
00366 
00367 void tgrid::demand_reduce_height(const unsigned /*maximum_height*/)
00368 {
00369     /** @todo Implement. */
00370 }
00371 
00372 tpoint tgrid::recalculate_best_size()
00373 {
00374     tpoint best_size = calculate_best_size();
00375     set_layout_size(best_size);
00376     return best_size;
00377 }
00378 
00379 tpoint tgrid::calculate_best_size() const
00380 {
00381     log_scope2(log_gui_layout, LOG_SCOPE_HEADER);
00382 
00383     // Reset the cached values.
00384     row_height_.clear();
00385     row_height_.resize(rows_, 0);
00386     col_width_.clear();
00387     col_width_.resize(cols_, 0);
00388 
00389     // First get the sizes for all items.
00390     for(unsigned row = 0; row < rows_; ++row) {
00391         for(unsigned col = 0; col < cols_; ++col) {
00392 
00393             const tpoint size = child(row, col).get_best_size();
00394 
00395             if(size.x > static_cast<int>(col_width_[col])) {
00396                 col_width_[col] = size.x;
00397             }
00398 
00399             if(size.y > static_cast<int>(row_height_[row])) {
00400                 row_height_[row] = size.y;
00401             }
00402 
00403         }
00404     }
00405 
00406     for(unsigned row = 0; row < rows_; ++row) {
00407         DBG_GUI_L << LOG_HEADER
00408                 << " the row_height_ for row " << row
00409                 << " will be " << row_height_[row]
00410                 << ".\n";
00411     }
00412 
00413     for(unsigned col = 0; col < cols_; ++col) {
00414         DBG_GUI_L << LOG_HEADER
00415                 << " the col_width_ for column " << col
00416                 << " will be " << col_width_[col]
00417                 << ".\n";
00418     }
00419 
00420     const tpoint result(
00421         std::accumulate(col_width_.begin(), col_width_.end(), 0),
00422         std::accumulate(row_height_.begin(), row_height_.end(), 0));
00423 
00424     DBG_GUI_L << LOG_HEADER << " returning " << result << ".\n";
00425     return result;
00426 }
00427 
00428 bool tgrid::can_wrap() const
00429 {
00430     foreach(const tchild& child, children_) {
00431         if(child.can_wrap()) {
00432             return true;
00433         }
00434     }
00435 
00436     // Inherited.
00437     return twidget::can_wrap();
00438 }
00439 
00440 void tgrid::place(const tpoint& origin, const tpoint& size)
00441 {
00442     log_scope2(log_gui_layout, LOG_SCOPE_HEADER);
00443 
00444     /***** INIT *****/
00445 
00446     twidget::place(origin, size);
00447 
00448     if(!rows_ || !cols_) {
00449         return;
00450     }
00451 
00452     // call the calculate so the size cache gets updated.
00453     const tpoint best_size = calculate_best_size();
00454 
00455     assert(row_height_.size() == rows_);
00456     assert(col_width_.size() == cols_);
00457     assert(row_grow_factor_.size() == rows_);
00458     assert(col_grow_factor_.size() == cols_);
00459 
00460     DBG_GUI_L << LOG_HEADER
00461         << " best size " << best_size
00462         << " available size " << size
00463         << ".\n";
00464 
00465     /***** BEST_SIZE *****/
00466 
00467     if(best_size == size) {
00468         layout(origin);
00469         return;
00470     }
00471 
00472     /***** GROW *****/
00473     if(best_size.x <= size.x && best_size.y <= size.y) {
00474 
00475         // expand it.
00476         if(size.x > best_size.x) {
00477             const unsigned w = size.x - best_size.x;
00478             unsigned w_size = std::accumulate(
00479                     col_grow_factor_.begin(), col_grow_factor_.end(), 0);
00480 
00481             DBG_GUI_L << LOG_HEADER
00482                     << " extra width " << w
00483                     << " will be divided amount " << w_size
00484                     << " units in " << cols_
00485                     << " columns.\n";
00486 
00487             if(w_size == 0) {
00488                 // If all sizes are 0 reset them to 1
00489                 foreach(unsigned& val, col_grow_factor_) {
00490                     val = 1;
00491                 }
00492                 w_size = cols_;
00493             }
00494             // We might have a bit 'extra' if the division doesn't fix exactly
00495             // but we ignore that part for now.
00496             const unsigned w_normal = w / w_size;
00497             for(unsigned i = 0; i < cols_; ++i) {
00498                 col_width_[i] += w_normal * col_grow_factor_[i];
00499                 DBG_GUI_L << LOG_HEADER
00500                         << " column " << i
00501                         << " with grow factor " << col_grow_factor_[i]
00502                         << " set width to " << col_width_[i]
00503                         << ".\n";
00504             }
00505 
00506         }
00507 
00508         if(size.y > best_size.y) {
00509             const unsigned h = size.y - best_size.y;
00510             unsigned h_size = std::accumulate(
00511                     row_grow_factor_.begin(), row_grow_factor_.end(), 0);
00512             DBG_GUI_L << LOG_HEADER
00513                     << " extra height " << h
00514                     << " will be divided amount " << h_size
00515                     << " units in " << rows_
00516                     << " rows.\n";
00517 
00518             if(h_size == 0) {
00519                 // If all sizes are 0 reset them to 1
00520                 foreach(unsigned& val, row_grow_factor_) {
00521                     val = 1;
00522                 }
00523                 h_size = rows_;
00524             }
00525             // We might have a bit 'extra' if the division doesn't fix exactly
00526             // but we ignore that part for now.
00527             const unsigned h_normal = h / h_size;
00528             for(unsigned i = 0; i < rows_; ++i) {
00529                 row_height_[i] += h_normal * row_grow_factor_[i];
00530                 DBG_GUI_L << LOG_HEADER
00531                         << " row " << i
00532                         << " with grow factor " << row_grow_factor_[i]
00533                         << " set height to " << row_height_[i]
00534                         << ".\n";
00535             }
00536         }
00537 
00538         layout(origin);
00539         return;
00540     }
00541 
00542     // This shouldn't be possible...
00543     assert(false);
00544 }
00545 
00546 void tgrid::set_origin(const tpoint& origin)
00547 {
00548     const tpoint movement = tpoint(
00549             origin.x - get_x(),
00550             origin.y - get_y());
00551 
00552     // Inherited.
00553     twidget::set_origin(origin);
00554 
00555     foreach(tchild& child, children_) {
00556 
00557         twidget* widget = child.widget();
00558         assert(widget);
00559 
00560         widget->set_origin(tpoint(
00561                 widget->get_x() + movement.x,
00562                 widget->get_y() + movement.y));
00563     }
00564 }
00565 
00566 void tgrid::set_visible_area(const SDL_Rect& area)
00567 {
00568     // Inherited.
00569     twidget::set_visible_area(area);
00570 
00571     foreach(tchild& child, children_) {
00572 
00573         twidget* widget = child.widget();
00574         assert(widget);
00575 
00576         widget->set_visible_area(area);
00577     }
00578 }
00579 
00580 void tgrid::layout_children()
00581 {
00582     foreach(tchild& child, children_) {
00583         assert(child.widget());
00584         child.widget()->layout_children();
00585     }
00586 }
00587 
00588 void tgrid::child_populate_dirty_list(twindow& caller,
00589             const std::vector<twidget*>& call_stack)
00590 {
00591     assert(!call_stack.empty() && call_stack.back() == this);
00592 
00593     foreach(tchild& child, children_) {
00594 
00595         assert(child.widget());
00596 
00597         std::vector<twidget*> child_call_stack = call_stack;
00598         child.widget()->populate_dirty_list(caller, child_call_stack);
00599     }
00600 }
00601 
00602 twidget* tgrid::find_at(const tpoint& coordinate,
00603         const bool must_be_active)
00604 {
00605     return tgrid_implementation::find_at<twidget>(
00606         *this, coordinate, must_be_active);
00607 }
00608 
00609 const twidget* tgrid::find_at(const tpoint& coordinate,
00610         const bool must_be_active) const
00611 {
00612     return tgrid_implementation::find_at<const twidget>(
00613         *this, coordinate, must_be_active);
00614 }
00615 
00616 twidget* tgrid::find(const std::string& id, const bool must_be_active)
00617 {
00618     return tgrid_implementation::find<twidget>(
00619             *this, id, must_be_active);
00620 }
00621 
00622 const twidget* tgrid::find(const std::string& id,
00623         const bool must_be_active) const
00624 {
00625     return tgrid_implementation::find<const twidget>(
00626             *this, id, must_be_active);
00627 }
00628 
00629 bool tgrid::has_widget(const twidget* widget) const
00630 {
00631     if(twidget::has_widget(widget)) {
00632         return true;
00633     }
00634 
00635     foreach(const tchild& child, children_) {
00636         if(child.widget()->has_widget(widget)) {
00637             return true;
00638         }
00639     }
00640     return false;
00641 }
00642 
00643 bool tgrid::disable_click_dismiss() const
00644 {
00645     if(get_visible() != twidget::VISIBLE) {
00646         return false;
00647     }
00648 
00649     foreach(const tchild& child, children_) {
00650         const twidget* widget = child.widget();
00651         assert(widget);
00652 
00653         if(widget->disable_click_dismiss()) {
00654             return true;
00655         }
00656     }
00657     return false;
00658 }
00659 
00660 iterator::twalker_* tgrid::create_walker()
00661 {
00662     return new gui2::iterator::tgrid(*this);
00663 }
00664 
00665 void tgrid::set_rows(const unsigned rows)
00666 {
00667     if(rows == rows_) {
00668         return;
00669     }
00670 
00671     set_rows_cols(rows, cols_);
00672 }
00673 
00674 void tgrid::set_cols(const unsigned cols)
00675 {
00676     if(cols == cols_) {
00677         return;
00678     }
00679 
00680     set_rows_cols(rows_, cols);
00681 }
00682 
00683 void tgrid::set_rows_cols(const unsigned rows, const unsigned cols)
00684 {
00685     if(rows == rows_ && cols == cols_) {
00686         return;
00687     }
00688 
00689     if(!children_.empty()) {
00690         WRN_GUI_G << LOG_HEADER << " resizing a non-empty grid "
00691                 << " may give unexpected problems.\n";
00692     }
00693 
00694     rows_ = rows;
00695     cols_ = cols;
00696     row_grow_factor_.resize(rows);
00697     col_grow_factor_.resize(cols);
00698     children_.resize(rows_ * cols_);
00699 }
00700 
00701 tpoint tgrid::tchild::get_best_size() const
00702 {
00703     log_scope2(log_gui_layout, LOG_CHILD_SCOPE_HEADER)
00704 
00705     if(!widget_) {
00706         DBG_GUI_L << LOG_CHILD_HEADER
00707             << " has widget " << false
00708             << " returning " << border_space()
00709             << ".\n";
00710         return border_space();
00711     }
00712 
00713     if(widget_->get_visible() == twidget::INVISIBLE) {
00714         DBG_GUI_L << LOG_CHILD_HEADER
00715             << " has widget " << true
00716             << " widget visible " << false
00717             << " returning 0,0"
00718             << ".\n";
00719         return tpoint(0, 0);
00720     }
00721 
00722     const tpoint best_size = widget_->get_best_size() + border_space();
00723 
00724     DBG_GUI_L << LOG_CHILD_HEADER
00725         << " has widget " << true
00726         << " widget visible " << true
00727         << " returning " << best_size
00728         << ".\n";
00729     return best_size;
00730 }
00731 
00732 void tgrid::tchild::place(tpoint origin, tpoint size)
00733 {
00734     assert(widget());
00735     if(widget()->get_visible() == twidget::INVISIBLE) {
00736         return;
00737     }
00738 
00739     if(border_size_) {
00740         if(flags_ & BORDER_TOP) {
00741             origin.y += border_size_;
00742             size.y -= border_size_;
00743         }
00744         if(flags_ & BORDER_BOTTOM) {
00745             size.y -= border_size_;
00746         }
00747 
00748         if(flags_ & BORDER_LEFT) {
00749             origin.x += border_size_;
00750             size.x -= border_size_;
00751         }
00752         if(flags_ & BORDER_RIGHT) {
00753             size.x -= border_size_;
00754         }
00755     }
00756 
00757     // If size smaller or equal to best size set that size.
00758     // No need to check > min size since this is what we got.
00759     const tpoint best_size = widget()->get_best_size();
00760     if(size <= best_size) {
00761         DBG_GUI_L << LOG_CHILD_HEADER
00762                 << " in best size range setting widget to "
00763                 << origin << " x " << size
00764                 << ".\n";
00765 
00766         widget()->place(origin, size);
00767         return;
00768     }
00769 
00770     const tcontrol* control = dynamic_cast<const tcontrol*>(widget());
00771     const tpoint maximum_size = control
00772         ? control->get_config_maximum_size()
00773         : tpoint(0, 0);
00774 
00775     if((flags_ & (HORIZONTAL_MASK | VERTICAL_MASK))
00776             == (HORIZONTAL_GROW_SEND_TO_CLIENT | VERTICAL_GROW_SEND_TO_CLIENT)) {
00777 
00778         if(maximum_size == tpoint(0,0) || size <= maximum_size) {
00779 
00780             DBG_GUI_L << LOG_CHILD_HEADER
00781                     << " in maximum size range setting widget to "
00782                     << origin << " x " << size
00783                     << ".\n";
00784 
00785             widget()->place(origin, size);
00786             return;
00787 
00788         }
00789     }
00790 
00791     tpoint widget_size = tpoint(
00792         std::min(size.x, best_size.x),
00793         std::min(size.y, best_size.y));
00794     tpoint widget_orig = origin;
00795 
00796     const unsigned v_flag = flags_ & VERTICAL_MASK;
00797 
00798     if(v_flag == VERTICAL_GROW_SEND_TO_CLIENT) {
00799         if(maximum_size.y) {
00800             widget_size.y = std::min(size.y, maximum_size.y);
00801         } else {
00802             widget_size.y = size.y;
00803         }
00804         DBG_GUI_L << LOG_CHILD_HEADER
00805                 << " vertical growing from " << best_size.y
00806                 << " to " << widget_size.y
00807                 << ".\n";
00808 
00809     } else if(v_flag == VERTICAL_ALIGN_TOP) {
00810         // Do nothing.
00811 
00812         DBG_GUI_L << LOG_CHILD_HEADER << " vertically aligned at the top.\n";
00813 
00814     } else if(v_flag == VERTICAL_ALIGN_CENTER) {
00815 
00816         widget_orig.y += (size.y - widget_size.y) / 2;
00817         DBG_GUI_L << LOG_CHILD_HEADER << " vertically centred.\n";
00818 
00819     } else if(v_flag == VERTICAL_ALIGN_BOTTOM) {
00820 
00821         widget_orig.y += (size.y - widget_size.y);
00822         DBG_GUI_L << LOG_CHILD_HEADER << " vertically aligned at the bottom.\n";
00823 
00824     } else {
00825         ERR_GUI_L << LOG_CHILD_HEADER
00826                 << " Invalid vertical alignment '"
00827                 << v_flag << "' specified.\n";
00828         assert(false);
00829     }
00830 
00831     const unsigned h_flag = flags_ & HORIZONTAL_MASK;
00832 
00833     if(h_flag == HORIZONTAL_GROW_SEND_TO_CLIENT) {
00834         if(maximum_size.x) {
00835             widget_size.x = std::min(size.x, maximum_size.x);
00836         } else {
00837             widget_size.x = size.x;
00838         }
00839         DBG_GUI_L << LOG_CHILD_HEADER
00840                 << " horizontal growing from " << best_size.x
00841                 << " to " << widget_size.x
00842                 << ".\n";
00843 
00844     } else if(h_flag == HORIZONTAL_ALIGN_LEFT) {
00845         // Do nothing.
00846         DBG_GUI_L << LOG_CHILD_HEADER << " horizontally aligned at the left.\n";
00847 
00848     } else if(h_flag == HORIZONTAL_ALIGN_CENTER) {
00849 
00850         widget_orig.x += (size.x - widget_size.x) / 2;
00851         DBG_GUI_L << LOG_CHILD_HEADER << " horizontally centred.\n";
00852 
00853     } else if(h_flag == HORIZONTAL_ALIGN_RIGHT) {
00854 
00855         widget_orig.x += (size.x - widget_size.x);
00856         DBG_GUI_L << LOG_CHILD_HEADER
00857                 << " horizontally aligned at the right.\n";
00858 
00859     } else {
00860         ERR_GUI_L << LOG_CHILD_HEADER
00861             << " No horizontal alignment '" << h_flag
00862             << "' specified.\n";
00863         assert(false);
00864     }
00865 
00866     DBG_GUI_L << LOG_CHILD_HEADER
00867             << " resize widget to " << widget_orig << " x " << widget_size
00868             << ".\n";
00869 
00870     widget()->place(widget_orig, widget_size);
00871 }
00872 
00873 void tgrid::tchild::layout_init(const bool full_initialization)
00874 {
00875     assert(widget_);
00876 
00877     if(widget_->get_visible() != twidget::INVISIBLE) {
00878         widget_->layout_init(full_initialization);
00879     }
00880 }
00881 
00882 const std::string& tgrid::tchild::id() const
00883 {
00884     assert(widget_);
00885     return widget_->id();
00886 }
00887 
00888 tpoint tgrid::tchild::border_space() const
00889 {
00890     tpoint result(0, 0);
00891 
00892     if(border_size_) {
00893 
00894         if(flags_ & BORDER_TOP) result.y += border_size_;
00895         if(flags_ & BORDER_BOTTOM) result.y += border_size_;
00896 
00897         if(flags_ & BORDER_LEFT) result.x += border_size_;
00898         if(flags_ & BORDER_RIGHT) result.x += border_size_;
00899     }
00900 
00901     return result;
00902 }
00903 
00904 void tgrid::layout(const tpoint& origin)
00905 {
00906     tpoint orig = origin;
00907     for(unsigned row = 0; row < rows_; ++row) {
00908         for(unsigned col = 0; col < cols_; ++col) {
00909 
00910             const tpoint size(col_width_[col], row_height_[row]);
00911             DBG_GUI_L << LOG_HEADER
00912                     << " set widget at " << row << ',' << col
00913                     << " at origin " << orig
00914                     << " with size " << size
00915                     << ".\n";
00916 
00917             if(child(row, col).widget()) {
00918                 child(row, col).place(orig, size);
00919             }
00920 
00921             orig.x += col_width_[col];
00922         }
00923         orig.y += row_height_[row];
00924         orig.x = origin.x;
00925     }
00926 }
00927 
00928 void tgrid::impl_draw_children(surface& frame_buffer)
00929 {
00930     /*
00931      * The call to SDL_PumpEvents seems a bit like black magic.
00932      * With the call the resizing doesn't seem to lose resize events.
00933      * But when added the queue still only contains one resize event and the
00934      * internal SDL queue doesn't seem to overflow (rarely more than 50 pending
00935      * events).
00936      * Without the call when resizing larger a black area of remains, this is
00937      * the area not used for resizing the screen, this call `fixes' that.
00938      */
00939     SDL_PumpEvents();
00940 
00941     assert(get_visible() == twidget::VISIBLE);
00942     set_dirty(false);
00943 
00944     foreach(tchild& child, children_) {
00945 
00946         twidget* widget = child.widget();
00947         assert(widget);
00948 
00949         if(widget->get_visible() != twidget::VISIBLE) {
00950             continue;
00951         }
00952 
00953         if(widget->get_drawing_action() == twidget::NOT_DRAWN) {
00954             continue;
00955         }
00956 
00957         widget->draw_background(frame_buffer);
00958         widget->draw_children(frame_buffer);
00959         widget->draw_foreground(frame_buffer);
00960         widget->set_dirty(false);
00961     }
00962 }
00963 
00964 void tgrid::impl_draw_children(
00965           surface& frame_buffer
00966         , int x_offset
00967         , int y_offset)
00968 {
00969     /*
00970      * The call to SDL_PumpEvents seems a bit like black magic.
00971      * With the call the resizing doesn't seem to lose resize events.
00972      * But when added the queue still only contains one resize event and the
00973      * internal SDL queue doesn't seem to overflow (rarely more than 50 pending
00974      * events).
00975      * Without the call when resizing larger a black area of remains, this is
00976      * the area not used for resizing the screen, this call `fixes' that.
00977      */
00978     SDL_PumpEvents();
00979 
00980     assert(get_visible() == twidget::VISIBLE);
00981     set_dirty(false);
00982 
00983     foreach(tchild& child, children_) {
00984 
00985         twidget* widget = child.widget();
00986         assert(widget);
00987 
00988         if(widget->get_visible() != twidget::VISIBLE) {
00989             continue;
00990         }
00991 
00992         if(widget->get_drawing_action() == twidget::NOT_DRAWN) {
00993             continue;
00994         }
00995 
00996         widget->draw_background(frame_buffer, x_offset, y_offset);
00997         widget->draw_children(frame_buffer, x_offset, y_offset);
00998         widget->draw_foreground(frame_buffer, x_offset, y_offset);
00999         widget->set_dirty(false);
01000     }
01001 }
01002 
01003 unsigned tgrid_implementation::row_request_reduce_height(tgrid& grid,
01004         const unsigned row, const unsigned maximum_height)
01005 {
01006     // The minimum height required.
01007     unsigned required_height = 0;
01008 
01009     for(size_t x = 0; x < grid.cols_; ++x) {
01010         tgrid::tchild& cell = grid.child(row, x);
01011         cell_request_reduce_height(cell, maximum_height);
01012 
01013         const tpoint size(cell.get_best_size());
01014 
01015         if(required_height == 0
01016                 || static_cast<size_t>(size.y) > required_height) {
01017 
01018             required_height = size.y;
01019         }
01020     }
01021 
01022     DBG_GUI_L << LOG_IMPL_HEADER
01023             << " maximum row height " << maximum_height
01024             << " returning " << required_height
01025             << ".\n";
01026 
01027     return required_height;
01028 }
01029 
01030 unsigned tgrid_implementation::column_request_reduce_width(tgrid& grid,
01031         const unsigned column, const unsigned maximum_width)
01032 {
01033     // The minimum width required.
01034     unsigned required_width = 0;
01035 
01036     for(size_t y = 0; y < grid.rows_; ++y) {
01037         tgrid::tchild& cell = grid.child(y, column);
01038         cell_request_reduce_width(cell, maximum_width);
01039 
01040         const tpoint size(cell.get_best_size());
01041 
01042         if(required_width == 0
01043                 || static_cast<size_t>(size.x) > required_width) {
01044 
01045             required_width = size.x;
01046         }
01047     }
01048 
01049     DBG_GUI_L << LOG_IMPL_HEADER
01050             << " maximum column width " << maximum_width
01051             << " returning " << required_width
01052             << ".\n";
01053 
01054     return required_width;
01055 }
01056 
01057 void tgrid_implementation::cell_request_reduce_height(
01058         tgrid::tchild& child, const unsigned maximum_height)
01059 {
01060     assert(child.widget_);
01061 
01062     if(child.widget_->get_visible() == twidget::INVISIBLE) {
01063         return;
01064     }
01065 
01066     child.widget_->request_reduce_height(
01067             maximum_height - child.border_space().y);
01068 }
01069 
01070 void tgrid_implementation::cell_request_reduce_width(
01071         tgrid::tchild& child, const unsigned maximum_width)
01072 {
01073     assert(child.widget_);
01074 
01075     if(child.widget_->get_visible() == twidget::INVISIBLE) {
01076         return;
01077     }
01078 
01079     child.widget_->request_reduce_width(
01080             maximum_width - child.border_space().x);
01081 }
01082 
01083 void set_single_child(tgrid& grid, twidget* widget)
01084 {
01085     grid.set_rows_cols(1, 1);
01086     grid.set_child(widget
01087             , 0
01088             , 0
01089             , tgrid::HORIZONTAL_GROW_SEND_TO_CLIENT
01090                 | tgrid::VERTICAL_GROW_SEND_TO_CLIENT
01091             , 0);
01092 }
01093 
01094 } // namespace gui2
01095 
01096 
01097 /*WIKI
01098  * @page = GUILayout
01099  *
01100  * {{Autogenerated}}
01101  *
01102  * = Abstract =
01103  *
01104  * In the widget library the placement and sizes of elements is determined by
01105  * a grid. Therefore most widgets have no fixed size.
01106  *
01107  *
01108  * = Theory =
01109  *
01110  * We have two examples for the addon dialog, the first example the lower
01111  * buttons are in one grid, that means if the remove button gets wider
01112  * (due to translations) the connect button (4.1 - 2.2) will be aligned
01113  * to the left of the remove button. In the second example the connect
01114  * button will be partial underneath the remove button.
01115  *
01116  * A grid exists of x rows and y columns for all rows the number of columns
01117  * needs to be the same, there is no column (nor row) span. If spanning is
01118  * required place a nested grid to do so. In the examples every row has 1 column
01119  * but rows 3, 4 (and in the second 5) have a nested grid to add more elements
01120  * per row.
01121  *
01122  * In the grid every cell needs to have a widget, if no widget is wanted place
01123  * the special widget ''spacer''. This is a non-visible item which normally
01124  * shouldn't have a size. It is possible to give a spacer a size as well but
01125  * that is discussed elsewhere.
01126  *
01127  * Every row and column has a ''grow_factor'', since all columns in a grid are
01128  * aligned only the columns in the first row need to define their grow factor.
01129  * The grow factor is used to determine with the extra size available in a
01130  * dialog. The algorithm determines the extra size work like this:
01131  *
01132  * * determine the extra size
01133  * * determine the sum of the grow factors
01134  * * if this sum is 0 set the grow factor for every item to 1 and sum to sum of items.
01135  * * divide the extra size with the sum of grow factors
01136  * * for every item multiply the grow factor with the division value
01137  *
01138  * eg
01139  *  extra size 100
01140  *  grow factors 1, 1, 2, 1
01141  *  sum 5
01142  *  division 100 / 5 = 20
01143  *  extra sizes 20, 20, 40, 20
01144  *
01145  * Since we force the factors to 1 if all zero it's not possible to have non
01146  * growing cells. This can be solved by adding an extra cell with a spacer and a
01147  * grow factor of 1. This is used for the buttons in the examples.
01148  *
01149  * Every cell has a ''border_size'' and ''border'' the ''border_size'' is the
01150  * number of pixels in the cell which aren't available for the widget. This is
01151  * used to make sure the items in different cells aren't put side to side. With
01152  * ''border'' it can be determined which sides get the border. So a border is
01153  * either 0 or ''border_size''.
01154  *
01155  * If the widget doesn't grow when there's more space available the alignment
01156  * determines where in the cell the widget is placed.
01157  *
01158  * == Examples ==
01159  *
01160  *  |---------------------------------------|
01161  *  | 1.1                                   |
01162  *  |---------------------------------------|
01163  *  | 2.1                                   |
01164  *  |---------------------------------------|
01165  *  | |-----------------------------------| |
01166  *  | | 3.1 - 1.1          | 3.1 - 1.2    | |
01167  *  | |-----------------------------------| |
01168  *  |---------------------------------------|
01169  *  | |-----------------------------------| |
01170  *  | | 4.1 - 1.1 | 4.1 - 1.2 | 4.1 - 1.3 | |
01171  *  | |-----------------------------------| |
01172  *  | | 4.1 - 2.1 | 4.1 - 2.2 | 4.1 - 2.3 | |
01173  *  | |-----------------------------------| |
01174  *  |---------------------------------------|
01175  *
01176  *
01177  *  1.1       label : title
01178  *  2.1       label : description
01179  *  3.1 - 1.1 label : server
01180  *  3.1 - 1.2 text box : server to connect to
01181  *  4.1 - 1.1 spacer
01182  *  4.1 - 1.2 spacer
01183  *  4.1 - 1.3 button : remove addon
01184  *  4.2 - 2.1 spacer
01185  *  4.2 - 2.2 button : connect
01186  *  4.2 - 2.3 button : cancel
01187  *
01188  *
01189  *  |---------------------------------------|
01190  *  | 1.1                                   |
01191  *  |---------------------------------------|
01192  *  | 2.1                                   |
01193  *  |---------------------------------------|
01194  *  | |-----------------------------------| |
01195  *  | | 3.1 - 1.1          | 3.1 - 1.2    | |
01196  *  | |-----------------------------------| |
01197  *  |---------------------------------------|
01198  *  | |-----------------------------------| |
01199  *  | | 4.1 - 1.1         | 4.1 - 1.2     | |
01200  *  | |-----------------------------------| |
01201  *  |---------------------------------------|
01202  *  | |-----------------------------------| |
01203  *  | | 5.1 - 1.1 | 5.1 - 1.2 | 5.1 - 2.3 | |
01204  *  | |-----------------------------------| |
01205  *  |---------------------------------------|
01206  *
01207  *
01208  *  1.1       label : title
01209  *  2.1       label : description
01210  *  3.1 - 1.1 label : server
01211  *  3.1 - 1.2 text box : server to connect to
01212  *  4.1 - 1.1 spacer
01213  *  4.1 - 1.2 button : remove addon
01214  *  5.2 - 1.1 spacer
01215  *  5.2 - 1.2 button : connect
01216  *  5.2 - 1.3 button : cancel
01217  *
01218  * = Praxis =
01219  *
01220  * This is the code needed to create the skeleton for the structure the extra
01221  * flags are ommitted.
01222  *
01223  *      [grid]
01224  *          [row]
01225  *              [column]
01226  *                  [label]
01227  *                      # 1.1
01228  *                  [/label]
01229  *              [/column]
01230  *          [/row]
01231  *          [row]
01232  *              [column]
01233  *                  [label]
01234  *                      # 2.1
01235  *                  [/label]
01236  *              [/column]
01237  *          [/row]
01238  *          [row]
01239  *              [column]
01240  *                  [grid]
01241  *                      [row]
01242  *                          [column]
01243  *                              [label]
01244  *                                  # 3.1 - 1.1
01245  *                              [/label]
01246  *                          [/column]
01247  *                          [column]
01248  *                              [text_box]
01249  *                                  # 3.1 - 1.2
01250  *                              [/text_box]
01251  *                          [/column]
01252  *                      [/row]
01253  *                  [/grid]
01254  *              [/column]
01255  *          [/row]
01256  *          [row]
01257  *              [column]
01258  *                  [grid]
01259  *                      [row]
01260  *                          [column]
01261  *                              [spacer]
01262  *                                  # 4.1 - 1.1
01263  *                              [/spacer]
01264  *                          [/column]
01265  *                          [column]
01266  *                              [spacer]
01267  *                                  # 4.1 - 1.2
01268  *                              [/spacer]
01269  *                          [/column]
01270  *                          [column]
01271  *                              [button]
01272  *                                  # 4.1 - 1.3
01273  *                              [/button]
01274  *                          [/column]
01275  *                      [/row]
01276  *                      [row]
01277  *                          [column]
01278  *                              [spacer]
01279  *                                  # 4.1 - 2.1
01280  *                              [/spacer]
01281  *                          [/column]
01282  *                          [column]
01283  *                              [button]
01284  *                                  # 4.1 - 2.2
01285  *                              [/button]
01286  *                          [/column]
01287  *                          [column]
01288  *                              [button]
01289  *                                  # 4.1 - 2.3
01290  *                              [/button]
01291  *                          [/column]
01292  *                      [/row]
01293  *                  [/grid]
01294  *              [/column]
01295  *          [/row]
01296  *      [/grid]
01297  *
01298  *
01299  * [[Category: WML Reference]]
01300  * [[Category: GUI WML Reference]]
01301  */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:59 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs