nux-1.14.0
|
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 }