2019-12-12 17:32:41 +01:00
|
|
|
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
|
|
|
// This file is part of the "Irrlicht Engine".
|
|
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
|
|
|
|
#include "CGUIContextMenu.h"
|
|
|
|
|
|
|
|
#ifdef _IRR_COMPILE_WITH_GUI_
|
|
|
|
|
|
|
|
#include "IGUISkin.h"
|
|
|
|
#include "IGUIEnvironment.h"
|
|
|
|
#include "IVideoDriver.h"
|
|
|
|
#include "IGUIFont.h"
|
|
|
|
#include "IGUISpriteBank.h"
|
|
|
|
#include "os.h"
|
|
|
|
|
|
|
|
namespace irr
|
|
|
|
{
|
|
|
|
namespace gui
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
//! constructor
|
|
|
|
CGUIContextMenu::CGUIContextMenu(IGUIEnvironment* environment,
|
|
|
|
IGUIElement* parent, s32 id,
|
|
|
|
core::rect<s32> rectangle, bool getFocus, bool allowFocus)
|
|
|
|
: IGUIContextMenu(environment, parent, id, rectangle), EventParent(0), LastFont(0),
|
|
|
|
CloseHandling(ECMC_REMOVE), HighLighted(-1), ChangeTime(0), AllowFocus(allowFocus)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
|
|
|
setDebugName("CGUIContextMenu");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Pos = rectangle.UpperLeftCorner;
|
|
|
|
recalculateSize();
|
|
|
|
|
|
|
|
if (getFocus)
|
|
|
|
Environment->setFocus(this);
|
|
|
|
|
|
|
|
setNotClipped(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! destructor
|
|
|
|
CGUIContextMenu::~CGUIContextMenu()
|
|
|
|
{
|
|
|
|
for (u32 i=0; i<Items.size(); ++i)
|
|
|
|
if (Items[i].SubMenu)
|
|
|
|
Items[i].SubMenu->drop();
|
|
|
|
|
|
|
|
if (LastFont)
|
|
|
|
LastFont->drop();
|
|
|
|
}
|
|
|
|
|
|
|
|
//! set behavior when menus are closed
|
|
|
|
void CGUIContextMenu::setCloseHandling(ECONTEXT_MENU_CLOSE onClose)
|
|
|
|
{
|
|
|
|
CloseHandling = onClose;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! get current behavior when the menue will be closed
|
|
|
|
ECONTEXT_MENU_CLOSE CGUIContextMenu::getCloseHandling() const
|
|
|
|
{
|
|
|
|
return CloseHandling;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns amount of menu items
|
|
|
|
u32 CGUIContextMenu::getItemCount() const
|
|
|
|
{
|
|
|
|
return Items.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Adds a menu item.
|
|
|
|
u32 CGUIContextMenu::addItem(const wchar_t* text, s32 commandId, bool enabled, bool hasSubMenu, bool checked, bool autoChecking)
|
|
|
|
{
|
|
|
|
return insertItem(Items.size(), text, commandId, enabled, hasSubMenu, checked, autoChecking);
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Insert a menu item at specified position.
|
|
|
|
u32 CGUIContextMenu::insertItem(u32 idx, const wchar_t* text, s32 commandId, 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;
|
|
|
|
s.CommandId = commandId;
|
2020-02-22 23:01:57 +01:00
|
|
|
s.PosY = 0;
|
2019-12-12 17:32:41 +01:00
|
|
|
|
|
|
|
if (hasSubMenu)
|
|
|
|
{
|
|
|
|
s.SubMenu = new CGUIContextMenu(Environment, this, commandId,
|
|
|
|
core::rect<s32>(0,0,100,100), false, false);
|
|
|
|
s.SubMenu->setVisible(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 result = idx;
|
|
|
|
if ( idx < Items.size() )
|
|
|
|
{
|
|
|
|
Items.insert(s, idx);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Items.push_back(s);
|
|
|
|
result = Items.size() - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
recalculateSize();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 CGUIContextMenu::findItemWithCommandId(s32 commandId, u32 idxStartSearch) const
|
|
|
|
{
|
|
|
|
for ( u32 i=idxStartSearch; i<Items.size(); ++i )
|
|
|
|
{
|
|
|
|
if ( Items[i].CommandId == commandId )
|
|
|
|
{
|
|
|
|
return (s32)i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Adds a sub menu from an element that already exists.
|
|
|
|
void CGUIContextMenu::setSubMenu(u32 index, CGUIContextMenu* menu)
|
|
|
|
{
|
|
|
|
if (index >= Items.size())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (menu)
|
|
|
|
menu->grab();
|
|
|
|
if (Items[index].SubMenu)
|
|
|
|
Items[index].SubMenu->drop();
|
|
|
|
|
|
|
|
Items[index].SubMenu = menu;
|
|
|
|
|
2020-11-14 18:09:39 +01:00
|
|
|
if (menu)
|
2019-12-12 17:32:41 +01:00
|
|
|
{
|
2020-11-14 18:09:39 +01:00
|
|
|
menu->setVisible(false);
|
2019-12-12 17:32:41 +01:00
|
|
|
menu->AllowFocus = false;
|
|
|
|
if ( Environment->getFocus() == menu )
|
|
|
|
{
|
|
|
|
Environment->setFocus( this );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
recalculateSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Adds a separator item to the menu
|
|
|
|
void CGUIContextMenu::addSeparator()
|
|
|
|
{
|
|
|
|
addItem(0, -1, true, false, false, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Returns text of the menu item.
|
|
|
|
const wchar_t* CGUIContextMenu::getItemText(u32 idx) const
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return Items[idx].Text.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Sets text of the menu item.
|
|
|
|
void CGUIContextMenu::setItemText(u32 idx, const wchar_t* text)
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
return;
|
|
|
|
|
|
|
|
Items[idx].Text = text;
|
|
|
|
recalculateSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
//! should the element change the checked status on clicking
|
|
|
|
void CGUIContextMenu::setItemAutoChecking(u32 idx, bool autoChecking)
|
|
|
|
{
|
|
|
|
if ( idx >= Items.size())
|
|
|
|
return;
|
|
|
|
|
|
|
|
Items[idx].AutoChecking = autoChecking;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! does the element change the checked status on clicking
|
|
|
|
bool CGUIContextMenu::getItemAutoChecking(u32 idx) const
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return Items[idx].AutoChecking;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Returns if a menu item is enabled
|
|
|
|
bool CGUIContextMenu::isItemEnabled(u32 idx) const
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Items[idx].Enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Returns if a menu item is checked
|
|
|
|
bool CGUIContextMenu::isItemChecked(u32 idx) const
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Items[idx].Checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Sets if the menu item should be enabled.
|
|
|
|
void CGUIContextMenu::setItemEnabled(u32 idx, bool enabled)
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
return;
|
|
|
|
|
|
|
|
Items[idx].Enabled = enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Sets if the menu item should be checked.
|
|
|
|
void CGUIContextMenu::setItemChecked(u32 idx, bool checked )
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
return;
|
|
|
|
|
|
|
|
Items[idx].Checked = checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Removes a menu item
|
|
|
|
void CGUIContextMenu::removeItem(u32 idx)
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (Items[idx].SubMenu)
|
|
|
|
{
|
|
|
|
Items[idx].SubMenu->drop();
|
|
|
|
Items[idx].SubMenu = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Items.erase(idx);
|
|
|
|
recalculateSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Removes all menu items
|
|
|
|
void CGUIContextMenu::removeAllItems()
|
|
|
|
{
|
|
|
|
for (u32 i=0; i<Items.size(); ++i)
|
|
|
|
if (Items[i].SubMenu)
|
|
|
|
Items[i].SubMenu->drop();
|
|
|
|
|
|
|
|
Items.clear();
|
|
|
|
recalculateSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! called if an event happened.
|
|
|
|
bool CGUIContextMenu::OnEvent(const SEvent& event)
|
|
|
|
{
|
|
|
|
if (isEnabled())
|
|
|
|
{
|
|
|
|
|
|
|
|
switch(event.EventType)
|
|
|
|
{
|
|
|
|
case EET_GUI_EVENT:
|
|
|
|
switch(event.GUIEvent.EventType)
|
|
|
|
{
|
|
|
|
case EGET_ELEMENT_FOCUS_LOST:
|
|
|
|
if (event.GUIEvent.Caller == this && !isMyChild(event.GUIEvent.Element) && AllowFocus)
|
|
|
|
{
|
|
|
|
// set event parent of submenus
|
|
|
|
IGUIElement * p = EventParent ? EventParent : Parent;
|
|
|
|
if ( p ) // can be 0 when element got removed already
|
|
|
|
{
|
|
|
|
setEventParent(p);
|
|
|
|
|
|
|
|
SEvent eventClose;
|
|
|
|
eventClose.EventType = EET_GUI_EVENT;
|
|
|
|
eventClose.GUIEvent.Caller = this;
|
|
|
|
eventClose.GUIEvent.Element = 0;
|
|
|
|
eventClose.GUIEvent.EventType = EGET_ELEMENT_CLOSED;
|
|
|
|
if ( !p->OnEvent(eventClose) )
|
|
|
|
{
|
|
|
|
if ( CloseHandling & ECMC_HIDE )
|
|
|
|
{
|
|
|
|
setVisible(false);
|
|
|
|
}
|
|
|
|
if ( CloseHandling & ECMC_REMOVE )
|
|
|
|
{
|
|
|
|
remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EGET_ELEMENT_FOCUSED:
|
|
|
|
if (event.GUIEvent.Caller == this && !AllowFocus)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EET_MOUSE_INPUT_EVENT:
|
|
|
|
switch(event.MouseInput.Event)
|
|
|
|
{
|
|
|
|
case EMIE_LMOUSE_LEFT_UP:
|
|
|
|
{
|
|
|
|
// menu might be removed if it loses focus in sendClick, so grab a reference
|
|
|
|
grab();
|
|
|
|
const u32 t = sendClick(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
|
|
|
|
if ((t==0 || t==1) && Environment->hasFocus(this))
|
|
|
|
Environment->removeFocus(this);
|
|
|
|
drop();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case EMIE_LMOUSE_PRESSED_DOWN:
|
|
|
|
return true;
|
|
|
|
case EMIE_MOUSE_MOVED:
|
|
|
|
if (Environment->hasFocus(this))
|
|
|
|
highlight(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y), true);
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return IGUIElement::OnEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Sets the visible state of this element.
|
|
|
|
void CGUIContextMenu::setVisible(bool visible)
|
|
|
|
{
|
|
|
|
HighLighted = -1;
|
|
|
|
ChangeTime = os::Timer::getTime();
|
|
|
|
for (u32 j=0; j<Items.size(); ++j)
|
|
|
|
if (Items[j].SubMenu)
|
|
|
|
Items[j].SubMenu->setVisible(false);
|
|
|
|
|
|
|
|
IGUIElement::setVisible(visible);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! sends a click Returns:
|
|
|
|
//! 0 if click went outside of the element,
|
|
|
|
//! 1 if a valid button was clicked,
|
|
|
|
//! 2 if a nonclickable element was clicked
|
|
|
|
u32 CGUIContextMenu::sendClick(const core::position2d<s32>& p)
|
|
|
|
{
|
|
|
|
u32 t = 0;
|
|
|
|
|
|
|
|
// get number of open submenu
|
|
|
|
s32 openmenu = -1;
|
|
|
|
s32 j;
|
|
|
|
for (j=0; j<(s32)Items.size(); ++j)
|
|
|
|
if (Items[j].SubMenu && Items[j].SubMenu->isVisible())
|
|
|
|
{
|
|
|
|
openmenu = j;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// delegate click operation to submenu
|
|
|
|
if (openmenu != -1)
|
|
|
|
{
|
|
|
|
t = Items[j].SubMenu->sendClick(p);
|
|
|
|
if (t != 0)
|
|
|
|
return t; // clicked something
|
|
|
|
}
|
|
|
|
|
|
|
|
// check click on myself
|
|
|
|
if (isPointInside(p) &&
|
|
|
|
(u32)HighLighted < Items.size())
|
|
|
|
{
|
|
|
|
if (!Items[HighLighted].Enabled ||
|
|
|
|
Items[HighLighted].IsSeparator ||
|
|
|
|
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 (EventParent)
|
|
|
|
EventParent->OnEvent(event);
|
|
|
|
else if (Parent)
|
|
|
|
Parent->OnEvent(event);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! returns true, if an element was highligted
|
|
|
|
bool CGUIContextMenu::highlight(const core::position2d<s32>& p, bool canOpenSubMenu)
|
|
|
|
{
|
|
|
|
if (!isEnabled())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get number of open submenu
|
|
|
|
s32 openmenu = -1;
|
|
|
|
s32 i;
|
|
|
|
for (i=0; i<(s32)Items.size(); ++i)
|
|
|
|
if (Items[i].Enabled && Items[i].SubMenu && Items[i].SubMenu->isVisible())
|
|
|
|
{
|
|
|
|
openmenu = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// delegate highlight operation to submenu
|
|
|
|
if (openmenu != -1)
|
|
|
|
{
|
|
|
|
if (Items[openmenu].Enabled && Items[openmenu].SubMenu->highlight(p, canOpenSubMenu))
|
|
|
|
{
|
|
|
|
HighLighted = openmenu;
|
|
|
|
ChangeTime = os::Timer::getTime();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// highlight myself
|
|
|
|
for (i=0; i<(s32)Items.size(); ++i)
|
|
|
|
{
|
|
|
|
if (Items[i].Enabled && getHRect(Items[i], AbsoluteRect).isPointInside(p))
|
|
|
|
{
|
|
|
|
HighLighted = i;
|
|
|
|
ChangeTime = os::Timer::getTime();
|
|
|
|
|
|
|
|
// make submenus visible/invisible
|
|
|
|
for (s32 j=0; j<(s32)Items.size(); ++j)
|
|
|
|
if (Items[j].SubMenu)
|
|
|
|
{
|
|
|
|
if ( j == i && canOpenSubMenu && Items[j].Enabled )
|
|
|
|
Items[j].SubMenu->setVisible(true);
|
|
|
|
else if ( j != i )
|
|
|
|
Items[j].SubMenu->setVisible(false);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HighLighted = openmenu;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! returns the item highlight-area
|
|
|
|
core::rect<s32> CGUIContextMenu::getHRect(const SItem& i, const core::rect<s32>& absolute) const
|
|
|
|
{
|
|
|
|
core::rect<s32> r = absolute;
|
|
|
|
r.UpperLeftCorner.Y += i.PosY;
|
|
|
|
r.LowerRightCorner.Y = r.UpperLeftCorner.Y + i.Dim.Height;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Gets drawing rect of Item
|
|
|
|
core::rect<s32> CGUIContextMenu::getRect(const SItem& i, const core::rect<s32>& absolute) const
|
|
|
|
{
|
|
|
|
core::rect<s32> r = absolute;
|
|
|
|
r.UpperLeftCorner.Y += i.PosY;
|
|
|
|
r.LowerRightCorner.Y = r.UpperLeftCorner.Y + i.Dim.Height;
|
|
|
|
r.UpperLeftCorner.X += 20;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! draws the element and its children
|
|
|
|
void CGUIContextMenu::draw()
|
|
|
|
{
|
|
|
|
if (!IsVisible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
IGUISkin* skin = Environment->getSkin();
|
|
|
|
|
|
|
|
if (!skin)
|
|
|
|
return;
|
|
|
|
|
|
|
|
IGUIFont* font = skin->getFont(EGDF_MENU);
|
|
|
|
if (font != LastFont)
|
|
|
|
{
|
|
|
|
if (LastFont)
|
|
|
|
LastFont->drop();
|
|
|
|
LastFont = font;
|
|
|
|
if (LastFont)
|
|
|
|
LastFont->grab();
|
|
|
|
|
|
|
|
recalculateSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
IGUISpriteBank* sprites = skin->getSpriteBank();
|
|
|
|
|
|
|
|
core::rect<s32> rect = AbsoluteRect;
|
|
|
|
core::rect<s32>* clip = 0;
|
|
|
|
|
|
|
|
// draw frame
|
|
|
|
skin->draw3DMenuPane(this, AbsoluteRect, clip);
|
|
|
|
|
|
|
|
// loop through all menu items
|
|
|
|
|
|
|
|
rect = AbsoluteRect;
|
|
|
|
s32 y = AbsoluteRect.UpperLeftCorner.Y;
|
|
|
|
|
|
|
|
for (s32 i=0; i<(s32)Items.size(); ++i)
|
|
|
|
{
|
|
|
|
if (Items[i].IsSeparator)
|
|
|
|
{
|
|
|
|
// draw separator
|
|
|
|
rect = AbsoluteRect;
|
|
|
|
rect.UpperLeftCorner.Y += Items[i].PosY + 3;
|
|
|
|
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1;
|
|
|
|
rect.UpperLeftCorner.X += 5;
|
|
|
|
rect.LowerRightCorner.X -= 5;
|
|
|
|
skin->draw2DRectangle(this, skin->getColor(EGDC_3D_SHADOW), rect, clip);
|
|
|
|
|
|
|
|
rect.LowerRightCorner.Y += 1;
|
|
|
|
rect.UpperLeftCorner.Y += 1;
|
|
|
|
skin->draw2DRectangle(this, skin->getColor(EGDC_3D_HIGH_LIGHT), rect, clip);
|
|
|
|
|
|
|
|
y += 10;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rect = getRect(Items[i], AbsoluteRect);
|
|
|
|
|
|
|
|
// draw highlighted
|
|
|
|
|
|
|
|
if (i == HighLighted && Items[i].Enabled)
|
|
|
|
{
|
|
|
|
core::rect<s32> r = AbsoluteRect;
|
|
|
|
r.LowerRightCorner.Y = rect.LowerRightCorner.Y;
|
|
|
|
r.UpperLeftCorner.Y = rect.UpperLeftCorner.Y;
|
|
|
|
r.LowerRightCorner.X -= 5;
|
|
|
|
r.UpperLeftCorner.X += 5;
|
|
|
|
skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), r, clip);
|
|
|
|
}
|
|
|
|
|
|
|
|
// draw text
|
|
|
|
|
|
|
|
EGUI_DEFAULT_COLOR c = EGDC_BUTTON_TEXT;
|
|
|
|
|
|
|
|
if (i == HighLighted)
|
|
|
|
c = EGDC_HIGH_LIGHT_TEXT;
|
|
|
|
|
|
|
|
if (!Items[i].Enabled)
|
|
|
|
c = EGDC_GRAY_TEXT;
|
|
|
|
|
|
|
|
if (font)
|
|
|
|
font->draw(Items[i].Text.c_str(), rect,
|
|
|
|
skin->getColor(c), false, true, clip);
|
|
|
|
|
|
|
|
// draw submenu symbol
|
|
|
|
if (Items[i].SubMenu && sprites)
|
|
|
|
{
|
|
|
|
core::rect<s32> r = rect;
|
|
|
|
r.UpperLeftCorner.X = r.LowerRightCorner.X - 15;
|
|
|
|
|
|
|
|
sprites->draw2DSprite(skin->getIcon(EGDI_CURSOR_RIGHT),
|
|
|
|
r.getCenter(), clip, skin->getColor(c),
|
|
|
|
(i == HighLighted) ? ChangeTime : 0,
|
|
|
|
(i == HighLighted) ? os::Timer::getTime() : 0,
|
|
|
|
(i == HighLighted), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// draw checked symbol
|
|
|
|
if (Items[i].Checked && sprites)
|
|
|
|
{
|
|
|
|
core::rect<s32> r = rect;
|
|
|
|
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,
|
|
|
|
(i == HighLighted) ? os::Timer::getTime() : 0,
|
|
|
|
(i == HighLighted), true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IGUIElement::draw();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CGUIContextMenu::recalculateSize()
|
|
|
|
{
|
|
|
|
IGUIFont* font = Environment->getSkin()->getFont(EGDF_MENU);
|
|
|
|
|
|
|
|
if (!font)
|
|
|
|
return;
|
|
|
|
|
|
|
|
core::rect<s32> rect;
|
|
|
|
rect.UpperLeftCorner = RelativeRect.UpperLeftCorner;
|
|
|
|
u32 width = 100;
|
|
|
|
u32 height = 3;
|
|
|
|
|
|
|
|
u32 i;
|
|
|
|
for (i=0; i<Items.size(); ++i)
|
|
|
|
{
|
|
|
|
if (Items[i].IsSeparator)
|
|
|
|
{
|
|
|
|
Items[i].Dim.Width = 100;
|
|
|
|
Items[i].Dim.Height = 10;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Items[i].Dim = font->getDimension(Items[i].Text.c_str());
|
|
|
|
Items[i].Dim.Width += 40;
|
|
|
|
|
|
|
|
if (Items[i].Dim.Width > width)
|
|
|
|
width = Items[i].Dim.Width;
|
|
|
|
}
|
|
|
|
|
|
|
|
Items[i].PosY = height;
|
|
|
|
height += Items[i].Dim.Height;
|
|
|
|
}
|
|
|
|
|
|
|
|
height += 5;
|
|
|
|
|
|
|
|
if (height < 10)
|
|
|
|
height = 10;
|
|
|
|
|
|
|
|
rect.LowerRightCorner.X = RelativeRect.UpperLeftCorner.X + width;
|
|
|
|
rect.LowerRightCorner.Y = RelativeRect.UpperLeftCorner.Y + height;
|
|
|
|
|
|
|
|
setRelativePosition(rect);
|
|
|
|
|
|
|
|
// recalculate submenus
|
|
|
|
for (i=0; i<Items.size(); ++i)
|
|
|
|
{
|
|
|
|
if (Items[i].SubMenu)
|
|
|
|
{
|
|
|
|
// move submenu
|
|
|
|
const s32 w = Items[i].SubMenu->getAbsolutePosition().getWidth();
|
|
|
|
const s32 h = Items[i].SubMenu->getAbsolutePosition().getHeight();
|
|
|
|
|
|
|
|
core::rect<s32> subRect(width-5, Items[i].PosY, width+w-5, Items[i].PosY+h);
|
|
|
|
|
|
|
|
gui::IGUIElement * root = Environment->getRootGUIElement();
|
|
|
|
if ( root )
|
|
|
|
{
|
|
|
|
core::rect<s32> rectRoot( root->getAbsolutePosition() );
|
|
|
|
|
|
|
|
// if it would be drawn beyond the right border, then add it to the left side
|
|
|
|
if ( getAbsolutePosition().UpperLeftCorner.X+subRect.LowerRightCorner.X > rectRoot.LowerRightCorner.X )
|
|
|
|
{
|
|
|
|
subRect.UpperLeftCorner.X = -w;
|
|
|
|
subRect.LowerRightCorner.X = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if it would be drawn below bottom border, move it up, but not further than to top.
|
|
|
|
irr::s32 belowBottom = getAbsolutePosition().UpperLeftCorner.Y+subRect.LowerRightCorner.Y - rectRoot.LowerRightCorner.Y;
|
|
|
|
if ( belowBottom > 0 )
|
|
|
|
{
|
|
|
|
irr::s32 belowTop = getAbsolutePosition().UpperLeftCorner.Y+subRect.UpperLeftCorner.Y;
|
|
|
|
irr::s32 moveUp = belowBottom < belowTop ? belowBottom : belowTop;
|
|
|
|
subRect.UpperLeftCorner.Y -= moveUp;
|
|
|
|
subRect.LowerRightCorner.Y -= moveUp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Items[i].SubMenu->setRelativePosition(subRect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Returns the selected item in the menu
|
|
|
|
s32 CGUIContextMenu::getSelectedItem() const
|
|
|
|
{
|
|
|
|
return HighLighted;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! \return Returns a pointer to the submenu of an item.
|
|
|
|
IGUIContextMenu* CGUIContextMenu::getSubMenu(u32 idx) const
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return Items[idx].SubMenu;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Returns command id of a menu item
|
|
|
|
s32 CGUIContextMenu::getItemCommandId(u32 idx) const
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return Items[idx].CommandId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Sets the command id of a menu item
|
|
|
|
void CGUIContextMenu::setItemCommandId(u32 idx, s32 id)
|
|
|
|
{
|
|
|
|
if (idx >= Items.size())
|
|
|
|
return;
|
|
|
|
|
|
|
|
Items[idx].CommandId = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Writes attributes of the element.
|
|
|
|
void CGUIContextMenu::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
|
|
|
|
{
|
|
|
|
IGUIElement::serializeAttributes(out,options);
|
|
|
|
out->addPosition2d("Position", Pos);
|
|
|
|
|
|
|
|
if (Parent->getType() == EGUIET_CONTEXT_MENU || Parent->getType() == EGUIET_MENU )
|
|
|
|
{
|
|
|
|
const IGUIContextMenu* const ptr = (const IGUIContextMenu*)Parent;
|
|
|
|
// find the position of this item in its parent's list
|
|
|
|
u32 i;
|
|
|
|
// VC6 needs the cast for this
|
|
|
|
for (i=0; (i<ptr->getItemCount()) && (ptr->getSubMenu(i) != (const IGUIContextMenu*)this); ++i)
|
|
|
|
; // do nothing
|
|
|
|
|
|
|
|
out->addInt("ParentItem", i);
|
|
|
|
}
|
|
|
|
|
|
|
|
out->addInt("CloseHandling", (s32)CloseHandling);
|
|
|
|
|
|
|
|
// write out the item list
|
|
|
|
out->addInt("ItemCount", Items.size());
|
|
|
|
|
|
|
|
core::stringc tmp;
|
|
|
|
|
|
|
|
for (u32 i=0; i < Items.size(); ++i)
|
|
|
|
{
|
|
|
|
tmp = "IsSeparator"; tmp += i;
|
|
|
|
out->addBool(tmp.c_str(), Items[i].IsSeparator);
|
|
|
|
|
|
|
|
if (!Items[i].IsSeparator)
|
|
|
|
{
|
|
|
|
tmp = "Text"; tmp += i;
|
|
|
|
out->addString(tmp.c_str(), Items[i].Text.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);
|
|
|
|
tmp = "Checked"; tmp += i;
|
|
|
|
out->addBool(tmp.c_str(), Items[i].Checked);
|
|
|
|
tmp = "AutoChecking"; tmp += i;
|
|
|
|
out->addBool(tmp.c_str(), Items[i].AutoChecking);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Reads attributes of the element
|
|
|
|
void CGUIContextMenu::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
|
|
|
|
{
|
|
|
|
IGUIElement::deserializeAttributes(in,options);
|
|
|
|
|
|
|
|
Pos = in->getAttributeAsPosition2d("Position");
|
|
|
|
|
|
|
|
// link to this item's parent
|
|
|
|
if (Parent && ( Parent->getType() == EGUIET_CONTEXT_MENU || Parent->getType() == EGUIET_MENU ) )
|
|
|
|
((CGUIContextMenu*)Parent)->setSubMenu(in->getAttributeAsInt("ParentItem"),this);
|
|
|
|
|
|
|
|
CloseHandling = (ECONTEXT_MENU_CLOSE)in->getAttributeAsInt("CloseHandling");
|
|
|
|
|
|
|
|
removeAllItems();
|
|
|
|
|
|
|
|
// read the item list
|
|
|
|
const s32 count = in->getAttributeAsInt("ItemCount");
|
|
|
|
|
|
|
|
for (s32 i=0; i<count; ++i)
|
|
|
|
{
|
|
|
|
core::stringc tmp;
|
|
|
|
core::stringw txt;
|
|
|
|
s32 commandid=-1;
|
|
|
|
bool enabled=true;
|
|
|
|
bool checked=false;
|
|
|
|
bool autochecking=false;
|
|
|
|
|
|
|
|
tmp = "IsSeparator"; tmp += i;
|
|
|
|
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());
|
|
|
|
|
|
|
|
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;
|
|
|
|
if ( in->existsAttribute(tmp.c_str()) )
|
|
|
|
checked = in->getAttributeAsBool(tmp.c_str());
|
|
|
|
|
|
|
|
tmp = "AutoChecking"; tmp += i;
|
|
|
|
if ( in->existsAttribute(tmp.c_str()) )
|
|
|
|
autochecking = in->getAttributeAsBool(tmp.c_str());
|
|
|
|
|
|
|
|
addItem(core::stringw(txt.c_str()).c_str(), commandid, enabled, false, checked, autochecking);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
recalculateSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// because sometimes the element has no parent at click time
|
|
|
|
void CGUIContextMenu::setEventParent(IGUIElement *parent)
|
|
|
|
{
|
|
|
|
EventParent = parent;
|
|
|
|
|
|
|
|
for (u32 i=0; i<Items.size(); ++i)
|
|
|
|
if (Items[i].SubMenu)
|
|
|
|
Items[i].SubMenu->setEventParent(parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CGUIContextMenu::hasOpenSubMenu() const
|
|
|
|
{
|
|
|
|
for (u32 i=0; i<Items.size(); ++i)
|
|
|
|
if (Items[i].SubMenu && Items[i].SubMenu->isVisible())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CGUIContextMenu::closeAllSubMenus()
|
|
|
|
{
|
|
|
|
for (u32 i=0; i<Items.size(); ++i)
|
|
|
|
if (Items[i].SubMenu)
|
|
|
|
Items[i].SubMenu->setVisible(false);
|
|
|
|
|
|
|
|
//HighLighted = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // end namespace
|
|
|
|
} // end namespace
|
|
|
|
|
|
|
|
#endif // _IRR_COMPILE_WITH_GUI_
|
|
|
|
|