// Copyright (C) 2002-2007 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CGUIListBox.h" #include "IGUISkin.h" #include "IGUIEnvironment.h" #include "IVideoDriver.h" #include "IGUIFont.h" #include "IGUISpriteBank.h" #include "CGUIScrollBar.h" #include "os.h" namespace irr { namespace gui { //! constructor CGUIListBox::CGUIListBox(IGUIEnvironment* environment, IGUIElement* parent, s32 id, core::rect rectangle, bool clip, bool drawBack, bool moveOverSelect) : IGUIListBox(environment, parent, id, rectangle), Selected(-1), ItemHeight(0), TotalItemHeight(0), ItemsIconWidth(0), Font(0), IconBank(0), ScrollBar(0), Selecting(false), DrawBack(drawBack), MoveOverSelect(moveOverSelect), selectTime(0) { #ifdef _DEBUG setDebugName("CGUIListBox"); #endif IGUISkin* skin = Environment->getSkin(); s32 s = skin->getSize(EGDS_SCROLLBAR_SIZE); ScrollBar = new CGUIScrollBar(false, Environment, this, 0, core::rect(RelativeRect.getWidth() - s, 0, RelativeRect.getWidth(), RelativeRect.getHeight()), !clip); ScrollBar->setSubElement(true); ScrollBar->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT); ScrollBar->drop(); ScrollBar->setPos(0); ScrollBar->grab(); setNotClipped(!clip); updateAbsolutePosition(); } //! destructor CGUIListBox::~CGUIListBox() { if (ScrollBar) ScrollBar->drop(); if (Font) Font->drop(); if (IconBank) IconBank->drop(); } //! returns amount of list items s32 CGUIListBox::getItemCount() { return Items.size(); } //! returns string of a list item. the may be a value from 0 to itemCount-1 const wchar_t* CGUIListBox::getListItem(s32 id) { if (id<0 || id>((s32)Items.size())-1) return 0; return Items[id].text.c_str(); } //! Returns the icon of an item s32 CGUIListBox::getIcon(s32 id) const { if (id<0 || id>((s32)Items.size())-1) return -1; return Items[id].icon; } //! adds an list item, returns id of item s32 CGUIListBox::addItem(const wchar_t* text) { ListItem i; i.text = text; Items.push_back(i); recalculateItemHeight(); return Items.size() - 1; } //! adds an list item, returns id of item void CGUIListBox::removeItem(s32 id) { if (id < 0 || id >= (s32)Items.size()) return; if (Selected==id) { Selected = -1; } else if (Selected > id) { Selected -= 1; selectTime = os::Timer::getTime(); } Items.erase(id); recalculateItemHeight(); } //! clears the list void CGUIListBox::clear() { Items.clear(); ItemsIconWidth = 0; Selected = -1; if (ScrollBar) ScrollBar->setPos(0); recalculateItemHeight(); } void CGUIListBox::recalculateItemHeight() { IGUISkin* skin = Environment->getSkin(); if (Font != skin->getFont()) { if (Font) Font->drop(); Font = skin->getFont(); ItemHeight = 0; if (Font) { ItemHeight = Font->getDimension(L"A").Height + 4; Font->grab(); } } TotalItemHeight = ItemHeight * Items.size(); ScrollBar->setMax(TotalItemHeight - AbsoluteRect.getHeight()); } //! returns id of selected item. returns -1 if no item is selected. s32 CGUIListBox::getSelected() { return Selected; } //! sets the selected item. Set this to -1 if no item should be selected void CGUIListBox::setSelected(s32 id) { if (id<0 || id>((s32)Items.size())-1) Selected = -1; else Selected = id; selectTime = os::Timer::getTime(); } //! called if an event happened. bool CGUIListBox::OnEvent(SEvent event) { switch(event.EventType) { case EET_GUI_EVENT: switch(event.GUIEvent.EventType) { case gui::EGET_SCROLL_BAR_CHANGED: if (event.GUIEvent.Caller == ScrollBar) { // s32 pos = ((gui::IGUIScrollBar*)event.GUIEvent.Caller)->getPos(); return true; } break; case gui::EGET_ELEMENT_FOCUS_LOST: { if (event.GUIEvent.Caller == (IGUIElement*)this) Selecting = false; } } break; case EET_MOUSE_INPUT_EVENT: { core::position2d p(event.MouseInput.X, event.MouseInput.Y); switch(event.MouseInput.Event) { case EMIE_MOUSE_WHEEL: ScrollBar->setPos(ScrollBar->getPos() + (s32)event.MouseInput.Wheel*-10); return true; case EMIE_LMOUSE_PRESSED_DOWN: { IGUIElement *el = Environment->getRootGUIElement()->getElementFromPoint( core::position2di(event.MouseInput.X, event.MouseInput.Y)); if (Environment->hasFocus(this) && ScrollBar == el && ScrollBar->OnEvent(event)) return true; } Selecting = true; Environment->setFocus(this); return true; case EMIE_LMOUSE_LEFT_UP: if (Environment->hasFocus(this) && ScrollBar->getAbsolutePosition().isPointInside(p) && ScrollBar->OnEvent(event)) return true; Selecting = false; Environment->removeFocus(this); selectNew(event.MouseInput.Y); return true; case EMIE_MOUSE_MOVED: if (Selecting || MoveOverSelect) { if (getAbsolutePosition().isPointInside(p)) { selectNew(event.MouseInput.Y, true); return true; } } } } break; } return Parent ? Parent->OnEvent(event) : false; } void CGUIListBox::selectNew(s32 ypos, bool onlyHover) { s32 oldSelected = Selected; // find new selected item. if (ItemHeight!=0) Selected = ((ypos - AbsoluteRect.UpperLeftCorner.Y - 1) + ScrollBar->getPos()) / ItemHeight; if (Selected >= (s32)Items.size()) Selected = Items.size() - 1; else if (Selected<0) Selected = 0; // post the news if (Parent && !onlyHover) { SEvent event; event.EventType = EET_GUI_EVENT; event.GUIEvent.Caller = this; event.GUIEvent.EventType = (Selected != oldSelected) ? EGET_LISTBOX_CHANGED : EGET_LISTBOX_SELECTED_AGAIN; Parent->OnEvent(event); } } //! Update the position and size of the listbox, and update the scrollbar void CGUIListBox::updateAbsolutePosition() { recalculateItemHeight(); IGUIElement::updateAbsolutePosition(); } //! draws the element and its children void CGUIListBox::draw() { if (!IsVisible) return; recalculateItemHeight(); // if the font changed IGUISkin* skin = Environment->getSkin(); irr::video::IVideoDriver* driver = Environment->getVideoDriver(); core::rect* clipRect = 0; // draw background core::rect frameRect(AbsoluteRect); // draw items core::rect clientClip(AbsoluteRect); clientClip.UpperLeftCorner.Y += 1; clientClip.UpperLeftCorner.X += 1; clientClip.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - skin->getSize(EGDS_SCROLLBAR_SIZE); clientClip.LowerRightCorner.Y -= 1; clientClip.clipAgainst(AbsoluteClippingRect); skin->draw3DSunkenPane(this, skin->getColor(EGDC_3D_HIGH_LIGHT), true, DrawBack, frameRect, &clientClip); if (clipRect) clientClip.clipAgainst(*clipRect); frameRect = AbsoluteRect; frameRect.UpperLeftCorner.X += 1; frameRect.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - skin->getSize(EGDS_SCROLLBAR_SIZE); frameRect.LowerRightCorner.Y = AbsoluteRect.UpperLeftCorner.Y + ItemHeight; frameRect.UpperLeftCorner.Y -= ScrollBar->getPos(); frameRect.LowerRightCorner.Y -= ScrollBar->getPos(); for (s32 i=0; i<(s32)Items.size(); ++i) { if (frameRect.LowerRightCorner.Y >= AbsoluteRect.UpperLeftCorner.Y && frameRect.UpperLeftCorner.Y <= AbsoluteRect.LowerRightCorner.Y) { if (i == Selected) driver->draw2DRectangle(skin->getColor(EGDC_HIGH_LIGHT), frameRect, &clientClip); core::rect textRect = frameRect; textRect.UpperLeftCorner.X += 3; if (Font) { if (IconBank && (Items[i].icon > -1)) { core::position2di iconPos = textRect.UpperLeftCorner; iconPos.Y += textRect.getHeight() / 2; iconPos.X += ItemsIconWidth/2; IconBank->draw2DSprite( (u32)Items[i].icon, iconPos, &clientClip, skin->getColor((i==Selected) ? EGDC_ICON_HIGH_LIGHT : EGDC_ICON), (i==Selected) ? selectTime : 0 , (i==Selected) ? os::Timer::getTime() : 0, false, true); } textRect.UpperLeftCorner.X += ItemsIconWidth+3; Font->draw(Items[i].text.c_str(), textRect, skin->getColor((i==Selected) ? EGDC_HIGH_LIGHT_TEXT : EGDC_BUTTON_TEXT), false, true, &clientClip); textRect.UpperLeftCorner.X -= ItemsIconWidth+3; } } frameRect.UpperLeftCorner.Y += ItemHeight; frameRect.LowerRightCorner.Y += ItemHeight; } IGUIElement::draw(); } //! adds an list item with an icon s32 CGUIListBox::addItem(const wchar_t* text, s32 icon) { ListItem i; i.text = text; i.icon = icon; Items.push_back(i); recalculateItemHeight(); if (IconBank && icon > -1 && IconBank->getSprites().size() > (u32)icon && IconBank->getSprites()[(u32)icon].Frames.size()) { u32 rno = IconBank->getSprites()[(u32)icon].Frames[0].rectNumber; if (IconBank->getPositions().size() > rno) { s32 w = IconBank->getPositions()[rno].getWidth(); if (w > ItemsIconWidth) ItemsIconWidth = w; } } return Items.size() - 1; } void CGUIListBox::setSpriteBank(IGUISpriteBank* bank) { if (IconBank) IconBank->drop(); IconBank = bank; if (IconBank) IconBank->grab(); } //! Writes attributes of the element. void CGUIListBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) { IGUIListBox::serializeAttributes(out,options); // todo: out->addString ("IconBank", IconBank->getName?); out->addBool ("DrawBack", DrawBack); out->addBool ("MoveOverSelect", MoveOverSelect); // todo: save list of items and icons. /*core::array tmpText; core::array tmpIcons; u32 i; for (i=0;iaddArray ("ItemText", tmpText); out->addArray ("ItemIcons", tmpIcons); out->addInt ("Selected", Selected); */ } //! Reads attributes of the element void CGUIListBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) { DrawBack = in->getAttributeAsBool("DrawBack"); MoveOverSelect = in->getAttributeAsBool("MoveOverSelect"); IGUIListBox::deserializeAttributes(in,options); // read arrays /* core::array tmpText; core::array tmpIcons; tmpText = in->getAttributeAsArray("ItemText"); tmpIcons = in->getAttributeAsArray("ItemIcons"); u32 i; for (i=0; isetSelected(in->getAttributeAsInt("Selected")); */ } } // end namespace gui } // end namespace irr