00001 /* 00002 libwt - Vassilis Virvilis Toolkit - a widget library 00003 Copyright (C) 2006 Vassilis Virvilis <vasvir2@fastmail.fm> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public 00007 License as published by the Free Software Foundation; either 00008 version 2.1 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the 00017 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 Boston, MA 02111-1307, SA. 00019 */ 00020 00021 #ifndef WT_LAYOUT_H 00022 #define WT_LAYOUT_H 00023 00024 #include <wt/object.h> 00025 #include <wt/wvar.h> 00026 #include <wt/sizepolicy.h> 00027 #include <wt/size.h> 00028 #include <wt/rect.h> 00029 #include <wt/trace.h> 00030 #include <wt/flags.h> 00031 00032 namespace Wt { 00033 00034 class LayoutItem; 00035 class Widget; 00036 class LayoutHintEvent; 00037 00038 /* 00039 We cannot possibly split the design to ContainerInterface and IteratorInterface 00040 because we can't return Iterator* from std containers 00041 */ 00042 class LayoutIteratorInterface { 00043 public: 00044 virtual ~LayoutIteratorInterface() {} 00045 virtual LayoutItem* operator*() const = 0; 00046 virtual bool operator!=(const LayoutIteratorInterface& other) const = 0; 00047 bool operator==(const LayoutIteratorInterface& other) const { 00048 return !(**this != *other); 00049 } 00050 //postfix only 00051 virtual LayoutIteratorInterface& operator++(int) = 0; 00052 00053 // we bastardize iterator and container here 00054 virtual void start() = 0; 00055 virtual bool finish() = 0; 00056 virtual LayoutItem* pop() = 0; 00057 }; 00058 00059 class NullLayoutIterator : public LayoutIteratorInterface { 00060 public: 00061 virtual LayoutItem* operator*() const { 00062 return 0; 00063 } 00064 virtual bool operator!=(const LayoutIteratorInterface&) const { 00065 return false; 00066 } 00067 //postfix only 00068 virtual NullLayoutIterator& operator++(int) { 00069 return *this; 00070 } 00071 00072 // we bastardize iterator and container here 00073 virtual void start() {} 00074 virtual bool finish() { 00075 return true; 00076 } 00077 virtual LayoutItem* pop() { 00078 return 0; 00079 } 00080 }; 00081 00082 class LayoutIterator { 00083 public: 00084 /// we cannot avoid the pointer here since we need the virtualized behaviour 00085 LayoutIterator(LayoutIteratorInterface * li) : 00086 li_(li) {} 00087 ~LayoutIterator () { 00088 delete li_; 00089 } 00090 LayoutItem* operator*() const { 00091 return **li_; 00092 } 00093 LayoutIterator operator=(const LayoutIterator &i) { 00094 return li_ = i.li_; 00095 } 00096 bool operator!=(const LayoutIteratorInterface& other) const { 00097 return (**li_ != *other); 00098 } 00099 //postfix only 00100 LayoutIterator& operator++(int) { 00101 (*li_)++; 00102 return *this; 00103 } 00104 // we bastardize iterator and container here 00105 void start() { 00106 return li_->start(); 00107 } 00108 bool finish() { 00109 return li_->finish(); 00110 } 00111 LayoutItem* pop() { 00112 return li_->pop(); 00113 } 00114 00115 private: 00116 LayoutIteratorInterface *li_; 00117 }; 00118 00119 /// all layoutItems should inherit from this. It's their interface 00120 /*! 00121 The following functions must be reimplemented 00122 - setGeometry() 00123 - isEmpty() const; 00124 - hasHeightForWidth() 00125 - heightForWidth() 00126 - iterator() 00127 */ 00128 class LayoutItem { 00129 public: 00130 LayoutItem(int alignment = AlignAuto, 00131 const SizePolicy& size_policy = SizePolicy(), 00132 const Size& size_hint = Size(default_hint_size, default_hint_size), 00133 const Size& minimum_size = Size(default_min_size, default_min_size), 00134 const Size& maximum_size = Size(default_max_size, default_max_size)) : 00135 alignment(alignment), 00136 invalidated_(true), 00137 size_policy_(size_policy), 00138 size_hint_(size_hint), 00139 minimum_size_(minimum_size), 00140 maximum_size_(maximum_size) {} 00141 00142 virtual ~LayoutItem() {} 00143 00144 virtual Size sizeHint() const { 00145 return size_hint_; 00146 } 00147 00148 virtual Size minimumSize() const { 00149 return minimum_size_; 00150 } 00151 virtual void setMinimumSize(const Size& size) { 00152 minimum_size_ = size; 00153 } 00154 00155 virtual Size maximumSize() const { 00156 return maximum_size_; 00157 } 00158 virtual void setMaximumSize(const Size& size) { 00159 maximum_size_ = size; 00160 } 00161 00162 virtual SizePolicy sizePolicy() const { 00163 return size_policy_; 00164 } 00165 00166 virtual void setSizePolicy(const SizePolicy& size_policy) { 00167 size_policy_ = size_policy; 00168 } 00169 00170 virtual SizePolicy::ExpandData expanding() const { 00171 return size_policy_.expandData; 00172 } 00173 00174 /// return the widget's geometry relative to its parent 00175 virtual const Rect& geometry() const { 00176 return geometry_rect_; 00177 } 00178 00179 // --- necessary interface --- default implementations 00180 virtual void setGeometry(const Rect& r) { 00181 geometry_rect_ = r; 00182 } 00183 00184 virtual bool isEmpty() const { 00185 return false; 00186 } 00187 00188 bool isNonEmpty() const { 00189 return (this) ? !isEmpty() : false; 00190 } 00191 00192 virtual bool hasHeightForWidth() const { 00193 return false; 00194 } 00195 virtual int heightForWidth(int) const { 00196 return -1; 00197 } 00198 00199 virtual LayoutIterator iterator() { 00200 return LayoutIterator(new NullLayoutIterator()); 00201 } 00202 00203 template<typename TEST_TYPE> 00204 TEST_TYPE type() const { 00205 return dynamic_cast<TEST_TYPE>(const_cast<LayoutItem *>(this)); 00206 } 00207 00208 template<typename TEST_TYPE> 00209 bool is() const { 00210 return (type<TEST_TYPE>() != 0); 00211 } 00212 00213 // sensible defaults 00214 static const int default_max_size = ((int) ((unsigned int) (~0) >> 1)); 00215 static const int default_min_size = 30; 00216 static const int default_hint_size = 100; 00217 00218 WVar<int> alignment; 00219 00220 protected: 00221 void invalidateRecursively(); 00222 00223 virtual void validate() { 00224 invalidated_ = false; 00225 } 00226 00227 bool invalidated() const { 00228 return invalidated_; 00229 } 00230 00231 virtual void setSizeHint(const Size& size) { 00232 size_hint_ = size; 00233 } 00234 00235 private: 00236 bool invalidated_; 00237 SizePolicy size_policy_; 00238 Size size_hint_; 00239 Size minimum_size_; 00240 Size maximum_size_; 00241 Rect geometry_rect_; 00242 }; 00243 00244 /// handles the layout of widgets 00245 /*! 00246 The Layout class takes ownership of child layouts (Objects), 00247 and other layoutitems that are not widgets. Widgets have their 00248 own ierarchy and so a Layout absolutely does not delete Widgets, 00249 in any way. 00250 00251 Layouts will be deleted when the parent layout dies or when they 00252 are explictely deleted. The rest of the layout items (spacer) 00253 will be deleted on Layout::remove() and/or ~Layout(). 00254 00255 In the mean time however, we keep track of all these pointers into 00256 our container layout and layout actively syncronizes the list when 00257 a child is deleted. 00258 00259 In short: Layout contains LayoutItems. LayoutItems can be Layouts, 00260 Widgets and Spacers. Layouts are also children to parent Layout. 00261 00262 Layout Widgets Spacer 00263 Deleted on remove y n y 00264 Deleted on ~Layout y n y 00265 removed on deletion y y * 00266 00267 * don't do that. That objects are now belong to Layout and cannot 00268 be deleted safely since they are not Objects. If you want them to 00269 be deleted remove them from the Layout via Layout::remove() 00270 00271 */ 00272 class Layout : public Object, public LayoutItem { 00273 public: 00274 Layout(Widget *parent, int margin = 0, int spacing = -1, 00275 const std::string &name = "Layout::anon"); 00276 Layout(Layout * parentLayout, int margin = 0, int spacing = -1, 00277 const std::string &name = "Layout::anon"); 00278 Layout(int spacing = -1, const std::string &name = "Layout::anon"); 00279 virtual ~Layout() { 00280 Layout *l = dynamic_cast<Layout *>(parent()); 00281 if (l) { 00282 l->markDeleted(this); 00283 l->removeItem(this); 00284 } 00285 } 00286 00287 Widget *mainWidget() const; 00288 bool isTopLevel() const; 00289 00290 virtual Size sizeHint() const; 00291 virtual Size minimumSize() const; 00292 // The reason we need this if Layout parent is a Layout (in constructor) 00293 virtual void addItem(LayoutItem *li); 00294 00295 /// returns true if a layoutitem is actually removed 00296 virtual bool removeItem(LayoutItem *li); 00297 00298 virtual bool eventFilter(Object *, Event *); 00299 00300 virtual void setGeometry(const Rect& r); 00301 00302 WVar<int> margin; 00303 WVar<int> spacing; 00304 WVar<bool> autoAdd; 00305 00306 protected: 00307 virtual bool onAboutToChangeAutoAdd(bool auto_add); 00308 virtual int onSpacingOverride(int spacing); 00309 void adjustMaximumSize(int margin); 00310 virtual bool exists(const LayoutItem *li) const = 0; 00311 void markDeleted(LayoutItem *li) { 00312 deleted_li_ = li; 00313 } 00314 bool isDeleted(LayoutItem *li) const { 00315 return deleted_li_ == li; 00316 } 00317 void enforce(); 00318 void postLayoutHintEvent(); 00319 00320 void setWidthasPrimaryLength(bool width = true); 00321 bool isWidthPrimaryLength() const { 00322 return width_as_primary_length; 00323 } 00324 00325 template<typename TYPE> 00326 int primaryLength(const TYPE& t) const { 00327 return (width_as_primary_length) ? t.width() : t.height(); 00328 } 00329 00330 int preferredLength(const LayoutItem *li, bool expand) const; 00331 00332 template<typename TYPE> 00333 int secondaryLength(const TYPE& t) const; 00334 00335 template<typename TYPE> 00336 int primaryOffset(const TYPE& t) const; 00337 00338 template<typename TYPE> 00339 int secondaryOffset(const TYPE& t) const; 00340 00341 Point getPoint(int primary_offset, int secondary_offset) const; 00342 Size getSize(int primary, int secondary) const; 00343 Rect getRect(int primary_offset, int secondary_offset, 00344 int primary_size, int secondary_size) const; 00345 00346 int primaryStretch(const SizePolicy& sp) const; 00347 bool mayGrowPrimally(const SizePolicy& sp) const; 00348 bool mayShrinkPrimally(const SizePolicy& sp) const; 00349 bool expandingPrimally(const SizePolicy& sp) const; 00350 00351 private: 00352 void on_wobject_item_death(const Object *obj); 00353 LayoutItem *deleted_li_; 00354 00355 void init(); 00356 bool width_as_primary_length; 00357 }; 00358 00359 class NullClass { 00360 public: 00361 }; 00362 00363 template <typename ValueType = NullClass> 00364 class LayoutItemCompound { 00365 public: 00366 typedef ValueType LayoutItemValue; 00367 00368 LayoutItemCompound(const LayoutItem * li_ = 0, 00369 const LayoutItemValue& val = LayoutItemValue()) : 00370 li(const_cast<LayoutItem *>(li_)), value(val) {} 00371 00372 operator LayoutItem *() const { 00373 return li; 00374 } 00375 00376 operator const LayoutItemValue& () const { 00377 return value; 00378 } 00379 00380 operator LayoutItemValue& () { 00381 return value; 00382 } 00383 00384 bool operator==(const LayoutItemCompound& other) const { 00385 return li == other.li; 00386 } 00387 00388 bool operator==(const LayoutItem *other) const { 00389 return li == other; 00390 } 00391 00392 private: 00393 LayoutItem *li; 00394 LayoutItemValue value; 00395 }; 00396 00397 template <typename Container> 00398 class LayoutStdContainerIterator : public LayoutIteratorInterface { 00399 public: 00400 typedef typename Container::value_type Compound; 00401 LayoutStdContainerIterator(Container& container) : 00402 container_(container), 00403 end(container_.end()) {} 00404 00405 virtual LayoutItem* operator*() const { 00406 return *it_; 00407 } 00408 00409 typename Compound::LayoutItemValue& value() { 00410 return *it_; 00411 } 00412 00413 virtual bool operator!=(const LayoutIteratorInterface& other) const { 00414 return *it_ != *other; 00415 } 00416 00417 //postfix only 00418 virtual LayoutStdContainerIterator& operator++(int) { 00419 ++it_; 00420 return *this; 00421 } 00422 00423 // we bastardize iterator and container here 00424 virtual void start() { 00425 it_ = container_.begin(); 00426 } 00427 virtual bool finish() { 00428 return (it_ == end); 00429 } 00430 virtual LayoutItem* pop() { 00431 LayoutItem *li = *it_; 00432 container_.erase(it_); 00433 return li; 00434 } 00435 00436 private: 00437 Container& container_; 00438 typename Container::iterator it_; 00439 typename Container::iterator end; 00440 }; 00441 00442 /*! 00443 Each LayoutItem is possible associated with a value class where the 00444 the Layout can store effective or current configuration per item. 00445 The way to access this value field is via the operator[] or by 00446 the value() function. Both are equivalents and return a reference 00447 to the value part of the compound type. Access is going through a 00448 limited (one item only) cache scheme. 00449 00450 The ways to access the value part are: 00451 (*this)[li].stretch = 1; 00452 or 00453 XXXLayout& self(*this); 00454 self[li].stretch = 1; 00455 or 00456 value(li).stretch = 1; 00457 00458 where stretch is a member of the value part of the compound type 00459 */ 00460 template <typename Container> 00461 class LayoutStdContainer : public Layout, public Container { 00462 public: 00463 typedef typename Container::value_type Compound; 00464 LayoutStdContainer(Widget *parent, int margin = 0, int spacing = -1, 00465 const std::string &name = "StdLayout::anon") : 00466 Layout(parent, margin, spacing, name), 00467 cached_li_(0) {} 00468 00469 LayoutStdContainer(Layout * parent_layout, int margin = 0, int spacing = -1, 00470 const std::string &name = "StdLayout::anon") : 00471 Layout(parent_layout, margin, spacing, name), 00472 cached_li_(0) {} 00473 00474 LayoutStdContainer(int spacing = -1, const std::string &name = "StdLayout::anon") : 00475 Layout(spacing, name), 00476 cached_li_(0) {} 00477 00478 ~LayoutStdContainer() { 00479 for (typename Container::iterator it(Container::begin()), 00480 e(Container::end()); it != e;) { 00481 LayoutItem *li = *it; 00482 // Delete only non Objects, Objects will be deleted by ~Object() 00483 if (li && !li->is<Object *>()) { 00484 delete li; 00485 } 00486 it = Container::erase(it); 00487 } 00488 } 00489 00490 virtual bool isEmpty() const { 00491 return Container::empty(); 00492 } 00493 00494 // you cannot have multiple inheritance in order to fill up pure virtual functions 00495 virtual void addItem(LayoutItem *li) { 00496 Layout::addItem(li); 00497 Container::push_back(li); 00498 } 00499 00500 virtual bool removeItem(LayoutItem *li) { 00501 bool exist = Layout::removeItem(li); 00502 if (exist) { 00503 Container::remove 00504 (li); 00505 } 00506 return exist; 00507 } 00508 00509 virtual LayoutIterator iterator() { 00510 return LayoutIterator(new LayoutStdContainerIterator<Container>(*this)); 00511 } 00512 00513 protected: 00514 virtual bool exists(const LayoutItem *li) const { 00515 typename Container::const_iterator end = Container::end(); 00516 typename Container::const_iterator p = find(Container::begin(), end, li); 00517 return (p != end); 00518 } 00519 00520 typename Compound::LayoutItemValue& value(const LayoutItem *li) { 00521 if (li != cached_li_) { 00522 cached_li_ = li; 00523 cached_it_ = find(Container::begin(), Container::end(), li); 00524 } 00525 return (cached_it_ != Container::end()) ? *cached_it_ : null_; 00526 } 00527 00528 typename Compound::LayoutItemValue& operator[](const LayoutItem *li) { 00529 return value(li); 00530 } 00531 00532 private: 00533 const LayoutItem *cached_li_; 00534 typename Container::iterator cached_it_; 00535 typename Compound::LayoutItemValue null_; 00536 }; 00537 00538 class BoxLayoutItemValue { 00539 public: 00540 BoxLayoutItemValue() : 00541 stretch(0), 00542 extra_space(0) {} 00543 00544 int stretch; 00545 int extra_space; 00546 }; 00547 00548 typedef LayoutItemCompound<BoxLayoutItemValue> BoxLayoutItemCompound; 00549 typedef std::list<BoxLayoutItemCompound> BoxLayoutItemList; 00550 00551 class BoxLayoutIterator : public LayoutStdContainerIterator<BoxLayoutItemList> { 00552 public: 00553 BoxLayoutIterator(BoxLayoutItemList& list) : 00554 LayoutStdContainerIterator<BoxLayoutItemList>(list) {} 00555 private: 00556 }; 00557 00558 /*! 00559 - stretch beats expanding 00560 - expanding beats mayGrow 00561 - expanding and or stretch does not propagate outside layout 00562 - if there is stretch turns on expanding 00563 00564 The extra space is first divided between layout items with a stretch value. 00565 If there is none then the space is divided among expanding layout items. If there 00566 is none then space is divided among layout items that have the mayGrow flag set. 00567 */ 00568 typedef LayoutStdContainer<BoxLayoutItemList> BoxLayoutBase; 00569 class BoxLayout : public BoxLayoutBase { 00570 public: 00571 friend class Box; 00572 00573 typedef enum { 00574 LeftToRight, 00575 RightToLeft, 00576 TopToBottom, 00577 BottomToTop, 00578 Down = TopToBottom, 00579 Up = BottomToTop 00580 } Direction; 00581 00582 BoxLayout(Widget *parent, Direction direction, int margin = 0, 00583 int spacing = -1, const std::string &name = "BoxLayout::anon") : 00584 BoxLayoutBase(parent, margin, spacing, name), 00585 direction(direction) { 00586 setWidthasPrimaryLength(direction == LeftToRight || 00587 direction == RightToLeft); 00588 } 00589 00590 BoxLayout(Layout * parent_layout, Direction direction, int margin = 0, 00591 int spacing = -1, const std::string &name = "BoxLayout::anon") : 00592 BoxLayoutBase(parent_layout, margin, spacing, name), 00593 direction(direction) { 00594 setWidthasPrimaryLength(direction == LeftToRight || 00595 direction == RightToLeft); 00596 } 00597 00598 BoxLayout(Direction direction, int spacing = -1, 00599 const std::string &name = "BoxLayout::anon") : 00600 BoxLayoutBase(spacing, name), 00601 direction(direction) { 00602 setWidthasPrimaryLength(direction == LeftToRight || 00603 direction == RightToLeft); 00604 } 00605 00606 Direction direction; 00607 00608 virtual void appendItem(LayoutItem *li); 00609 virtual void prependItem(LayoutItem *li); 00610 virtual void addItem(LayoutItem *li); 00611 virtual void setGeometry(const Rect& r); 00612 00613 protected: 00614 virtual void validate(); 00615 }; 00616 00617 class HBoxLayout : public BoxLayout { 00618 public: 00619 HBoxLayout(Widget *parent, int margin = 0, int spacing = -1, 00620 const std::string &name = "HBoxLayout::anon") : 00621 BoxLayout(parent, LeftToRight, margin, spacing, name) {} 00622 00623 HBoxLayout(Layout * parent_layout, int margin = 0, int spacing = -1, 00624 const std::string &name = "HBoxLayout::anon") : 00625 BoxLayout(parent_layout, LeftToRight, margin, spacing, name) {} 00626 00627 HBoxLayout(int spacing = -1, const std::string &name = "HBoxLayout::anon") : 00628 BoxLayout(LeftToRight, spacing, name) {} 00629 00630 private: 00631 }; 00632 00633 class VBoxLayout : public BoxLayout { 00634 public: 00635 VBoxLayout(Widget *parent, int margin = 0, int spacing = -1, 00636 const std::string &name = "VBoxLayout::anon") : 00637 BoxLayout(parent, Down, margin, spacing, name) {} 00638 00639 VBoxLayout(Layout * parent_layout, int margin = 0, int spacing = -1, 00640 const std::string &name = "VBoxLayout::anon") : 00641 BoxLayout(parent_layout, Down, margin, spacing, name) {} 00642 00643 VBoxLayout(int spacing = -1, const std::string &name = "VBoxLayout::anon") : 00644 BoxLayout(Down, spacing, name) {} 00645 00646 private: 00647 }; 00648 00649 } // namespace 00650 00651 #endif // WT_LAYOUT_H
This document is licensed under the terms of the GNU Free Documentation License and may be freely distributed under the conditions given by this license.