diff -abBdpuNPr --exclude='*.svn' irrlicht-svn-ss/trunk/include/IGUIContextMenu.h Irrlicht_starsonata/include/IGUIContextMenu.h --- irrlicht-svn-ss/trunk/include/IGUIContextMenu.h 2007-07-26 02:11:22.000000000 +0200 +++ Irrlicht_starsonata/include/IGUIContextMenu.h 2008-06-06 07:41:40.000000000 +0200 @@ -11,6 +11,24 @@ namespace irr { namespace gui { + // starsonata, micha (removing menus would conflict with sane event handling, as ID's would have to be saved extra otherwise) + //! close behaviour + //! flags can be combined, but that will make only sense in extreme cases where you know what you do. + //! default is ECMC_REMOVE + enum ECONTEXT_MENU_CLOSE + { + //! do nothing - menu stays open + ECMC_IGNORE = 0, + + //! remove the gui element + ECMC_REMOVE = 1, + + //! drop the gui element + ECMC_DROP = 2, + + //! call setVisible(false) + ECMC_HIDE = 4, + }; //! GUI Context menu interface. class IGUIContextMenu : public IGUIElement @@ -40,7 +58,7 @@ namespace gui \return Returns the index of the new item */ virtual s32 addItem(const wchar_t* text, s32 commandId=-1, bool enabled=true, bool hasSubMenu=false, - bool checked=false + bool checked=false, bool autoChecking=false ) = 0; //! Adds a separator item to the menu @@ -55,6 +73,10 @@ namespace gui \param text: New text of the item. */ virtual void setItemText(s32 idx, const wchar_t* text) = 0; + //! Starsonata, MICHA: textID for stringtables + virtual const wchar_t* getItemTextID(s32 idx) = 0; + virtual void setItemTextID(s32 idx, const wchar_t* text) = 0; + //! Check if a menu item is enabled /** \param idx: Zero based index of the menu item */ virtual bool isItemEnabled(s32 idx) = 0; @@ -94,11 +116,38 @@ namespace gui set to whatever you want. */ virtual void setItemCommandId(s32 idx, s32 id) = 0; + // starsonata, micha - a useful comfort function + //! Find a item which has the given CommandId starting from given index (or idxStartSearch=0 for searching from start) + //! return -1 if no such item was found + virtual s32 findItemWithCommandId(s32 commandId, u32 idxStartSearch=0) = 0; + //! Get a pointer to the submenu of an item. /** 0 is returned if there is no submenu \param idx: Zero based index of the menu item \return Returns a pointer to the submenu of an item. */ virtual IGUIContextMenu* getSubMenu(s32 idx) = 0; + + // starsonata, micha - just removing won't always work + //! set behaviour when menus are closed + virtual void setCloseHandling(ECONTEXT_MENU_CLOSE onClose) = 0; + + //! get current behaviour when the menue will be closed + virtual ECONTEXT_MENU_CLOSE getCloseHandling() const = 0; + + // starsonata, micha - grownup menues can handle checking flags themself + //! should the element change the checked status on clicking + virtual void setItemAutoChecking(s32 idx, bool autoChecking) = 0; + + //! does the element change the checked status on clicking + virtual bool getItemAutoChecking(s32 idx) = 0; + + // starsonata, micha + //! Make sure to show it at a good place around the given coordinates. + //! A comfort function which can for example be used after right-clicks with the mouse coordinates. + virtual void popupAt(irr::s32 x_, irr::s32 y_) = 0; + + // Starsonata, Micha, this was protected formerly, but it's rather useful as general functions for popups + virtual void setEventParent(IGUIElement *parent) = 0; }; } // end namespace gui diff -abBdpuNPr --exclude='*.svn' irrlicht-svn-ss/trunk/source/Irrlicht/CGUIContextMenu.cpp Irrlicht_starsonata/source/Irrlicht/CGUIContextMenu.cpp --- irrlicht-svn-ss/trunk/source/Irrlicht/CGUIContextMenu.cpp 2007-07-26 02:11:08.000000000 +0200 +++ Irrlicht_starsonata/source/Irrlicht/CGUIContextMenu.cpp 2008-06-06 07:40:36.000000000 +0200 @@ -22,7 +22,7 @@ CGUIContextMenu::CGUIContextMenu(IGUIEnv IGUIElement* parent, s32 id, core::rect rectangle, bool getFocus, bool allowFocus) : IGUIContextMenu(environment, parent, id, rectangle), HighLighted(-1), - ChangeTime(0), EventParent(0), AllowFocus(allowFocus) + ChangeTime(0), EventParent(0), AllowFocus(allowFocus), CloseHandling(ECMC_REMOVE) { #ifdef _DEBUG setDebugName("CGUIContextMenu"); @@ -55,11 +55,12 @@ s32 CGUIContextMenu::getItemCount() cons //! Adds a menu item. -s32 CGUIContextMenu::addItem(const wchar_t* text, s32 id, bool enabled, bool hasSubMenu, bool checked) +s32 CGUIContextMenu::addItem(const wchar_t* text, s32 id, bool enabled, bool hasSubMenu, bool checked, bool autoChecking) { SItem s; s.Enabled = enabled; s.Checked = checked; + s.AutoChecking = autoChecking; s.Text = text; s.IsSeparator = (text == 0); s.SubMenu = 0; @@ -78,6 +79,18 @@ s32 CGUIContextMenu::addItem(const wchar return Items.size() - 1; } +s32 CGUIContextMenu::findItemWithCommandId(s32 commandId, u32 idxStartSearch) +{ + for ( u32 i=idxStartSearch; isetVisible(false); if (Items[index].SubMenu) + { menu->grab(); + menu->AllowFocus = false; + if ( Environment->getFocus() == menu ) + { + Environment->setFocus( this ); + } + } recalculateSize(); } @@ -100,7 +120,7 @@ void CGUIContextMenu::setSubMenu(s32 ind //! Adds a separator item to the menu void CGUIContextMenu::addSeparator() { - addItem(0, -1, true, false, false); + addItem(0, -1, true, false, false, false); } @@ -125,6 +145,41 @@ void CGUIContextMenu::setItemText(s32 id } +void CGUIContextMenu::setItemAutoChecking(s32 idx, bool autoChecking) +{ + if (idx < 0 || idx >= (s32)Items.size()) + return; + + Items[idx].AutoChecking = autoChecking; +} + +bool CGUIContextMenu::getItemAutoChecking(s32 idx) +{ + if (idx < 0 || idx >= (s32)Items.size()) + return false; + + return Items[idx].AutoChecking; +} + +//! Starsonata, MICHA: textID for stringtables +const wchar_t* CGUIContextMenu::getItemTextID(s32 idx) +{ + if (idx < 0 || idx >= (s32)Items.size()) + return 0; + + return Items[idx].TextID.c_str(); +} + +//! Starsonata, MICHA: textID for stringtables +void CGUIContextMenu::setItemTextID(s32 idx, const wchar_t* text) +{ + if (idx < 0 || idx >= (s32)Items.size()) + return; + + Items[idx].TextID = text; +} + + //! Returns if a menu item is enabled bool CGUIContextMenu::isItemEnabled(s32 idx) { @@ -199,12 +254,23 @@ void CGUIContextMenu::removeAllItems() recalculateSize(); } +void CGUIContextMenu::popupAt(irr::s32 x_, irr::s32 y_) +{ + core::position2d p(x_, y_); + move( p - getAbsolutePosition().UpperLeftCorner ); + setVisible(true); + bringToTop(); + Environment->setFocus(this); + IGUIElement * root = Environment->getRootGUIElement(); + if ( root ) + moveInside( root->getAbsolutePosition(), EGUIA_UPPERLEFT); +} //! called if an event happened. bool CGUIContextMenu::OnEvent(SEvent event) { - if (!IsEnabled) - return Parent ? Parent->OnEvent(event) : false; + if (!IsEnabled || !isVisible() ) + return IGUIElement::OnEvent(event); switch(event.EventType) { @@ -212,11 +278,23 @@ bool CGUIContextMenu::OnEvent(SEvent eve switch(event.GUIEvent.EventType) { case EGET_ELEMENT_FOCUS_LOST: - if (event.GUIEvent.Caller == this && !isMyChild(event.GUIEvent.Element)) + if (event.GUIEvent.Caller == this && !isMyChild(event.GUIEvent.Element) && AllowFocus) { // set event parent of submenus - setEventParent(Parent); + setEventParent(EventParent ? EventParent : Parent); + + if ( CloseHandling & ECMC_HIDE ) + { + setVisible(false); + } + if ( CloseHandling & ECMC_DROP ) + { + drop(); + } + if ( CloseHandling & ECMC_REMOVE ) + { remove(); + } return false; } break; @@ -225,6 +303,9 @@ bool CGUIContextMenu::OnEvent(SEvent eve { return true; } + break; + default: + break; } break; case EET_MOUSE_INPUT_EVENT: @@ -241,16 +322,17 @@ bool CGUIContextMenu::OnEvent(SEvent eve } return true; case EMIE_LMOUSE_PRESSED_DOWN: + // to prevent losing focus return true; case EMIE_MOUSE_MOVED: if (Environment->hasFocus(this)) - highlight(core::position2d(event.MouseInput.X, event.MouseInput.Y)); - return true; + highlight(core::position2d(event.MouseInput.X, event.MouseInput.Y), true); + return false; } break; } - return Parent ? Parent->OnEvent(event) : false; + return IGUIElement::OnEvent(event); } @@ -302,16 +384,20 @@ s32 CGUIContextMenu::sendClick(core::pos Items[HighLighted].SubMenu) return 2; + if ( Items[HighLighted].AutoChecking ) + { + Items[HighLighted].Checked = Items[HighLighted].Checked ? false : true; + } + SEvent event; event.EventType = EET_GUI_EVENT; event.GUIEvent.Caller = this; event.GUIEvent.Element = 0; event.GUIEvent.EventType = EGET_MENU_ITEM_SELECTED; - if (Parent) - Parent->OnEvent(event); - else if (EventParent) EventParent->OnEvent(event); + else if (Parent) + Parent->OnEvent(event); return 1; } @@ -321,7 +407,7 @@ s32 CGUIContextMenu::sendClick(core::pos //! returns true, if an element was highligted -bool CGUIContextMenu::highlight(core::position2d p) +bool CGUIContextMenu::highlight(core::position2d p, bool canOpenSubMenu) { // get number of open submenu s32 openmenu = -1; @@ -336,7 +422,7 @@ bool CGUIContextMenu::highlight(core::po // delegate highlight operation to submenu if (openmenu != -1) { - if (Items[openmenu].SubMenu->highlight(p)) + if (Items[openmenu].SubMenu->highlight(p, canOpenSubMenu)) { HighLighted = openmenu; ChangeTime = os::Timer::getTime(); @@ -354,7 +440,12 @@ bool CGUIContextMenu::highlight(core::po // make submenus visible/invisible for (s32 j=0; j<(s32)Items.size(); ++j) if (Items[j].SubMenu) - Items[j].SubMenu->setVisible(j == i); + { + if ( j == i && canOpenSubMenu ) + Items[j].SubMenu->setVisible(true); + else if ( j != i ) + Items[j].SubMenu->setVisible(false); + } return true; } @@ -474,7 +565,8 @@ void CGUIContextMenu::draw() if (Items[i].Checked && sprites) { core::rect r = rect; - r.UpperLeftCorner.X -= 15; + r.LowerRightCorner.X = r.UpperLeftCorner.X - 15; + r.UpperLeftCorner.X = r.LowerRightCorner.X + 15; sprites->draw2DSprite(skin->getIcon(EGDI_CHECK_BOX_CHECKED), r.getCenter(), clip, skin->getColor(c), (i == HighLighted) ? ChangeTime : 0, @@ -599,6 +691,8 @@ void CGUIContextMenu::serializeAttribute out->addInt("ParentItem", i); } + out->addInt("CloseHandling", (s32)CloseHandling); + // write out the item list out->addInt("ItemCount", Items.size()); @@ -614,10 +708,23 @@ void CGUIContextMenu::serializeAttribute { tmp = "Text"; tmp += i; out->addString(tmp.c_str(), Items[i].Text.c_str()); + + // Starsonata, MICHA: textID for stringtables + tmp = "TextID"; tmp += i; + out->addString(tmp.c_str(), Items[i].TextID.c_str()); + tmp = "CommandID"; tmp += i; out->addInt(tmp.c_str(), Items[i].CommandId); tmp = "Enabled"; tmp += i; out->addBool(tmp.c_str(), Items[i].Enabled); + + // Starsonata, MICHA: was not serialized + tmp = "Checked"; tmp += i; + out->addBool(tmp.c_str(), Items[i].Checked); + + // Starsonata, MICHA: menus can set the checkflag themself + tmp = "AutoChecking"; tmp += i; + out->addBool(tmp.c_str(), Items[i].AutoChecking); } } } @@ -639,36 +746,58 @@ void CGUIContextMenu::deserializeAttribu // read the item list s32 count = in->getAttributeAsInt("ItemCount"); + // starsonata, micha: + // Added some existsAttribute checks to allow for incomplete xml's as the usual default mechanism + // does not work for sub-elements. So defaults are hardcoded, but still better than having to type them + // so often in the xml's. + s32 i=0; for (; i<(s32)count; ++i) { core::stringc tmp; core::stringw txt; - s32 commandid; - bool enabled; - bool checked; + core::stringw txtID; + s32 commandid=-1; + bool enabled=true; + bool checked=false; + bool autochecking=false; tmp = "IsSeparator"; tmp += i; - if ( in->getAttributeAsBool(tmp.c_str()) ) + if ( in->existsAttribute(tmp.c_str()) && in->getAttributeAsBool(tmp.c_str()) ) addSeparator(); else { tmp = "Text"; tmp += i; + if ( in->existsAttribute(tmp.c_str()) ) txt = in->getAttributeAsStringW(tmp.c_str()); + // Starsonata, MICHA: textID for stringtables + tmp = "TextID"; tmp += i; + if ( in->existsAttribute(tmp.c_str()) ) + txtID = in->getAttributeAsStringW(tmp.c_str()); + tmp = "CommandID"; tmp += i; + if ( in->existsAttribute(tmp.c_str()) ) commandid = in->getAttributeAsInt(tmp.c_str()); tmp = "Enabled"; tmp += i; + if ( in->existsAttribute(tmp.c_str()) ) enabled = in->getAttributeAsBool(tmp.c_str()); tmp = "Checked"; tmp += i; checked = in->getAttributeAsBool(tmp.c_str()); - addItem(core::stringw(txt.c_str()).c_str(), commandid, enabled, false, checked); + // Starsonata, MICHA: menus can set the checkflag themself + tmp = "AutoChecking"; tmp += i; + if ( in->existsAttribute(tmp.c_str()) ) + autochecking = in->getAttributeAsBool(tmp.c_str()); + + s32 id = addItem(core::stringw(txt.c_str()).c_str(), commandid, enabled, false, checked, autochecking); + Items[id].TextID = txtID; } } + CloseHandling = (ECONTEXT_MENU_CLOSE)in->getAttributeAsInt("CloseHandling"); recalculateSize(); @@ -686,6 +815,35 @@ void CGUIContextMenu::setEventParent(IGU } } +bool CGUIContextMenu::hasOpenSubMenu() +{ + for (s32 i=0; i<(s32)Items.size(); ++i) + if (Items[i].SubMenu) + if ( Items[i].SubMenu->isVisible() ) + return true; + return false; +} + +void CGUIContextMenu::closeAllSubMenus() +{ + for (s32 i=0; i<(s32)Items.size(); ++i) + if (Items[i].SubMenu) + Items[i].SubMenu->setVisible(false); + + //HighLighted = -1; +} + +void CGUIContextMenu::setCloseHandling(ECONTEXT_MENU_CLOSE onClose) +{ + CloseHandling = onClose; +} + +ECONTEXT_MENU_CLOSE CGUIContextMenu::getCloseHandling() const +{ + return CloseHandling; +} + + } // end namespace } // end namespace diff -abBdpuNPr --exclude='*.svn' irrlicht-svn-ss/trunk/source/Irrlicht/CGUIContextMenu.h Irrlicht_starsonata/source/Irrlicht/CGUIContextMenu.h --- irrlicht-svn-ss/trunk/source/Irrlicht/CGUIContextMenu.h 2007-07-26 02:11:08.000000000 +0200 +++ Irrlicht_starsonata/source/Irrlicht/CGUIContextMenu.h 2008-06-06 07:41:28.000000000 +0200 @@ -33,7 +33,7 @@ namespace gui //! Adds a menu item. virtual s32 addItem(const wchar_t* text, s32 commandid, bool enabled, bool hasSubMenu, - bool checked); + bool checked, bool autoChecking); //! Adds a separator item to the menu virtual void addSeparator(); @@ -44,6 +44,10 @@ namespace gui //! Sets text of the menu item. virtual void setItemText(s32 idx, const wchar_t* text); + //! Starsonata, MICHA: textID for stringtables + virtual const wchar_t* getItemTextID(s32 idx); + virtual void setItemTextID(s32 idx, const wchar_t* text); + //! Returns if a menu item is enabled virtual bool isItemEnabled(s32 idx); @@ -84,6 +88,11 @@ namespace gui //! Sets the command id of a menu item virtual void setItemCommandId(s32 idx, s32 id); + // starsonata, micha - a useful comfort function + //! Find a item which has the given CommandId starting from given index (or idxStartSearch=0 for searching from start) + //! return -1 if no such item was found + virtual s32 findItemWithCommandId(s32 commandId, u32 idxStartSearch); + //! Adds a sub menu from an element that already exists. virtual void setSubMenu(s32 index, CGUIContextMenu* menu); @@ -93,14 +102,38 @@ namespace gui //! Reads attributes of the element virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options); + //! set behaviour when menus are closed + virtual void setCloseHandling(ECONTEXT_MENU_CLOSE onClose); + + //! get current behaviour when the menue will be closed + virtual ECONTEXT_MENU_CLOSE getCloseHandling() const; + + //! should the element change the checked status on clicking + virtual void setItemAutoChecking(s32 idx, bool autoChecking); + + //! does the element change the checked status on clicking + virtual bool getItemAutoChecking(s32 idx); + + //! Make sure to show it at a good place around the given coordinates. + //! A comfort function which can for example be used after right-clicks with the mouse coordinates. + virtual void popupAt(irr::s32 x_, irr::s32 y_); + + // Starsonata, Micha, this was protected formerly, but it's rather useful as general functions for popups + virtual void setEventParent(IGUIElement *parent); + protected: + void closeAllSubMenus(); + bool hasOpenSubMenu(); + struct SItem { core::stringw Text; + core::stringw TextID; // Starsonata, MICHA: textID for stringtables bool IsSeparator; bool Enabled; bool Checked; + bool AutoChecking; core::dimension2d Dim; s32 PosY; CGUIContextMenu* SubMenu; @@ -109,8 +142,8 @@ namespace gui virtual void recalculateSize(); - //! returns true, if an element was highligted - virtual bool highlight(core::position2d p); + //! returns true, if an element was highlighted + virtual bool highlight(core::position2d p, bool canOpenSubMenu); //! sends a click Returns: //! 0 if click went outside of the element, @@ -124,14 +157,13 @@ namespace gui //! Gets drawing rect of Item virtual core::rect getRect(const SItem& i, const core::rect& absolute); - void setEventParent(IGUIElement *parent); - s32 HighLighted; core::array Items; core::position2d Pos; u32 ChangeTime; IGUIElement* EventParent; bool AllowFocus; + ECONTEXT_MENU_CLOSE CloseHandling; }; } // end namespace gui diff -abBdpuNPr --exclude='*.svn' irrlicht-svn-ss/trunk/source/Irrlicht/CGUIMenu.cpp Irrlicht_starsonata/source/Irrlicht/CGUIMenu.cpp --- irrlicht-svn-ss/trunk/source/Irrlicht/CGUIMenu.cpp 2007-07-26 02:11:08.000000000 +0200 +++ Irrlicht_starsonata/source/Irrlicht/CGUIMenu.cpp 2008-03-19 17:47:42.000000000 +0100 @@ -91,7 +91,7 @@ void CGUIMenu::draw() bool CGUIMenu::OnEvent(SEvent event) { if (!IsEnabled) - return Parent ? Parent->OnEvent(event) : false; + return IGUIElement::OnEvent(event); switch(event.EventType) { @@ -100,52 +100,55 @@ bool CGUIMenu::OnEvent(SEvent event) { case gui::EGET_ELEMENT_FOCUS_LOST: if (event.GUIEvent.Caller == this && !isMyChild(event.GUIEvent.Element)) + { closeAllSubMenus(); + HighLighted = -1; + } break; case gui::EGET_ELEMENT_FOCUSED: if (event.GUIEvent.Caller == this && Parent) + { Parent->bringToFront(this); - + } + break; } break; case EET_MOUSE_INPUT_EVENT: switch(event.MouseInput.Event) { - case EMIE_LMOUSE_LEFT_UP: + case EMIE_LMOUSE_PRESSED_DOWN: { - core::position2d p(event.MouseInput.X, event.MouseInput.Y); - if (AbsoluteClippingRect.isPointInside(p)) + if (!Environment->hasFocus(this)) { - if (HighLighted != -1) - Environment->removeFocus(this); - else - highlight(core::position2d(event.MouseInput.X, event.MouseInput.Y)); + Environment->setFocus(this); } - else + if (Parent) + Parent->bringToFront(this); + + core::position2d p(event.MouseInput.X, event.MouseInput.Y); + bool shouldCloseSubMenu = hasOpenSubMenu(); + if (!AbsoluteClippingRect.isPointInside(p)) { + shouldCloseSubMenu = false; s32 t = sendClick(p); if ((t==0 || t==1) && Environment->hasFocus(this)) Environment->removeFocus(this); } - } + highlight(core::position2d(event.MouseInput.X, event.MouseInput.Y), true); + if ( shouldCloseSubMenu ) + closeAllSubMenus(); + return true; - case EMIE_LMOUSE_PRESSED_DOWN: - if (!Environment->hasFocus(this)) - { - Environment->setFocus(this); - if (Parent) - Parent->bringToFront(this); } - return true; case EMIE_MOUSE_MOVED: if (Environment->hasFocus(this)) - highlight(core::position2d(event.MouseInput.X, event.MouseInput.Y)); - return true; + highlight(core::position2d(event.MouseInput.X, event.MouseInput.Y), hasOpenSubMenu()); + return false; } break; } - return Parent ? Parent->OnEvent(event) : false; + return IGUIElement::OnEvent(event); } @@ -227,17 +230,6 @@ core::rect CGUIMenu::getRect(const return getHRect(i, absolute); } - -void CGUIMenu::closeAllSubMenus() -{ - for (s32 i=0; i<(s32)Items.size(); ++i) - if (Items[i].SubMenu) - Items[i].SubMenu->setVisible(false); - - HighLighted = -1; -} - - void CGUIMenu::updateAbsolutePosition() { if (Parent) diff -abBdpuNPr --exclude='*.svn' irrlicht-svn-ss/trunk/source/Irrlicht/CGUIMenu.h Irrlicht_starsonata/source/Irrlicht/CGUIMenu.h --- irrlicht-svn-ss/trunk/source/Irrlicht/CGUIMenu.h 2007-07-26 02:11:08.000000000 +0200 +++ Irrlicht_starsonata/source/Irrlicht/CGUIMenu.h 2007-10-22 18:56:54.000000000 +0200 @@ -41,9 +41,6 @@ namespace gui //! Gets drawing rect of Item virtual core::rect getRect(const SItem& i, const core::rect& absolute); - - void closeAllSubMenus(); - }; } // end namespace gui diff -abBdpuNPr --exclude='*.svn' irrlicht-svn-ss/trunk/source/Irrlicht/CGUIMessageBox.cpp Irrlicht_starsonata/source/Irrlicht/CGUIMessageBox.cpp --- irrlicht-svn-ss/trunk/source/Irrlicht/CGUIMessageBox.cpp 2007-07-26 02:11:08.000000000 +0200 +++ Irrlicht_starsonata/source/Irrlicht/CGUIMessageBox.cpp 2008-02-26 04:27:40.000000000 +0100 @@ -7,6 +7,8 @@ #include "IGUIEnvironment.h" #include "IGUIButton.h" #include "IGUIFont.h" +#include "IGUIImage.h" +#include "ITexture.h" namespace irr { @@ -15,11 +17,13 @@ namespace gui //! constructor CGUIMessageBox::CGUIMessageBox(IGUIEnvironment* environment, const wchar_t* caption, - const wchar_t* text, s32 flags, - IGUIElement* parent, s32 id, core::rect rectangle) -: CGUIWindow(environment, parent, id, rectangle), + const wchar_t* text, s32 boxflags, + IGUIElement* parent, s32 id, core::rect rectangle, + video::ITexture* image, s32 windowflags) +: CGUIWindow(environment, parent, id, rectangle, windowflags), OkButton(0), CancelButton(0), YesButton(0), NoButton(0), StaticText(0), - Flags(flags), MessageText(text), Pressed(false) + BoxFlags(boxflags), MessageText(text), Pressed(false) + , Icon(0), IconTexture(image) { #ifdef _DEBUG setDebugName("CGUIMessageBox"); @@ -41,76 +45,136 @@ CGUIMessageBox::CGUIMessageBox(IGUIEnvir Environment->setFocus(this); + if ( IconTexture ) + IconTexture->grab(); + refreshControls(); } void CGUIMessageBox::refreshControls() { + // Layout can be seen as 4 boxes (a layoutmanager would be nice...) + // One box at top over the whole width for title + // Two boxes with same height at the middle beside each other for icon and for text + // One box at the bottom for the buttons + IGUISkin* skin = Environment->getSkin(); IGUIElement* focusMe = 0; s32 buttonHeight = skin->getSize(EGDS_BUTTON_HEIGHT); s32 buttonWidth = skin->getSize(EGDS_BUTTON_WIDTH); - s32 titleHeight = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH)+2; + s32 titleHeight = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH)+2; //MICHA: it seems the titlebar has no own constant yet. Rather strange... s32 buttonDistance = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH); + s32 borderWidth = buttonDistance; // MICHA: Clear Borders at sides and between icon-text and between content-buttons A constant in the skin for that would be nicer. - // add static multiline text - - core::dimension2d dim(AbsoluteClippingRect.getWidth() - buttonWidth, - AbsoluteClippingRect.getHeight() - (buttonHeight * 3)); - core::position2d pos((AbsoluteClippingRect.getWidth() - dim.Width) / 2, - buttonHeight / 2 + titleHeight); - + // add the static text first using the maximum possible size + // We need to do so, because it's at the moment the only way to find out the size which text will need. + // A common class or function (outside statictext) to calculate textsplitting + needed textarea should be done someday if (!StaticText) { - StaticText = Environment->addStaticText(MessageText.c_str(), - core::rect(pos, dim), false, false, this); + core::rect staticRect; + staticRect.UpperLeftCorner.X = borderWidth; + staticRect.UpperLeftCorner.Y = titleHeight + borderWidth; + staticRect.LowerRightCorner.X = getRelativePosition().getWidth() - borderWidth; + staticRect.LowerRightCorner.Y = getRelativePosition().getHeight() - borderWidth; + + StaticText = Environment->addStaticText(MessageText.c_str(), staticRect, false, false, this); StaticText->setWordWrap(true); StaticText->setSubElement(true); StaticText->grab(); } else { - StaticText->setRelativePosition( core::rect(pos, dim)); + StaticText->setRelativePosition( AbsoluteClippingRect ); StaticText->setText(MessageText.c_str()); } - // adjust static text height - s32 textHeight = StaticText->getTextHeight(); - core::rect tmp = StaticText->getRelativePosition(); - tmp.LowerRightCorner.Y = tmp.UpperLeftCorner.Y + textHeight; - StaticText->setRelativePosition(tmp); - dim.Height = textHeight; + s32 textWidth = StaticText->getTextWidth() + 6; // +6 because the static itself needs that + s32 iconHeight = IconTexture ? IconTexture->getOriginalSize().Height : 0; - // adjust message box height + // content is text + icons + borders (but not titlebar) + s32 contentHeight = textHeight > iconHeight ? textHeight : iconHeight; + contentHeight += borderWidth; + s32 contentWidth = 0; - tmp = getRelativePosition(); - s32 msgBoxHeight = textHeight + (s32)(2.5f * buttonHeight) + titleHeight; + // add icon + if ( IconTexture ) + { + core::position2d iconPos; + iconPos.Y = titleHeight + borderWidth; + if ( iconHeight < textHeight ) + iconPos.Y += (textHeight-iconHeight) / 2; + iconPos.X = borderWidth; - // adjust message box position + if (!Icon) + { + Icon = Environment->addImage(IconTexture, iconPos, true, this); + Icon->setSubElement(true); + Icon->grab(); + } + else + { + core::rect iconRect( iconPos.X, iconPos.Y, iconPos.X + IconTexture->getOriginalSize().Width, iconPos.Y + IconTexture->getOriginalSize().Height ); + Icon->setRelativePosition(iconRect); + } + + contentWidth += borderWidth + IconTexture->getOriginalSize().Width; + } + else if ( Icon ) + { + Icon->drop(); + Icon->remove(); + Icon = 0; + } + // position text + core::rect textRect; + textRect.UpperLeftCorner.X = contentWidth + borderWidth; + textRect.UpperLeftCorner.Y = titleHeight + borderWidth; + if ( textHeight < iconHeight ) + textRect.UpperLeftCorner.Y += (iconHeight-textHeight) / 2; + textRect.LowerRightCorner.X = textRect.UpperLeftCorner.X + textWidth; + textRect.LowerRightCorner.Y = textRect.UpperLeftCorner.Y + textHeight; + contentWidth += 2*borderWidth + textWidth; + StaticText->setRelativePosition( textRect ); + + // find out button size needs + s32 countButtons = 0; + if (BoxFlags & EMBF_OK) ++countButtons; + if (BoxFlags & EMBF_CANCEL) ++countButtons; + if (BoxFlags & EMBF_YES) ++countButtons; + if (BoxFlags & EMBF_NO) ++countButtons; + s32 buttonBoxWidth = countButtons * buttonWidth + 2 * borderWidth; + if ( countButtons > 1 ) + buttonBoxWidth += (countButtons-1)*buttonDistance; + s32 buttonBoxHeight = buttonHeight + 2 * borderWidth; + + + // calc new message box sizes + core::rect tmp = getRelativePosition(); + s32 msgBoxHeight = titleHeight + contentHeight + buttonBoxHeight; + s32 msgBoxWidth = contentWidth > buttonBoxWidth ? contentWidth : buttonBoxWidth; + + // adjust message box position tmp.UpperLeftCorner.Y = (Parent->getAbsolutePosition().getHeight() - msgBoxHeight) / 2; tmp.LowerRightCorner.Y = tmp.UpperLeftCorner.Y + msgBoxHeight; + tmp.UpperLeftCorner.X = (Parent->getAbsolutePosition().getWidth() - msgBoxWidth) / 2; + tmp.LowerRightCorner.X = tmp.UpperLeftCorner.X + msgBoxWidth; setRelativePosition(tmp); // add buttons - s32 countButtons = 0; - if (Flags & EMBF_OK) ++countButtons; - if (Flags & EMBF_CANCEL) ++countButtons; - if (Flags & EMBF_YES) ++countButtons; - if (Flags & EMBF_NO) ++countButtons; - core::rect btnRect; - btnRect.UpperLeftCorner.Y = pos.Y + dim.Height + buttonHeight / 2; + btnRect.UpperLeftCorner.Y = titleHeight + contentHeight + borderWidth; btnRect.LowerRightCorner.Y = btnRect.UpperLeftCorner.Y + buttonHeight; - btnRect.UpperLeftCorner.X = (AbsoluteClippingRect.getWidth() - - ((buttonWidth + buttonDistance)*countButtons)) / 2; + btnRect.UpperLeftCorner.X = borderWidth; + if ( contentWidth > buttonBoxWidth ) + btnRect.UpperLeftCorner.X += (contentWidth - buttonBoxWidth) / 2; // center buttons btnRect.LowerRightCorner.X = btnRect.UpperLeftCorner.X + buttonWidth; // add/remove ok button - if (Flags & EMBF_OK) + if (BoxFlags & EMBF_OK) { if (!OkButton) { @@ -136,7 +200,7 @@ void CGUIMessageBox::refreshControls() } // add cancel button - if (Flags & EMBF_CANCEL) + if (BoxFlags & EMBF_CANCEL) { if (!CancelButton) { @@ -148,7 +212,6 @@ void CGUIMessageBox::refreshControls() CancelButton->setRelativePosition(btnRect); CancelButton->setText(skin->getDefaultText(EGDT_MSG_BOX_CANCEL)); - CancelButton->grab(); btnRect.LowerRightCorner.X += buttonWidth + buttonDistance; btnRect.UpperLeftCorner.X += buttonWidth + buttonDistance; @@ -166,7 +229,7 @@ void CGUIMessageBox::refreshControls() // add/remove yes button - if (Flags & EMBF_YES) + if (BoxFlags & EMBF_YES) { if (!YesButton) { @@ -193,7 +256,7 @@ void CGUIMessageBox::refreshControls() } // add no button - if (Flags & EMBF_NO) + if (BoxFlags & EMBF_NO) { if (!NoButton) { @@ -242,6 +305,12 @@ CGUIMessageBox::~CGUIMessageBox() if (NoButton) NoButton->drop(); + + if (Icon) + Icon->drop(); + + if ( IconTexture ) + IconTexture->drop(); } @@ -389,10 +458,11 @@ void CGUIMessageBox::serializeAttributes { CGUIWindow::serializeAttributes(out,options); - out->addBool ("OkayButton", (Flags & EMBF_OK) != 0 ); - out->addBool ("CancelButton", (Flags & EMBF_CANCEL)!= 0 ); - out->addBool ("YesButton", (Flags & EMBF_YES) != 0 ); - out->addBool ("NoButton", (Flags & EMBF_NO) != 0 ); + out->addBool ("OkayButton", (BoxFlags & EMBF_OK) != 0 ); + out->addBool ("CancelButton", (BoxFlags & EMBF_CANCEL)!= 0 ); + out->addBool ("YesButton", (BoxFlags & EMBF_YES) != 0 ); + out->addBool ("NoButton", (BoxFlags & EMBF_NO) != 0 ); + out->addTexture ("Texture", IconTexture); out->addString ("MessageText", MessageText.c_str() ); } @@ -400,12 +470,21 @@ void CGUIMessageBox::serializeAttributes //! Reads attributes of the element void CGUIMessageBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) { - Flags = 0; + BoxFlags = 0; - Flags = in->getAttributeAsBool("OkayButton") ? EMBF_OK : 0; - Flags |= in->getAttributeAsBool("CancelButton")? EMBF_CANCEL : 0; - Flags |= in->getAttributeAsBool("YesButton") ? EMBF_YES : 0; - Flags |= in->getAttributeAsBool("NoButton") ? EMBF_NO : 0; + BoxFlags = in->getAttributeAsBool("OkayButton") ? EMBF_OK : 0; + BoxFlags |= in->getAttributeAsBool("CancelButton")? EMBF_CANCEL : 0; + BoxFlags |= in->getAttributeAsBool("YesButton") ? EMBF_YES : 0; + BoxFlags |= in->getAttributeAsBool("NoButton") ? EMBF_NO : 0; + + if ( IconTexture ) + { + IconTexture->drop(); + IconTexture = NULL; + } + IconTexture = in->getAttributeAsTexture("Texture"); + if ( IconTexture ) + IconTexture->grab(); MessageText = in->getAttributeAsStringW("MessageText").c_str();