diff --git a/panda/src/gui/guiItem.I b/panda/src/gui/guiItem.I index e34c06e527..215b7b6ede 100644 --- a/panda/src/gui/guiItem.I +++ b/panda/src/gui/guiItem.I @@ -51,6 +51,14 @@ INLINE LVector4f GuiItem::get_frame(void) const { return LVector4f(_left, _right, _bottom, _top); } +INLINE float GuiItem::get_width(void) const { + return _right - _left; +} + +INLINE float GuiItem::get_height(void) const { + return _top - _bottom; +} + INLINE GuiItem::Priority GuiItem::get_priority(void) const { return _pri; } diff --git a/panda/src/gui/guiItem.h b/panda/src/gui/guiItem.h index 00780fdc57..13fd7c8339 100644 --- a/panda/src/gui/guiItem.h +++ b/panda/src/gui/guiItem.h @@ -45,6 +45,8 @@ PUBLISHED: INLINE float get_bottom(void) const; INLINE float get_top(void) const; INLINE LVector4f get_frame(void) const; + INLINE float get_width(void) const; + INLINE float get_height(void) const; INLINE Priority get_priority(void) const; INLINE void recompute(void); diff --git a/panda/src/gui/guiListBox.cxx b/panda/src/gui/guiListBox.cxx index e7f9f3afca..9b01f5542c 100644 --- a/panda/src/gui/guiListBox.cxx +++ b/panda/src/gui/guiListBox.cxx @@ -9,37 +9,239 @@ TypeHandle GuiListBox::_type_handle; void GuiListBox::recompute_frame(void) { GuiItem::recompute_frame(); + LVector3f p = _pos; + for (ItemVector::iterator i=_visible.begin(); i!=_visible.end(); + p+=(*i)->get_height(), ++i) + (*i)->set_pos(p); + // NEED TO COMPUTE FRAME AT SAME TIME! } -GuiListBox::GuiListBox(const string& name) : GuiItem(name) { +void GuiListBox::visible_patching(void) { + // check for 2 cases on both top and bottom of the visible list. First, + // if there is an item off that edge, but no arrow active; then put an arrow + // up (needing to move a second item off the edge). Second, if there is an + // arrow up, but there is only a single item off the edge; replace the arrow + // with the item. + + // top first + if (_arrow_top) { + if (_top_stack.size() == 1) { + // replace the up arrow with the single item in the top stack + _arrow_top = false; + _visible[0]->unmanage(); + _visible[0] = *(_top_stack.begin()); + _visible[0]->manage(_mgr, *_eh); + _top_stack.pop_back(); + } + } else { + if (_top_stack.size() > 0) { + // move the top of the visible list to the top stack and put the up + // arrow in it's place + _arrow_top = true; + _visible[0]->unmanage(); + _top_stack.push_back(_visible[0]); + _visible[0] = _up_arrow; + _up_arrow->manage(_mgr, *_eh); + } + } + + // now bottom + if (_arrow_bottom) { + if (_bottom_stack.size() == 1) { + // replace the down arrow with the single item in the bottom stack + _arrow_bottom = false; + int last = _n_visible-1; + _visible[last]->unmanage(); + _visible[last] = *(_bottom_stack.begin()); + if (_mgr != (GuiManager*)0L) + _visible[last]->manage(_mgr, *_eh); + _bottom_stack.pop_back(); + } + } else { + if (_bottom_stack.size() > 0) { + // move the bottom of the visible list to the bottom stack and put the + // down arrow in it's place + _arrow_bottom = true; + int last = _n_visible-1; + _visible[last]->unmanage(); + _bottom_stack.push_back(_visible[last]); + _visible[last] = _down_arrow; + if (_mgr != (GuiManager*)0L) + _down_arrow->manage(_mgr, *_eh); + } + } +} + +GuiListBox::GuiListBox(const string& name, int N, GuiItem* up, GuiItem* down) + : GuiItem(name), _arrow_top(false), _arrow_bottom(false), _up_arrow(up), + _down_arrow(down), _n_visible(N) { + if (N < 4) { + gui_cat->warning() << "ListBoxes should have at least 4 visible slots" + << endl; + _n_visible = 4; + } } GuiListBox::~GuiListBox(void) { this->unmanage(); } +void GuiListBox::scroll_up(void) { + if (_bottom_stack.size() == 0) + return; // nothing to scroll + // compute what the first and list item in the visible list are + int first = 0; + int last = _n_visible-1; + if (_arrow_top) + ++first; + if (_arrow_bottom) + --last; + // first push one off the top onto the top stack + _top_stack.push_back(_visible[first]); + _visible[first]->unmanage(); + // then move everything up one + for (int i=first; i!=last; ++i) + _visible[i] = _visible[i+1]; + // then add one from the bottom stack to the bottom + _visible[last] = *(_bottom_stack.rbegin()); + if (_mgr != (GuiManager*)0L) + _visible[last]->manage(_mgr, *_eh); + // and pop it off the bottom stack + _bottom_stack.pop_back(); + // now patch-up any dangling items + visible_patching(); + // finally recompute all the possitions + this->recompute_frame(); +} + +void GuiListBox::scroll_down(void) { + if (_top_stack.size() == 0) + return; // nothing to scroll + // compute what the first and last item in the visible list are + int first = 0; + int last = _n_visible - 1; + if (_arrow_top) + ++first; + if (_arrow_bottom) + --last; + // first push one off the bottom onto the bottom stack + _bottom_stack.push_back(_visible[last]); + _visible[last]->unmanage(); + // then move everything down one + for (int i=last; i!=first; --i) + _visible[i] = _visible[i-1]; + // then add one from the top stack to the top + _visible[first] = *(_top_stack.rbegin()); + if (_mgr != (GuiManager*)0L) + _visible[first]->manage(_mgr, *_eh); + // and pop it off the top stack + _top_stack.pop_back(); + // now patch-up any dangling item + visible_patching(); + // finally recompute all the possitions + this->recompute_frame(); +} + +void GuiListBox::add_item(GuiItem* item) { + if (_bottom_stack.size() > 0) + _bottom_stack.push_front(item); + else { + if (_visible.size() < _n_visible) { + _visible.push_back(item); + if (_mgr != (GuiManager*)0L) + item->manage(_mgr, *_eh); + } else + _bottom_stack.push_back(item); + } +} + +int GuiListBox::freeze(void) { + int result = 0; + + for (ItemVector::iterator i=_visible.begin(); i!=_visible.end(); ++i) { + int count = (*i)->freeze(); + result = max(result, count); + } + return result; +} + +int GuiListBox::thaw(void) { + int result = 0; + + for (ItemVector::iterator i=_visible.begin(); i!=_visible.end(); ++i) { + int count = (*i)->thaw(); + result = max(result, count); + } + return result; +} + void GuiListBox::manage(GuiManager* mgr, EventHandler& eh) { - if (_mgr == (GuiManager*)0L) + if (_mgr == (GuiManager*)0L) { + for (ItemVector::iterator i=_visible.begin(); i!=_visible.end(); ++i) + (*i)->manage(mgr, eh); + _eh = &eh; GuiItem::manage(mgr, eh); - else + } else gui_cat->warning() << "tried to manage listbox (0x" << (void*)this << ") that is already managed" << endl; } void GuiListBox::unmanage(void) { + for (ItemVector::iterator i=_visible.begin(); i!=_visible.end(); ++i) + (*i)->unmanage(); GuiItem::unmanage(); } void GuiListBox::set_scale(float f) { + ItemVector::iterator i; + for (i=_top_stack.begin(); i!=_top_stack.end(); ++i) + (*i)->set_scale(f); + for (i=_visible.begin(); i!=_visible.end(); ++i) + (*i)->set_scale(f); + for (ItemDeque::iterator j=_bottom_stack.begin(); j!=_bottom_stack.end(); + ++j) + (*j)->set_scale(f); GuiItem::set_scale(f); } void GuiListBox::set_pos(const LVector3f& p) { + ItemVector::iterator i; + for (i=_top_stack.begin(); i!=_top_stack.end(); ++i) + (*i)->set_pos(p); + for (i=_visible.begin(); i!=_visible.end(); ++i) + (*i)->set_pos(p); + for (ItemDeque::iterator j=_bottom_stack.begin(); j!=_bottom_stack.end(); + ++j) + (*j)->set_pos(p); GuiItem::set_pos(p); } void GuiListBox::output(ostream& os) const { GuiItem::output(os); os << " Listbox data:" << endl; - os << " none" << endl; + os << " There is "; + if (!_arrow_top) + os << "no "; + os << "top arrow (0x" << (void*)_up_arrow << ")" << endl; + os << " There is "; + if (!_arrow_bottom) + os << "no "; + os << "bottom arrow (0x" << (void*)_down_arrow << ")" << endl; + os << " Top stack (" << _top_stack.size() << "):" << endl; + ItemVector::const_iterator i; + for (i=_top_stack.begin(); i!=_top_stack.end(); ++i) + os << " 0x" << (void*)(*i) << endl; + os << " Visible (" << _visible.size() << "):" << endl; + for (i=_visible.begin(); i!=_visible.end(); ++i) + os << " 0x" << (void*)(*i) << endl; + os << " Bottom stack (" << _bottom_stack.size() << "):" << endl; + ItemDeque::const_iterator j; + for (j=_bottom_stack.begin(); j!=_bottom_stack.end(); ++j) + os << " 0x" << (void*)(*j) << endl; + for (i=_top_stack.begin(); i!=_top_stack.end(); ++i) + os << *(*i); + for (i=_visible.begin(); i!=_visible.end(); ++i) + os << *(*i); + for (j=_bottom_stack.begin(); j!=_bottom_stack.end(); ++j) + os << *(*j); } diff --git a/panda/src/gui/guiListBox.h b/panda/src/gui/guiListBox.h index ee0549f9ee..4122d6549a 100644 --- a/panda/src/gui/guiListBox.h +++ b/panda/src/gui/guiListBox.h @@ -9,15 +9,37 @@ #include "guiItem.h" #include +#include class EXPCL_PANDA GuiListBox : public GuiItem { private: + typedef vector< PT(GuiItem) > ItemVector; + typedef deque< PT(GuiItem) > ItemDeque; + + ItemVector _top_stack; + ItemDeque _bottom_stack; + ItemVector _visible; + bool _arrow_top; + bool _arrow_bottom; + PT(GuiItem) _up_arrow; + PT(GuiItem) _down_arrow; + unsigned int _n_visible; + EventHandler* _eh; + INLINE GuiListBox(void); virtual void recompute_frame(void); + void visible_patching(void); PUBLISHED: - GuiListBox(const string&); + GuiListBox(const string&, int, GuiItem*, GuiItem*); ~GuiListBox(void); + void scroll_up(void); + void scroll_down(void); + void add_item(GuiItem*); + + virtual int freeze(void); + virtual int thaw(void); + virtual void manage(GuiManager*, EventHandler&); virtual void unmanage(void);