nux-1.14.0
GridHLayout.cpp
00001 /*
00002  * Copyright 2010 Inalogic® Inc.
00003  *
00004  * This program is free software: you can redistribute it and/or modify it
00005  * under the terms of the GNU Lesser General Public License, as
00006  * published by the  Free Software Foundation; either version 2.1 or 3.0
00007  * of the License.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranties of
00011  * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
00012  * PURPOSE.  See the applicable version of the GNU Lesser General Public
00013  * License for more details.
00014  *
00015  * You should have received a copy of both the GNU Lesser General Public
00016  * License along with this program. If not, see <http://www.gnu.org/licenses/>
00017  *
00018  * Authored by: Jay Taoko <jaytaoko@inalogic.com>
00019  *
00020  */
00021 
00022 
00023 #include "Nux.h"
00024 #include "View.h"
00025 #include "GridHLayout.h"
00026 #include "HLayout.h"
00027 #include "VLayout.h"
00028 
00029 namespace nux
00030 {
00031   static const t_s32 HERROR = 0;
00032   NUX_IMPLEMENT_OBJECT_TYPE (GridHLayout);
00033 
00034   GridHLayout::GridHLayout (NUX_FILE_LINE_DECL)
00035     :   Layout (NUX_FILE_LINE_PARAM)
00036   {
00037 #if DEBUG_LAYOUT
00038     m_h_in_margin = 10;
00039     m_h_out_margin = 10;
00040     m_v_in_margin = 10;
00041     m_v_out_margin = 10;
00042 #endif
00043 
00044     _children_size = Size (64, 64);
00045     _force_children_size = true;
00046     _partial_visibility = true;
00047     _num_row = 1;
00048     _num_column = 1;
00049     _dynamic_column = true;
00050     _height_match_content = true;
00051 
00052     // Start packing the elements from the top. Is the layout has more space than the elements can use,
00053     // leave that space at the bottom of the GridHLayout.
00054     m_ContentStacking = eStackLeft;
00055 
00056     SetMinimumSize(32, 32);
00057   }
00058 
00059   GridHLayout::~GridHLayout()
00060   {
00061 
00062   }
00063 
00064   int GridHLayout::GetChildPos (Area *child)
00065   {
00066     int position = 0;
00067     std::list<Area *>::const_iterator it;
00068     for (it = GetChildren ().begin(); it != GetChildren ().end(); it++)
00069     {
00070       if ((*it) == child)
00071         break;
00072       
00073       if ((*it)->CanFocus ())
00074       {
00075         position++;
00076       }
00077     }
00078 
00079     return position;
00080   }
00081 
00082   Area* GridHLayout::GetChildAtPosition (int pos)
00083   {
00084     int position = 0;
00085     std::list<Area *>::const_iterator it;
00086     for (it = GetChildren ().begin(); it != GetChildren ().end(); it++)
00087     {
00088       if (position == pos)
00089         return (*it);
00090       
00091       if ((*it)->CanFocus ())
00092       {
00093         position++;
00094       }
00095     }   
00096 
00097     return NULL;
00098   }
00099 
00100   long GridHLayout::DoFocusLeft (IEvent &ievent, long TraverseInfo, long ProcessEventInfo)
00101   {
00102     // if we are left on an edge, then send up
00103     Area* focused_child = GetFocusedChild ();
00104     int position = GetChildPos (focused_child);
00105     Area *parent = GetParentObject ();
00106 
00107     if (parent == NULL || position % GetNumColumn ())
00108     {
00109       return Layout::DoFocusLeft (ievent, TraverseInfo, ProcessEventInfo);
00110     }
00111     else
00112     {
00113       // left edge
00114       return SendEventToArea (parent, ievent, TraverseInfo, ProcessEventInfo);
00115     }
00116   }
00117 
00118   long GridHLayout::DoFocusRight (IEvent &ievent, long TraverseInfo, long ProcessEventInfo)
00119   {
00120     // if we are left on an edge, then send up
00121     Area* focused_child = GetFocusedChild ();
00122     int position = GetChildPos (focused_child);
00123     Area *parent = GetParentObject ();
00124 
00125     if (parent == NULL || (position + 1) % GetNumColumn ())
00126     {
00127       return Layout::DoFocusRight (ievent, TraverseInfo, ProcessEventInfo);
00128     }
00129     else
00130     {
00131       // Right Edge
00132       return SendEventToArea (parent, ievent, TraverseInfo, ProcessEventInfo);
00133     }
00134   }
00135 
00136   //up and down should pass event to parent
00137   long GridHLayout::DoFocusUp (IEvent &ievent, long TraverseInfo, long ProcessEventInfo)
00138   {
00139     Area* focused_child = GetFocusedChild ();
00140     int position = GetChildPos (focused_child);
00141     Area *parent = GetParentObject ();
00142     
00143     if (focused_child == NULL || position < GetNumColumn ())
00144     {
00145       
00146       if (parent != NULL)
00147         return SendEventToArea (parent, ievent, TraverseInfo, ProcessEventInfo);
00148       else
00149         FocusFirstChild ();
00150 
00151     }
00152     else
00153     {
00154       // so hacky, but its cheap!
00155       // just focus the child position - numcolumns
00156       //focused_child->SetFocused (false);
00157       focused_child = GetChildAtPosition (position - GetNumColumn ());
00158       if (focused_child)
00159       {
00160         focused_child->SetFocused (true);
00161         ChildFocusChanged.emit (/*this,*/ focused_child);
00162       }
00163       else
00164       {
00165         if (parent != NULL)
00166           return SendEventToArea (parent, ievent, TraverseInfo, ProcessEventInfo);
00167         else
00168           FocusFirstChild ();
00169       }
00170     }
00171 
00172     return TraverseInfo;
00173   }
00174   long GridHLayout::DoFocusDown (IEvent &ievent, long TraverseInfo, long ProcessEventInfo)
00175   {
00176     Area* focused_child = GetFocusedChild ();
00177     int position = GetChildPos (focused_child);
00178     Area *parent = GetParentObject ();
00179     
00180     if (focused_child == NULL || position > GetNumColumn () * (GetNumRow () - 1))
00181     {
00182       if (parent != NULL)
00183         return SendEventToArea (parent, ievent, TraverseInfo, ProcessEventInfo);
00184       else
00185         FocusLastChild ();
00186     }
00187     else
00188     {
00189       // so hacky, but its cheap!
00190       // just focus the child position - numcolumns
00191       //focused_child->SetFocused (false);
00192       focused_child = GetChildAtPosition (position + GetNumColumn ());
00193       if (focused_child)
00194       {
00195         focused_child->SetFocused (true);
00196         ChildFocusChanged.emit (/*this,*/ focused_child);
00197       }
00198       else
00199       {
00200         if (parent != NULL)
00201           return SendEventToArea (parent, ievent, TraverseInfo, ProcessEventInfo);
00202         else
00203           FocusLastChild ();
00204       }
00205     }
00206 
00207     return TraverseInfo;
00208   }
00209 
00210   void GridHLayout::EnablePartialVisibility (bool partial_visibility)
00211   {
00212     _partial_visibility = partial_visibility;
00213   }
00214 
00215   void GridHLayout::SetChildrenSize (int width, int height)
00216   {
00217     _children_size = Size (width, height);
00218   }
00219 
00220   Size GridHLayout::GetChildrenSize () const
00221   {
00222     return _children_size;
00223   }
00224 
00225   void GridHLayout::ForceChildrenSize (bool force)
00226   {
00227     _force_children_size = force;
00228   }
00229 
00230   int GridHLayout::GetNumColumn () const
00231   {
00232     return _num_column;
00233   }
00234 
00235   int GridHLayout::GetNumRow () const
00236   {
00237     return _num_row;
00238   }
00239 
00240   void GridHLayout::SetHeightMatchContent (bool match_content)
00241   {
00242     _height_match_content = match_content;
00243   }
00244 
00245   bool GridHLayout::GetHeightMatchContent () const
00246   {
00247     return _height_match_content;
00248   }
00249 
00250   void GridHLayout::GetCompositeList (std::list<Area *> *ViewList)
00251   {
00252     std::list<Area *>::iterator it;
00253 
00254     for (it = _layout_element_list.begin(); it != _layout_element_list.end(); it++)
00255     {
00256       if ( (*it)->IsView() )
00257       {
00258         View *ic = static_cast<View *>(*it);
00259         ViewList->push_back (ic);
00260       }
00261       else if ( (*it)->IsLayout() )
00262       {
00263         Layout *layout = static_cast<Layout *>(*it);
00264         layout->GetCompositeList (ViewList);
00265       }
00266     }
00267   }
00268 
00269   long GridHLayout::ComputeLayout2()
00270   {
00271     std::list<Area *> elements;
00272 
00273     if (GetStretchFactor() == 0)
00274     {
00275       ApplyMinWidth();
00276     }
00277 
00278     if (_layout_element_list.size() == 0)
00279     {
00280       return eCompliantHeight | eCompliantWidth;
00281     }
00282 
00283     t_s32 num_elements = 0;
00284 
00285     std::list<Area *>::iterator it;
00286     for (it = _layout_element_list.begin(); it != _layout_element_list.end(); it++)
00287     {
00288       if ((*it)->IsVisible ())
00289       {
00290         (*it)->SetLayoutDone (false);
00291         elements.push_back (*it);
00292         num_elements++;
00293       }
00294       (*it)->SetLayoutDone (false);
00295     }
00296 
00297     t_s32 original_height = GetBaseHeight();
00298 
00299     // The grid layout goes through the child elements and assign them a size and position. Children are filled in the grid like this:
00300     //  0   1   2   3   4   5
00301     //  6   7   8   9   10  11
00302     //  12  13  ..  ..  ..  ..
00303     // This is a left to right fill going down. An option can be added the fill the grid from top to bottom and going toward the right.
00304 
00305     nux::Geometry base = GetGeometry();
00306     it = elements.begin();
00307     int num_row = 0;
00308     int num_column = 0;
00309 
00310     if (num_elements > 0)
00311       ++num_row;
00312 
00313     if (_dynamic_column)
00314     {
00315       int X = base.x + m_h_out_margin;
00316       int Y = base.y + m_v_out_margin;
00317 
00318       bool first_element_of_row = true;
00319 
00320       for(int i = 0; i < num_elements; i++)
00321       {
00322         if (num_row == 1)
00323           num_column++;
00324 
00325         if(first_element_of_row)
00326         {
00327           first_element_of_row = false;
00328         }
00329 
00330         if (_force_children_size)
00331         {
00332           (*it)->SetMinimumSize(_children_size.width, _children_size.height);
00333         }
00334 
00335         (*it)->SetGeometry(nux::Geometry (X, Y, _children_size.width, _children_size.height));
00336 
00337         (*it)->ComputeLayout2();
00338 
00339         X += _children_size.width + m_h_in_margin;
00340 
00341         it++;
00342 
00343         if ((!_partial_visibility) && (X + _children_size.width > base.x + base.width))
00344         {
00345           X = base.x + m_h_out_margin;
00346           Y += _children_size.height + m_v_in_margin;
00347 
00348           first_element_of_row = true;
00349           if (i < num_elements - 1)
00350             ++num_row;
00351         }
00352         else if (X >= base.x + base.width)
00353         {
00354           X = base.x + m_h_out_margin;
00355           Y += _children_size.height + m_v_in_margin;
00356 
00357           first_element_of_row = true;
00358           if (i < num_elements - 1)
00359             ++num_row;
00360         }
00361       }
00362     }
00363 
00364     _num_row = num_row;
00365     _num_column = num_column;
00366 
00367     if ((GetStretchFactor() == 0) || _height_match_content)
00368     {
00369       int h = num_row * _children_size.height + 2 * m_v_out_margin + (num_row - 1) * m_v_in_margin;
00370       SetMinimumHeight(h);
00371       SetBaseHeight(h);
00372     }
00373 
00374     long size_compliance = 0L;
00375     {
00376 #if DEBUG_LAYOUT_COMPUTATION
00377       // The layout and its content resized together without trouble.
00378       std::cout << "ComputeLayout2: GridHLayout Width compliant = " << m_fittingWidth << std::endl;
00379 #endif
00380       size_compliance |= eCompliantWidth;
00381     }
00382 
00383     // The layout has been resized to tightly pack its content
00384     if (GetBaseHeight() > original_height + HERROR)
00385     {
00386 #if DEBUG_LAYOUT_COMPUTATION
00387       // The layout has been resized larger in height to tightly pack its content.
00388       // Or you can say that the layout refuse to be smaller than total HEIGHT of its elements.
00389       std::cout << "ComputeLayout2: GridHLayout Height block at " << GetBaseHeight() << std::endl;
00390 #endif
00391       size_compliance |= eLargerHeight; // need scrollbar
00392     }
00393     else if (GetBaseHeight() + HERROR < original_height)
00394     {
00395 #if DEBUG_LAYOUT_COMPUTATION
00396       // The layout is smaller.
00397       std::cout << "ComputeLayout2: GridHLayout Height is smaller = " << GetBaseHeight() << std::endl;
00398 #endif
00399       size_compliance |= eSmallerHeight;
00400     }
00401     else
00402     {
00403 #if DEBUG_LAYOUT_COMPUTATION
00404       // The layout and its content resized together without trouble.
00405       std::cout << "ComputeLayout2: GridHLayout Height compliant = " << GetBaseHeight() << std::endl;
00406 #endif
00407       size_compliance |= eCompliantHeight;
00408     }
00409 
00410 //    if(GetStretchFactor() == 0)
00411 //    {
00412 //        return size_compliance | eForceComply;
00413 //    }
00414 
00415     //SetDirty (false);
00416     return size_compliance;
00417   }
00418 
00419   void GridHLayout::ProcessDraw (GraphicsEngine &GfxContext, bool force_draw)
00420   {
00421     if (_layout_element_list.size() == 0)
00422       return;
00423 
00424     std::list<Area *> elements;
00425     std::list<Area *>::iterator it = _layout_element_list.begin ();
00426 
00427     GfxContext.PushModelViewMatrix(Get2DMatrix());
00428 
00429     Geometry base = GetGeometry();
00430     Geometry absolute_geometry = GetAbsoluteGeometry();
00431     Geometry parent_geometry = absolute_geometry;
00432     Geometry visibility_geometry = absolute_geometry;
00433     if (GetToplevel())
00434     {
00435       parent_geometry = GetToplevel()->GetAbsoluteGeometry();
00436     }
00437 
00438     visibility_geometry = parent_geometry.Intersect(absolute_geometry);
00439 
00440     GfxContext.PushClippingRectangle(base);
00441 
00442     it = _layout_element_list.begin();
00443 
00444     bool first = false;
00445     bool last = false;
00446     for (int j = 0; j < _num_row; j++)
00447     {
00448       for (int i = 0; i < _num_column; i++)
00449       {
00450         if (it == _layout_element_list.end())
00451           break;
00452 
00453         if ((*it)->IsVisible() == false)
00454         {
00455           ++it;
00456           continue;
00457         }
00458 
00459         Geometry it_geo = (*it)->GetAbsoluteGeometry();
00460         Geometry intersection = visibility_geometry.Intersect(it_geo);
00461 
00462         // Test if the element is inside the Grid before rendering.
00463         if (!intersection.IsNull())
00464         {
00465           if (first == false)
00466           {
00467             first = true; // First invisible child.
00468           }
00469 
00470           int x = base.x + m_h_out_margin + i * (_children_size.width + m_h_in_margin);
00471           int y = base.y + m_v_out_margin + j * (_children_size.height + m_v_in_margin);
00472 
00473           GfxContext.PushClippingRectangle(Geometry (x, y, _children_size.width, _children_size.height));
00474 
00475           if ((*it)->IsView())
00476           {
00477             View *ic = static_cast<View *>(*it);
00478             ic->ProcessDraw(GfxContext, force_draw);
00479           }
00480           else if ((*it)->IsLayout())
00481           {
00482             Layout *layout = static_cast<Layout *>(*it);
00483             layout->ProcessDraw(GfxContext, force_draw);
00484           }
00485 
00486           GfxContext.PopClippingRectangle();
00487         }
00488         else
00489         {
00490           if (first)
00491           {
00492             // First invisible child. Exit!
00493             last = true;
00494           }
00495         }
00496 
00497         if (first && last)
00498         {
00499           // Early exit
00500           break;
00501         }
00502 
00503         it++;
00504       }
00505       if (first && last)
00506         break;
00507     }
00508 
00509     GfxContext.PopClippingRectangle ();
00510     GfxContext.PopModelViewMatrix ();
00511 
00512     _queued_draw = false;
00513   }
00514 
00515   Area* GridHLayout::KeyNavIteration(KeyNavDirection direction)
00516   {
00517     if (_layout_element_list.size() == 0)
00518       return NULL;
00519 
00520     if (IsVisible() == false)
00521       return NULL;
00522 
00523     if (next_object_to_key_focus_area_)
00524     {
00525       std::list<Area*>::iterator it;
00526       std::list<Area*>::iterator it_next;
00527       it = std::find (_layout_element_list.begin(), _layout_element_list.end(), next_object_to_key_focus_area_);
00528       it_next = it;
00529       ++it_next;
00530 
00531       if (it == _layout_element_list.end())
00532       {
00533         // Should never happen
00534         nuxAssert (0);
00535         return NULL;
00536       }
00537 
00538       int position = GetChildPos(*it); // note that (*it) == next_object_to_key_focus_area_
00539       int nun_column = GetNumColumn();
00540       int nun_row = GetNumRow();
00541 
00542       if ((direction == KEY_NAV_LEFT) && (it == _layout_element_list.begin()))
00543       {
00544         // first item
00545         return NULL;
00546       }
00547 
00548       if ((direction == KEY_NAV_RIGHT) && (it_next == _layout_element_list.end()))
00549       {
00550         // last item
00551         return NULL;
00552       }
00553 
00554       if ((direction == KEY_NAV_LEFT) && ((position % nun_column) == 0))
00555       {
00556         // Left edge
00557         return NULL;
00558       }
00559       
00560       if ((direction == KEY_NAV_RIGHT) && (position == (position / nun_column) * nun_column + (nun_column -1)))
00561       {
00562         // right edge
00563         return NULL;
00564       }
00565 
00566       if ((direction == KEY_NAV_UP) && ((position / nun_column) == 0))
00567       {
00568         // top edge
00569         return NULL;
00570       }
00571 
00572       if ((direction == KEY_NAV_DOWN) && ((position / nun_column) == nun_row))
00573       {
00574         // bottom edge
00575         return NULL;
00576       }
00577 
00579       if (direction == KEY_NAV_LEFT)
00580       {
00581         --it;
00582         Area* key_nav_focus = (*it)->KeyNavIteration(direction);
00583 
00584         while (key_nav_focus == NULL)
00585         {
00586           int pos = GetChildPos(*it);
00587           if (it == _layout_element_list.begin() || ((pos % nun_column) == 0))
00588             break;
00589 
00590           --it;
00591           key_nav_focus = (*it)->KeyNavIteration(direction);
00592         }
00593 
00594         return key_nav_focus;
00595       }
00596 
00597       if (direction == KEY_NAV_RIGHT)
00598       {
00599         ++it;
00600         Area* key_nav_focus = (*it)->KeyNavIteration(direction);
00601 
00602         while (key_nav_focus == NULL)
00603         {
00604           ++it;
00605           int pos = GetChildPos(*it);
00606 
00607           if ((it == _layout_element_list.end()) || (pos == (pos / nun_column) * nun_column + (nun_column -1)))
00608             break;
00609 
00610           key_nav_focus = (*it)->KeyNavIteration(direction);
00611         }
00612 
00613         return key_nav_focus;
00614       }
00615 
00616       if (direction == KEY_NAV_UP)
00617       {
00618         for (int i = 0; i < nun_column; ++i)
00619         {
00620           --it;
00621         }
00622         return (*it)->KeyNavIteration(direction);
00623       }
00624 
00625       if (direction == KEY_NAV_DOWN)
00626       {
00627         for (int i = 0; i < nun_column; ++i)
00628         {
00629           ++it;
00630           if (it == _layout_element_list.end())
00631             return NULL;
00632         }
00633         return (*it)->KeyNavIteration(direction);
00634       }
00635     }
00636     else
00637     {
00638       std::list<Area*>::iterator it;
00639       it = _layout_element_list.begin();
00640       return (*it)->KeyNavIteration(direction);
00641     }
00642 
00643     return NULL;
00644   }
00645 }
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends